unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#51658: [PATCH] Haiku port (again)
       [not found] <87ee7surtv.fsf.ref@yahoo.com>
@ 2021-11-07 11:29 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-11-09 17:58   ` Eli Zaretskii
  0 siblings, 1 reply; 31+ messages in thread
From: Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2021-11-07 11:29 UTC (permalink / raw)
  To: 51658

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


OK, after using this port for more than a month now I can say it's
finally stable enough for heavy use.

I think all the major bugs have been ironed out now, so I'm submitting
it for inclusion.

I couldn't figure out how to turn multiple (hundereds) of git commits
into a single patch formatted like `git format-patch' would, so I
attached the output of `git diff' instead.

Please take some time to review the code and possibly install it.
Thanks!

Here is a commit message I wrote that should be appropriate:

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, HAVE_BE_FREETYPE, 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/ftbefont.c: New file.
* src/ftcrfont.c (RED_FROM_ULONG, GREEN_FROM_ULONG)
(BLUE_FROM_ULONG): New macros.
(ftcrfont_draw): Add haiku specific code for BE_CAIRO builds.

* 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 fields for BE_CAIRO
builds, and add face and shape cache fields for ftbefont.

* src/haiku_draw_support.cc:
* src/haiku_font_support.cc:
* src/haiku_freetype.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.


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

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..1386cbb765 100644
--- a/configure.ac
+++ b/configure.ac
@@ -510,6 +510,15 @@ AC_DEFUN
 OPTION_DEFAULT_OFF([xwidgets],
   [enable use of xwidgets in Emacs buffers (requires gtk3 or macOS Cocoa)])
 
+OPTION_DEFAULT_OFF([be-app],
+  [enable use of Haiku's Application Kit as a window system])
+
+OPTION_DEFAULT_OFF([be-freetype],
+  [enable use of FreeType under Haiku's Application Kit])
+
+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 +795,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 +920,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 +1589,8 @@ AC_DEFUN
 
   ## Motif needs -lgen.
   unixware) LIBS_SYSTEM="-lsocket -lnsl -lelf -lgen" ;;
+
+  haiku) LIBS_SYSTEM="-lnetwork" ;;
 esac
 
 AC_SUBST(LIBS_SYSTEM)
@@ -2079,6 +2096,26 @@ AC_DEFUN
    fi
 fi
 
+HAVE_BE_APP=no
+if test "${opsys}" = "haiku" && test "${with_be_app}" = "yes"; then
+   dnl Only GCC and Clang work for now
+   AC_PROG_CXX([gcc cl CC cxx cc++ c++ g++ clang clang++])
+   CXXFLAGS="$CXXFLAGS $emacs_g3_CFLAGS"
+   AC_LANG_PUSH([C++])
+   AC_CHECK_HEADER([app/Application.h], [HAVE_BE_APP=yes],
+                   [AC_MSG_ERROR([The Application Kit headers required for building
+with the Application Kit were not found or cannot be compiled. Either fix this, or
+re-configure with the option '--without-be-app'.])])
+   AC_LANG_POP([C++])
+fi
+
+AC_SUBST(HAVE_BE_APP)
+
+HAVE_BE_FREETYPE=no
+if test "${HAVE_BE_APP}" = "yes" && test "${with_be_freetype}" = "yes"; then
+   HAVE_BE_FREETYPE=yes
+fi
+
 HAVE_W32=no
 W32_OBJ=
 W32_LIBS=
@@ -2200,6 +2237,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 +2305,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 +2639,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 +2664,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 +2684,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 +3335,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 +3427,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 +3592,76 @@ AC_DEFUN
   fi
 fi
 
+### Start of font-backend (under Haiku) selectionn.
+if test "${HAVE_BE_APP}" = "yes"; then
+  if test $HAVE_BE_FREETYPE = "yes"; then
+    EMACS_CHECK_MODULES([FREETYPE], [freetype2 >= 2.5.0])
+    test "$HAVE_FREETYPE" = "no" && AC_MSG_ERROR(freetype on Haiku requires libfreetype)
+    EMACS_CHECK_MODULES([FONTCONFIG], [fontconfig >= 2.2.0])
+    test "$HAVE_FONTCONFIG" = "no" && AC_MSG_ERROR(freetype on Haiku requires libfontconfig)
+    HAVE_XFT=no
+    AC_DEFINE(HAVE_BE_FREETYPE, 1, [Define to 1 if using the FreeType font backend on Haiku.])
+  fi
+  if test $HAVE_CAIRO = "yes" && test $HAVE_BE_FREETYPE != "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 +3783,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 +4101,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 +4176,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 +4205,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 +4623,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 +4966,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 +5221,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 +5317,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 +5546,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 +5607,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 +5735,21 @@ 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_BE_FREETYPE}" = "yes"; then
+    FONT_OBJ="$FONT_OBJ ftbefont.o"
+    HAIKU_CXX_OBJ="$HAIKU_CXX_OBJ haiku_freetype.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 +6138,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..b9bf6540c8
--- /dev/null
+++ b/doc/emacs/haiku.texi
@@ -0,0 +1,224 @@
+@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 ftbe font backend
+
+  The @code{ftbe} font backend is available whenever Emacs is built
+with Be FreeType support.  It is rather slow, but does not cause
+artifacting when double-buffering is disabled.  It also does not
+support displaying color emoji.
+
+@cindex ftcr font backend
+
+  The @code{ftcr} font backend is available whenever Emacs is built
+with Be Cairo support.  Of all three backends, 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 the former may change if Haiku gains support for it at some
+later time, independent of Emacs).
+
+  This font backend has a known problem with displaying certain bold
+or italic fonts.  It is documented in @file{etc/PROBLEMS}, see
+@ref{Known Problems}.
+
+@cindex HarfBuzz support (Haiku)
+
+  Both the @code{ftcr} and @code{ftbe} backends feature HarfBuzz
+variants when Emacs is built with HarfBuzz support.  They are named
+@code{ftcrhb} and @code{ftbehb} respectively.
diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi
index b6bd14f887..ed2550ec2a 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},
@@ -8186,6 +8187,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
@@ -8232,6 +8235,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
@@ -8240,6 +8244,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 a056513002..10530d983d 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,17 @@ 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 five font drivers, three of which are available
+without HarfBuzz: @code{haiku} (the server-side font driver, always
+available), @code{ftbe} (a font-driver using FreeType, only available
+if Emacs was built with FreeType), and @code{ftcr} (a font-driver
+using Cairo, only available if Emacs was built with Cairo).  For each
+of these drivers except @code{haiku}, there also exists a variant that
+shapes text using HarfBuzz; for @code{ftcr}, the corresponding driver
+is @code{ftcrhb}, and for @code{ftbe}, the corresponding driver is
+@code{ftbehb}.
+
+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 +3162,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 +3185,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 +3297,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.
+
 \f
 * Obsolete platforms
 
diff --git a/etc/NEWS b/etc/NEWS
index 4c1513a73e..bdac75c7a6 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -24,6 +24,32 @@ applies, and please also update docstrings as needed.
 \f
 * 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.
+
+** FreeType and HarfBuzz support has been ported to the Haiku operating system.
+To enable FreeType support, ensure that the FreeType development
+headers are present on your system, and configure Emacs with
+'--with-be-freetype'.  If Emacs is not configured for FreeType, the
+default font driver that will be enabled which will not have HarfBuzz
+support.
+
++++
+** 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 b069ccee56..14f0e3ff16 100644
--- a/etc/PROBLEMS
+++ b/etc/PROBLEMS
@@ -1017,6 +1017,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 7a6bfa58ad..6560973a43 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 61c7801b10..21f81cdbc9 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 e82eb4fa9e..9d71ac07a3 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..cc889d9b8f 100644
--- a/src/font.c
+++ b/src/font.c
@@ -5700,6 +5700,9 @@ syms_of_font (void)
 #endif  /* HAVE_XFT */
 #endif  /* not USE_CAIRO */
 #endif	/* HAVE_X_WINDOWS */
+#ifdef HAVE_BE_FREETYPE
+  syms_of_ftbefont ();
+#endif
 #else	/* not HAVE_FREETYPE */
 #ifdef HAVE_X_WINDOWS
   syms_of_xfont ();
@@ -5711,6 +5714,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..a5b470e237 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,9 @@ #define FONT_DEFERRED_LOG(ACTION, ARG, RESULT)		\
 INLINE bool
 font_data_structures_may_be_ill_formed (void)
 {
-#ifdef USE_CAIRO
+  /* ftbefont experiences this too... odd.  */
+#if defined USE_CAIRO || defined USE_BE_CAIRO || \
+  defined HAVE_BE_FREETYPE
   /* 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/ftbefont.c b/src/ftbefont.c
new file mode 100644
index 0000000000..80f7a3a34c
--- /dev/null
+++ b/src/ftbefont.c
@@ -0,0 +1,250 @@
+/* FreeType font driver for Haiku
+   Copyright (C) 2021 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+#include "blockinput.h"
+#include "haiku_support.h"
+#include "lisp.h"
+#include "dispextern.h"
+#include "font.h"
+#include "pdumper.h"
+#include "frame.h"
+#include "haikuterm.h"
+#include "termchar.h"
+#include "math.h"
+#include "ftfont.h"
+
+#ifdef HAVE_HARFBUZZ
+#include <hb.h>
+#include <hb-ft.h>
+#endif
+
+static Lisp_Object
+ftbefont_list (struct frame *f, Lisp_Object spec)
+{
+  return ftfont_list2 (f, spec, Qftbe);
+}
+
+static Lisp_Object
+ftbefont_match (struct frame *f, Lisp_Object spec)
+{
+  return ftfont_match2 (f, spec, Qftbe);
+}
+
+#ifdef HAVE_HARFBUZZ
+
+static Lisp_Object
+ftbehbfont_list (struct frame *f, Lisp_Object spec)
+{
+  return ftfont_list2 (f, spec, Qftbehb);
+}
+
+static Lisp_Object
+ftbehbfont_match (struct frame *f, Lisp_Object spec)
+{
+  return ftfont_match2 (f, spec, Qftbehb);
+}
+
+#endif
+
+static int
+ftbefont_draw (struct glyph_string *s,
+               int from, int to, int x, int y, bool with_background)
+{
+  struct frame *f = s->f;
+  struct face *face = s->face;
+  struct font_info *info = (struct font_info *) s->font;
+  void *view = FRAME_HAIKU_VIEW (f);
+
+  block_input ();
+  prepare_face_for_display (s->f, face);
+
+  if (info->ft_size != info->face->size)
+    FT_Activate_Size (info->ft_size);
+
+  BView_draw_lock (view);
+  BView_StartClip (view);
+
+  if (with_background)
+    {
+      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_FT_render_glyphs (info->face, &s->char2b[from], to - from,
+			    FRAME_OUTPUT_DATA (s->f)->cursor_fg, x, y, view,
+			    info->sc, FT_LOAD_DEFAULT);
+  else
+    BView_FT_render_glyphs (info->face, &s->char2b[from], to - from,
+			    face->foreground, x, y, view, info->sc,
+			    FT_LOAD_DEFAULT);
+
+  BView_EndClip (view);
+  BView_draw_unlock (view);
+  unblock_input ();
+  return 0;
+}
+
+static Lisp_Object
+ftbefont_open (struct frame *f, Lisp_Object entity, int pixel_size)
+{
+  Lisp_Object ft = ftfont_open (f, entity, pixel_size);
+  if (FONT_OBJECT_P (ft))
+    {
+      struct font_info *info = (struct font_info *) XFONT_OBJECT (ft);
+      ASET (ft, FONT_TYPE_INDEX, Qftbe);
+      info->font.driver = &ftbefont_driver;
+      info->sc = be_make_ft_shape_cache ();
+    }
+  return ft;
+}
+
+#ifdef HAVE_HARFBUZZ
+static hb_font_t *
+ftbehbfont_begin_hb_font (struct font *font, double *position_unit)
+{
+  struct font_info *ftfont_info = (struct font_info *) font;
+  FT_Face ft_face = ftfont_info->face;
+
+  ftfont_info->ft_size = ft_face->size;
+  return fthbfont_begin_hb_font (font, position_unit);
+}
+
+static void
+ftbehbfont_end_hb_font (struct font *font, hb_font_t *hb_font)
+{
+  struct font_info *ftfont_info = (struct font_info *) font;
+  ftfont_info->ft_size = NULL;
+}
+
+static Lisp_Object
+ftbehbfont_open (struct frame *f, Lisp_Object entity, int pixel_size)
+{
+  Lisp_Object ft = ftfont_open (f, entity, pixel_size);
+  if (FONT_OBJECT_P (ft))
+    {
+      struct font_info *info = (struct font_info *) XFONT_OBJECT (ft);
+      ASET (ft, FONT_TYPE_INDEX, Qftbehb);
+      info->font.driver = &ftbehbfont_driver;
+      info->sc = be_make_ft_shape_cache ();
+    }
+  return ft;
+}
+#endif
+
+static void
+ftbefont_close (struct font *font)
+{
+  if (font_data_structures_may_be_ill_formed ())
+    return;
+  be_free_ft_shape_cache (((struct font_info *) font)->sc);
+  ftfont_close (font);
+}
+
+struct font_driver const ftbefont_driver =
+  {
+    .type = LISPSYM_INITIALLY (Qftbe),
+    .get_cache = ftfont_get_cache,
+    .list = ftbefont_list,
+    .match = ftbefont_match,
+    .draw = ftbefont_draw,
+    .list_family = ftfont_list_family,
+    .open_font = ftbefont_open,
+    .close_font = ftbefont_close,
+    .has_char = ftfont_has_char,
+    .encode_char = ftfont_encode_char,
+    .text_extents = ftfont_text_extents,
+    .get_bitmap = ftfont_get_bitmap,
+    .anchor_point = ftfont_anchor_point,
+#ifdef HAVE_LIBOTF
+    .otf_capability = ftfont_otf_capability,
+#endif
+#if defined HAVE_M17N_FLT && defined HAVE_LIBOTF
+    .shape = ftfont_shape,
+#endif
+#if defined HAVE_OTF_GET_VARIATION_GLYPHS || defined HAVE_FT_FACE_GETCHARVARIANTINDEX
+    .get_variation_glyphs = ftfont_variation_glyphs,
+#endif
+    .filter_properties = ftfont_filter_properties,
+    .combining_capability = ftfont_combining_capability
+  };
+
+#ifdef HAVE_HARFBUZZ
+struct font_driver ftbehbfont_driver;
+#endif
+
+static void
+syms_of_ftbefont_for_pdumper (void)
+{
+  register_font_driver (&ftbefont_driver, NULL);
+#ifdef HAVE_HARFBUZZ
+  ftbehbfont_driver = ftbefont_driver;
+  ftbehbfont_driver.type = Qftbehb;
+  ftbehbfont_driver.list = ftbehbfont_list;
+  ftbehbfont_driver.match = ftbehbfont_match;
+  ftbehbfont_driver.otf_capability = hbfont_otf_capability;
+  ftbehbfont_driver.shape = hbfont_shape;
+  ftbehbfont_driver.open_font = ftbehbfont_open;
+  ftbehbfont_driver.combining_capability = hbfont_combining_capability;
+  ftbehbfont_driver.begin_hb_font = ftbehbfont_begin_hb_font;
+  ftbehbfont_driver.end_hb_font = ftbehbfont_end_hb_font;
+
+  register_font_driver (&ftbehbfont_driver, NULL);
+#endif
+}
+
+void
+syms_of_ftbefont (void)
+{
+  DEFSYM (Qftbe, "ftbe");
+#ifdef HAVE_HARFBUZZ
+  DEFSYM (Qftbehb, "ftbehb");
+  Fput (Qftbe, Qfont_driver_superseded_by, Qftbehb);
+#endif
+  pdumper_do_now_and_after_load (syms_of_ftbefont_for_pdumper);
+}
diff --git a/src/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..ea2358ed0f 100644
--- a/src/ftfont.c
+++ b/src/ftfont.c
@@ -1336,6 +1336,9 @@ ftfont_open (struct frame *f, Lisp_Object entity, int pixel_size)
   ftfont_info = (struct font_info *) font;
   ftfont_info->ft_size = ft_face->size;
   ftfont_info->index = XFIXNUM (idx);
+#ifdef HAVE_HAIKU
+  ftfont_info->face = ft_face;
+#endif
 #ifdef HAVE_LIBOTF
   ftfont_info->maybe_otf = (ft_face->face_flags & FT_FACE_FLAG_SFNT) != 0;
   ftfont_info->otf = NULL;
@@ -1511,6 +1514,10 @@ ftfont_has_char (Lisp_Object font, int c)
 ftfont_encode_char (struct font *font, int c)
 {
   struct font_info *ftfont_info = (struct font_info *) font;
+#if defined (HAVE_HAIKU)
+  if (!ftfont_info->ft_size)
+    ftfont_info->ft_size = ftfont_info->face->size;
+#endif
   FT_Face ft_face = ftfont_info->ft_size->face;
   FT_ULong charcode = c;
   FT_UInt code = FT_Get_Char_Index (ft_face, charcode);
@@ -1541,6 +1548,10 @@ ftfont_text_extents (struct font *font, const unsigned int *code,
 		     int nglyphs, struct font_metrics *metrics)
 {
   struct font_info *ftfont_info = (struct font_info *) font;
+#if defined (HAVE_HAIKU)
+  if (!ftfont_info->ft_size)
+    ftfont_info->ft_size = ftfont_info->face->size;
+#endif
   FT_Face ft_face = ftfont_info->ft_size->face;
   int i, width = 0;
   bool first;
@@ -3110,6 +3121,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..e92d9fa61b 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,6 +75,10 @@ #define EMACS_FTFONT_H
   /* Font metrics cache.  */
   struct font_metrics **metrics;
   short metrics_nrows;
+#endif
+#ifdef HAVE_HAIKU
+  FT_Face face;
+  void *sc;
 #else
   /* These are used by the XFT backend.  */
   Display *display;
diff --git a/src/haiku_draw_support.cc b/src/haiku_draw_support.cc
new file mode 100644
index 0000000000..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_freetype.cc b/src/haiku_freetype.cc
new file mode 100644
index 0000000000..ab90c46cab
--- /dev/null
+++ b/src/haiku_freetype.cc
@@ -0,0 +1,250 @@
+/* FreeType font driver for Haiku
+   Copyright (C) 2021 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+#include "haiku_support.h"
+
+#include <interface/View.h>
+#include <interface/Bitmap.h>
+#include <interface/Point.h>
+#include <interface/Region.h>
+#include <interface/Shape.h>
+
+#include <alloca.h>
+#include <cstddef>
+#include <cmath>
+
+#include <algorithm>
+#include <unordered_map>
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_SIZES_H
+#include FT_BITMAP_H
+#include FT_IMAGE_H
+#include FT_OUTLINE_H
+
+#define FLOAT_FROM_26_6(t) ((float) (t) / 64.0f)
+#define DOUBLE_TO_16_16(d) ((FT_Fixed) ((d) * 65536.0))
+
+#define VECTOR_POINT(v) (BPoint (FLOAT_FROM_26_6 ((v)->x),	\
+				 FLOAT_FROM_26_6 ((v)->y)))
+
+static int tx;
+static int mty;
+
+struct ftbe_shape_cache
+{
+  std::unordered_map <unsigned int, BShape *> shapes;
+};
+
+static int
+move_to (FT_Vector *to, void *closure)
+{
+  BShape *shape = (BShape *) closure;
+  return shape->MoveTo (VECTOR_POINT (to)) != B_OK;
+}
+
+static int
+line_to (FT_Vector *to, void *closure)
+{
+  BShape *shape = (BShape *) closure;
+  return shape->LineTo (VECTOR_POINT (to)) != B_OK;
+}
+
+static int
+conic_to (FT_Vector *control, FT_Vector *to, void *closure)
+{
+  BShape *shape = (BShape *) closure;
+  BPoint pt_0 = shape->CurrentPosition ();
+
+  return shape->BezierTo
+    (BPoint (pt_0.x + 2.0f / 3.0f * (FLOAT_FROM_26_6 (control->x) - pt_0.x),
+	     pt_0.y + 2.0f / 3.0f * (FLOAT_FROM_26_6 (control->y) - pt_0.y)),
+     BPoint (FLOAT_FROM_26_6 (to->x) + 2.0f / 3.0f * (FLOAT_FROM_26_6 (control->x) -
+						      FLOAT_FROM_26_6 (to->x)),
+	     FLOAT_FROM_26_6 (to->y) + 2.0f / 3.0f * (FLOAT_FROM_26_6 (control->y) -
+						      FLOAT_FROM_26_6 (to->y))),
+     VECTOR_POINT (to)) != B_OK;
+}
+
+static int
+cubic_to (FT_Vector *control1, FT_Vector *control2,
+	  FT_Vector *to, void *closure)
+{
+  BShape *shape = (BShape *) closure;
+  return shape->BezierTo (VECTOR_POINT (control1),
+			  VECTOR_POINT (control2),
+			  VECTOR_POINT (to)) != B_OK;
+}
+
+static int
+digest_outline (FT_Outline *outline, FT_Face face, BShape *shape)
+{
+  static const FT_Matrix invert_y =
+    {
+      DOUBLE_TO_16_16 (1.0), 0,
+      0, DOUBLE_TO_16_16 (-1.0),
+    };
+  static const FT_Outline_Funcs outline_funcs =
+    {
+      (FT_Outline_MoveToFunc) move_to,
+      (FT_Outline_LineToFunc) line_to,
+      (FT_Outline_ConicToFunc) conic_to,
+      (FT_Outline_CubicToFunc) cubic_to,
+      0, 0
+    };
+
+  FT_Outline_Translate (outline, tx * 64.0, -mty * 64.0);
+  FT_Outline_Transform (outline, &invert_y);
+
+  if (FT_Outline_Decompose (outline, &outline_funcs, shape) != FT_Err_Ok)
+    return 1;
+
+  if (shape->Close () != B_OK)
+    return 1;
+
+  return 0;
+}
+
+static void
+digest_color_bitmap_bgra (FT_Bitmap *bitmap, BBitmap *be_bitmap,
+			  BRegion *cr)
+{
+  if (be_bitmap->ColorSpace () != B_RGBA32)
+    abort ();
+
+  uint8_t *_src = bitmap->buffer;
+  uint8_t *_dst = (uint8_t *) be_bitmap->Bits ();
+  for (int y = 0; y < bitmap->rows; ++y)
+    {
+      uint8_t *src = _src + bitmap->pitch * y;
+      uint8_t *dst = _dst + be_bitmap->BytesPerRow () * y;
+      for (int x = 0; x < bitmap->width; ++x)
+        {
+	  uint8_t b = *src++, g = *src++, r = *src++, a = *src++;
+          *dst++ = r;
+          *dst++ = g;
+          *dst++ = b;
+          *dst++ = cr->Contains (BPoint (x, y)) ? a : 0;
+        }
+    }
+}
+
+extern "C"
+{
+  extern void emacs_abort (void);
+}
+
+extern void
+be_free_ft_shape_cache (void *c)
+{
+  for (const std::pair <unsigned int, BShape *> &pair :
+	 ((struct ftbe_shape_cache *) c)->shapes)
+    delete pair.second;
+  delete (struct ftbe_shape_cache *) c;
+}
+
+extern void *
+be_make_ft_shape_cache (void)
+{
+  return new struct ftbe_shape_cache;
+}
+
+extern void
+BView_FT_render_glyphs (FT_Face face, unsigned int *codes,
+			int len, uint32_t color, int x, int y,
+			void *view, void *ft_shape_cache, int flags)
+{
+  FT_Error error = FT_Err_Ok;
+  BView *vw = (BView *) find_appropriate_view_for_draw (view);
+  struct ftbe_shape_cache *c = (struct ftbe_shape_cache *) ft_shape_cache;
+  BShape gshape;
+  gshape.Clear ();
+
+  tx = 0, mty = 0;
+
+  if (len >= 25)
+    {
+      vw->MovePenTo (x, y);
+      x = 0, y = 0;
+    }
+
+  BView_SetHighColor (view, color);
+
+  for (int i = 0; i < len; ++i)
+    {
+      error = FT_Load_Glyph (face, codes[i], flags);
+
+      if (error != FT_Err_Ok)
+	continue;
+
+      if (face->glyph->format == FT_GLYPH_FORMAT_OUTLINE)
+	{
+	  BShape *shape;
+	  if (len < 25)
+	    shape = c->shapes[codes[i]];
+	  if (len >= 25)
+	    tx = x, mty = y;
+	  if (!shape || len >= 25)
+	    {
+	      if (len < 25)
+		{
+		  shape = new BShape ();
+		  shape->Clear ();
+		}
+	      digest_outline (&face->glyph->outline, face,
+			      len >= 25 ? &gshape : shape);
+	      if (len < 25)
+		c->shapes[codes[i]] = shape;
+	    }
+	  if (len < 25)
+	    {
+	      vw->MovePenTo (x, y);
+	      vw->FillShape (shape);
+	    }
+	}
+      else
+	{
+	  error = FT_Render_Glyph (face->glyph, FT_RENDER_MODE_NORMAL);
+	  if (error != FT_Err_Ok)
+	    continue;
+
+	  if (face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA)
+	    {
+	      BBitmap bm (BRect (0, 0, face->glyph->bitmap.width,
+				 face->glyph->bitmap.rows),
+			  B_RGBA32);
+	      BRegion cr;
+	      vw->GetClippingRegion (&cr);
+
+	      digest_color_bitmap_bgra (&face->glyph->bitmap, &bm, &cr);
+	      vw->SetDrawingMode (B_OP_OVER);
+	      vw->MovePenTo (x + face->glyph->bitmap_left,
+			     y - face->glyph->bitmap_top);
+	      vw->DrawBitmap (&bm);
+	    }
+	}
+      x += face->glyph->advance.x >> 6;
+      y += face->glyph->advance.y >> 6;
+    }
+
+  if (len >= 25)
+    vw->FillShape (&gshape);
+}
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..3da0c965a8
--- /dev/null
+++ b/src/haikufns.c
@@ -0,0 +1,2619 @@
+/* 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 HAVE_BE_FREETYPE
+  register_font_driver (&ftbefont_driver, f);
+#ifdef HAVE_HARFBUZZ
+  register_font_driver (&ftbehbfont_driver, f);
+#endif
+#endif
+#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 ();
+}
+
+\f
+
+DEFUN ("haiku-set-mouse-absolute-pixel-position",
+       Fhaiku_set_mouse_absolute_pixel_position,
+       Shaiku_set_mouse_absolute_pixel_position, 2, 2, 0,
+       doc: /* Move mouse pointer to a pixel position at (X, Y).  The
+coordinates X and Y are interpreted to start from the top-left
+corner of the screen.  */)
+  (Lisp_Object x, Lisp_Object y)
+{
+  int xval = check_integer_range (x, INT_MIN, INT_MAX);
+  int yval = check_integer_range (y, INT_MIN, INT_MAX);
+
+  if (!x_display_list)
+    error ("Window system not initialized");
+
+  block_input ();
+  be_warp_pointer (xval, yval);
+  unblock_input ();
+  return Qnil;
+}
+
+DEFUN ("haiku-mouse-absolute-pixel-position", Fhaiku_mouse_absolute_pixel_position,
+       Shaiku_mouse_absolute_pixel_position, 0, 0, 0,
+       doc: /* Return absolute position of mouse cursor in pixels.
+The position is returned as a cons cell (X . Y) of the coordinates of
+the mouse cursor position in pixels relative to a position (0, 0) of the
+selected frame's display.  */)
+  (void)
+{
+  if (!x_display_list)
+    return Qnil;
+
+  struct frame *f = SELECTED_FRAME ();
+
+  if (FRAME_INITIAL_P (f) || !FRAME_HAIKU_P (f)
+      || !FRAME_HAIKU_VIEW (f))
+    return Qnil;
+
+  block_input ();
+  void *view = FRAME_HAIKU_VIEW (f);
+
+  int x, y;
+  BView_get_mouse (view, &x, &y);
+  BView_convert_to_screen (view, &x, &y);
+  unblock_input ();
+
+  return Fcons (make_fixnum (x), make_fixnum (y));
+}
+
+DEFUN ("xw-display-color-p", Fxw_display_color_p, Sxw_display_color_p, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+     (Lisp_Object terminal)
+{
+  return Qt;
+}
+
+DEFUN ("xw-color-defined-p", Fxw_color_defined_p, Sxw_color_defined_p, 1, 2, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object color, Lisp_Object frame)
+{
+  Emacs_Color col;
+  CHECK_STRING (color);
+  decode_window_system_frame (frame);
+
+  return haiku_get_color (SSDATA (color), &col) ? Qnil : Qt;
+}
+
+DEFUN ("xw-color-values", Fxw_color_values, Sxw_color_values, 1, 2, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+     (Lisp_Object color, Lisp_Object frame)
+{
+  Emacs_Color col;
+  CHECK_STRING (color);
+  decode_window_system_frame (frame);
+
+  block_input ();
+  if (haiku_get_color (SSDATA (color), &col))
+    {
+      unblock_input ();
+      return Qnil;
+    }
+  unblock_input ();
+  return list3i (lrint (col.red), lrint (col.green), lrint (col.blue));
+}
+
+DEFUN ("x-display-grayscale-p", Fx_display_grayscale_p, Sx_display_grayscale_p,
+       0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  return Qnil;
+}
+
+DEFUN ("x-open-connection", Fx_open_connection, Sx_open_connection,
+       1, 3, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+     (Lisp_Object display, Lisp_Object resource_string, Lisp_Object must_succeed)
+{
+  struct haiku_display_info *dpy_info;
+  CHECK_STRING (display);
+
+  if (NILP (Fstring_equal (display, build_string ("be"))))
+    !NILP (must_succeed) ? fatal ("Bad display") : error ("Bad display");
+  dpy_info = haiku_term_init ();
+
+  if (!dpy_info)
+    !NILP (must_succeed) ? fatal ("Display not responding") :
+      error ("Display not responding");
+
+  return Qnil;
+}
+
+DEFUN ("x-display-pixel-width", Fx_display_pixel_width, Sx_display_pixel_width,
+       0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+
+{
+  check_haiku_display_info (terminal);
+
+  int width, height;
+  BScreen_px_dim (&width, &height);
+  return make_fixnum (width);
+}
+
+DEFUN ("x-display-pixel-height", Fx_display_pixel_height, Sx_display_pixel_height,
+       0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+
+{
+  check_haiku_display_info (terminal);
+
+  int width, height;
+  BScreen_px_dim (&width, &height);
+  return make_fixnum (width);
+}
+
+DEFUN ("x-display-mm-height", Fx_display_mm_height, Sx_display_mm_height, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  struct haiku_display_info *dpyinfo = check_haiku_display_info (terminal);
+
+  int width, height;
+  BScreen_px_dim (&width, &height);
+
+  return make_fixnum (height / (dpyinfo->resy / 25.4));
+}
+
+
+DEFUN ("x-display-mm-width", Fx_display_mm_width, Sx_display_mm_width, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  struct haiku_display_info *dpyinfo = check_haiku_display_info (terminal);
+
+  int width, height;
+  BScreen_px_dim (&width, &height);
+
+  return make_fixnum (height / (dpyinfo->resy / 25.4));
+}
+
+DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame,
+       1, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+     (Lisp_Object parms)
+{
+  return haiku_create_frame (parms, 0);
+}
+
+DEFUN ("x-display-visual-class", Fx_display_visual_class,
+       Sx_display_visual_class, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  check_haiku_display_info (terminal);
+
+  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..ac7e7863dd
--- /dev/null
+++ b/src/haikufont.c
@@ -0,0 +1,1080 @@
+/* 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);
+
+#ifdef HAVE_BE_FREETYPE
+#ifndef HAVE_HARFBUZZ
+  Fput (Qhaiku, Qfont_driver_superseded_by, Qftbe);
+#else
+  Fput (Qhaiku, Qfont_driver_superseded_by, Qftbehb);
+#endif
+#endif
+}
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..af9ba97d45
--- /dev/null
+++ b/src/haikuterm.h
@@ -0,0 +1,304 @@
+/* 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;
+
+#ifdef HAVE_BE_FREETYPE
+extern struct font_driver const ftbefont_driver;
+#ifdef HAVE_HARFBUZZ
+extern struct font_driver ftbehbfont_driver;
+#endif
+#endif
+
+struct scroll_bar
+{
+  /* These fields are shared by all vectors.  */
+  union vectorlike_header header;
+
+  /* The window we're a scroll bar for.  */
+  Lisp_Object window;
+
+  /* The next and previous in the chain of scroll bars in this frame.  */
+  Lisp_Object next, prev;
+
+  /* Fields after 'prev' are not traced by the GC.  */
+
+  /* The position and size of the scroll bar in pixels, relative to the
+     frame.  */
+  int top, left, width, height;
+
+  /* The actual scrollbar. */
+  void *scroll_bar;
+
+  /* Non-nil if the scroll bar handle is currently being dragged by
+     the user.  */
+  int dragging;
+
+  /* The update position if we are waiting for a scrollbar update, or
+     -1. */
+  int update;
+
+  /* The last known position of this scrollbar. */
+  int position;
+
+  /* The total number of units inside this scrollbar. */
+  int total;
+
+  /* True if the scroll bar is horizontal.  */
+  bool horizontal;
+};
+
+#define XSCROLL_BAR(vec) ((struct scroll_bar *) XVECTOR (vec))
+
+#define FRAME_DIRTY_P(f) (FRAME_OUTPUT_DATA (f)->dirty_p)
+#define MAKE_FRAME_DIRTY(f) (FRAME_DIRTY_P (f) = 1)
+#define FRAME_OUTPUT_DATA(f) ((f)->output_data.haiku)
+#define FRAME_HAIKU_WINDOW(f) (FRAME_OUTPUT_DATA (f)->window)
+#define FRAME_HAIKU_VIEW(f) ((MAKE_FRAME_DIRTY (f)), FRAME_OUTPUT_DATA (f)->view)
+#define FRAME_HAIKU_MENU_BAR(f) (FRAME_OUTPUT_DATA (f)->menubar)
+#define FRAME_DISPLAY_INFO(f) (FRAME_OUTPUT_DATA (f)->display_info)
+#define FRAME_FONT(f) (FRAME_OUTPUT_DATA (f)->font)
+#define FRAME_FONTSET(f) (FRAME_OUTPUT_DATA (f)->fontset)
+#define FRAME_NATIVE_WINDOW(f) (FRAME_OUTPUT_DATA (f)->window)
+#define FRAME_BASELINE_OFFSET(f) (FRAME_OUTPUT_DATA (f)->baseline_offset)
+#define FRAME_CURSOR_COLOR(f) (FRAME_OUTPUT_DATA (f)->cursor_color)
+
+#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_BE_FREETYPE
+extern void syms_of_ftbefont (void);
+#endif
+
+#ifdef HAVE_NATIVE_IMAGE_API
+extern bool haiku_can_use_native_image_api (Lisp_Object type);
+extern int haiku_load_image (struct frame *f, struct image *img,
+			     Lisp_Object spec_file, Lisp_Object spec_data);
+extern void syms_of_haikuimage (void);
+#endif
+
+#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)  */
 
 \f
 /* 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;
 
 \f
@@ -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 24f12d6e24..f82ac1fdb7 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -13829,7 +13829,7 @@ syms_of_xterm (void)
 A value of nil means Emacs doesn't use toolkit scroll bars.
 With the X Window system, the value is a symbol describing the
 X toolkit.  Possible values are: gtk, motif, xaw, or xaw3d.
-With MS Windows or Nextstep, the value is t.  */);
+With MS Windows, Haiku windowing or Nextstep, the value is t.  */);
 #ifdef USE_TOOLKIT_SCROLL_BARS
 #ifdef USE_MOTIF
   Vx_toolkit_scroll_bars = intern_c_string ("motif");

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

* bug#51658: [PATCH] Haiku port (again)
  2021-11-07 11:29 ` bug#51658: [PATCH] Haiku port (again) Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2021-11-09 17:58   ` Eli Zaretskii
  2021-11-10  0:00     ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  0 siblings, 1 reply; 31+ messages in thread
From: Eli Zaretskii @ 2021-11-09 17:58 UTC (permalink / raw)
  To: Po Lu; +Cc: 51658

> Date: Sun, 07 Nov 2021 19:29:32 +0800
> From:  Po Lu via "Bug reports for GNU Emacs,
>  the Swiss army knife of text editors" <bug-gnu-emacs@gnu.org>
> 
> OK, after using this port for more than a month now I can say it's
> finally stable enough for heavy use.
> 
> I think all the major bugs have been ironed out now, so I'm submitting
> it for inclusion.
> 
> I couldn't figure out how to turn multiple (hundereds) of git commits
> into a single patch formatted like `git format-patch' would, so I
> attached the output of `git diff' instead.
> 
> Please take some time to review the code and possibly install it.

Thanks.

It's a large patch, so let's start with the general, a.k.a. "big"
aspects.

First, do we really need to use *.cc files and compile with a C++
compiler?  Is that a necessity?  AFAICT, the code in those *.cc files
is plain C, so why not use a C compiler, as we do on every other
platform?

Next, the font backend stuff: do we really need 5 (five) backends?
How about having just one: HarfBuzz+Cairo?  That's the direction we go
on other platforms, so how about making the Haiku code smaller and
simpler and support just that single backend, and drop all the older
ones?  I'd definitely won't want to drag the unmaintained libm17n-flt
into this port.

If you are okay with the above, could you please update the patch, so
that we could avoid reviewing code which eventually won't be
installed?





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

* bug#51658: [PATCH] Haiku port (again)
  2021-11-09 17:58   ` Eli Zaretskii
@ 2021-11-10  0:00     ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-11-10  4:33       ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-11-10 12:38       ` Eli Zaretskii
  0 siblings, 2 replies; 31+ messages in thread
From: Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2021-11-10  0:00 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 51658

Eli Zaretskii <eliz@gnu.org> writes:

> Thanks.
>
> It's a large patch, so let's start with the general, a.k.a. "big"
> aspects.
>
> First, do we really need to use *.cc files and compile with a C++
> compiler?  Is that a necessity?  AFAICT, the code in those *.cc files
> is plain C, so why not use a C compiler, as we do on every other
> platform?

That isn't C code.  The code in the .cc code is written in C++, in order
to use the Haiku GUI libraries which require C++.  I tried to keep it
simple and understandable for people who don't know C++, but know C.

That C++ code, in turn, exports functions the rest of Emacs uses as
`extern "C"'.

> Next, the font backend stuff: do we really need 5 (five) backends?
> How about having just one: HarfBuzz+Cairo?  That's the direction we go
> on other platforms, so how about making the Haiku code smaller and
> simpler and support just that single backend, and drop all the older
> ones?  I'd definitely won't want to drag the unmaintained libm17n-flt
> into this port.

I'm fine with removing the ftbe backend, but I would prefer to keep the
haikufont backend working.  The reason is that Haiku users might not
have Cairo installed, and it likes to break every now and then, as it's
not considered important for that platform.

> If you are okay with the above, could you please update the patch, so
> that we could avoid reviewing code which eventually won't be
> installed?

Yes, but please tell me if you're OK with keeping haikufont first.
Thanks.





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

* bug#51658: [PATCH] Haiku port (again)
  2021-11-10  0:00     ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2021-11-10  4:33       ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-11-10 12:38       ` Eli Zaretskii
  1 sibling, 0 replies; 31+ messages in thread
From: Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2021-11-10  4:33 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 51658

Po Lu <luangruo@yahoo.com> writes:

> I'm fine with removing the ftbe backend, but I would prefer to keep the
> haikufont backend working.  The reason is that Haiku users might not
> have Cairo installed, and it likes to break every now and then, as it's
> not considered important for that platform.

Actually, I now recall another problem with the Cairo font backend,
which is that it can't support color-mapped visuals.  This is a
limitation of how Cairo and Haiku define color mapping differently.

So I don't think it's good to remove ftbe anymore, either.  Thanks.





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

* bug#51658: [PATCH] Haiku port (again)
  2021-11-10  0:00     ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-11-10  4:33       ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2021-11-10 12:38       ` Eli Zaretskii
  2021-11-10 12:56         ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  1 sibling, 1 reply; 31+ messages in thread
From: Eli Zaretskii @ 2021-11-10 12:38 UTC (permalink / raw)
  To: Po Lu; +Cc: 51658

> From: Po Lu <luangruo@yahoo.com>
> Cc: 51658@debbugs.gnu.org
> Date: Wed, 10 Nov 2021 08:00:25 +0800
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> > Thanks.
> >
> > It's a large patch, so let's start with the general, a.k.a. "big"
> > aspects.
> >
> > First, do we really need to use *.cc files and compile with a C++
> > compiler?  Is that a necessity?  AFAICT, the code in those *.cc files
> > is plain C, so why not use a C compiler, as we do on every other
> > platform?
> 
> That isn't C code.  The code in the .cc code is written in C++, in order
> to use the Haiku GUI libraries which require C++.

Too bad.  IMNSHO, that's a ticking time bomb, and it will certainly
bite us at some point, since mixing C and C++ is tricky and requires a
lot of diligence.  It also means that you will probably be unable to
use any compiler but GCC (or whatever is currently supported by
Haiku), since different C++ compilers are generally
binary-incompatible.

> > Next, the font backend stuff: do we really need 5 (five) backends?
> > How about having just one: HarfBuzz+Cairo?  That's the direction we go
> > on other platforms, so how about making the Haiku code smaller and
> > simpler and support just that single backend, and drop all the older
> > ones?  I'd definitely won't want to drag the unmaintained libm17n-flt
> > into this port.
> 
> I'm fine with removing the ftbe backend, but I would prefer to keep the
> haikufont backend working.  The reason is that Haiku users might not
> have Cairo installed, and it likes to break every now and then, as it's
> not considered important for that platform.

Since you later say Cairo is unreliable, why not drop Cairo?

And that still leaves us with 3 backends, IIUC.  Why not just one?

Let me clarify why I insist on those issues: this port's usability
will depend crucially on its support by the developers who are
interested in Haiku, and that's for now just you.  Any decrease in the
level of support will most probably mean the port will bitrot and die,
given the fast pace of Emacs development.  So every feature that
doesn't absolutely have to be there is better removed, because having
too many supported configurations makes the code difficult to
understand and test, and thus fewer people will be able to support it.
Please keep this in mind when you insist on optional non-essential
features.  From experience, a port or a feature that is not supported
well enough bitrots very quickly around here.

So my suggestion is to choose wisely which features you really need to
keep.





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

* bug#51658: [PATCH] Haiku port (again)
  2021-11-10 12:38       ` Eli Zaretskii
@ 2021-11-10 12:56         ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-11-10 14:19           ` Eli Zaretskii
  0 siblings, 1 reply; 31+ messages in thread
From: Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2021-11-10 12:56 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 51658

Eli Zaretskii <eliz@gnu.org> writes:

> Too bad.  IMNSHO, that's a ticking time bomb, and it will certainly
> bite us at some point, since mixing C and C++ is tricky and requires a
> lot of diligence.  It also means that you will probably be unable to
> use any compiler but GCC (or whatever is currently supported by
> Haiku), since different C++ compilers are generally
> binary-incompatible.

AFAIU, the Haiku developers only support GCC for compiling C++ code, as
they have a requirement to keep support for old BeOS software, which
depends on the GCC ABI.

If that changes in the future, it would only be alongside a major change
in the operating system's other aspects, that would break the build
anyway.

> Since you later say Cairo is unreliable, why not drop Cairo?

What I meant by "unreliable" is that the Haiku developers tend to tweak
their Cairo package in various manners, which breaks the Emacs build.

People building Cairo themselves won't experience this problem, but
building the BeOS cairo backend is... tricky, to say the least.  I
managed to get it to work, but there is a lot of pain involved in it
that I wouldn't want to repeat.

The Haiku developers have a package repository and build automation
system that would make this easy, should they ship the Haiku port of
Emacs in the future.  (At present, they only ship Emacs built to run in
a TTY.)

So it is useful to have Cairo around, for people whom it is useful, but
if not, it would be good to have something to fall back to.

> And that still leaves us with 3 backends, IIUC.  Why not just one?

Well, one of those backends is simply a HarfBuzz variant of the other,
which is how xftfont and ftcrfont do it, and IIUC, that's how it works
under MS-Windows too.

So, if we keep only the haikufont backend (for Haiku users who don't
want to install FreeType or Cairo), and one of `ftbe' or cairo font
support, there will really just be two font backends.

(haikufont.c and related support functions in haiku_font_support.cc use
the BFont API, which is the native font API on Haiku, but doesn't
support fancy text shaping or HarfBuzz, not unlike X11 Core Fonts.)

Aside from not depending on Cairo or FreeType, which are not always
available on Haiku, it also comes with the advantage of being fast even
on remote sessions (not unlike X11 remote access), as characters drawn
are sent down the wire as plain text, instead of as bitmap data, which
is very slow when operating over a remote connection.

So I think it will be useful as a fallback in many cases.

> Let me clarify why I insist on those issues: this port's usability
> will depend crucially on its support by the developers who are
> interested in Haiku, and that's for now just you.  Any decrease in the
> level of support will most probably mean the port will bitrot and die,
> given the fast pace of Emacs development.  So every feature that
> doesn't absolutely have to be there is better removed, because having
> too many supported configurations makes the code difficult to
> understand and test, and thus fewer people will be able to support it.
> Please keep this in mind when you insist on optional non-essential
> features.  From experience, a port or a feature that is not supported
> well enough bitrots very quickly around here.

Thanks, I understand the problem.  I was tempted to add many features,
but refrained from doing so for precisely this reason.

> So my suggestion is to choose wisely which features you really need to
> keep.

Yes, thanks.





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

* bug#51658: [PATCH] Haiku port (again)
  2021-11-10 12:56         ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2021-11-10 14:19           ` Eli Zaretskii
  2021-11-11  0:27             ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  0 siblings, 1 reply; 31+ messages in thread
From: Eli Zaretskii @ 2021-11-10 14:19 UTC (permalink / raw)
  To: Po Lu; +Cc: 51658

> From: Po Lu <luangruo@yahoo.com>
> Cc: 51658@debbugs.gnu.org
> Date: Wed, 10 Nov 2021 20:56:10 +0800
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> > Too bad.  IMNSHO, that's a ticking time bomb, and it will certainly
> > bite us at some point, since mixing C and C++ is tricky and requires a
> > lot of diligence.  It also means that you will probably be unable to
> > use any compiler but GCC (or whatever is currently supported by
> > Haiku), since different C++ compilers are generally
> > binary-incompatible.
> 
> AFAIU, the Haiku developers only support GCC for compiling C++ code, as
> they have a requirement to keep support for old BeOS software, which
> depends on the GCC ABI.

And for a good reason.  Then I think you should remove the part of
configure.ac that looks for other C++ compilers, as that is entirely
unnecessary, and probably will always be.

> > Since you later say Cairo is unreliable, why not drop Cairo?
> 
> What I meant by "unreliable" is that the Haiku developers tend to tweak
> their Cairo package in various manners, which breaks the Emacs build.
> 
> People building Cairo themselves won't experience this problem, but
> building the BeOS cairo backend is... tricky, to say the least.  I
> managed to get it to work, but there is a lot of pain involved in it
> that I wouldn't want to repeat.
> 
> The Haiku developers have a package repository and build automation
> system that would make this easy, should they ship the Haiku port of
> Emacs in the future.  (At present, they only ship Emacs built to run in
> a TTY.)
> 
> So it is useful to have Cairo around, for people whom it is useful, but
> if not, it would be good to have something to fall back to.
> 
> > And that still leaves us with 3 backends, IIUC.  Why not just one?
> 
> Well, one of those backends is simply a HarfBuzz variant of the other,
> which is how xftfont and ftcrfont do it, and IIUC, that's how it works
> under MS-Windows too.

The other platforms have past history, which Haiku doesn't.  And we
plan on removing more old backends in the future; for example
Uniscribe for Windows will die soon enough because MS deprecated it,
and it is not being developed anymore, so falls behind in supporting
new scripts and features.

> So, if we keep only the haikufont backend (for Haiku users who don't
> want to install FreeType or Cairo), and one of `ftbe' or cairo font
> support, there will really just be two font backends.

OK, let's go with two.  I understand that both will use HarfBuzz?  I'd
prefer not to have backends that use other shaping engines, unless
Haiku has some shaping engine that is better than HarfBuzz.

> Aside from not depending on Cairo or FreeType, which are not always
> available on Haiku, it also comes with the advantage of being fast even
> on remote sessions (not unlike X11 remote access), as characters drawn
> are sent down the wire as plain text, instead of as bitmap data, which
> is very slow when operating over a remote connection.
> 
> So I think it will be useful as a fallback in many cases.

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.

> > So my suggestion is to choose wisely which features you really need to
> > keep.
> 
> Yes, thanks.

OK, please post an updated patch, and we'll take it from there.

Thanks.





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

* bug#51658: [PATCH] Haiku port (again)
  2021-11-10 14:19           ` Eli Zaretskii
@ 2021-11-11  0:27             ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-11-11  6:51               ` Eli Zaretskii
  0 siblings, 1 reply; 31+ messages in thread
From: Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2021-11-11  0:27 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 51658

Eli Zaretskii <eliz@gnu.org> writes:

>> AFAIU, the Haiku developers only support GCC for compiling C++ code, as
>> they have a requirement to keep support for old BeOS software, which
>> depends on the GCC ABI.
>
> And for a good reason.  Then I think you should remove the part of
> configure.ac that looks for other C++ compilers, as that is entirely
> unnecessary, and probably will always be.

Thanks, done.

> The other platforms have past history, which Haiku doesn't.  And we
> plan on removing more old backends in the future; for example
> Uniscribe for Windows will die soon enough because MS deprecated it,
> and it is not being developed anymore, so falls behind in supporting
> new scripts and features.

I understand now.  Thanks.

> OK, let's go with two.  I understand that both will use HarfBuzz?  I'd
> prefer not to have backends that use other shaping engines, unless
> Haiku has some shaping engine that is better than HarfBuzz.

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!





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

* bug#51658: [PATCH] Haiku port (again)
  2021-11-11  0:27             ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2021-11-11  6:51               ` Eli Zaretskii
  2021-11-11  7:40                 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  0 siblings, 1 reply; 31+ messages in thread
From: Eli Zaretskii @ 2021-11-11  6:51 UTC (permalink / raw)
  To: Po Lu; +Cc: 51658

> From: Po Lu <luangruo@yahoo.com>
> Cc: 51658@debbugs.gnu.org
> 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.





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

* bug#51658: [PATCH] Haiku port (again)
  2021-11-11  6:51               ` Eli Zaretskii
@ 2021-11-11  7:40                 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-11-13 18:31                   ` Eli Zaretskii
  0 siblings, 1 reply; 31+ messages in thread
From: Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2021-11-11  7:40 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 51658

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

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Po Lu <luangruo@yahoo.com>
>> Cc: 51658@debbugs.gnu.org
>> 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.


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

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.
+
 \f
 * 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.
 \f
 * 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 ();
+}
+
+\f
+
+DEFUN ("haiku-set-mouse-absolute-pixel-position",
+       Fhaiku_set_mouse_absolute_pixel_position,
+       Shaiku_set_mouse_absolute_pixel_position, 2, 2, 0,
+       doc: /* Move mouse pointer to a pixel position at (X, Y).  The
+coordinates X and Y are interpreted to start from the top-left
+corner of the screen.  */)
+  (Lisp_Object x, Lisp_Object y)
+{
+  int xval = check_integer_range (x, INT_MIN, INT_MAX);
+  int yval = check_integer_range (y, INT_MIN, INT_MAX);
+
+  if (!x_display_list)
+    error ("Window system not initialized");
+
+  block_input ();
+  be_warp_pointer (xval, yval);
+  unblock_input ();
+  return Qnil;
+}
+
+DEFUN ("haiku-mouse-absolute-pixel-position", Fhaiku_mouse_absolute_pixel_position,
+       Shaiku_mouse_absolute_pixel_position, 0, 0, 0,
+       doc: /* Return absolute position of mouse cursor in pixels.
+The position is returned as a cons cell (X . Y) of the coordinates of
+the mouse cursor position in pixels relative to a position (0, 0) of the
+selected frame's display.  */)
+  (void)
+{
+  if (!x_display_list)
+    return Qnil;
+
+  struct frame *f = SELECTED_FRAME ();
+
+  if (FRAME_INITIAL_P (f) || !FRAME_HAIKU_P (f)
+      || !FRAME_HAIKU_VIEW (f))
+    return Qnil;
+
+  block_input ();
+  void *view = FRAME_HAIKU_VIEW (f);
+
+  int x, y;
+  BView_get_mouse (view, &x, &y);
+  BView_convert_to_screen (view, &x, &y);
+  unblock_input ();
+
+  return Fcons (make_fixnum (x), make_fixnum (y));
+}
+
+DEFUN ("xw-display-color-p", Fxw_display_color_p, Sxw_display_color_p, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+     (Lisp_Object terminal)
+{
+  return Qt;
+}
+
+DEFUN ("xw-color-defined-p", Fxw_color_defined_p, Sxw_color_defined_p, 1, 2, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object color, Lisp_Object frame)
+{
+  Emacs_Color col;
+  CHECK_STRING (color);
+  decode_window_system_frame (frame);
+
+  return haiku_get_color (SSDATA (color), &col) ? Qnil : Qt;
+}
+
+DEFUN ("xw-color-values", Fxw_color_values, Sxw_color_values, 1, 2, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+     (Lisp_Object color, Lisp_Object frame)
+{
+  Emacs_Color col;
+  CHECK_STRING (color);
+  decode_window_system_frame (frame);
+
+  block_input ();
+  if (haiku_get_color (SSDATA (color), &col))
+    {
+      unblock_input ();
+      return Qnil;
+    }
+  unblock_input ();
+  return list3i (lrint (col.red), lrint (col.green), lrint (col.blue));
+}
+
+DEFUN ("x-display-grayscale-p", Fx_display_grayscale_p, Sx_display_grayscale_p,
+       0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  return Qnil;
+}
+
+DEFUN ("x-open-connection", Fx_open_connection, Sx_open_connection,
+       1, 3, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+     (Lisp_Object display, Lisp_Object resource_string, Lisp_Object must_succeed)
+{
+  struct haiku_display_info *dpy_info;
+  CHECK_STRING (display);
+
+  if (NILP (Fstring_equal (display, build_string ("be"))))
+    !NILP (must_succeed) ? fatal ("Bad display") : error ("Bad display");
+  dpy_info = haiku_term_init ();
+
+  if (!dpy_info)
+    !NILP (must_succeed) ? fatal ("Display not responding") :
+      error ("Display not responding");
+
+  return Qnil;
+}
+
+DEFUN ("x-display-pixel-width", Fx_display_pixel_width, Sx_display_pixel_width,
+       0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+
+{
+  check_haiku_display_info (terminal);
+
+  int width, height;
+  BScreen_px_dim (&width, &height);
+  return make_fixnum (width);
+}
+
+DEFUN ("x-display-pixel-height", Fx_display_pixel_height, Sx_display_pixel_height,
+       0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+
+{
+  check_haiku_display_info (terminal);
+
+  int width, height;
+  BScreen_px_dim (&width, &height);
+  return make_fixnum (width);
+}
+
+DEFUN ("x-display-mm-height", Fx_display_mm_height, Sx_display_mm_height, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  struct haiku_display_info *dpyinfo = check_haiku_display_info (terminal);
+
+  int width, height;
+  BScreen_px_dim (&width, &height);
+
+  return make_fixnum (height / (dpyinfo->resy / 25.4));
+}
+
+
+DEFUN ("x-display-mm-width", Fx_display_mm_width, Sx_display_mm_width, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  struct haiku_display_info *dpyinfo = check_haiku_display_info (terminal);
+
+  int width, height;
+  BScreen_px_dim (&width, &height);
+
+  return make_fixnum (height / (dpyinfo->resy / 25.4));
+}
+
+DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame,
+       1, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+     (Lisp_Object parms)
+{
+  return haiku_create_frame (parms, 0);
+}
+
+DEFUN ("x-display-visual-class", Fx_display_visual_class,
+       Sx_display_visual_class, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  check_haiku_display_info (terminal);
+
+  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)  */
 
 \f
 /* 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;
 
 \f
@@ -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");

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

* bug#51658: [PATCH] Haiku port (again)
  2021-11-11  7:40                 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2021-11-13 18:31                   ` Eli Zaretskii
  2021-11-14  1:08                     ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  0 siblings, 1 reply; 31+ messages in thread
From: Eli Zaretskii @ 2021-11-13 18:31 UTC (permalink / raw)
  To: Po Lu; +Cc: 51658

> From: Po Lu <luangruo@yahoo.com>
> Cc: 51658@debbugs.gnu.org
> Date: Thu, 11 Nov 2021 15:40:01 +0800
> 
> Here is the updated commit message; the patch is attached.

See the comments below.

> +  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

Do we need libm17n-flt?  For which font back-end is it needed?

> +@cindex TTY Emacs in haiku

@cindex entries should begin from a lower-case letter, so that they
sort predictably regardless of the locale where the manual is built.

> +  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
                 ^^
Should be "if", not capitalized.

> +to launch separate instances of Emacs, or do not care for the
> +aforementioned system integration features, the binary named

What "system integration features" does this text refer to?  It's
unclear from the text.

> +@code{emacs} should be used instead.
                ^^^^^^^^^^^^^^
Passive tense!

> +  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.

This sounds no different from any other system, so I wonder why this
needs to be described.  On most systems, there's no Super or Hyper
keys, and they can be simulated via "C-x @", unless the user
configures the keyboard to do some remapping.

> +  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.

This description focuses on the underlying technical issue, and leaves
the user-facing behavior not sufficiently clear.  This is a user
manual, so it is best to describe the behavior users will see, and
leave out the technical details.  It sounds like all you want to say
is that accented letters cannot be typed using the super key? then
just say that without going int the details too much.

> +@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}.

This should be in PROBLEMS, not here.

> +  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}).

How is this different from the GTK build?  If it isn't different,
maybe it shouldn't be mentioned at all.

> +@subsection What to do when Emacs crashes
> +@cindex haiku debugger
> +@vindex haiku-debug-on-fatal-error

This should have an index entry starting from "crashes", so that users
could find it easily.

> +@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

These descriptions should be in the same place where the corresponding
behaviors are described.  Having them in a separate section makes them
harder to discover.

> +@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}.

These should be in the same section that describes the keyboard
peculiarities.

> +@vindex haiku-shift-keysym
> +@item haiku-super-keysym
               ^^^^^
Copy/paste error?

> +@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.

Why isn't this treated as drag-and-drop on other platforms?  Then you
won't need Haiku-specific documentation and events.

> +@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}.

And this should be connected to the session manager, see xsmfns.c, and
then it can just trigger the mechanism defined there, as on other
platforms.

> +@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}.

"Resources" is a general section, unrelated to Haiku.  If there's
nothing special we must say about resources on Haiku, why do we need
this section?

> +@node Haiku Fonts
> +@section Font and font backend selection on Haiku
> +@cindex font backend selection (Haiku)

This should be reworded to say that Haiku uses 2 font backends used by
other platforms, and has one Haiku-specific backend.  There's no
reason to describe the backends that are named the same as their
GNU/Linux counterparts, except if their behavior significantly
differs.

> +  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.

The right place for this is in PROBLEMS again.

> +  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}.

No need to repeat this in the manual.

> +  The @code{ftcr} backend features HarfBuzz variants when Emacs is
               ^^^^
Typo (but I see no reason to have this part in the manual, see above).

> @@ -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}.

It is not a good idea to have the same feature described in more than
one place: it leads to inconsistencies, and is generally a maintenance
burden.  Decide whether you want to describe this here or in the Haiku
appendix, and either have the description in that one place, or leave
only the cross-reference in the other with minimum text.  And make
sure the index entry leads to the place with the actual description,
not to the place where you have a cross-reference.

> +On Haiku, setting @code{fullscreen} to @code{fullwidth} or
> +@code{fullheight} has no effect.

This is not important enough to be in the text; please make it a
@footnote.

> +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.

This sounds like stuff for PROBLEMS again.

> @@ -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.

I don't understand: non-selected windows don't receive keyboard input
on all systems.  So what is special about Haiku here?

> @@ -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}.

This is an unnecessary repetition of what you already say in the
appendix.

> +Lowering frames is not supported on Haiku, due to limitations imposed
> +by the system.

This should also be in a @footnote.

> +Restacking frames is not supported on Haiku, due to limitations
> +imposed by the system.

And this.

> +  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.

And this.

> +** 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.
> +

This should be a single "**" parent entry with "***" sub-entries.

> --- 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")))

Why do you need ucs-normalize?  And what about the condition not to
load it if uni-*.el files are unavailable?

> --- 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

I don't understand what the situation with FP_ILOGB0 has to do with
logb function's implementation and the workaround you add here.
Please clarify the comment.

> -#ifdef USE_CAIRO
> +#if defined (USE_CAIRO) || defined (USE_BE_CAIRO)

Does this mean that USE_CAIRO code is not used by Haiku when Emacs is
built with Cairo on Haiku?  Why is that?  And why cannot Haiku use
USE_CAIRO?

> 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

There isn't a single comment in this and other Haiku-specific source
files.  Please consider adding at least comments describing what the
important non-trivial functions do and what their arguments mean.

Btw, many comments in source files that are similar to X and w32 seem
to have been copied wholesale from those other systems.  I have no way
of verifying whether the copied comments are accurate for Haiku, so
it's up to you to audit them.

> +/* 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.  */};

Why not use script-representative-chars here?

> +_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 ();
> +}

Are these writes to stderr going to stay in the production code?

> +	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");
> +	  }
> +      }

And these calls to perror?

> -#ifdef HAVE_EXT_MENU_BAR
> +#if defined (HAVE_EXT_MENU_BAR) || defined (HAVE_HAIKU)

Why is this needed?  You did define HAVE_EXT_MENU_BAR on Haiku, right?

> @@ -3119,6 +3143,23 @@ list_system_processes (void)
>    return  proclist;
>  }
>  
> +#elif defined HAIKU
> +
> +Lisp_Object
> +list_system_processes (void)
> +{

Can the Haiku-specific bits here be moved to some Haiku-specific file?

Thanks.





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

* bug#51658: [PATCH] Haiku port (again)
  2021-11-13 18:31                   ` Eli Zaretskii
@ 2021-11-14  1:08                     ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-11-14  7:54                       ` Eli Zaretskii
  0 siblings, 1 reply; 31+ messages in thread
From: Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2021-11-14  1:08 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 51658

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

Eli Zaretskii <eliz@gnu.org> writes:

> See the comments below.

>> +  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

> Do we need libm17n-flt?  For which font back-end is it needed?

The Cairo font backend has the ability to use libm17n-flt.  The code for
that is in ftcrfont.c, as it is on X-Windows.

>> +@cindex TTY Emacs in haiku

> @cindex entries should begin from a lower-case letter, so that they
> sort predictably regardless of the locale where the manual is built.

Thanks, fixed.

>> +  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
>                  ^^
> Should be "if", not capitalized.
>
>> +to launch separate instances of Emacs, or do not care for the
>> +aforementioned system integration features, the binary named
>
> What "system integration features" does this text refer to?  It's
> unclear from the text.
>
>> +@code{emacs} should be used instead.
>                 ^^^^^^^^^^^^^^
> Passive tense!

>> +  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.

> This sounds no different from any other system, so I wonder why this
> needs to be described.  On most systems, there's no Super or Hyper
> keys, and they can be simulated via "C-x @", unless the user
> configures the keyboard to do some remapping.

The "option key" and "command key" in Haiku are unusual concepts not
found in other systems, so it's worthwhile to mention it here.

>> +  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.

> This description focuses on the underlying technical issue, and leaves
> the user-facing behavior not sufficiently clear.  This is a user
> manual, so it is best to describe the behavior users will see, and
> leave out the technical details.  It sounds like all you want to say
> is that accented letters cannot be typed using the super key? then
> just say that without going int the details too much.

Okay, thanks.

>> +@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}.

> This should be in PROBLEMS, not here.

Done, thanks.

>> +  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}).

> How is this different from the GTK build?  If it isn't different,
> maybe it shouldn't be mentioned at all.

The variable used is different, as it doesn't make sense to name a Haiku
variable `x-gtk-...'

>> +@subsection What to do when Emacs crashes
>> +@cindex haiku debugger
>> +@vindex haiku-debug-on-fatal-error

> This should have an index entry starting from "crashes", so that users
> could find it easily.

>> +@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}.

> These should be in the same section that describes the keyboard
> peculiarities.

Thanks.

>> +@vindex haiku-shift-keysym
>> +@item haiku-super-keysym
>                ^^^^^
> Copy/paste error?

Good catch, thanks.

>> +@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.

> Why isn't this treated as drag-and-drop on other platforms?  Then you
> won't need Haiku-specific documentation and events.

It might not specifically be a drag event: for example, the Tracker
could ask Emacs to open a file, because the user selected it from the
"Recent files" menu.

>> +@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}.

> And this should be connected to the session manager, see xsmfns.c, and
> then it can just trigger the mechanism defined there, as on other
> platforms.

Isn't the mechanism there specific to X?  I could not find an example of
any other port using it, and it seems to use X specific functions.

>> +@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}.

> "Resources" is a general section, unrelated to Haiku.  If there's
> nothing special we must say about resources on Haiku, why do we need
> this section?

Makes sense, I deleted it, thanks.

>> +@node Haiku Fonts
>> +@section Font and font backend selection on Haiku
>> +@cindex font backend selection (Haiku)

> This should be reworded to say that Haiku uses 2 font backends used by
> other platforms, and has one Haiku-specific backend.  There's no
> reason to describe the backends that are named the same as their
> GNU/Linux counterparts, except if their behavior significantly
> differs.

>> +  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.
>
> The right place for this is in PROBLEMS again.

Thanks.

>> +  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}.

> No need to repeat this in the manual.

Thanks.

>> @@ -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}.

> It is not a good idea to have the same feature described in more than
> one place: it leads to inconsistencies, and is generally a maintenance
> burden.  Decide whether you want to describe this here or in the Haiku
> appendix, and either have the description in that one place, or leave
> only the cross-reference in the other with minimum text.  And make
> sure the index entry leads to the place with the actual description,
> not to the place where you have a cross-reference.

Understood, that entry seems to be entirely redundant, so it has been
deleted.

>> +On Haiku, setting @code{fullscreen} to @code{fullwidth} or
>> +@code{fullheight} has no effect.
>
> This is not important enough to be in the text; please make it a
> @footnote.

Thanks, I did that.

> This sounds like stuff for PROBLEMS again.

Thanks.

>> @@ -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.

> I don't understand: non-selected windows don't receive keyboard input
> on all systems.  So what is special about Haiku here?

`no-accept-focus' means that the frame isn't willing to accept focus
from mouse clicks and other mouse motion events.

On X-Windows, for instance, it is possible to give the keyboard focus to
such a frame by hitting Alt-TAB (in most window managers); this is not
possible on Haiku.

>> @@ -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}.

> This is an unnecessary repetition of what you already say in the
> appendix.

Thanks, I replaced it with a reference.

>> +Lowering frames is not supported on Haiku, due to limitations imposed
>> +by the system.
>
> This should also be in a @footnote.
>
>> +Restacking frames is not supported on Haiku, due to limitations
>> +imposed by the system.
>
> And this.
>
>> +  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.
>
> And this.
>
>> +** 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.
>> +
>
> This should be a single "**" parent entry with "***" sub-entries.

Thanks, all done.

>> --- 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")))

> Why do you need ucs-normalize?  And what about the condition not to
> load it if uni-*.el files are unavailable?

Without ucs-normalize, some mysterious error occured during fontset
creation earlier in development, but it seems to be gone now, so I
deleted it along with the form that loaded `international/mule-util',
thanks.

>> --- 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

> I don't understand what the situation with FP_ILOGB0 has to do with
> logb function's implementation and the workaround you add here.
> Please clarify the comment.

Thanks, I tried.

>> -#ifdef USE_CAIRO
>> +#if defined (USE_CAIRO) || defined (USE_BE_CAIRO)

> Does this mean that USE_CAIRO code is not used by Haiku when Emacs is
> built with Cairo on Haiku?  Why is that?  And why cannot Haiku use
> USE_CAIRO?

USE_CAIRO, when defined, tries to define a lot of X related features
that aren't necessary.  USE_BE_CAIRO only enables the parts required for
the Cairo font driver to operate correctly.

>> 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

> There isn't a single comment in this and other Haiku-specific source
> files.  Please consider adding at least comments describing what the
> important non-trivial functions do and what their arguments mean.

Many of these functions are described in the header file defining their
prototypes, haiku_support.h.  The ones that are not documented (such as
most of the drawing functions) are ones that behave identically to the
Haiku counterparts they wrap (i.e. BView_FillTriangle), and ones that
behave identically to the documentation of Haiku functions by almost the
same name, but reimplemented to work around bugs in Haiku
(i.e. BView_DrawMask).

I may have missed a few of the function that do need documentation
though.  I fixed some of that in the revised patch.

> Btw, many comments in source files that are similar to X and w32 seem
> to have been copied wholesale from those other systems.  I have no way
> of verifying whether the copied comments are accurate for Haiku, so
> it's up to you to audit them.

I tried my best to audit those comments and delete the ones that did not
make sense.  Many of them are valid, because I have tried to keep the
code as close as possible to the X and w32 code.

>> +/* 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.  */};

AFAIK, `script-representative-chars' doesn't handle languages such as
Korean or Japanese, but I could be mistaken.

> Why not use script-representative-chars here?
>
>> +_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 ();
>> +}

> Are these writes to stderr going to stay in the production code?

It's used to report an error in Emacs that will cause it to abort
immediately afterwards, with some explanation as to why.  It will be
valuable for bug reports, as `addr2line', `gdb' and friends are hard to
set up on Haiku, and the output of the system debugger is not very
helpful with crashes in Emacs C++ code.  So I think it's OK.  (Something
similar is done in xterm to explain the GTK display disconnect abort
bug.)

>> +	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");
>> +	  }
>> +      }

> And these calls to perror?

The call to perror is redundant: gui_abort should suffice.  So they have
been deleted.

>> -#ifdef HAVE_EXT_MENU_BAR
>> +#if defined (HAVE_EXT_MENU_BAR) || defined (HAVE_HAIKU)

> Why is this needed?  You did define HAVE_EXT_MENU_BAR on Haiku, right?

Thanks, fixed.

>> @@ -3119,6 +3143,23 @@ list_system_processes (void)
>>    return  proclist;
>>  }
>>  
>> +#elif defined HAIKU
>> +
>> +Lisp_Object
>> +list_system_processes (void)
>> +{

> Can the Haiku-specific bits here be moved to some Haiku-specific file?

It would be more consistent with the other surrounding Unix variants
(i.e. GNU/Linux, Darwin, and the BSDs) to define them in that file.

But if you still insist, I have no objection it it.

> Thanks.

Thanks, please see the revised patch.


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

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 c231c2ceae..44650ee938 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])
 
@@ -4814,7 +4968,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
@@ -5069,7 +5223,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
@@ -5165,6 +5319,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
 
 
@@ -5386,8 +5548,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)
@@ -5430,6 +5609,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
@@ -5542,6 +5737,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
@@ -5930,7 +6136,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..6f6ab0cd60 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,12 @@ 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 Events::        Specifics of window system event handling under 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 +1625,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..83afed8141
--- /dev/null
+++ b/doc/emacs/haiku.texi
@@ -0,0 +1,173 @@
+@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 Events::        Specifics of window system event handling under 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}; ff 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, use the binary named
+@code{emacs} 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.
+
+  It is 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.  The variables that allow for
+that are described below.
+
+@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-shift-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.
+
+@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}).
+
+@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.
+@end table
+
+@subsection What to do when Emacs crashes
+@cindex crashes, Haiku
+@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.
+
+@table @code
+@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
+
+@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 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}).
+
+  Two of these backends, @code{ftcr} and @code{ftcrfont} are identical
+to their counterparts on the X Window System.  There is also a
+Haiku-specific backend named @code{haiku}, that uses the App Server to
+draw fonts, but does not at present support display of color font and
+emoji.
diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi
index a8a7837a4a..a7d33ff620 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},
@@ -8208,6 +8209,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
@@ -8254,6 +8257,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
diff --git a/doc/lispref/frames.texi b/doc/lispref/frames.texi
index 31ad82b7ad..56fc8859a0 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.
 
+@footnote{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,10 @@ 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 can be several font drivers (@pxref{Haiku Fonts,,, emacs,
+The GNU Emacs Manual}).
+
+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 +3155,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.
+
+@footnote{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 +3178,9 @@ Raising and Lowering
 @var{frame1} remains unaltered.
 
 Some window managers may refuse to restack windows.
+
+@footnote{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 +3290,12 @@ Child Frames
 allowing them to be positioned so they do not obscure the parent frame
 while still being visible themselves.
 
+@footnote{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.
+
 \f
 * Obsolete platforms
 
diff --git a/etc/NEWS b/etc/NEWS
index c362e56cee..7f4fa134ad 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -24,6 +24,25 @@ applies, and please also update docstrings as needed.
 \f
 * 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 f506881a4b..c548ee9b36 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.
@@ -1105,6 +1114,13 @@ In your ~/.Xresources file, then run
 
 And restart Emacs.
 
+** On Haiku, BeCJK doesn't work properly with Emacs
+
+Some popular Haiku input methods such BeCJK are known to behave badly
+when interacting with Emacs, in ways such as stealing input focus and
+displaying popup windows that don't disappear.  If you are affected,
+you should use an Emacs input method instead.
+
 * X runtime problems
 
 ** X keyboard problems
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..5b16d63e54 100644
--- a/lisp/loadup.el
+++ b/lisp/loadup.el
@@ -302,6 +302,11 @@
       (load "term/common-win")
       (load "term/x-win")))
 
+(if (featurep 'haiku)
+    (progn
+      (load "term/common-win")
+      (load "term/haiku-win")))
+
 (if (or (eq system-type 'windows-nt)
         (featurep 'w32))
     (progn
@@ -557,6 +562,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 4015448ece..859b30564a 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..f52dae4719 100644
--- a/src/floatfns.c
+++ b/src/floatfns.c
@@ -347,6 +347,21 @@ DEFUN ("logb", Flogb, Slogb, 1, 1, 0,
 double_integer_scale (double d)
 {
   int exponent = ilogb (d);
+#ifdef HAIKU
+  /* On Haiku, the values returned by ilogb are nonsensical when
+     confronted with tiny numbers, inf, or NaN, which breaks the trick
+     used by code on other platforms, so we have to test for each case
+     manually, and return the appropriate value.  */
+  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 420a4f8e70..c94e2d85cc 100644
--- a/src/font.c
+++ b/src/font.c
@@ -5732,6 +5732,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 03e44ec30e..cf592759ab 100644
--- a/src/ftfont.c
+++ b/src/ftfont.c
@@ -3108,6 +3108,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..928eb6bd5b
--- /dev/null
+++ b/src/haiku_support.cc
@@ -0,0 +1,2808 @@
+/* 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)
+	  gui_abort ("strdup failed");
+      }
+
+    if (help)
+      {
+	this->help = strdup (help);
+	if (!this->help)
+	  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..30fb0de1d8
--- /dev/null
+++ b/src/haiku_support.h
@@ -0,0 +1,1058 @@
+/* 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
+  /* Return VIEW's cairo surface.  */
+  extern cairo_surface_t *
+  EmacsView_cairo_surface (void *view);
+
+  /* Transfer each clip rectangle in VIEW to the cairo context
+     CTX.  */
+  extern void
+  BView_cr_dump_clipping (void *view, cairo_t *ctx);
+
+  /* Lock WINDOW in preparation for drawing using Cairo.  */
+  extern void
+  EmacsWindow_begin_cr_critical_section (void *window);
+
+  /* Unlock WINDOW in preparation for drawing using Cairo.  */
+  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 ();
+}
+
+\f
+
+DEFUN ("haiku-set-mouse-absolute-pixel-position",
+       Fhaiku_set_mouse_absolute_pixel_position,
+       Shaiku_set_mouse_absolute_pixel_position, 2, 2, 0,
+       doc: /* Move mouse pointer to a pixel position at (X, Y).  The
+coordinates X and Y are interpreted to start from the top-left
+corner of the screen.  */)
+  (Lisp_Object x, Lisp_Object y)
+{
+  int xval = check_integer_range (x, INT_MIN, INT_MAX);
+  int yval = check_integer_range (y, INT_MIN, INT_MAX);
+
+  if (!x_display_list)
+    error ("Window system not initialized");
+
+  block_input ();
+  be_warp_pointer (xval, yval);
+  unblock_input ();
+  return Qnil;
+}
+
+DEFUN ("haiku-mouse-absolute-pixel-position", Fhaiku_mouse_absolute_pixel_position,
+       Shaiku_mouse_absolute_pixel_position, 0, 0, 0,
+       doc: /* Return absolute position of mouse cursor in pixels.
+The position is returned as a cons cell (X . Y) of the coordinates of
+the mouse cursor position in pixels relative to a position (0, 0) of the
+selected frame's display.  */)
+  (void)
+{
+  if (!x_display_list)
+    return Qnil;
+
+  struct frame *f = SELECTED_FRAME ();
+
+  if (FRAME_INITIAL_P (f) || !FRAME_HAIKU_P (f)
+      || !FRAME_HAIKU_VIEW (f))
+    return Qnil;
+
+  block_input ();
+  void *view = FRAME_HAIKU_VIEW (f);
+
+  int x, y;
+  BView_get_mouse (view, &x, &y);
+  BView_convert_to_screen (view, &x, &y);
+  unblock_input ();
+
+  return Fcons (make_fixnum (x), make_fixnum (y));
+}
+
+DEFUN ("xw-display-color-p", Fxw_display_color_p, Sxw_display_color_p, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+     (Lisp_Object terminal)
+{
+  return Qt;
+}
+
+DEFUN ("xw-color-defined-p", Fxw_color_defined_p, Sxw_color_defined_p, 1, 2, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object color, Lisp_Object frame)
+{
+  Emacs_Color col;
+  CHECK_STRING (color);
+  decode_window_system_frame (frame);
+
+  return haiku_get_color (SSDATA (color), &col) ? Qnil : Qt;
+}
+
+DEFUN ("xw-color-values", Fxw_color_values, Sxw_color_values, 1, 2, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+     (Lisp_Object color, Lisp_Object frame)
+{
+  Emacs_Color col;
+  CHECK_STRING (color);
+  decode_window_system_frame (frame);
+
+  block_input ();
+  if (haiku_get_color (SSDATA (color), &col))
+    {
+      unblock_input ();
+      return Qnil;
+    }
+  unblock_input ();
+  return list3i (lrint (col.red), lrint (col.green), lrint (col.blue));
+}
+
+DEFUN ("x-display-grayscale-p", Fx_display_grayscale_p, Sx_display_grayscale_p,
+       0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  return Qnil;
+}
+
+DEFUN ("x-open-connection", Fx_open_connection, Sx_open_connection,
+       1, 3, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+     (Lisp_Object display, Lisp_Object resource_string, Lisp_Object must_succeed)
+{
+  struct haiku_display_info *dpy_info;
+  CHECK_STRING (display);
+
+  if (NILP (Fstring_equal (display, build_string ("be"))))
+    !NILP (must_succeed) ? fatal ("Bad display") : error ("Bad display");
+  dpy_info = haiku_term_init ();
+
+  if (!dpy_info)
+    !NILP (must_succeed) ? fatal ("Display not responding") :
+      error ("Display not responding");
+
+  return Qnil;
+}
+
+DEFUN ("x-display-pixel-width", Fx_display_pixel_width, Sx_display_pixel_width,
+       0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+
+{
+  check_haiku_display_info (terminal);
+
+  int width, height;
+  BScreen_px_dim (&width, &height);
+  return make_fixnum (width);
+}
+
+DEFUN ("x-display-pixel-height", Fx_display_pixel_height, Sx_display_pixel_height,
+       0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+
+{
+  check_haiku_display_info (terminal);
+
+  int width, height;
+  BScreen_px_dim (&width, &height);
+  return make_fixnum (width);
+}
+
+DEFUN ("x-display-mm-height", Fx_display_mm_height, Sx_display_mm_height, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  struct haiku_display_info *dpyinfo = check_haiku_display_info (terminal);
+
+  int width, height;
+  BScreen_px_dim (&width, &height);
+
+  return make_fixnum (height / (dpyinfo->resy / 25.4));
+}
+
+
+DEFUN ("x-display-mm-width", Fx_display_mm_width, Sx_display_mm_width, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  struct haiku_display_info *dpyinfo = check_haiku_display_info (terminal);
+
+  int width, height;
+  BScreen_px_dim (&width, &height);
+
+  return make_fixnum (height / (dpyinfo->resy / 25.4));
+}
+
+DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame,
+       1, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+     (Lisp_Object parms)
+{
+  return haiku_create_frame (parms, 0);
+}
+
+DEFUN ("x-display-visual-class", Fx_display_visual_class,
+       Sx_display_visual_class, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  check_haiku_display_info (terminal);
+
+  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)  */
 
 \f
 /* 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..ab01e1bfad 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
@@ -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;
 
 \f
@@ -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 442fcf47d3..1e9417b9f1 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 fd498c0e32..0143b590a7 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");

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

* bug#51658: [PATCH] Haiku port (again)
  2021-11-14  1:08                     ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2021-11-14  7:54                       ` Eli Zaretskii
  2021-11-14  9:36                         ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  0 siblings, 1 reply; 31+ messages in thread
From: Eli Zaretskii @ 2021-11-14  7:54 UTC (permalink / raw)
  To: Po Lu; +Cc: 51658

> From: Po Lu <luangruo@yahoo.com>
> Cc: 51658@debbugs.gnu.org
> Date: Sun, 14 Nov 2021 09:08:17 +0800
> 
> >> +  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
> 
> > Do we need libm17n-flt?  For which font back-end is it needed?
> 
> The Cairo font backend has the ability to use libm17n-flt.  The code for
> that is in ftcrfont.c, as it is on X-Windows.

Let's use Cairo with HarfBuzz instead, then.  I don't want to use
libm17n-flt in any new code: it is unmaintained and has known
problems.

> > This sounds no different from any other system, so I wonder why this
> > needs to be described.  On most systems, there's no Super or Hyper
> > keys, and they can be simulated via "C-x @", unless the user
> > configures the keyboard to do some remapping.
> 
> The "option key" and "command key" in Haiku are unusual concepts not
> found in other systems, so it's worthwhile to mention it here.

But the users of Haiku know about those keys, don't they?  The Emacs
manual is not supposed to teach the users about their systems, it's
supposed to teach them about Emacs.

> >> +  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}).
> 
> > How is this different from the GTK build?  If it isn't different,
> > maybe it shouldn't be mentioned at all.
> 
> The variable used is different, as it doesn't make sense to name a Haiku
> variable `x-gtk-...'

But you already describe the variable elsewhere, so why does it have
to be mentioned in this appendix as well?

> >> +@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.
> 
> > Why isn't this treated as drag-and-drop on other platforms?  Then you
> > won't need Haiku-specific documentation and events.
> 
> It might not specifically be a drag event: for example, the Tracker
> could ask Emacs to open a file, because the user selected it from the
> "Recent files" menu.

The result is the same: Emacs visits a file.  I don't think I
understand why these events should be exposed to Lisp.

> >> +@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}.
> 
> > And this should be connected to the session manager, see xsmfns.c, and
> > then it can just trigger the mechanism defined there, as on other
> > platforms.
> 
> Isn't the mechanism there specific to X?

Which part is specific to X?

And if the current implementation uses X-specific code, it just means
the implementation should be extended to allow other platforms trigger
the same mechanism.  Any reason Haiku couldn't do that?  Having
disparate platform-specific mechanisms for performing the same task is
bad for maintenance and bad for documentation and users.  We should
try to present as uniform UI as possible on all platforms, even when
the internal details are different.

> >> @@ -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.
> 
> > I don't understand: non-selected windows don't receive keyboard input
> > on all systems.  So what is special about Haiku here?
> 
> `no-accept-focus' means that the frame isn't willing to accept focus
> from mouse clicks and other mouse motion events.
> 
> On X-Windows, for instance, it is possible to give the keyboard focus to
> such a frame by hitting Alt-TAB (in most window managers); this is not
> possible on Haiku.

Then the text should explicitly mention Alt-TAB.

> > There isn't a single comment in this and other Haiku-specific source
> > files.  Please consider adding at least comments describing what the
> > important non-trivial functions do and what their arguments mean.
> 
> Many of these functions are described in the header file defining their
> prototypes, haiku_support.h.

I realize that many projects document functions in header files, but
we don't.  Our style is to document them in the *.c files instead.

> >> +/* 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.  */};
> 
> AFAIK, `script-representative-chars' doesn't handle languages such as
> Korean or Japanese, but I could be mistaken.

Of course, it does.  It just goes by script, not by language.  But if
script-representative-chars lacks some characters you need, we could
add them as alternatives.

> >> +_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 ();
> >> +}
> 
> > Are these writes to stderr going to stay in the production code?
> 
> It's used to report an error in Emacs that will cause it to abort
> immediately afterwards, with some explanation as to why.  It will be
> valuable for bug reports, as `addr2line', `gdb' and friends are hard to
> set up on Haiku, and the output of the system debugger is not very
> helpful with crashes in Emacs C++ code.  So I think it's OK.  (Something
> similar is done in xterm to explain the GTK display disconnect abort
> bug.)

Is it guaranteed that Emacs has stderr stream connected to some file
or device when it runs in GUI session?  If not, this stuff will be
lost, and it might be a better idea to pop up a GUI dialog window with
this text instead.

> >> +Lisp_Object
> >> +list_system_processes (void)
> >> +{
> 
> > Can the Haiku-specific bits here be moved to some Haiku-specific file?
> 
> It would be more consistent with the other surrounding Unix variants
> (i.e. GNU/Linux, Darwin, and the BSDs) to define them in that file.
> 
> But if you still insist, I have no objection it it.

I'd prefer to have it elsewhere.  We already have quite a mes with
this in sysdep.c.

> Thanks, please see the revised patch.

Will review this later.





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

* bug#51658: [PATCH] Haiku port (again)
  2021-11-14  7:54                       ` Eli Zaretskii
@ 2021-11-14  9:36                         ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-11-14 10:28                           ` Eli Zaretskii
  0 siblings, 1 reply; 31+ messages in thread
From: Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2021-11-14  9:36 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 51658

Eli Zaretskii <eliz@gnu.org> writes:

> Let's use Cairo with HarfBuzz instead, then.  I don't want to use
> libm17n-flt in any new code: it is unmaintained and has known
> problems.

Okay, I deleted the part of configure.ac that checks for m17n-flt when
using Cairo on Haiku.  Thanks.

>> The "option key" and "command key" in Haiku are unusual concepts not
>> found in other systems, so it's worthwhile to mention it here.

> But the users of Haiku know about those keys, don't they?  The Emacs
> manual is not supposed to teach the users about their systems, it's
> supposed to teach them about Emacs.

That is correct, but the correspondence between those keys and the Emacs
keys is not immediately obvious, so I thought it would be worth
mentioning.

>> The variable used is different, as it doesn't make sense to name a Haiku
>> variable `x-gtk-...'

> But you already describe the variable elsewhere, so why does it have
> to be mentioned in this appendix as well?

I moved the description of that variable to the appendix, because it
makes more sense to have it there instead.

>> >> +@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.
>> 
>> > Why isn't this treated as drag-and-drop on other platforms?  Then you
>> > won't need Haiku-specific documentation and events.
>> 
>> It might not specifically be a drag event: for example, the Tracker
>> could ask Emacs to open a file, because the user selected it from the
>> "Recent files" menu.

> The result is the same: Emacs visits a file.  I don't think I
> understand why these events should be exposed to Lisp.

But drag-n-drop events have a POSITION argument, while a position isn't
available when the system sends Emacs a B_REFS_FOUND message.

> Which part is specific to X?

The entire file is preconditioned on HAVE_X_SM, and is based on things
such as `SmcInteractDone' that only make sense on X.

It also relies on functions like `emacs-session-save', which are in
x-win.el and rely on X specific code.

> And if the current implementation uses X-specific code, it just means
> the implementation should be extended to allow other platforms trigger
> the same mechanism.  Any reason Haiku couldn't do that?

Haiku doesn't have a session manager, so it doesn't make sense to use
the mechanism in xsmfns.c: the system doesn't try to restore Emacs when
the system restarts, or to save Emacs's session information when it
quits.

It tells the application that it's about to quit as a courtesy, so it
can perhaps run a few popup dialogs informing the user to save his
files.

> Having disparate platform-specific mechanisms for performing the same
> task is bad for maintenance and bad for documentation and users.  We
> should try to present as uniform UI as possible on all platforms, even
> when the internal details are different.

Otherwise, I agree, thanks.

> Then the text should explicitly mention Alt-TAB.

Thanks, I'll update the text to say that.

> I realize that many projects document functions in header files, but
> we don't.  Our style is to document them in the *.c files instead.

Thanks, I will move the comments there instead.

>> >> +/* 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.  */};
>> 
>> AFAIK, `script-representative-chars' doesn't handle languages such as
>> Korean or Japanese, but I could be mistaken.
>
> Of course, it does.  It just goes by script, not by language.  But if
> script-representative-chars lacks some characters you need, we could
> add them as alternatives.

Makes sense, I think I see what I need in script_representative_chars
now.

>> It's used to report an error in Emacs that will cause it to abort
>> immediately afterwards, with some explanation as to why.  It will be
>> valuable for bug reports, as `addr2line', `gdb' and friends are hard to
>> set up on Haiku, and the output of the system debugger is not very
>> helpful with crashes in Emacs C++ code.  So I think it's OK.  (Something
>> similar is done in xterm to explain the GTK display disconnect abort
>> bug.)

> Is it guaranteed that Emacs has stderr stream connected to some file
> or device when it runs in GUI session?  If not, this stuff will be
> lost, and it might be a better idea to pop up a GUI dialog window with
> this text instead.

Yes, stderr is connected to syslog_daemon when launched from anything
other than a pseudo terminal, IIUC.

> I'd prefer to have it elsewhere.  We already have quite a mes with
> this in sysdep.c.

OK, I'll move it somewhere else.

> Will review this later.

Thank you!  I'll also work on implementing the changes I talked about
earlier later, as some of them involve rather large changes.

I'll post another patch once it's ready.





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

* bug#51658: [PATCH] Haiku port (again)
  2021-11-14  9:36                         ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2021-11-14 10:28                           ` Eli Zaretskii
  2021-11-14 10:39                             ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  0 siblings, 1 reply; 31+ messages in thread
From: Eli Zaretskii @ 2021-11-14 10:28 UTC (permalink / raw)
  To: Po Lu; +Cc: 51658

> From: Po Lu <luangruo@yahoo.com>
> Cc: 51658@debbugs.gnu.org
> Date: Sun, 14 Nov 2021 17:36:01 +0800
> 
> >> > Why isn't this treated as drag-and-drop on other platforms?  Then you
> >> > won't need Haiku-specific documentation and events.
> >> 
> >> It might not specifically be a drag event: for example, the Tracker
> >> could ask Emacs to open a file, because the user selected it from the
> >> "Recent files" menu.
> 
> > The result is the same: Emacs visits a file.  I don't think I
> > understand why these events should be exposed to Lisp.
> 
> But drag-n-drop events have a POSITION argument, while a position isn't
> available when the system sends Emacs a B_REFS_FOUND message.

How is POSITION used?  Can't you fake POSITION, so we get the same
event as on other platforms?

> > Which part is specific to X?
> 
> The entire file is preconditioned on HAVE_X_SM, and is based on things
> such as `SmcInteractDone' that only make sense on X.
> 
> It also relies on functions like `emacs-session-save', which are in
> x-win.el and rely on X specific code.
> 
> > And if the current implementation uses X-specific code, it just means
> > the implementation should be extended to allow other platforms trigger
> > the same mechanism.  Any reason Haiku couldn't do that?
> 
> Haiku doesn't have a session manager, so it doesn't make sense to use
> the mechanism in xsmfns.c: the system doesn't try to restore Emacs when
> the system restarts, or to save Emacs's session information when it
> quits.
> 
> It tells the application that it's about to quit as a courtesy, so it
> can perhaps run a few popup dialogs informing the user to save his
> files.

Every modern system has something similar, so it would make sense to
have a unified mechanism for handling those system messages in Emacs.
If xsmfns.c is too X-specific, we should build a layer above it, and
then implement th lower layer for Haiku.  It makes no sense to me to
introduce new events for specific platforms when similar features
already exist and just need to be extended or abstracted.





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

* bug#51658: [PATCH] Haiku port (again)
  2021-11-14 10:28                           ` Eli Zaretskii
@ 2021-11-14 10:39                             ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-11-14 10:54                               ` Eli Zaretskii
  2021-11-15  2:59                               ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  0 siblings, 2 replies; 31+ messages in thread
From: Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2021-11-14 10:39 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 51658

Eli Zaretskii <eliz@gnu.org> writes:

> How is POSITION used?  Can't you fake POSITION, so we get the same
> event as on other platforms?

AFAIU, POSITION is used to determine the window where the file should
appear.  It should be possible to fake.

We could get the mouse position on-screen, but it may lead to odd
results if the user asks Emacs to open a file through the Tracker or the
Recents menu, and the mouse is not on top of an Emacs frame, or if the
system starts Emacs to open a file.

What position do you think would be reasonable to report here?

BTW, it seems that the NS port has the same problem here: it defines the
custom events `ns-open-file' and `ns-drag-and-drop' and `ns-power-off'.

> Every modern system has something similar, so it would make sense to
> have a unified mechanism for handling those system messages in Emacs.
> If xsmfns.c is too X-specific, we should build a layer above it, and
> then implement th lower layer for Haiku.  It makes no sense to me to
> introduce new events for specific platforms when similar features
> already exist and just need to be extended or abstracted.

Yes, makes sense.  It would be worthwhile just to solve this problem
with the NS port.  I will look into it.

Thanks in advance.





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

* bug#51658: [PATCH] Haiku port (again)
  2021-11-14 10:39                             ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2021-11-14 10:54                               ` Eli Zaretskii
  2021-11-14 11:06                                 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-11-15  2:59                               ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  1 sibling, 1 reply; 31+ messages in thread
From: Eli Zaretskii @ 2021-11-14 10:54 UTC (permalink / raw)
  To: Po Lu; +Cc: 51658

> From: Po Lu <luangruo@yahoo.com>
> Cc: 51658@debbugs.gnu.org
> Date: Sun, 14 Nov 2021 18:39:04 +0800
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> > How is POSITION used?  Can't you fake POSITION, so we get the same
> > event as on other platforms?
> 
> AFAIU, POSITION is used to determine the window where the file should
> appear.  It should be possible to fake.
> 
> We could get the mouse position on-screen, but it may lead to odd
> results if the user asks Emacs to open a file through the Tracker or the
> Recents menu, and the mouse is not on top of an Emacs frame, or if the
> system starts Emacs to open a file.
> 
> What position do you think would be reasonable to report here?

The position in a window where your current code visits the file, I
guess?  Because this feature already works in your modified Emacs,
right?

> BTW, it seems that the NS port has the same problem here: it defines the
> custom events `ns-open-file' and `ns-drag-and-drop' and `ns-power-off'.

Which is BAAAAD!

> > Every modern system has something similar, so it would make sense to
> > have a unified mechanism for handling those system messages in Emacs.
> > If xsmfns.c is too X-specific, we should build a layer above it, and
> > then implement th lower layer for Haiku.  It makes no sense to me to
> > introduce new events for specific platforms when similar features
> > already exist and just need to be extended or abstracted.
> 
> Yes, makes sense.  It would be worthwhile just to solve this problem
> with the NS port.  I will look into it.

Thanks.





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

* bug#51658: [PATCH] Haiku port (again)
  2021-11-14 10:54                               ` Eli Zaretskii
@ 2021-11-14 11:06                                 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  0 siblings, 0 replies; 31+ messages in thread
From: Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2021-11-14 11:06 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 51658

Eli Zaretskii <eliz@gnu.org> writes:

>> AFAIU, POSITION is used to determine the window where the file should
>> appear.  It should be possible to fake.
>> 
>> We could get the mouse position on-screen, but it may lead to odd
>> results if the user asks Emacs to open a file through the Tracker or the
>> Recents menu, and the mouse is not on top of an Emacs frame, or if the
>> system starts Emacs to open a file.
>> 
>> What position do you think would be reasonable to report here?

> The position in a window where your current code visits the file, I
> guess?  Because this feature already works in your modified Emacs,
> right?

Yes, thanks, it makes sense now.





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

* bug#51658: [PATCH] Haiku port (again)
  2021-11-14 10:39                             ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-11-14 10:54                               ` Eli Zaretskii
@ 2021-11-15  2:59                               ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-11-20  7:03                                 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  1 sibling, 1 reply; 31+ messages in thread
From: Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2021-11-15  2:59 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 51658

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

Po Lu <luangruo@yahoo.com> writes:

> AFAIU, POSITION is used to determine the window where the file should
> appear.  It should be possible to fake.
>
> We could get the mouse position on-screen, but it may lead to odd
> results if the user asks Emacs to open a file through the Tracker or the
> Recents menu, and the mouse is not on top of an Emacs frame, or if the
> system starts Emacs to open a file.
>
> What position do you think would be reasonable to report here?

I moved the Haiku system-dependents from sysdep.c to a separate file
`haiku.c'.  I also removed the extraneous events (drag-and-drop now
sends drag-n-drop events, like everywhere else, and haiku-quit-requested
has been removed for now.)

Here's an updated changelog.  Please see the attached patch, thanks.

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/haiku.c: Add new files for Haiku operating system 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.

* 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.

* src/termhooks.h (enum output_method): Add output_haiku.
(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.


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

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 c231c2ceae..1ee5f0c6ad 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,39 @@ AC_DEFUN
   with_xft=no
 fi
 
+HAIKU_OBJ=
+HAIKU_CXX_OBJ=
+HAIKU_LIBS=
+HAIKU_CFLAGS=
+
+if test "$opsys" = "haiku"; then
+  HAIKU_OBJ="$HAIKU_OBJ haiku.o"
+fi
+
+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 +2302,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 +2636,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 +2661,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 +2681,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 +3332,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 +3424,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 +3589,58 @@ 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
+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 +3762,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 +4080,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 +4155,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 +4184,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 +4602,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])
 
@@ -4814,7 +4962,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
@@ -5069,7 +5217,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
@@ -5165,6 +5313,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
 
 
@@ -5386,8 +5542,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)
@@ -5430,6 +5603,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
@@ -5542,6 +5731,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
@@ -5930,7 +6130,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..ce92435ae7 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,11 @@ 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 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 +1624,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..cf6f2c2b47
--- /dev/null
+++ b/doc/emacs/haiku.texi
@@ -0,0 +1,144 @@
+@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 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}; ff 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, use the binary named
+@code{emacs} 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.
+
+  It is 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.  The variables that allow for
+that are described below.
+
+@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-shift-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.
+
+@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}).
+
+@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.
+@end table
+
+@subsection What to do when Emacs crashes
+@cindex crashes, Haiku
+@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.
+
+@table @code
+@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
+
+@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}).
+
+  Two of these backends, @code{ftcr} and @code{ftcrfont} are identical
+to their counterparts on the X Window System.  There is also a
+Haiku-specific backend named @code{haiku}, that uses the App Server to
+draw fonts, but does not at present support display of color font and
+emoji.
diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi
index dd2c6e003f..0dd1b5bdd0 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},
@@ -8247,6 +8248,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
@@ -8293,6 +8296,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
diff --git a/doc/lispref/frames.texi b/doc/lispref/frames.texi
index 31ad82b7ad..56fc8859a0 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.
 
+@footnote{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,10 @@ 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 can be several font drivers (@pxref{Haiku Fonts,,, emacs,
+The GNU Emacs Manual}).
+
+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 +3155,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.
+
+@footnote{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 +3178,9 @@ Raising and Lowering
 @var{frame1} remains unaltered.
 
 Some window managers may refuse to restack windows.
+
+@footnote{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 +3290,12 @@ Child Frames
 allowing them to be positioned so they do not obscure the parent frame
 while still being visible themselves.
 
+@footnote{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.
+
 \f
 * Obsolete platforms
 
diff --git a/etc/NEWS b/etc/NEWS
index 312fc18f4f..cc9335851b 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -24,6 +24,25 @@ applies, and please also update docstrings as needed.
 \f
 * 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 f506881a4b..c548ee9b36 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.
@@ -1105,6 +1114,13 @@ In your ~/.Xresources file, then run
 
 And restart Emacs.
 
+** On Haiku, BeCJK doesn't work properly with Emacs
+
+Some popular Haiku input methods such BeCJK are known to behave badly
+when interacting with Emacs, in ways such as stealing input focus and
+displaying popup windows that don't disappear.  If you are affected,
+you should use an Emacs input method instead.
+
 * X runtime problems
 
 ** X keyboard problems
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..5b16d63e54 100644
--- a/lisp/loadup.el
+++ b/lisp/loadup.el
@@ -302,6 +302,11 @@
       (load "term/common-win")
       (load "term/x-win")))
 
+(if (featurep 'haiku)
+    (progn
+      (load "term/common-win")
+      (load "term/haiku-win")))
+
 (if (or (eq system-type 'windows-nt)
         (featurep 'w32))
     (progn
@@ -557,6 +562,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..36af10d2c7
--- /dev/null
+++ b/lisp/term/haiku-win.el
@@ -0,0 +1,134 @@
+;;; 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-dnd-handle-drag-n-drop-event (event)
+  "Handle specified drag-n-drop EVENT."
+  (interactive "e")
+  (let* ((string (caddr event))
+	 (window (posn-window (event-start event))))
+    (with-selected-window window
+      (raise-frame)
+      (dnd-handle-one-url window 'private (concat "file:" string)))))
+
+(define-key special-event-map [drag-n-drop]
+            'haiku-dnd-handle-drag-n-drop-event)
+
+(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 4015448ece..859b30564a 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..f52dae4719 100644
--- a/src/floatfns.c
+++ b/src/floatfns.c
@@ -347,6 +347,21 @@ DEFUN ("logb", Flogb, Slogb, 1, 1, 0,
 double_integer_scale (double d)
 {
   int exponent = ilogb (d);
+#ifdef HAIKU
+  /* On Haiku, the values returned by ilogb are nonsensical when
+     confronted with tiny numbers, inf, or NaN, which breaks the trick
+     used by code on other platforms, so we have to test for each case
+     manually, and return the appropriate value.  */
+  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 b503123b96..d423fd46b7 100644
--- a/src/font.c
+++ b/src/font.c
@@ -5751,6 +5751,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 6694164e09..2da5ec4504 100644
--- a/src/font.h
+++ b/src/font.h
@@ -965,7 +965,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;
@@ -999,7 +999,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 03e44ec30e..cf592759ab 100644
--- a/src/ftfont.c
+++ b/src/ftfont.c
@@ -3108,6 +3108,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.c b/src/haiku.c
new file mode 100644
index 0000000000..efed5d064f
--- /dev/null
+++ b/src/haiku.c
@@ -0,0 +1,288 @@
+/* Haiku subroutines that are general to the Haiku operating system.
+   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 "process.h"
+#include "coding.h"
+
+#include <kernel/OS.h>
+
+#include <pwd.h>
+#include <stdlib.h>
+
+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;
+}
+
+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;
+}
+
+
+/* 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;
+}
diff --git a/src/haiku_draw_support.cc b/src/haiku_draw_support.cc
new file mode 100644
index 0000000000..5b1eccfbe6
--- /dev/null
+++ b/src/haiku_draw_support.cc
@@ -0,0 +1,488 @@
+/* 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 ();
+}
+
+/* Convert RGB32 color color from RGB color space to its
+   HSL components pointed to by H, S and L.  */
+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..9ac0400969
--- /dev/null
+++ b/src/haiku_font_support.cc
@@ -0,0 +1,596 @@
+/* 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;
+}
+
+/* Return non-null if FONT contains CHR, a Unicode code-point.  */
+int
+BFont_have_char_p (void *font, int32_t chr)
+{
+  BFont *ft = (BFont *) font;
+  return ft->IncludesBlock (chr, chr);
+}
+
+/* Return non-null if font contains a block from BEG to END.  */
+int
+BFont_have_char_block (void *font, int32_t beg, int32_t end)
+{
+  BFont *ft = (BFont *) font;
+  return ft->IncludesBlock (beg, 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.  */
+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);
+}
+
+/* The same, but for a variable amount of chars.  */
+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);
+    }
+}
+
+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;
+}
+
+/* Delete every element of the font pattern PT.  */
+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;
+    }
+}
+
+/* Find all fonts matching the font pattern PT.  */
+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;
+}
+
+/* Find and open a font matching the pattern PAT, which must have its
+   family set.  */
+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;
+}
+
+/* Query the family of the default fixed font.  */
+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..c152d9b086
--- /dev/null
+++ b/src/haiku_io.c
@@ -0,0 +1,207 @@
+/* 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
+
+/* The port used to send messages from the application thread to
+   Emacs.  */
+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 ();
+}
+
+/* Read the size of the next message into len, returning -1 if the
+   query fails or there is no next message.  */
+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;
+}
+
+/* Read the next message into BUF, putting its type into TYPE,
+   assuming the message is at most LEN long.  Return 0 if successful
+   and -1 if the read fails.  */
+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;
+}
+
+/* The same as haiku_read, but time out after TIMEOUT microseconds.
+   Input is blocked when an attempt to read is in progress.  */
+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;
+}
+
+/* Write a message with type TYPE into BUF.  */
+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");
+}
+
+/* Record an unwind protect from C++ code.  */
+void
+record_c_unwind_protect_from_cxx (void (*fn) (void *), void *r)
+{
+  record_unwind_protect_ptr (fn, r);
+}
+
+/* SPECPDL_IDX that is safe from C++ code.  */
+ptrdiff_t
+c_specpdl_idx_from_cxx (void)
+{
+  return SPECPDL_INDEX ();
+}
+
+/* unbind_to (IDX, Qnil), but safe from C++ code.  */
+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..99d4ee7914
--- /dev/null
+++ b/src/haiku_support.cc
@@ -0,0 +1,2930 @@
+/* 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;
+    int32 x, y;
+    void *window;
+
+    if ((msg->FindPointer ("window", 0, &window) != B_OK)
+	|| (msg->FindInt32 ("x", 0, &x) != B_OK)
+	|| (msg->FindInt32 ("y", 0, &y) != B_OK))
+      return;
+
+    rq.window = window;
+    rq.x = x;
+    rq.y = y;
+
+    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;
+	BPoint whereto;
+
+        if (msg->FindRef ("refs", &ref) == B_OK)
+	  {
+	    msg->what = B_REFS_RECEIVED;
+	    msg->AddPointer ("window", this);
+	    if (msg->FindPoint ("_drop_point_", &whereto) == B_OK)
+	      {
+		this->ConvertFromScreen (&whereto);
+		msg->AddInt32 ("x", whereto.x);
+		msg->AddInt32 ("y", whereto.y);
+	      }
+	    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)
+	  gui_abort ("strdup failed");
+      }
+
+    if (help)
+      {
+	this->help = strdup (help);
+	if (!this->help)
+	  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;
+}
+
+/* Take BITMAP, a reference to a BBitmap, and return a pointer to its
+   data.  */
+void *
+BBitmap_data (void *bitmap)
+{
+  return ((BBitmap *) bitmap)->Bits ();
+}
+
+/* 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'.  */
+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;
+}
+
+/* Create new bitmap in RGB32 format, or in GRAY1 if MONO_P is
+   non-zero.  */
+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);
+}
+
+/* Set up an application and return it.  If starting the application
+   thread fails, abort Emacs.  */
+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;
+}
+
+/* Set up and return a window with its view put in VIEW.  */
+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 ();
+}
+
+/* Set WINDOW's offset to X, Y.  */
+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);
+}
+
+/* Iconify WINDOW.  */
+void
+BWindow_iconify (void *window)
+{
+  if (((BWindow *) window)->IsHidden ())
+    BWindow_set_visible (window, true);
+  ((BWindow *) window)->Minimize (true);
+}
+
+/* Show or hide WINDOW.  */
+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 ();
+}
+
+/* Change the title of WINDOW to the multibyte string TITLE.  */
+void
+BWindow_retitle (void *window, const char *title)
+{
+  ((BWindow *) window)->SetTitle (title);
+}
+
+/* Resize WINDOW to WIDTH by HEIGHT.  */
+void
+BWindow_resize (void *window, int width, int height)
+{
+  ((BWindow *) window)->ResizeTo (width, height);
+}
+
+/* Activate WINDOW, making it the subject of keyboard focus and
+   bringing it to the front of the screen.  */
+void
+BWindow_activate (void *window)
+{
+  ((BWindow *) window)->Activate ();
+}
+
+/* Return the pixel dimensions of the main screen in WIDTH and
+   HEIGHT.  */
+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;
+}
+
+/* Resize VIEW to WIDTH, HEIGHT.  */
+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 ();
+}
+
+/* Map the keycode KC, storing the result in CODE and 1 in
+   NON_ASCII_P if it should be used.  */
+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;
+}
+
+/* Make a scrollbar, attach it to VIEW's window, and return it.  */
+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);
+}
+
+/* Return the default scrollbar size.  */
+int
+BScrollBar_default_size (int horizontal_p)
+{
+  return horizontal_p ? B_H_SCROLL_BAR_HEIGHT : B_V_SCROLL_BAR_WIDTH;
+}
+
+/* Invalidate VIEW, causing it to be drawn again.  */
+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 ();
+}
+
+/* Lock VIEW in preparation for drawing operations.  This should be
+   called before any attempt to draw onto VIEW or to lock it for Cairo
+   drawing.  `BView_draw_unlock' should be called afterwards.  */
+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 ();
+}
+
+/* Tell VIEW it has been clicked at X by Y.  */
+void
+BView_mouse_down (void *view, int x, int y)
+{
+  BView *vw = (BView *) view;
+  if (vw->LockLooper ())
+    {
+      vw->MouseDown (BPoint (x, y));
+      vw->UnlockLooper ();
+    }
+}
+
+/* Tell VIEW the mouse has been released at X by Y.  */
+void
+BView_mouse_up (void *view, int x, int y)
+{
+  BView *vw = (BView *) view;
+  if (vw->LockLooper ())
+    {
+      vw->MouseUp (BPoint (x, y));
+      vw->UnlockLooper ();
+    }
+}
+
+/* Tell VIEW that the mouse has moved to Y by Y.  */
+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 ();
+    }
+}
+
+/* Import BITS into BITMAP using the B_GRAY1 colorspace.  */
+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 ();
+    }
+}
+
+/* Make a scrollbar at X, Y known to the view VIEW.  */
+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 ();
+    }
+}
+
+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);
+}
+
+/* Perform an in-place conversion of X and Y from VIEW's coordinate
+   system to its screen's coordinate system.  */
+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);
+}
+
+/* Decorate or undecorate WINDOW depending on DECORATE_P.  */
+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 ();
+}
+
+/* Decorate WINDOW appropriately for use as a tooltip.  */
+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 ();
+}
+
+/* Set B_AVOID_FOCUS on WINDOW if AVOID_FOCUS_P is non-nil, or clear
+   it otherwise.  */
+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;
+}
+
+/* Return the current workspace.  */
+uint32_t
+haiku_current_workspace (void)
+{
+  return current_workspace ();
+}
+
+/* Return a bitmask consisting of workspaces WINDOW is on.  */
+uint32_t
+BWindow_workspaces (void *window)
+{
+  return ((BWindow *) window)->Workspaces ();
+}
+
+/* Create a popup menu.  */
+void *
+BPopUpMenu_new (const char *name)
+{
+  BPopUpMenu *menu = new EmacsPopUpMenu (name);
+  menu->SetRadioMode (0);
+  return menu;
+}
+
+/* Add a title item to MENU.  These items cannot be highlighted or
+   triggered, and their labels will display as bold text.  */
+void
+BMenu_add_title (void *menu, const char *text)
+{
+  EmacsTitleMenuItem *it = new EmacsTitleMenuItem (text);
+  BMenu *mn = (BMenu *) menu;
+  mn->AddItem (it);
+}
+
+/* Add an item to the menu MENU.  */
+void
+BMenu_add_item (void *menu, const char *label, void *ptr, bool enabled_p,
+		bool marked_p, bool mbar_p, void *mbw_ptr, const char *key,
+		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);
+}
+
+/* Add a separator to the menu MENU.  */
+void
+BMenu_add_separator (void *menu)
+{
+  BMenu *m = (BMenu *) menu;
+
+  m->AddSeparatorItem ();
+}
+
+/* Create a submenu and attach it to MENU.  */
+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;
+}
+
+/* Create a submenu that notifies Emacs upon opening.  */
+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;
+}
+
+/* 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.  */
+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;
+}
+
+/* Delete the entire menu hierarchy of MENU, and then delete MENU
+   itself.  */
+void
+BPopUpMenu_delete (void *menu)
+{
+  delete (BPopUpMenu *) menu;
+}
+
+/* Create a menubar, attach it to VIEW, and return it.  */
+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;
+}
+
+/* Delete MENUBAR along with all subitems. */
+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;
+}
+
+/* Delete all items from MENU.  */
+void
+BMenu_delete_all (void *menu)
+{
+  BMenu *mn = (BMenu *) menu;
+  mn->RemoveItems (0, mn->CountItems (), true);
+}
+
+/* Delete COUNT items from MENU starting from START.  */
+void
+BMenu_delete_from (void *menu, int start, int count)
+{
+  BMenu *mn = (BMenu *) menu;
+  mn->RemoveItems (start, count, true);
+}
+
+/* Count items in menu MENU.  */
+int
+BMenu_count_items (void *menu)
+{
+  return ((BMenu *) menu)->CountItems ();
+}
+
+/* Find the item in MENU at IDX.  */
+void *
+BMenu_item_at (void *menu, int idx)
+{
+  return ((BMenu *) menu)->ItemAt (idx);
+}
+
+/* Set ITEM's label to LABEL.  */
+void
+BMenu_item_set_label (void *item, const char *label)
+{
+  ((BMenuItem *) item)->SetLabel (label);
+}
+
+/* Get ITEM's menu.  */
+void *
+BMenu_item_get_menu (void *item)
+{
+  return ((BMenuItem *) item)->Submenu ();
+}
+
+/* Emit a beep noise.  */
+void
+haiku_ring_bell (void)
+{
+  beep ();
+}
+
+/* Create a BAlert with TEXT.  */
+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);
+}
+
+/* Add a button to ALERT and return the button.  */
+void *
+BAlert_add_button (void *alert, const char *text)
+{
+  BAlert *al = (BAlert *) alert;
+  al->AddButton (text);
+  return al->ButtonAt (al->CountButtons () - 1);
+}
+
+/* Run ALERT, returning the number of the button that was selected,
+   or -1 if no button was selected before the alert was closed.  */
+int32_t
+BAlert_go (void *alert)
+{
+  return ((BAlert *) alert)->Go ();
+}
+
+/* Enable or disable BUTTON depending on ENABLED_P.  */
+void
+BButton_set_enabled (void *button, int enabled_p)
+{
+  ((BButton *) button)->SetEnabled (enabled_p);
+}
+
+/* Set VIEW's tooltip to TOOLTIP.  */
+void
+BView_set_tooltip (void *view, const char *tooltip)
+{
+  ((BView *) view)->SetToolTip (tooltip);
+}
+
+/* Set VIEW's tooltip to a sticky tooltip at X by Y.  */
+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 ();
+}
+
+/* Delete ALERT.  */
+void
+BAlert_delete (void *alert)
+{
+  delete (BAlert *) alert;
+}
+
+/* Place the resolution of the monitor in DPI in RSSX and RSSY.  */
+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;
+    }
+}
+
+/* Add WINDOW to OTHER_WINDOW's subset and parent it to
+   OTHER_WINDOW.  */
+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 ();
+}
+
+/* Place text describing the current version of Haiku in VERSION,
+   which should be a buffer LEN bytes wide.  */
+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);
+    }
+}
+
+/* Return the amount of color planes in the current display.  */
+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;
+}
+
+/* Return the amount of colors the display can handle.  */
+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;
+}
+
+/* Warp the pointer to X by Y.  */
+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);
+}
+
+/* Update the position of CHILD in WINDOW without actually moving
+   it.  */
+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 ();
+}
+
+/* Find an appropriate view to draw onto.  If VW is double-buffered,
+   this will be the view used for double buffering instead of VW
+   itself.  */
+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;
+}
+
+/* Set up double buffering for 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 ();
+}
+
+/* Flip and invalidate the view VW.  */
+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 ();
+}
+
+/* Disable double buffering for VW.  */
+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 ();
+}
+
+/* Return non-0 if VW is double-buffered.  */
+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));
+}
+
+/* Popup a file dialog.  */
+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);
+    }
+}
+
+/* Temporarily fill VIEW with COLOR.  */
+void
+EmacsView_do_visible_bell (void *view, uint32_t color)
+{
+  EmacsView *vw = (EmacsView *) view;
+  vw->DoVisibleBell (color);
+}
+
+/* Zoom WINDOW.  */
+void
+BWindow_zoom (void *window)
+{
+  BWindow *w = (BWindow *) window;
+  w->Zoom ();
+}
+
+/* Make WINDOW fullscreen if FULLSCREEN_P.  */
+void
+EmacsWindow_make_fullscreen (void *window, int fullscreen_p)
+{
+  EmacsWindow *w = (EmacsWindow *) window;
+  w->MakeFullscreen (fullscreen_p);
+}
+
+/* Unzoom (maximize) WINDOW.  */
+void
+EmacsWindow_unzoom (void *window)
+{
+  EmacsWindow *w = (EmacsWindow *) window;
+  w->UnZoom ();
+}
+
+/* Move the pointer into MBAR and start tracking.  */
+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
+
+/* Return the size of BITMAP's data, in bytes.  */
+size_t
+BBitmap_bytes_length (void *bitmap)
+{
+  BBitmap *bm = (BBitmap *) bitmap;
+  return bm->BitsLength ();
+}
+
+/* Show VIEW's tooltip.  */
+void
+BView_show_tooltip (void *view)
+{
+  BView *vw = (BView *) view;
+  if (vw->LockLooper ())
+    {
+      vw->ShowToolTip (vw->ToolTip ());
+      vw->UnlockLooper ();
+    }
+}
+
+
+#ifdef USE_BE_CAIRO
+/* Return VIEW's cairo surface.  */
+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;
+}
+
+/* Transfer each clip rectangle in VIEW to the cairo context
+   CTX.  */
+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);
+}
+
+/* Lock WINDOW in preparation for drawing using Cairo.  */
+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");
+}
+
+/* Unlock WINDOW in preparation for drawing using Cairo.  */
+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
+
+/* Get the width of STR in the plain font.  */
+int
+be_string_width_with_plain_font (const char *str)
+{
+  return be_plain_font->StringWidth (str);
+}
+
+/* Get the ascent + descent of the plain font.  */
+int
+be_plain_font_height (void)
+{
+  struct font_height fheight;
+  be_plain_font->GetHeight (&fheight);
+
+  return fheight.ascent + fheight.descent;
+}
+
+/* Return the number of physical displays connected.  */
+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;
+}
+
+/* Set the minimum width the user can resize WINDOW to.  */
+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 ();
+}
+
+/* Set the alignment of WINDOW's dimensions.  */
+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..9f5f3c77e3
--- /dev/null
+++ b/src/haiku_support.h
@@ -0,0 +1,869 @@
+/* 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
+{
+  void *window;
+  int x, y;
+  /* 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
+
+  extern port_id port_application_to_emacs;
+
+  extern void haiku_io_init (void);
+  extern void haiku_io_init_in_app_thread (void);
+
+  extern void
+  haiku_read_size (ssize_t *len);
+
+  extern int
+  haiku_read (enum haiku_event_type *type, void *buf, ssize_t len);
+
+  extern int
+  haiku_read_with_timeout (enum haiku_event_type *type, void *buf, ssize_t len,
+			   time_t timeout);
+
+  extern int
+  haiku_write (enum haiku_event_type type, void *buf);
+
+  extern int
+  haiku_write_without_signal (enum haiku_event_type type, void *buf);
+
+  extern void
+  rgb_color_hsl (uint32_t rgb, double *h, double *s, double *l);
+
+  extern void
+  hsl_color_rgb (double h, double s, double l, uint32_t *rgb);
+
+  extern void *
+  BBitmap_new (int width, int height, int mono_p);
+
+  extern void *
+  BBitmap_data (void *bitmap);
+
+  extern int
+  BBitmap_convert (void *bitmap, void **new_bitmap);
+
+  extern void
+  BBitmap_free (void *bitmap);
+
+  extern void
+  BBitmap_dimensions (void *bitmap, int *left, int *top,
+		      int *right, int *bottom, int32_t *bytes_per_row,
+		      int *mono_p);
+
+  extern void *
+  BApplication_setup (void);
+
+  extern void *
+  BWindow_new (void *view);
+
+  extern void
+  BWindow_quit (void *window);
+
+  extern void
+  BWindow_set_offset (void *window, int x, int y);
+
+  extern void
+  BWindow_iconify (void *window);
+
+  extern void
+  BWindow_set_visible (void *window, int visible_p);
+
+  extern void
+  BFont_close (void *font);
+
+  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);
+
+  extern int
+  BFont_have_char_p (void *font, int32_t chr);
+
+  extern int
+  BFont_have_char_block (void *font, int32_t beg, int32_t end);
+
+  extern void
+  BFont_char_bounds (void *font, const char *mb_str, int *advance,
+		     int *lb, int *rb);
+
+  extern void
+  BFont_nchar_bounds (void *font, const char *mb_str, int *advance,
+		      int *lb, int *rb, int32_t n);
+
+  extern void
+  BWindow_retitle (void *window, const char *title);
+
+  extern void
+  BWindow_resize (void *window, int width, int height);
+
+  extern void
+  BWindow_activate (void *window);
+
+  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);
+
+  extern void
+  BScreen_px_dim (int *width, int *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);
+
+  extern void
+  BCursor_delete (void *cursor);
+
+  extern void
+  BView_set_view_cursor (void *view, void *cursor);
+
+  extern void
+  BWindow_Flush (void *window);
+
+  extern void
+  BMapKey (uint32_t kc, int *non_ascii_p, unsigned *code);
+
+  extern void *
+  BScrollBar_make_for_view (void *view, int horizontal_p,
+			    int x, int y, int x1, int y1,
+			    void *scroll_bar_ptr);
+
+  extern void
+  BScrollBar_delete (void *sb);
+
+  extern void
+  BView_move_frame (void *view, int x, int y, int x1, int y1);
+
+  extern void
+  BView_scroll_bar_update (void *sb, int portion, int whole, int position);
+
+  extern int
+  BScrollBar_default_size (int horizontal_p);
+
+  extern void
+  BView_invalidate (void *view);
+
+  extern void
+  BView_draw_lock (void *view);
+
+  extern void
+  BView_draw_unlock (void *view);
+
+  extern void
+  BWindow_center_on_screen (void *window);
+
+  extern void
+  BView_mouse_moved (void *view, int x, int y, uint32_t transit);
+
+  extern void
+  BView_mouse_down (void *view, int x, int y);
+
+  extern void
+  BView_mouse_up (void *view, int x, int y);
+
+  extern void
+  BBitmap_import_mono_bits (void *bitmap, void *bits, int wd, int h);
+
+  extern void
+  haiku_font_pattern_free (struct haiku_font_pattern *pt);
+
+  extern struct haiku_font_pattern *
+  BFont_find (struct haiku_font_pattern *pt);
+
+  extern int
+  BFont_open_pattern (struct haiku_font_pattern *pat, void **font, float size);
+
+  extern void
+  BFont_populate_fixed_family (struct haiku_font_pattern *ptn);
+
+  extern void
+  BFont_populate_plain_family (struct haiku_font_pattern *ptn);
+
+  extern void
+  BView_publish_scroll_bar (void *view, int x, int y, int width, int height);
+
+  extern void
+  BView_forget_scroll_bar (void *view, int x, int y, int width, int height);
+
+  extern void
+  BView_get_mouse (void *view, int *x, int *y);
+
+  extern void
+  BView_convert_to_screen (void *view, int *x, int *y);
+
+  extern void
+  BView_convert_from_screen (void *view, int *x, int *y);
+
+  extern void
+  BWindow_change_decoration (void *window, int decorate_p);
+
+  extern void
+  BWindow_set_tooltip_decoration (void *window);
+
+  extern void
+  BWindow_set_avoid_focus (void *window, int avoid_focus_p);
+
+  extern void
+  BView_emacs_delete (void *view);
+
+  extern uint32_t
+  haiku_current_workspace (void);
+
+  extern uint32_t
+  BWindow_workspaces (void *window);
+
+  extern void *
+  BPopUpMenu_new (const char *name);
+
+  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);
+
+  extern void
+  BMenu_add_separator (void *menu);
+
+  extern void *
+  BMenu_new_submenu (void *menu, const char *label, bool enabled_p);
+
+  extern void *
+  BMenu_new_menu_bar_submenu (void *menu, const char *label);
+
+  extern int
+  BMenu_count_items (void *menu);
+
+  extern void *
+  BMenu_item_at (void *menu, int idx);
+
+  extern void *
+  BMenu_run (void *menu, int x, int y);
+
+  extern void
+  BPopUpMenu_delete (void *menu);
+
+  extern void *
+  BMenuBar_new (void *view);
+
+  extern void
+  BMenu_delete_all (void *menu);
+
+  extern void
+  BMenuBar_delete (void *menubar);
+
+  extern void
+  BMenu_item_set_label (void *item, const char *label);
+
+  extern void *
+  BMenu_item_get_menu (void *item);
+
+  extern void
+  BMenu_delete_from (void *menu, int start, int count);
+
+  extern void
+  haiku_ring_bell (void);
+
+  extern void *
+  BAlert_new (const char *text, enum haiku_alert_type type);
+
+  extern void *
+  BAlert_add_button (void *alert, const char *text);
+
+  extern int32_t
+  BAlert_go (void *alert);
+
+  extern void
+  BButton_set_enabled (void *button, int enabled_p);
+
+  extern void
+  BView_set_tooltip (void *view, const char *tooltip);
+
+  extern void
+  BAlert_delete (void *alert);
+
+  extern void
+  BScreen_res (double *rrsx, double *rrsy);
+
+  extern void
+  EmacsWindow_parent_to (void *window, void *other_window);
+
+  extern void
+  EmacsWindow_unparent (void *window);
+
+  extern int
+  BFont_string_width (void *font, const char *utf8);
+
+  extern void
+  be_get_version_string (char *version, int len);
+
+  extern int
+  be_get_display_planes (void);
+
+  extern int
+  be_get_display_color_cells (void);
+
+  extern void
+  be_warp_pointer (int x, int y);
+
+  extern void
+  EmacsWindow_move_weak_child (void *window, void *child, int xoff, int yoff);
+
+  extern void
+  EmacsView_set_up_double_buffering (void *vw);
+
+  extern void
+  EmacsView_disable_double_buffering (void *vw);
+
+  extern void
+  EmacsView_flip_and_blit (void *vw);
+
+  extern int
+  EmacsView_double_buffered_p (void *vw);
+
+  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));
+
+  extern void
+  record_c_unwind_protect_from_cxx (void (*) (void *), void *);
+
+  extern ptrdiff_t
+  c_specpdl_idx_from_cxx (void);
+
+  extern void
+  c_unbind_to_nil_from_cxx (ptrdiff_t idx);
+
+  extern void
+  EmacsView_do_visible_bell (void *view, uint32_t color);
+
+  extern void
+  BWindow_zoom (void *window);
+
+  extern void
+  EmacsWindow_make_fullscreen (void *window, int fullscreen_p);
+
+  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
+
+  extern void
+  BMenuBar_start_tracking (void *mbar);
+
+  extern size_t
+  BBitmap_bytes_length (void *bitmap);
+
+  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
+
+  extern void
+  BView_set_and_show_sticky_tooltip (void *view, const char *tooltip,
+				     int x, int y);
+
+  extern void
+  BMenu_add_title (void *menu, const char *text);
+
+  extern int
+  be_plain_font_height (void);
+
+  extern int
+  be_string_width_with_plain_font (const char *str);
+
+  extern int
+  be_get_display_screens (void);
+
+  extern void
+  BWindow_set_min_size (void *window, int width, int height);
+
+  extern void
+  BWindow_set_size_alignment (void *window, int align_width, int align_height);
+
+#ifdef __cplusplus
+  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..868fc71f97
--- /dev/null
+++ b/src/haikufns.c
@@ -0,0 +1,2448 @@
+/* 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);
+}
+
+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 ();
+}
+
+\f
+
+DEFUN ("haiku-set-mouse-absolute-pixel-position",
+       Fhaiku_set_mouse_absolute_pixel_position,
+       Shaiku_set_mouse_absolute_pixel_position, 2, 2, 0,
+       doc: /* Move mouse pointer to a pixel position at (X, Y).  The
+coordinates X and Y are interpreted to start from the top-left
+corner of the screen.  */)
+  (Lisp_Object x, Lisp_Object y)
+{
+  int xval = check_integer_range (x, INT_MIN, INT_MAX);
+  int yval = check_integer_range (y, INT_MIN, INT_MAX);
+
+  if (!x_display_list)
+    error ("Window system not initialized");
+
+  block_input ();
+  be_warp_pointer (xval, yval);
+  unblock_input ();
+  return Qnil;
+}
+
+DEFUN ("haiku-mouse-absolute-pixel-position", Fhaiku_mouse_absolute_pixel_position,
+       Shaiku_mouse_absolute_pixel_position, 0, 0, 0,
+       doc: /* Return absolute position of mouse cursor in pixels.
+The position is returned as a cons cell (X . Y) of the coordinates of
+the mouse cursor position in pixels relative to a position (0, 0) of the
+selected frame's display.  */)
+  (void)
+{
+  if (!x_display_list)
+    return Qnil;
+
+  struct frame *f = SELECTED_FRAME ();
+
+  if (FRAME_INITIAL_P (f) || !FRAME_HAIKU_P (f)
+      || !FRAME_HAIKU_VIEW (f))
+    return Qnil;
+
+  block_input ();
+  void *view = FRAME_HAIKU_VIEW (f);
+
+  int x, y;
+  BView_get_mouse (view, &x, &y);
+  BView_convert_to_screen (view, &x, &y);
+  unblock_input ();
+
+  return Fcons (make_fixnum (x), make_fixnum (y));
+}
+
+DEFUN ("xw-display-color-p", Fxw_display_color_p, Sxw_display_color_p, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+     (Lisp_Object terminal)
+{
+  return Qt;
+}
+
+DEFUN ("xw-color-defined-p", Fxw_color_defined_p, Sxw_color_defined_p, 1, 2, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object color, Lisp_Object frame)
+{
+  Emacs_Color col;
+  CHECK_STRING (color);
+  decode_window_system_frame (frame);
+
+  return haiku_get_color (SSDATA (color), &col) ? Qnil : Qt;
+}
+
+DEFUN ("xw-color-values", Fxw_color_values, Sxw_color_values, 1, 2, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+     (Lisp_Object color, Lisp_Object frame)
+{
+  Emacs_Color col;
+  CHECK_STRING (color);
+  decode_window_system_frame (frame);
+
+  block_input ();
+  if (haiku_get_color (SSDATA (color), &col))
+    {
+      unblock_input ();
+      return Qnil;
+    }
+  unblock_input ();
+  return list3i (lrint (col.red), lrint (col.green), lrint (col.blue));
+}
+
+DEFUN ("x-display-grayscale-p", Fx_display_grayscale_p, Sx_display_grayscale_p,
+       0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  return Qnil;
+}
+
+DEFUN ("x-open-connection", Fx_open_connection, Sx_open_connection,
+       1, 3, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+     (Lisp_Object display, Lisp_Object resource_string, Lisp_Object must_succeed)
+{
+  struct haiku_display_info *dpy_info;
+  CHECK_STRING (display);
+
+  if (NILP (Fstring_equal (display, build_string ("be"))))
+    !NILP (must_succeed) ? fatal ("Bad display") : error ("Bad display");
+  dpy_info = haiku_term_init ();
+
+  if (!dpy_info)
+    !NILP (must_succeed) ? fatal ("Display not responding") :
+      error ("Display not responding");
+
+  return Qnil;
+}
+
+DEFUN ("x-display-pixel-width", Fx_display_pixel_width, Sx_display_pixel_width,
+       0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+
+{
+  check_haiku_display_info (terminal);
+
+  int width, height;
+  BScreen_px_dim (&width, &height);
+  return make_fixnum (width);
+}
+
+DEFUN ("x-display-pixel-height", Fx_display_pixel_height, Sx_display_pixel_height,
+       0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+
+{
+  check_haiku_display_info (terminal);
+
+  int width, height;
+  BScreen_px_dim (&width, &height);
+  return make_fixnum (width);
+}
+
+DEFUN ("x-display-mm-height", Fx_display_mm_height, Sx_display_mm_height, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  struct haiku_display_info *dpyinfo = check_haiku_display_info (terminal);
+
+  int width, height;
+  BScreen_px_dim (&width, &height);
+
+  return make_fixnum (height / (dpyinfo->resy / 25.4));
+}
+
+
+DEFUN ("x-display-mm-width", Fx_display_mm_width, Sx_display_mm_width, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  struct haiku_display_info *dpyinfo = check_haiku_display_info (terminal);
+
+  int width, height;
+  BScreen_px_dim (&width, &height);
+
+  return make_fixnum (height / (dpyinfo->resy / 25.4));
+}
+
+DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame,
+       1, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+     (Lisp_Object parms)
+{
+  return haiku_create_frame (parms, 0);
+}
+
+DEFUN ("x-display-visual-class", Fx_display_visual_class,
+       Sx_display_visual_class, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  check_haiku_display_info (terminal);
+
+  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..05fbd1021b
--- /dev/null
+++ b/src/haikuterm.c
@@ -0,0 +1,3608 @@
+/* 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;
+	    struct frame *f = haiku_window_to_frame (b->window);
+
+	    if (!f)
+	      continue;
+
+	    inev.kind = DRAG_N_DROP_EVENT;
+	    inev.arg = build_string_from_utf8 (b->ref);
+
+	    XSETINT (inev.x, b->x);
+	    XSETINT (inev.y, b->y);
+	    XSETFRAME (inev.frame_or_window, f);
+
+	    /* There should be no problem with calling free here.
+	       free on Haiku is thread-safe.  */
+	    free (b->ref);
+	    break;
+	  }
+	case APP_QUIT_REQUESTED_EVENT:
+	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..2cae32d1c6 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)
@@ -6152,7 +6152,6 @@ make_lispy_event (struct input_event *event)
     case CONFIG_CHANGED_EVENT:
 	return list3 (Qconfig_changed_event,
 		      event->arg, event->frame_or_window);
-
       /* The 'kind' field of the event is something we don't recognize.  */
     default:
       emacs_abort ();
@@ -7180,7 +7179,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 +7242,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 +7258,7 @@ deliver_input_available_signal (int sig)
 {
   deliver_process_signal (sig, handle_input_available_signal);
 }
-#endif /* USABLE_SIGIO */
+#endif /* defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)  */
 
 \f
 /* User signal events.  */
@@ -7329,7 +7328,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 +11098,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 +11109,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 +11540,16 @@ init_keyboard (void)
       sigaction (SIGQUIT, &action, 0);
 #endif /* not DOS_NT */
     }
-#ifdef USABLE_SIGIO
+#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)
   if (!noninteractive)
     {
       struct sigaction action;
       emacs_sigaction_init (&action, deliver_input_available_signal);
+#ifdef USABLE_SIGIO
       sigaction (SIGIO, &action, 0);
+#else
+      sigaction (SIGPOLL, &action, 0);
+#endif
     }
 #endif
 
diff --git a/src/lisp.h b/src/lisp.h
index 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..ab01e1bfad 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
@@ -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..67b6b2aa3d 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.,
@@ -3120,8 +3144,9 @@ list_system_processes (void)
 }
 
 /* The WINDOWSNT implementation is in w32.c.
-   The MSDOS implementation is in dosfns.c.  */
-#elif !defined (WINDOWSNT) && !defined (MSDOS)
+   The MSDOS implementation is in dosfns.c.
+   The Haiku implementation is in haiku.c.  */
+#elif !defined (WINDOWSNT) && !defined (MSDOS) && !defined (HAIKU)
 
 Lisp_Object
 list_system_processes (void)
@@ -4200,8 +4225,9 @@ system_process_attributes (Lisp_Object pid)
 }
 
 /* The WINDOWSNT implementation is in w32.c.
-   The MSDOS implementation is in dosfns.c.  */
-#elif !defined (WINDOWSNT) && !defined (MSDOS)
+   The MSDOS implementation is in dosfns.c.
+   The HAIKU implementation is in haiku.c.  */
+#elif !defined (WINDOWSNT) && !defined (MSDOS) && !defined (HAIKU)
 
 Lisp_Object
 system_process_attributes (Lisp_Object pid)
diff --git a/src/termhooks.h b/src/termhooks.h
index e7539bbce2..cf853e2885 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.  */
@@ -263,7 +264,6 @@ #define EMACS_TERMHOOKS_H
   /* File or directory was changed.  */
   , FILE_NOTIFY_EVENT
 #endif
-
 };
 
 /* Bit width of an enum event_kind tag at the start of structs and unions.  */
@@ -444,6 +444,7 @@ #define EVENT_INIT(event) memset (&(event), 0, sizeof (struct input_event))
     struct x_display_info *x;         /* xterm.h */
     struct w32_display_info *w32;     /* w32term.h */
     struct ns_display_info *ns;       /* nsterm.h */
+    struct haiku_display_info *haiku; /* haikuterm.h */
   } display_info;
 
 \f
@@ -832,6 +833,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 442fcf47d3..1e9417b9f1 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 5988d3a15f..ed5af0a86d 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");

[-- Attachment #3: Type: text/plain, Size: 185 bytes --]


> Yes, makes sense.  It would be worthwhile just to solve this problem
> with the NS port.  I will look into it.

I haven't had time to look at this yet, but I will in a bit.  Thanks.

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

* bug#51658: [PATCH] Haiku port (again)
  2021-11-15  2:59                               ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2021-11-20  7:03                                 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-11-20  8:33                                   ` Eli Zaretskii
  0 siblings, 1 reply; 31+ messages in thread
From: Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2021-11-20  7:03 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 51658

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

Po Lu <luangruo@yahoo.com> writes:

> I moved the Haiku system-dependents from sysdep.c to a separate file
> `haiku.c'.  I also removed the extraneous events (drag-and-drop now
> sends drag-n-drop events, like everywhere else, and haiku-quit-requested
> has been removed for now.)

This version of the patch doesn't apply to master any longer, so here's
an updated version.

The commit message shouldn't have to be updated.


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

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 c231c2ceae..1ee5f0c6ad 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,39 @@ AC_DEFUN
   with_xft=no
 fi
 
+HAIKU_OBJ=
+HAIKU_CXX_OBJ=
+HAIKU_LIBS=
+HAIKU_CFLAGS=
+
+if test "$opsys" = "haiku"; then
+  HAIKU_OBJ="$HAIKU_OBJ haiku.o"
+fi
+
+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 +2302,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 +2636,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 +2661,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 +2681,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 +3332,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 +3424,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 +3589,58 @@ 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
+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 +3762,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 +4080,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 +4155,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 +4184,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 +4602,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])
 
@@ -4814,7 +4962,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
@@ -5069,7 +5217,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
@@ -5165,6 +5313,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
 
 
@@ -5386,8 +5542,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)
@@ -5430,6 +5603,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
@@ -5542,6 +5731,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
@@ -5930,7 +6130,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..ce92435ae7 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,11 @@ 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 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 +1624,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..cf6f2c2b47
--- /dev/null
+++ b/doc/emacs/haiku.texi
@@ -0,0 +1,144 @@
+@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 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}; ff 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, use the binary named
+@code{emacs} 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.
+
+  It is 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.  The variables that allow for
+that are described below.
+
+@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-shift-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.
+
+@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}).
+
+@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.
+@end table
+
+@subsection What to do when Emacs crashes
+@cindex crashes, Haiku
+@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.
+
+@table @code
+@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
+
+@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}).
+
+  Two of these backends, @code{ftcr} and @code{ftcrfont} are identical
+to their counterparts on the X Window System.  There is also a
+Haiku-specific backend named @code{haiku}, that uses the App Server to
+draw fonts, but does not at present support display of color font and
+emoji.
diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi
index 12257fda54..b143db44db 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},
@@ -8267,6 +8268,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
@@ -8313,6 +8316,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
diff --git a/doc/lispref/frames.texi b/doc/lispref/frames.texi
index 31ad82b7ad..56fc8859a0 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.
 
+@footnote{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,10 @@ 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 can be several font drivers (@pxref{Haiku Fonts,,, emacs,
+The GNU Emacs Manual}).
+
+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 +3155,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.
+
+@footnote{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 +3178,9 @@ Raising and Lowering
 @var{frame1} remains unaltered.
 
 Some window managers may refuse to restack windows.
+
+@footnote{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 +3290,12 @@ Child Frames
 allowing them to be positioned so they do not obscure the parent frame
 while still being visible themselves.
 
+@footnote{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.
+
 \f
 * Obsolete platforms
 
diff --git a/etc/NEWS b/etc/NEWS
index 70ba5341d8..1b9e6929d8 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -24,6 +24,25 @@ applies, and please also update docstrings as needed.
 \f
 * 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 f506881a4b..c548ee9b36 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.
@@ -1105,6 +1114,13 @@ In your ~/.Xresources file, then run
 
 And restart Emacs.
 
+** On Haiku, BeCJK doesn't work properly with Emacs
+
+Some popular Haiku input methods such BeCJK are known to behave badly
+when interacting with Emacs, in ways such as stealing input focus and
+displaying popup windows that don't disappear.  If you are affected,
+you should use an Emacs input method instead.
+
 * X runtime problems
 
 ** X keyboard problems
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 6c353b0d9e..b7c53a4dfe 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 15a71ef244..ed1570e778 100644
--- a/lisp/loadup.el
+++ b/lisp/loadup.el
@@ -303,6 +303,11 @@
       (load "term/common-win")
       (load "term/x-win")))
 
+(if (featurep 'haiku)
+    (progn
+      (load "term/common-win")
+      (load "term/haiku-win")))
+
 (if (or (eq system-type 'windows-nt)
         (featurep 'w32))
     (progn
@@ -558,6 +563,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 94e75efeeb..76457d0c69 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 031a73143e..e86d21f889 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..36af10d2c7
--- /dev/null
+++ b/lisp/term/haiku-win.el
@@ -0,0 +1,134 @@
+;;; 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-dnd-handle-drag-n-drop-event (event)
+  "Handle specified drag-n-drop EVENT."
+  (interactive "e")
+  (let* ((string (caddr event))
+	 (window (posn-window (event-start event))))
+    (with-selected-window window
+      (raise-frame)
+      (dnd-handle-one-url window 'private (concat "file:" string)))))
+
+(define-key special-event-map [drag-n-drop]
+            'haiku-dnd-handle-drag-n-drop-event)
+
+(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 4015448ece..859b30564a 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..f52dae4719 100644
--- a/src/floatfns.c
+++ b/src/floatfns.c
@@ -347,6 +347,21 @@ DEFUN ("logb", Flogb, Slogb, 1, 1, 0,
 double_integer_scale (double d)
 {
   int exponent = ilogb (d);
+#ifdef HAIKU
+  /* On Haiku, the values returned by ilogb are nonsensical when
+     confronted with tiny numbers, inf, or NaN, which breaks the trick
+     used by code on other platforms, so we have to test for each case
+     manually, and return the appropriate value.  */
+  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 b503123b96..d423fd46b7 100644
--- a/src/font.c
+++ b/src/font.c
@@ -5751,6 +5751,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 6694164e09..2da5ec4504 100644
--- a/src/font.h
+++ b/src/font.h
@@ -965,7 +965,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;
@@ -999,7 +999,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 03e44ec30e..cf592759ab 100644
--- a/src/ftfont.c
+++ b/src/ftfont.c
@@ -3108,6 +3108,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.c b/src/haiku.c
new file mode 100644
index 0000000000..efed5d064f
--- /dev/null
+++ b/src/haiku.c
@@ -0,0 +1,288 @@
+/* Haiku subroutines that are general to the Haiku operating system.
+   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 "process.h"
+#include "coding.h"
+
+#include <kernel/OS.h>
+
+#include <pwd.h>
+#include <stdlib.h>
+
+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;
+}
+
+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;
+}
+
+
+/* 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;
+}
diff --git a/src/haiku_draw_support.cc b/src/haiku_draw_support.cc
new file mode 100644
index 0000000000..5b1eccfbe6
--- /dev/null
+++ b/src/haiku_draw_support.cc
@@ -0,0 +1,488 @@
+/* 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 ();
+}
+
+/* Convert RGB32 color color from RGB color space to its
+   HSL components pointed to by H, S and L.  */
+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..9ac0400969
--- /dev/null
+++ b/src/haiku_font_support.cc
@@ -0,0 +1,596 @@
+/* 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;
+}
+
+/* Return non-null if FONT contains CHR, a Unicode code-point.  */
+int
+BFont_have_char_p (void *font, int32_t chr)
+{
+  BFont *ft = (BFont *) font;
+  return ft->IncludesBlock (chr, chr);
+}
+
+/* Return non-null if font contains a block from BEG to END.  */
+int
+BFont_have_char_block (void *font, int32_t beg, int32_t end)
+{
+  BFont *ft = (BFont *) font;
+  return ft->IncludesBlock (beg, 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.  */
+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);
+}
+
+/* The same, but for a variable amount of chars.  */
+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);
+    }
+}
+
+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;
+}
+
+/* Delete every element of the font pattern PT.  */
+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;
+    }
+}
+
+/* Find all fonts matching the font pattern PT.  */
+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;
+}
+
+/* Find and open a font matching the pattern PAT, which must have its
+   family set.  */
+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;
+}
+
+/* Query the family of the default fixed font.  */
+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..c152d9b086
--- /dev/null
+++ b/src/haiku_io.c
@@ -0,0 +1,207 @@
+/* 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
+
+/* The port used to send messages from the application thread to
+   Emacs.  */
+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 ();
+}
+
+/* Read the size of the next message into len, returning -1 if the
+   query fails or there is no next message.  */
+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;
+}
+
+/* Read the next message into BUF, putting its type into TYPE,
+   assuming the message is at most LEN long.  Return 0 if successful
+   and -1 if the read fails.  */
+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;
+}
+
+/* The same as haiku_read, but time out after TIMEOUT microseconds.
+   Input is blocked when an attempt to read is in progress.  */
+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;
+}
+
+/* Write a message with type TYPE into BUF.  */
+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");
+}
+
+/* Record an unwind protect from C++ code.  */
+void
+record_c_unwind_protect_from_cxx (void (*fn) (void *), void *r)
+{
+  record_unwind_protect_ptr (fn, r);
+}
+
+/* SPECPDL_IDX that is safe from C++ code.  */
+ptrdiff_t
+c_specpdl_idx_from_cxx (void)
+{
+  return SPECPDL_INDEX ();
+}
+
+/* unbind_to (IDX, Qnil), but safe from C++ code.  */
+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..99d4ee7914
--- /dev/null
+++ b/src/haiku_support.cc
@@ -0,0 +1,2930 @@
+/* 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;
+    int32 x, y;
+    void *window;
+
+    if ((msg->FindPointer ("window", 0, &window) != B_OK)
+	|| (msg->FindInt32 ("x", 0, &x) != B_OK)
+	|| (msg->FindInt32 ("y", 0, &y) != B_OK))
+      return;
+
+    rq.window = window;
+    rq.x = x;
+    rq.y = y;
+
+    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;
+	BPoint whereto;
+
+        if (msg->FindRef ("refs", &ref) == B_OK)
+	  {
+	    msg->what = B_REFS_RECEIVED;
+	    msg->AddPointer ("window", this);
+	    if (msg->FindPoint ("_drop_point_", &whereto) == B_OK)
+	      {
+		this->ConvertFromScreen (&whereto);
+		msg->AddInt32 ("x", whereto.x);
+		msg->AddInt32 ("y", whereto.y);
+	      }
+	    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)
+	  gui_abort ("strdup failed");
+      }
+
+    if (help)
+      {
+	this->help = strdup (help);
+	if (!this->help)
+	  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;
+}
+
+/* Take BITMAP, a reference to a BBitmap, and return a pointer to its
+   data.  */
+void *
+BBitmap_data (void *bitmap)
+{
+  return ((BBitmap *) bitmap)->Bits ();
+}
+
+/* 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'.  */
+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;
+}
+
+/* Create new bitmap in RGB32 format, or in GRAY1 if MONO_P is
+   non-zero.  */
+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);
+}
+
+/* Set up an application and return it.  If starting the application
+   thread fails, abort Emacs.  */
+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;
+}
+
+/* Set up and return a window with its view put in VIEW.  */
+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 ();
+}
+
+/* Set WINDOW's offset to X, Y.  */
+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);
+}
+
+/* Iconify WINDOW.  */
+void
+BWindow_iconify (void *window)
+{
+  if (((BWindow *) window)->IsHidden ())
+    BWindow_set_visible (window, true);
+  ((BWindow *) window)->Minimize (true);
+}
+
+/* Show or hide WINDOW.  */
+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 ();
+}
+
+/* Change the title of WINDOW to the multibyte string TITLE.  */
+void
+BWindow_retitle (void *window, const char *title)
+{
+  ((BWindow *) window)->SetTitle (title);
+}
+
+/* Resize WINDOW to WIDTH by HEIGHT.  */
+void
+BWindow_resize (void *window, int width, int height)
+{
+  ((BWindow *) window)->ResizeTo (width, height);
+}
+
+/* Activate WINDOW, making it the subject of keyboard focus and
+   bringing it to the front of the screen.  */
+void
+BWindow_activate (void *window)
+{
+  ((BWindow *) window)->Activate ();
+}
+
+/* Return the pixel dimensions of the main screen in WIDTH and
+   HEIGHT.  */
+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;
+}
+
+/* Resize VIEW to WIDTH, HEIGHT.  */
+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 ();
+}
+
+/* Map the keycode KC, storing the result in CODE and 1 in
+   NON_ASCII_P if it should be used.  */
+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;
+}
+
+/* Make a scrollbar, attach it to VIEW's window, and return it.  */
+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);
+}
+
+/* Return the default scrollbar size.  */
+int
+BScrollBar_default_size (int horizontal_p)
+{
+  return horizontal_p ? B_H_SCROLL_BAR_HEIGHT : B_V_SCROLL_BAR_WIDTH;
+}
+
+/* Invalidate VIEW, causing it to be drawn again.  */
+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 ();
+}
+
+/* Lock VIEW in preparation for drawing operations.  This should be
+   called before any attempt to draw onto VIEW or to lock it for Cairo
+   drawing.  `BView_draw_unlock' should be called afterwards.  */
+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 ();
+}
+
+/* Tell VIEW it has been clicked at X by Y.  */
+void
+BView_mouse_down (void *view, int x, int y)
+{
+  BView *vw = (BView *) view;
+  if (vw->LockLooper ())
+    {
+      vw->MouseDown (BPoint (x, y));
+      vw->UnlockLooper ();
+    }
+}
+
+/* Tell VIEW the mouse has been released at X by Y.  */
+void
+BView_mouse_up (void *view, int x, int y)
+{
+  BView *vw = (BView *) view;
+  if (vw->LockLooper ())
+    {
+      vw->MouseUp (BPoint (x, y));
+      vw->UnlockLooper ();
+    }
+}
+
+/* Tell VIEW that the mouse has moved to Y by Y.  */
+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 ();
+    }
+}
+
+/* Import BITS into BITMAP using the B_GRAY1 colorspace.  */
+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 ();
+    }
+}
+
+/* Make a scrollbar at X, Y known to the view VIEW.  */
+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 ();
+    }
+}
+
+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);
+}
+
+/* Perform an in-place conversion of X and Y from VIEW's coordinate
+   system to its screen's coordinate system.  */
+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);
+}
+
+/* Decorate or undecorate WINDOW depending on DECORATE_P.  */
+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 ();
+}
+
+/* Decorate WINDOW appropriately for use as a tooltip.  */
+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 ();
+}
+
+/* Set B_AVOID_FOCUS on WINDOW if AVOID_FOCUS_P is non-nil, or clear
+   it otherwise.  */
+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;
+}
+
+/* Return the current workspace.  */
+uint32_t
+haiku_current_workspace (void)
+{
+  return current_workspace ();
+}
+
+/* Return a bitmask consisting of workspaces WINDOW is on.  */
+uint32_t
+BWindow_workspaces (void *window)
+{
+  return ((BWindow *) window)->Workspaces ();
+}
+
+/* Create a popup menu.  */
+void *
+BPopUpMenu_new (const char *name)
+{
+  BPopUpMenu *menu = new EmacsPopUpMenu (name);
+  menu->SetRadioMode (0);
+  return menu;
+}
+
+/* Add a title item to MENU.  These items cannot be highlighted or
+   triggered, and their labels will display as bold text.  */
+void
+BMenu_add_title (void *menu, const char *text)
+{
+  EmacsTitleMenuItem *it = new EmacsTitleMenuItem (text);
+  BMenu *mn = (BMenu *) menu;
+  mn->AddItem (it);
+}
+
+/* Add an item to the menu MENU.  */
+void
+BMenu_add_item (void *menu, const char *label, void *ptr, bool enabled_p,
+		bool marked_p, bool mbar_p, void *mbw_ptr, const char *key,
+		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);
+}
+
+/* Add a separator to the menu MENU.  */
+void
+BMenu_add_separator (void *menu)
+{
+  BMenu *m = (BMenu *) menu;
+
+  m->AddSeparatorItem ();
+}
+
+/* Create a submenu and attach it to MENU.  */
+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;
+}
+
+/* Create a submenu that notifies Emacs upon opening.  */
+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;
+}
+
+/* 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.  */
+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;
+}
+
+/* Delete the entire menu hierarchy of MENU, and then delete MENU
+   itself.  */
+void
+BPopUpMenu_delete (void *menu)
+{
+  delete (BPopUpMenu *) menu;
+}
+
+/* Create a menubar, attach it to VIEW, and return it.  */
+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;
+}
+
+/* Delete MENUBAR along with all subitems. */
+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;
+}
+
+/* Delete all items from MENU.  */
+void
+BMenu_delete_all (void *menu)
+{
+  BMenu *mn = (BMenu *) menu;
+  mn->RemoveItems (0, mn->CountItems (), true);
+}
+
+/* Delete COUNT items from MENU starting from START.  */
+void
+BMenu_delete_from (void *menu, int start, int count)
+{
+  BMenu *mn = (BMenu *) menu;
+  mn->RemoveItems (start, count, true);
+}
+
+/* Count items in menu MENU.  */
+int
+BMenu_count_items (void *menu)
+{
+  return ((BMenu *) menu)->CountItems ();
+}
+
+/* Find the item in MENU at IDX.  */
+void *
+BMenu_item_at (void *menu, int idx)
+{
+  return ((BMenu *) menu)->ItemAt (idx);
+}
+
+/* Set ITEM's label to LABEL.  */
+void
+BMenu_item_set_label (void *item, const char *label)
+{
+  ((BMenuItem *) item)->SetLabel (label);
+}
+
+/* Get ITEM's menu.  */
+void *
+BMenu_item_get_menu (void *item)
+{
+  return ((BMenuItem *) item)->Submenu ();
+}
+
+/* Emit a beep noise.  */
+void
+haiku_ring_bell (void)
+{
+  beep ();
+}
+
+/* Create a BAlert with TEXT.  */
+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);
+}
+
+/* Add a button to ALERT and return the button.  */
+void *
+BAlert_add_button (void *alert, const char *text)
+{
+  BAlert *al = (BAlert *) alert;
+  al->AddButton (text);
+  return al->ButtonAt (al->CountButtons () - 1);
+}
+
+/* Run ALERT, returning the number of the button that was selected,
+   or -1 if no button was selected before the alert was closed.  */
+int32_t
+BAlert_go (void *alert)
+{
+  return ((BAlert *) alert)->Go ();
+}
+
+/* Enable or disable BUTTON depending on ENABLED_P.  */
+void
+BButton_set_enabled (void *button, int enabled_p)
+{
+  ((BButton *) button)->SetEnabled (enabled_p);
+}
+
+/* Set VIEW's tooltip to TOOLTIP.  */
+void
+BView_set_tooltip (void *view, const char *tooltip)
+{
+  ((BView *) view)->SetToolTip (tooltip);
+}
+
+/* Set VIEW's tooltip to a sticky tooltip at X by Y.  */
+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 ();
+}
+
+/* Delete ALERT.  */
+void
+BAlert_delete (void *alert)
+{
+  delete (BAlert *) alert;
+}
+
+/* Place the resolution of the monitor in DPI in RSSX and RSSY.  */
+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;
+    }
+}
+
+/* Add WINDOW to OTHER_WINDOW's subset and parent it to
+   OTHER_WINDOW.  */
+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 ();
+}
+
+/* Place text describing the current version of Haiku in VERSION,
+   which should be a buffer LEN bytes wide.  */
+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);
+    }
+}
+
+/* Return the amount of color planes in the current display.  */
+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;
+}
+
+/* Return the amount of colors the display can handle.  */
+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;
+}
+
+/* Warp the pointer to X by Y.  */
+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);
+}
+
+/* Update the position of CHILD in WINDOW without actually moving
+   it.  */
+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 ();
+}
+
+/* Find an appropriate view to draw onto.  If VW is double-buffered,
+   this will be the view used for double buffering instead of VW
+   itself.  */
+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;
+}
+
+/* Set up double buffering for 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 ();
+}
+
+/* Flip and invalidate the view VW.  */
+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 ();
+}
+
+/* Disable double buffering for VW.  */
+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 ();
+}
+
+/* Return non-0 if VW is double-buffered.  */
+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));
+}
+
+/* Popup a file dialog.  */
+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);
+    }
+}
+
+/* Temporarily fill VIEW with COLOR.  */
+void
+EmacsView_do_visible_bell (void *view, uint32_t color)
+{
+  EmacsView *vw = (EmacsView *) view;
+  vw->DoVisibleBell (color);
+}
+
+/* Zoom WINDOW.  */
+void
+BWindow_zoom (void *window)
+{
+  BWindow *w = (BWindow *) window;
+  w->Zoom ();
+}
+
+/* Make WINDOW fullscreen if FULLSCREEN_P.  */
+void
+EmacsWindow_make_fullscreen (void *window, int fullscreen_p)
+{
+  EmacsWindow *w = (EmacsWindow *) window;
+  w->MakeFullscreen (fullscreen_p);
+}
+
+/* Unzoom (maximize) WINDOW.  */
+void
+EmacsWindow_unzoom (void *window)
+{
+  EmacsWindow *w = (EmacsWindow *) window;
+  w->UnZoom ();
+}
+
+/* Move the pointer into MBAR and start tracking.  */
+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
+
+/* Return the size of BITMAP's data, in bytes.  */
+size_t
+BBitmap_bytes_length (void *bitmap)
+{
+  BBitmap *bm = (BBitmap *) bitmap;
+  return bm->BitsLength ();
+}
+
+/* Show VIEW's tooltip.  */
+void
+BView_show_tooltip (void *view)
+{
+  BView *vw = (BView *) view;
+  if (vw->LockLooper ())
+    {
+      vw->ShowToolTip (vw->ToolTip ());
+      vw->UnlockLooper ();
+    }
+}
+
+
+#ifdef USE_BE_CAIRO
+/* Return VIEW's cairo surface.  */
+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;
+}
+
+/* Transfer each clip rectangle in VIEW to the cairo context
+   CTX.  */
+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);
+}
+
+/* Lock WINDOW in preparation for drawing using Cairo.  */
+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");
+}
+
+/* Unlock WINDOW in preparation for drawing using Cairo.  */
+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
+
+/* Get the width of STR in the plain font.  */
+int
+be_string_width_with_plain_font (const char *str)
+{
+  return be_plain_font->StringWidth (str);
+}
+
+/* Get the ascent + descent of the plain font.  */
+int
+be_plain_font_height (void)
+{
+  struct font_height fheight;
+  be_plain_font->GetHeight (&fheight);
+
+  return fheight.ascent + fheight.descent;
+}
+
+/* Return the number of physical displays connected.  */
+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;
+}
+
+/* Set the minimum width the user can resize WINDOW to.  */
+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 ();
+}
+
+/* Set the alignment of WINDOW's dimensions.  */
+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..9f5f3c77e3
--- /dev/null
+++ b/src/haiku_support.h
@@ -0,0 +1,869 @@
+/* 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
+{
+  void *window;
+  int x, y;
+  /* 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
+
+  extern port_id port_application_to_emacs;
+
+  extern void haiku_io_init (void);
+  extern void haiku_io_init_in_app_thread (void);
+
+  extern void
+  haiku_read_size (ssize_t *len);
+
+  extern int
+  haiku_read (enum haiku_event_type *type, void *buf, ssize_t len);
+
+  extern int
+  haiku_read_with_timeout (enum haiku_event_type *type, void *buf, ssize_t len,
+			   time_t timeout);
+
+  extern int
+  haiku_write (enum haiku_event_type type, void *buf);
+
+  extern int
+  haiku_write_without_signal (enum haiku_event_type type, void *buf);
+
+  extern void
+  rgb_color_hsl (uint32_t rgb, double *h, double *s, double *l);
+
+  extern void
+  hsl_color_rgb (double h, double s, double l, uint32_t *rgb);
+
+  extern void *
+  BBitmap_new (int width, int height, int mono_p);
+
+  extern void *
+  BBitmap_data (void *bitmap);
+
+  extern int
+  BBitmap_convert (void *bitmap, void **new_bitmap);
+
+  extern void
+  BBitmap_free (void *bitmap);
+
+  extern void
+  BBitmap_dimensions (void *bitmap, int *left, int *top,
+		      int *right, int *bottom, int32_t *bytes_per_row,
+		      int *mono_p);
+
+  extern void *
+  BApplication_setup (void);
+
+  extern void *
+  BWindow_new (void *view);
+
+  extern void
+  BWindow_quit (void *window);
+
+  extern void
+  BWindow_set_offset (void *window, int x, int y);
+
+  extern void
+  BWindow_iconify (void *window);
+
+  extern void
+  BWindow_set_visible (void *window, int visible_p);
+
+  extern void
+  BFont_close (void *font);
+
+  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);
+
+  extern int
+  BFont_have_char_p (void *font, int32_t chr);
+
+  extern int
+  BFont_have_char_block (void *font, int32_t beg, int32_t end);
+
+  extern void
+  BFont_char_bounds (void *font, const char *mb_str, int *advance,
+		     int *lb, int *rb);
+
+  extern void
+  BFont_nchar_bounds (void *font, const char *mb_str, int *advance,
+		      int *lb, int *rb, int32_t n);
+
+  extern void
+  BWindow_retitle (void *window, const char *title);
+
+  extern void
+  BWindow_resize (void *window, int width, int height);
+
+  extern void
+  BWindow_activate (void *window);
+
+  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);
+
+  extern void
+  BScreen_px_dim (int *width, int *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);
+
+  extern void
+  BCursor_delete (void *cursor);
+
+  extern void
+  BView_set_view_cursor (void *view, void *cursor);
+
+  extern void
+  BWindow_Flush (void *window);
+
+  extern void
+  BMapKey (uint32_t kc, int *non_ascii_p, unsigned *code);
+
+  extern void *
+  BScrollBar_make_for_view (void *view, int horizontal_p,
+			    int x, int y, int x1, int y1,
+			    void *scroll_bar_ptr);
+
+  extern void
+  BScrollBar_delete (void *sb);
+
+  extern void
+  BView_move_frame (void *view, int x, int y, int x1, int y1);
+
+  extern void
+  BView_scroll_bar_update (void *sb, int portion, int whole, int position);
+
+  extern int
+  BScrollBar_default_size (int horizontal_p);
+
+  extern void
+  BView_invalidate (void *view);
+
+  extern void
+  BView_draw_lock (void *view);
+
+  extern void
+  BView_draw_unlock (void *view);
+
+  extern void
+  BWindow_center_on_screen (void *window);
+
+  extern void
+  BView_mouse_moved (void *view, int x, int y, uint32_t transit);
+
+  extern void
+  BView_mouse_down (void *view, int x, int y);
+
+  extern void
+  BView_mouse_up (void *view, int x, int y);
+
+  extern void
+  BBitmap_import_mono_bits (void *bitmap, void *bits, int wd, int h);
+
+  extern void
+  haiku_font_pattern_free (struct haiku_font_pattern *pt);
+
+  extern struct haiku_font_pattern *
+  BFont_find (struct haiku_font_pattern *pt);
+
+  extern int
+  BFont_open_pattern (struct haiku_font_pattern *pat, void **font, float size);
+
+  extern void
+  BFont_populate_fixed_family (struct haiku_font_pattern *ptn);
+
+  extern void
+  BFont_populate_plain_family (struct haiku_font_pattern *ptn);
+
+  extern void
+  BView_publish_scroll_bar (void *view, int x, int y, int width, int height);
+
+  extern void
+  BView_forget_scroll_bar (void *view, int x, int y, int width, int height);
+
+  extern void
+  BView_get_mouse (void *view, int *x, int *y);
+
+  extern void
+  BView_convert_to_screen (void *view, int *x, int *y);
+
+  extern void
+  BView_convert_from_screen (void *view, int *x, int *y);
+
+  extern void
+  BWindow_change_decoration (void *window, int decorate_p);
+
+  extern void
+  BWindow_set_tooltip_decoration (void *window);
+
+  extern void
+  BWindow_set_avoid_focus (void *window, int avoid_focus_p);
+
+  extern void
+  BView_emacs_delete (void *view);
+
+  extern uint32_t
+  haiku_current_workspace (void);
+
+  extern uint32_t
+  BWindow_workspaces (void *window);
+
+  extern void *
+  BPopUpMenu_new (const char *name);
+
+  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);
+
+  extern void
+  BMenu_add_separator (void *menu);
+
+  extern void *
+  BMenu_new_submenu (void *menu, const char *label, bool enabled_p);
+
+  extern void *
+  BMenu_new_menu_bar_submenu (void *menu, const char *label);
+
+  extern int
+  BMenu_count_items (void *menu);
+
+  extern void *
+  BMenu_item_at (void *menu, int idx);
+
+  extern void *
+  BMenu_run (void *menu, int x, int y);
+
+  extern void
+  BPopUpMenu_delete (void *menu);
+
+  extern void *
+  BMenuBar_new (void *view);
+
+  extern void
+  BMenu_delete_all (void *menu);
+
+  extern void
+  BMenuBar_delete (void *menubar);
+
+  extern void
+  BMenu_item_set_label (void *item, const char *label);
+
+  extern void *
+  BMenu_item_get_menu (void *item);
+
+  extern void
+  BMenu_delete_from (void *menu, int start, int count);
+
+  extern void
+  haiku_ring_bell (void);
+
+  extern void *
+  BAlert_new (const char *text, enum haiku_alert_type type);
+
+  extern void *
+  BAlert_add_button (void *alert, const char *text);
+
+  extern int32_t
+  BAlert_go (void *alert);
+
+  extern void
+  BButton_set_enabled (void *button, int enabled_p);
+
+  extern void
+  BView_set_tooltip (void *view, const char *tooltip);
+
+  extern void
+  BAlert_delete (void *alert);
+
+  extern void
+  BScreen_res (double *rrsx, double *rrsy);
+
+  extern void
+  EmacsWindow_parent_to (void *window, void *other_window);
+
+  extern void
+  EmacsWindow_unparent (void *window);
+
+  extern int
+  BFont_string_width (void *font, const char *utf8);
+
+  extern void
+  be_get_version_string (char *version, int len);
+
+  extern int
+  be_get_display_planes (void);
+
+  extern int
+  be_get_display_color_cells (void);
+
+  extern void
+  be_warp_pointer (int x, int y);
+
+  extern void
+  EmacsWindow_move_weak_child (void *window, void *child, int xoff, int yoff);
+
+  extern void
+  EmacsView_set_up_double_buffering (void *vw);
+
+  extern void
+  EmacsView_disable_double_buffering (void *vw);
+
+  extern void
+  EmacsView_flip_and_blit (void *vw);
+
+  extern int
+  EmacsView_double_buffered_p (void *vw);
+
+  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));
+
+  extern void
+  record_c_unwind_protect_from_cxx (void (*) (void *), void *);
+
+  extern ptrdiff_t
+  c_specpdl_idx_from_cxx (void);
+
+  extern void
+  c_unbind_to_nil_from_cxx (ptrdiff_t idx);
+
+  extern void
+  EmacsView_do_visible_bell (void *view, uint32_t color);
+
+  extern void
+  BWindow_zoom (void *window);
+
+  extern void
+  EmacsWindow_make_fullscreen (void *window, int fullscreen_p);
+
+  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
+
+  extern void
+  BMenuBar_start_tracking (void *mbar);
+
+  extern size_t
+  BBitmap_bytes_length (void *bitmap);
+
+  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
+
+  extern void
+  BView_set_and_show_sticky_tooltip (void *view, const char *tooltip,
+				     int x, int y);
+
+  extern void
+  BMenu_add_title (void *menu, const char *text);
+
+  extern int
+  be_plain_font_height (void);
+
+  extern int
+  be_string_width_with_plain_font (const char *str);
+
+  extern int
+  be_get_display_screens (void);
+
+  extern void
+  BWindow_set_min_size (void *window, int width, int height);
+
+  extern void
+  BWindow_set_size_alignment (void *window, int align_width, int align_height);
+
+#ifdef __cplusplus
+  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..868fc71f97
--- /dev/null
+++ b/src/haikufns.c
@@ -0,0 +1,2448 @@
+/* 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);
+}
+
+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 ();
+}
+
+\f
+
+DEFUN ("haiku-set-mouse-absolute-pixel-position",
+       Fhaiku_set_mouse_absolute_pixel_position,
+       Shaiku_set_mouse_absolute_pixel_position, 2, 2, 0,
+       doc: /* Move mouse pointer to a pixel position at (X, Y).  The
+coordinates X and Y are interpreted to start from the top-left
+corner of the screen.  */)
+  (Lisp_Object x, Lisp_Object y)
+{
+  int xval = check_integer_range (x, INT_MIN, INT_MAX);
+  int yval = check_integer_range (y, INT_MIN, INT_MAX);
+
+  if (!x_display_list)
+    error ("Window system not initialized");
+
+  block_input ();
+  be_warp_pointer (xval, yval);
+  unblock_input ();
+  return Qnil;
+}
+
+DEFUN ("haiku-mouse-absolute-pixel-position", Fhaiku_mouse_absolute_pixel_position,
+       Shaiku_mouse_absolute_pixel_position, 0, 0, 0,
+       doc: /* Return absolute position of mouse cursor in pixels.
+The position is returned as a cons cell (X . Y) of the coordinates of
+the mouse cursor position in pixels relative to a position (0, 0) of the
+selected frame's display.  */)
+  (void)
+{
+  if (!x_display_list)
+    return Qnil;
+
+  struct frame *f = SELECTED_FRAME ();
+
+  if (FRAME_INITIAL_P (f) || !FRAME_HAIKU_P (f)
+      || !FRAME_HAIKU_VIEW (f))
+    return Qnil;
+
+  block_input ();
+  void *view = FRAME_HAIKU_VIEW (f);
+
+  int x, y;
+  BView_get_mouse (view, &x, &y);
+  BView_convert_to_screen (view, &x, &y);
+  unblock_input ();
+
+  return Fcons (make_fixnum (x), make_fixnum (y));
+}
+
+DEFUN ("xw-display-color-p", Fxw_display_color_p, Sxw_display_color_p, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+     (Lisp_Object terminal)
+{
+  return Qt;
+}
+
+DEFUN ("xw-color-defined-p", Fxw_color_defined_p, Sxw_color_defined_p, 1, 2, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object color, Lisp_Object frame)
+{
+  Emacs_Color col;
+  CHECK_STRING (color);
+  decode_window_system_frame (frame);
+
+  return haiku_get_color (SSDATA (color), &col) ? Qnil : Qt;
+}
+
+DEFUN ("xw-color-values", Fxw_color_values, Sxw_color_values, 1, 2, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+     (Lisp_Object color, Lisp_Object frame)
+{
+  Emacs_Color col;
+  CHECK_STRING (color);
+  decode_window_system_frame (frame);
+
+  block_input ();
+  if (haiku_get_color (SSDATA (color), &col))
+    {
+      unblock_input ();
+      return Qnil;
+    }
+  unblock_input ();
+  return list3i (lrint (col.red), lrint (col.green), lrint (col.blue));
+}
+
+DEFUN ("x-display-grayscale-p", Fx_display_grayscale_p, Sx_display_grayscale_p,
+       0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  return Qnil;
+}
+
+DEFUN ("x-open-connection", Fx_open_connection, Sx_open_connection,
+       1, 3, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+     (Lisp_Object display, Lisp_Object resource_string, Lisp_Object must_succeed)
+{
+  struct haiku_display_info *dpy_info;
+  CHECK_STRING (display);
+
+  if (NILP (Fstring_equal (display, build_string ("be"))))
+    !NILP (must_succeed) ? fatal ("Bad display") : error ("Bad display");
+  dpy_info = haiku_term_init ();
+
+  if (!dpy_info)
+    !NILP (must_succeed) ? fatal ("Display not responding") :
+      error ("Display not responding");
+
+  return Qnil;
+}
+
+DEFUN ("x-display-pixel-width", Fx_display_pixel_width, Sx_display_pixel_width,
+       0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+
+{
+  check_haiku_display_info (terminal);
+
+  int width, height;
+  BScreen_px_dim (&width, &height);
+  return make_fixnum (width);
+}
+
+DEFUN ("x-display-pixel-height", Fx_display_pixel_height, Sx_display_pixel_height,
+       0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+
+{
+  check_haiku_display_info (terminal);
+
+  int width, height;
+  BScreen_px_dim (&width, &height);
+  return make_fixnum (width);
+}
+
+DEFUN ("x-display-mm-height", Fx_display_mm_height, Sx_display_mm_height, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  struct haiku_display_info *dpyinfo = check_haiku_display_info (terminal);
+
+  int width, height;
+  BScreen_px_dim (&width, &height);
+
+  return make_fixnum (height / (dpyinfo->resy / 25.4));
+}
+
+
+DEFUN ("x-display-mm-width", Fx_display_mm_width, Sx_display_mm_width, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  struct haiku_display_info *dpyinfo = check_haiku_display_info (terminal);
+
+  int width, height;
+  BScreen_px_dim (&width, &height);
+
+  return make_fixnum (height / (dpyinfo->resy / 25.4));
+}
+
+DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame,
+       1, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+     (Lisp_Object parms)
+{
+  return haiku_create_frame (parms, 0);
+}
+
+DEFUN ("x-display-visual-class", Fx_display_visual_class,
+       Sx_display_visual_class, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  check_haiku_display_info (terminal);
+
+  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..05fbd1021b
--- /dev/null
+++ b/src/haikuterm.c
@@ -0,0 +1,3608 @@
+/* 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;
+	    struct frame *f = haiku_window_to_frame (b->window);
+
+	    if (!f)
+	      continue;
+
+	    inev.kind = DRAG_N_DROP_EVENT;
+	    inev.arg = build_string_from_utf8 (b->ref);
+
+	    XSETINT (inev.x, b->x);
+	    XSETINT (inev.y, b->y);
+	    XSETFRAME (inev.frame_or_window, f);
+
+	    /* There should be no problem with calling free here.
+	       free on Haiku is thread-safe.  */
+	    free (b->ref);
+	    break;
+	  }
+	case APP_QUIT_REQUESTED_EVENT:
+	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..734ccdac31 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,30 @@ 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)
+    {
+      *pimg = NULL;
+      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 +3056,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 +3183,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 +4132,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 +4152,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 +4170,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 +4394,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 +4430,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 +4801,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 +5541,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 +5614,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 +5622,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 +5936,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 +5954,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 +6512,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 +6587,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 +9739,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 +11030,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 +11121,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 +11269,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 c3bc8307d7..c8ca3f920c 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)
@@ -6152,7 +6152,6 @@ make_lispy_event (struct input_event *event)
     case CONFIG_CHANGED_EVENT:
 	return list3 (Qconfig_changed_event,
 		      event->arg, event->frame_or_window);
-
       /* The 'kind' field of the event is something we don't recognize.  */
     default:
       emacs_abort ();
@@ -7243,7 +7242,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 +7258,7 @@ deliver_input_available_signal (int sig)
 {
   deliver_process_signal (sig, handle_input_available_signal);
 }
-#endif /* USABLE_SIGIO */
+#endif /* defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)  */
 
 \f
 /* User signal events.  */
@@ -7329,7 +7328,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 +11098,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 +11109,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 +11540,16 @@ init_keyboard (void)
       sigaction (SIGQUIT, &action, 0);
 #endif /* not DOS_NT */
     }
-#ifdef USABLE_SIGIO
+#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)
   if (!noninteractive)
     {
       struct sigaction action;
       emacs_sigaction_init (&action, deliver_input_available_signal);
+#ifdef USABLE_SIGIO
       sigaction (SIGIO, &action, 0);
+#else
+      sigaction (SIGPOLL, &action, 0);
+#endif
     }
 #endif
 
diff --git a/src/lisp.h b/src/lisp.h
index 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..ab01e1bfad 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
@@ -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 a00426795b..241ffe9a8d 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);
@@ -5730,7 +5730,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,
@@ -5738,7 +5738,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,
@@ -7732,7 +7736,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..67b6b2aa3d 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.,
@@ -3120,8 +3144,9 @@ list_system_processes (void)
 }
 
 /* The WINDOWSNT implementation is in w32.c.
-   The MSDOS implementation is in dosfns.c.  */
-#elif !defined (WINDOWSNT) && !defined (MSDOS)
+   The MSDOS implementation is in dosfns.c.
+   The Haiku implementation is in haiku.c.  */
+#elif !defined (WINDOWSNT) && !defined (MSDOS) && !defined (HAIKU)
 
 Lisp_Object
 list_system_processes (void)
@@ -4200,8 +4225,9 @@ system_process_attributes (Lisp_Object pid)
 }
 
 /* The WINDOWSNT implementation is in w32.c.
-   The MSDOS implementation is in dosfns.c.  */
-#elif !defined (WINDOWSNT) && !defined (MSDOS)
+   The MSDOS implementation is in dosfns.c.
+   The HAIKU implementation is in haiku.c.  */
+#elif !defined (WINDOWSNT) && !defined (MSDOS) && !defined (HAIKU)
 
 Lisp_Object
 system_process_attributes (Lisp_Object pid)
diff --git a/src/termhooks.h b/src/termhooks.h
index e7539bbce2..cf853e2885 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.  */
@@ -263,7 +264,6 @@ #define EMACS_TERMHOOKS_H
   /* File or directory was changed.  */
   , FILE_NOTIFY_EVENT
 #endif
-
 };
 
 /* Bit width of an enum event_kind tag at the start of structs and unions.  */
@@ -444,6 +444,7 @@ #define EVENT_INIT(event) memset (&(event), 0, sizeof (struct input_event))
     struct x_display_info *x;         /* xterm.h */
     struct w32_display_info *w32;     /* w32term.h */
     struct ns_display_info *ns;       /* nsterm.h */
+    struct haiku_display_info *haiku; /* haikuterm.h */
   } display_info;
 
 \f
@@ -832,6 +833,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 6c70ce60bb..8d34b7c4c3 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 d0d73eb828..fec6b2654b 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 816b6dc5a8..974a3fa906 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -13849,7 +13849,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");

[-- Attachment #3: Type: text/plain, Size: 9 bytes --]


Thanks.

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

* bug#51658: [PATCH] Haiku port (again)
  2021-11-20  7:03                                 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2021-11-20  8:33                                   ` Eli Zaretskii
  2021-11-20  9:34                                     ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  0 siblings, 1 reply; 31+ messages in thread
From: Eli Zaretskii @ 2021-11-20  8:33 UTC (permalink / raw)
  To: Po Lu; +Cc: 51658

> From: Po Lu <luangruo@yahoo.com>
> Cc: 51658@debbugs.gnu.org
> Date: Sat, 20 Nov 2021 15:03:11 +0800
> 
> +The value of each variable can one of the symbols @code{command},
                              ^^^^^^^^^^^^^^^^^^^^^^
"can be one of the symbols"

> +  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.

 "If you need those features, customize the variable
 @code{haiku-use-system-tooltips} to the nil value, and Emacs will use
 its own implementation of tooltips."

(And which advanced display features are those?  Perhaps they are
important enough to have this variable be nil by default?

> +                                                  (@pxref{Tooltips,,,
> +elisp, The Emacs Lisp Reference Manual}).

This should be @xref and without the parens.  Also, make sure you
leave 2 spaces between sentences.

> +@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.
> +@end table

Since you already explained what this does and mentioned the variable
name, this @table seems redundant.  Just move the @vindex entry to the
above text.

> +  Two of these backends, @code{ftcr} and @code{ftcrfont} are identical
                                  ^^^^
"ftcr" or "ftfont"?  Or maybe you meant ftcrfont and ftcrhb?

> @@ -3143,6 +3155,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.
> +
> +@footnote{Lowering frames is not supported on Haiku, due to limitations
> +imposed by the system.}

@footnote generates a superscript number, so it should follow some
text, presumably the last sentence before it.  Please see how
@footnote is used elsewhere in the manual.

>  Some window managers may refuse to restack windows.
> +
> +@footnote{Restacking frames is not supported on Haiku, due to limitations
> +imposed by the system.}

Likewise.

> @@ -3272,6 +3290,12 @@ Child Frames
>  allowing them to be positioned so they do not obscure the parent frame
>  while still being visible themselves.
>  
> +@footnote{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.}

Likewise.

> +** 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.
> +

The lines in these NEWS entries are too long, please use the default
value of fill-column when you file them.

>  ** 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 f506881a4b..c548ee9b36 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.
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This would benefit from telling what configure option or run-time
command-line arguments to use to do that.

>  (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))))

This seems to contradict what you wrote in the user manual about
tooltip options?

> +  while (get_next_team_info (&cookie, &info) == B_OK)
> +    {
> +      lval = Fcons (make_fixnum (info.team), lval);
> +    }

These braces are redundant.

> diff --git a/src/sysdep.c b/src/sysdep.c
> index 8eaee22498..67b6b2aa3d 100644
> --- a/src/sysdep.c
> +++ b/src/sysdep.c
> @@ -71,6 +71,10 @@
>  # include <math.h>
>  #endif
>  
> +#ifdef HAIKU
> +#include <kernel/OS.h>
> +#endif

Is this include still needed in sysdep.c?





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

* bug#51658: [PATCH] Haiku port (again)
  2021-11-20  8:33                                   ` Eli Zaretskii
@ 2021-11-20  9:34                                     ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-11-20  9:56                                       ` Eli Zaretskii
  0 siblings, 1 reply; 31+ messages in thread
From: Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2021-11-20  9:34 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 51658

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

Eli Zaretskii <eliz@gnu.org> writes:

>>  (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))))

> This seems to contradict what you wrote in the user manual about
> tooltip options?

This only disables tooltips on Haiku if a menu or popup is active, as
Haiku doesn't let you map windows or tooltips above them.

Here's a patch with the other issues you mentioned fixed.  Please see if
it's better, thanks.


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

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 c231c2ceae..1ee5f0c6ad 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,39 @@ AC_DEFUN
   with_xft=no
 fi
 
+HAIKU_OBJ=
+HAIKU_CXX_OBJ=
+HAIKU_LIBS=
+HAIKU_CFLAGS=
+
+if test "$opsys" = "haiku"; then
+  HAIKU_OBJ="$HAIKU_OBJ haiku.o"
+fi
+
+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 +2302,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 +2636,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 +2661,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 +2681,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 +3332,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 +3424,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 +3589,58 @@ 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
+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 +3762,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 +4080,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 +4155,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 +4184,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 +4602,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])
 
@@ -4814,7 +4962,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
@@ -5069,7 +5217,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
@@ -5165,6 +5313,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
 
 
@@ -5386,8 +5542,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)
@@ -5430,6 +5603,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
@@ -5542,6 +5731,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
@@ -5930,7 +6130,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..ce92435ae7 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,11 @@ 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 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 +1624,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..d6dca685e0
--- /dev/null
+++ b/doc/emacs/haiku.texi
@@ -0,0 +1,134 @@
+@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 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}; ff 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, use the binary named
+@code{emacs} 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.
+
+  It is 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.  The variables that allow for
+that are described below.
+
+@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-shift-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 be 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.
+
+@cindex tooltips (haiku)
+@cindex haiku tooltips
+@vindex haiku-use-system-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 display text properties or faces.  If you need those
+features, customize the variable @code{haiku-use-system-tooltips} to
+the nil value, and Emacs will use its own implementation of tooltips.
+
+@subsection What to do when Emacs crashes
+@cindex crashes, Haiku
+@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.
+
+@table @code
+@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
+
+@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}).
+
+  Two of these backends, @code{ftcr} and @code{ftcrhb} are identical
+to their counterparts on the X Window System.  There is also a
+Haiku-specific backend named @code{haiku}, that uses the App Server to
+draw fonts, but does not at present support display of color font and
+emoji.
diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi
index 08426032e0..e0fd70fc93 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},
@@ -8268,6 +8269,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
@@ -8314,6 +8317,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
diff --git a/doc/lispref/frames.texi b/doc/lispref/frames.texi
index 31ad82b7ad..923ff19997 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
@@ -1729,7 +1730,9 @@ Size Parameters
 @item fullscreen
 This parameter specifies whether to maximize the frame's width, height
 or both.  Its value can be @code{fullwidth}, @code{fullheight},
-@code{fullboth}, or @code{maximized}.  A @dfn{fullwidth} frame is as
+@code{fullboth}, or @code{maximized}.@footnote{On Haiku, setting
+@code{fullscreen} to @code{fullwidth} or @code{fullheight} has no
+effect.}  A @dfn{fullwidth} frame is as
 wide as possible, a @dfn{fullheight} frame is as tall as possible, and
 a @dfn{fullboth} frame is both as wide and as tall as possible.  A
 @dfn{maximized} frame is like a ``fullboth'' frame, except that it usually
@@ -2191,7 +2194,10 @@ 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, not even
+if the user switches to the frame using the key combination
+@kbd{Alt-@key{TAB}}.
 
 @vindex undecorated@r{, a frame parameter}
 @item undecorated
@@ -2352,7 +2358,10 @@ 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 can be several font drivers (@pxref{Haiku Fonts,,, emacs,
+The GNU Emacs Manual}).
+
+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}
@@ -3141,8 +3150,10 @@ Raising and Lowering
 @deffn Command lower-frame &optional frame
 This function lowers frame @var{frame} (default, the selected frame)
 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.
+@var{frame}.@footnote{Lowering frames is not supported on Haiku, due
+to limitations imposed by the system.}  If @var{frame} is a child
+frame (@pxref{Child Frames}), this lowers @var{frame} below all other
+child frames of its parent.
 @end deffn
 
 @defun frame-restack frame1 frame2 &optional above
@@ -3152,7 +3163,8 @@ Raising and Lowering
 third argument @var{above} is non-@code{nil}, this function restacks
 @var{frame1} above @var{frame2}.  This means that if both frames are
 visible and their display areas overlap, @var{frame1} will (partially)
-obscure @var{frame2}.
+obscure @var{frame2}.@footnote{Restacking frames is not supported on
+Haiku, due to limitations imposed by the system.}
 
 Technically, this function may be thought of as an atomic action
 performed in two steps: The first step removes @var{frame1}'s
@@ -3247,12 +3259,16 @@ Child Frames
 
 @cindex reparent frame
 @cindex nest frame
-  The @code{parent-frame} parameter can be changed at any time.  Setting
-it to another frame @dfn{reparents} the child frame.  Setting it to
-another child frame makes the frame a @dfn{nested} child frame.  Setting
-it to @code{nil} restores the frame's status as a top-level frame---a
-frame whose window-system window is a child of its display's root
-window.
+  The @code{parent-frame} parameter can be changed at any time.
+Setting it to another frame @dfn{reparents} the child frame.  Setting
+it to another child frame makes the frame a @dfn{nested} child frame.
+Setting it to @code{nil} restores the frame's status as a top-level
+frame---a frame whose window-system window is a child of its display's
+root window.@footnote{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.}
 
   Since child frames can be arbitrarily nested, a frame can be both a
 child and a parent frame.  Also, the relative roles of child and parent
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.
+
 \f
 * Obsolete platforms
 
diff --git a/etc/NEWS b/etc/NEWS
index 70ba5341d8..adc00e1b3d 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -24,6 +24,27 @@ applies, and please also update docstrings as needed.
 \f
 * 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 f506881a4b..acff3be7da 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 configuring Emacs '--with-be-cairo'.
+
+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.
@@ -1105,6 +1114,13 @@ In your ~/.Xresources file, then run
 
 And restart Emacs.
 
+** On Haiku, BeCJK doesn't work properly with Emacs
+
+Some popular Haiku input methods such BeCJK are known to behave badly
+when interacting with Emacs, in ways such as stealing input focus and
+displaying popup windows that don't disappear.  If you are affected,
+you should use an Emacs input method instead.
+
 * X runtime problems
 
 ** X keyboard problems
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 6c353b0d9e..b7c53a4dfe 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 15a71ef244..ed1570e778 100644
--- a/lisp/loadup.el
+++ b/lisp/loadup.el
@@ -303,6 +303,11 @@
       (load "term/common-win")
       (load "term/x-win")))
 
+(if (featurep 'haiku)
+    (progn
+      (load "term/common-win")
+      (load "term/haiku-win")))
+
 (if (or (eq system-type 'windows-nt)
         (featurep 'w32))
     (progn
@@ -558,6 +563,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 94e75efeeb..76457d0c69 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 031a73143e..e86d21f889 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..36af10d2c7
--- /dev/null
+++ b/lisp/term/haiku-win.el
@@ -0,0 +1,134 @@
+;;; 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-dnd-handle-drag-n-drop-event (event)
+  "Handle specified drag-n-drop EVENT."
+  (interactive "e")
+  (let* ((string (caddr event))
+	 (window (posn-window (event-start event))))
+    (with-selected-window window
+      (raise-frame)
+      (dnd-handle-one-url window 'private (concat "file:" string)))))
+
+(define-key special-event-map [drag-n-drop]
+            'haiku-dnd-handle-drag-n-drop-event)
+
+(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 4015448ece..859b30564a 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..f52dae4719 100644
--- a/src/floatfns.c
+++ b/src/floatfns.c
@@ -347,6 +347,21 @@ DEFUN ("logb", Flogb, Slogb, 1, 1, 0,
 double_integer_scale (double d)
 {
   int exponent = ilogb (d);
+#ifdef HAIKU
+  /* On Haiku, the values returned by ilogb are nonsensical when
+     confronted with tiny numbers, inf, or NaN, which breaks the trick
+     used by code on other platforms, so we have to test for each case
+     manually, and return the appropriate value.  */
+  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 b503123b96..d423fd46b7 100644
--- a/src/font.c
+++ b/src/font.c
@@ -5751,6 +5751,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 6694164e09..2da5ec4504 100644
--- a/src/font.h
+++ b/src/font.h
@@ -965,7 +965,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;
@@ -999,7 +999,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 03e44ec30e..cf592759ab 100644
--- a/src/ftfont.c
+++ b/src/ftfont.c
@@ -3108,6 +3108,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.c b/src/haiku.c
new file mode 100644
index 0000000000..485d86983c
--- /dev/null
+++ b/src/haiku.c
@@ -0,0 +1,286 @@
+/* Haiku subroutines that are general to the Haiku operating system.
+   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 "process.h"
+#include "coding.h"
+
+#include <kernel/OS.h>
+
+#include <pwd.h>
+#include <stdlib.h>
+
+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;
+}
+
+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;
+}
+
+
+/* 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;
+}
diff --git a/src/haiku_draw_support.cc b/src/haiku_draw_support.cc
new file mode 100644
index 0000000000..5b1eccfbe6
--- /dev/null
+++ b/src/haiku_draw_support.cc
@@ -0,0 +1,488 @@
+/* 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 ();
+}
+
+/* Convert RGB32 color color from RGB color space to its
+   HSL components pointed to by H, S and L.  */
+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..9ac0400969
--- /dev/null
+++ b/src/haiku_font_support.cc
@@ -0,0 +1,596 @@
+/* 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;
+}
+
+/* Return non-null if FONT contains CHR, a Unicode code-point.  */
+int
+BFont_have_char_p (void *font, int32_t chr)
+{
+  BFont *ft = (BFont *) font;
+  return ft->IncludesBlock (chr, chr);
+}
+
+/* Return non-null if font contains a block from BEG to END.  */
+int
+BFont_have_char_block (void *font, int32_t beg, int32_t end)
+{
+  BFont *ft = (BFont *) font;
+  return ft->IncludesBlock (beg, 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.  */
+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);
+}
+
+/* The same, but for a variable amount of chars.  */
+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);
+    }
+}
+
+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;
+}
+
+/* Delete every element of the font pattern PT.  */
+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;
+    }
+}
+
+/* Find all fonts matching the font pattern PT.  */
+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;
+}
+
+/* Find and open a font matching the pattern PAT, which must have its
+   family set.  */
+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;
+}
+
+/* Query the family of the default fixed font.  */
+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..c152d9b086
--- /dev/null
+++ b/src/haiku_io.c
@@ -0,0 +1,207 @@
+/* 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
+
+/* The port used to send messages from the application thread to
+   Emacs.  */
+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 ();
+}
+
+/* Read the size of the next message into len, returning -1 if the
+   query fails or there is no next message.  */
+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;
+}
+
+/* Read the next message into BUF, putting its type into TYPE,
+   assuming the message is at most LEN long.  Return 0 if successful
+   and -1 if the read fails.  */
+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;
+}
+
+/* The same as haiku_read, but time out after TIMEOUT microseconds.
+   Input is blocked when an attempt to read is in progress.  */
+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;
+}
+
+/* Write a message with type TYPE into BUF.  */
+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");
+}
+
+/* Record an unwind protect from C++ code.  */
+void
+record_c_unwind_protect_from_cxx (void (*fn) (void *), void *r)
+{
+  record_unwind_protect_ptr (fn, r);
+}
+
+/* SPECPDL_IDX that is safe from C++ code.  */
+ptrdiff_t
+c_specpdl_idx_from_cxx (void)
+{
+  return SPECPDL_INDEX ();
+}
+
+/* unbind_to (IDX, Qnil), but safe from C++ code.  */
+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..99d4ee7914
--- /dev/null
+++ b/src/haiku_support.cc
@@ -0,0 +1,2930 @@
+/* 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;
+    int32 x, y;
+    void *window;
+
+    if ((msg->FindPointer ("window", 0, &window) != B_OK)
+	|| (msg->FindInt32 ("x", 0, &x) != B_OK)
+	|| (msg->FindInt32 ("y", 0, &y) != B_OK))
+      return;
+
+    rq.window = window;
+    rq.x = x;
+    rq.y = y;
+
+    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;
+	BPoint whereto;
+
+        if (msg->FindRef ("refs", &ref) == B_OK)
+	  {
+	    msg->what = B_REFS_RECEIVED;
+	    msg->AddPointer ("window", this);
+	    if (msg->FindPoint ("_drop_point_", &whereto) == B_OK)
+	      {
+		this->ConvertFromScreen (&whereto);
+		msg->AddInt32 ("x", whereto.x);
+		msg->AddInt32 ("y", whereto.y);
+	      }
+	    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)
+	  gui_abort ("strdup failed");
+      }
+
+    if (help)
+      {
+	this->help = strdup (help);
+	if (!this->help)
+	  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;
+}
+
+/* Take BITMAP, a reference to a BBitmap, and return a pointer to its
+   data.  */
+void *
+BBitmap_data (void *bitmap)
+{
+  return ((BBitmap *) bitmap)->Bits ();
+}
+
+/* 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'.  */
+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;
+}
+
+/* Create new bitmap in RGB32 format, or in GRAY1 if MONO_P is
+   non-zero.  */
+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);
+}
+
+/* Set up an application and return it.  If starting the application
+   thread fails, abort Emacs.  */
+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;
+}
+
+/* Set up and return a window with its view put in VIEW.  */
+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 ();
+}
+
+/* Set WINDOW's offset to X, Y.  */
+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);
+}
+
+/* Iconify WINDOW.  */
+void
+BWindow_iconify (void *window)
+{
+  if (((BWindow *) window)->IsHidden ())
+    BWindow_set_visible (window, true);
+  ((BWindow *) window)->Minimize (true);
+}
+
+/* Show or hide WINDOW.  */
+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 ();
+}
+
+/* Change the title of WINDOW to the multibyte string TITLE.  */
+void
+BWindow_retitle (void *window, const char *title)
+{
+  ((BWindow *) window)->SetTitle (title);
+}
+
+/* Resize WINDOW to WIDTH by HEIGHT.  */
+void
+BWindow_resize (void *window, int width, int height)
+{
+  ((BWindow *) window)->ResizeTo (width, height);
+}
+
+/* Activate WINDOW, making it the subject of keyboard focus and
+   bringing it to the front of the screen.  */
+void
+BWindow_activate (void *window)
+{
+  ((BWindow *) window)->Activate ();
+}
+
+/* Return the pixel dimensions of the main screen in WIDTH and
+   HEIGHT.  */
+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;
+}
+
+/* Resize VIEW to WIDTH, HEIGHT.  */
+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 ();
+}
+
+/* Map the keycode KC, storing the result in CODE and 1 in
+   NON_ASCII_P if it should be used.  */
+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;
+}
+
+/* Make a scrollbar, attach it to VIEW's window, and return it.  */
+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);
+}
+
+/* Return the default scrollbar size.  */
+int
+BScrollBar_default_size (int horizontal_p)
+{
+  return horizontal_p ? B_H_SCROLL_BAR_HEIGHT : B_V_SCROLL_BAR_WIDTH;
+}
+
+/* Invalidate VIEW, causing it to be drawn again.  */
+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 ();
+}
+
+/* Lock VIEW in preparation for drawing operations.  This should be
+   called before any attempt to draw onto VIEW or to lock it for Cairo
+   drawing.  `BView_draw_unlock' should be called afterwards.  */
+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 ();
+}
+
+/* Tell VIEW it has been clicked at X by Y.  */
+void
+BView_mouse_down (void *view, int x, int y)
+{
+  BView *vw = (BView *) view;
+  if (vw->LockLooper ())
+    {
+      vw->MouseDown (BPoint (x, y));
+      vw->UnlockLooper ();
+    }
+}
+
+/* Tell VIEW the mouse has been released at X by Y.  */
+void
+BView_mouse_up (void *view, int x, int y)
+{
+  BView *vw = (BView *) view;
+  if (vw->LockLooper ())
+    {
+      vw->MouseUp (BPoint (x, y));
+      vw->UnlockLooper ();
+    }
+}
+
+/* Tell VIEW that the mouse has moved to Y by Y.  */
+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 ();
+    }
+}
+
+/* Import BITS into BITMAP using the B_GRAY1 colorspace.  */
+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 ();
+    }
+}
+
+/* Make a scrollbar at X, Y known to the view VIEW.  */
+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 ();
+    }
+}
+
+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);
+}
+
+/* Perform an in-place conversion of X and Y from VIEW's coordinate
+   system to its screen's coordinate system.  */
+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);
+}
+
+/* Decorate or undecorate WINDOW depending on DECORATE_P.  */
+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 ();
+}
+
+/* Decorate WINDOW appropriately for use as a tooltip.  */
+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 ();
+}
+
+/* Set B_AVOID_FOCUS on WINDOW if AVOID_FOCUS_P is non-nil, or clear
+   it otherwise.  */
+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;
+}
+
+/* Return the current workspace.  */
+uint32_t
+haiku_current_workspace (void)
+{
+  return current_workspace ();
+}
+
+/* Return a bitmask consisting of workspaces WINDOW is on.  */
+uint32_t
+BWindow_workspaces (void *window)
+{
+  return ((BWindow *) window)->Workspaces ();
+}
+
+/* Create a popup menu.  */
+void *
+BPopUpMenu_new (const char *name)
+{
+  BPopUpMenu *menu = new EmacsPopUpMenu (name);
+  menu->SetRadioMode (0);
+  return menu;
+}
+
+/* Add a title item to MENU.  These items cannot be highlighted or
+   triggered, and their labels will display as bold text.  */
+void
+BMenu_add_title (void *menu, const char *text)
+{
+  EmacsTitleMenuItem *it = new EmacsTitleMenuItem (text);
+  BMenu *mn = (BMenu *) menu;
+  mn->AddItem (it);
+}
+
+/* Add an item to the menu MENU.  */
+void
+BMenu_add_item (void *menu, const char *label, void *ptr, bool enabled_p,
+		bool marked_p, bool mbar_p, void *mbw_ptr, const char *key,
+		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);
+}
+
+/* Add a separator to the menu MENU.  */
+void
+BMenu_add_separator (void *menu)
+{
+  BMenu *m = (BMenu *) menu;
+
+  m->AddSeparatorItem ();
+}
+
+/* Create a submenu and attach it to MENU.  */
+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;
+}
+
+/* Create a submenu that notifies Emacs upon opening.  */
+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;
+}
+
+/* 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.  */
+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;
+}
+
+/* Delete the entire menu hierarchy of MENU, and then delete MENU
+   itself.  */
+void
+BPopUpMenu_delete (void *menu)
+{
+  delete (BPopUpMenu *) menu;
+}
+
+/* Create a menubar, attach it to VIEW, and return it.  */
+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;
+}
+
+/* Delete MENUBAR along with all subitems. */
+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;
+}
+
+/* Delete all items from MENU.  */
+void
+BMenu_delete_all (void *menu)
+{
+  BMenu *mn = (BMenu *) menu;
+  mn->RemoveItems (0, mn->CountItems (), true);
+}
+
+/* Delete COUNT items from MENU starting from START.  */
+void
+BMenu_delete_from (void *menu, int start, int count)
+{
+  BMenu *mn = (BMenu *) menu;
+  mn->RemoveItems (start, count, true);
+}
+
+/* Count items in menu MENU.  */
+int
+BMenu_count_items (void *menu)
+{
+  return ((BMenu *) menu)->CountItems ();
+}
+
+/* Find the item in MENU at IDX.  */
+void *
+BMenu_item_at (void *menu, int idx)
+{
+  return ((BMenu *) menu)->ItemAt (idx);
+}
+
+/* Set ITEM's label to LABEL.  */
+void
+BMenu_item_set_label (void *item, const char *label)
+{
+  ((BMenuItem *) item)->SetLabel (label);
+}
+
+/* Get ITEM's menu.  */
+void *
+BMenu_item_get_menu (void *item)
+{
+  return ((BMenuItem *) item)->Submenu ();
+}
+
+/* Emit a beep noise.  */
+void
+haiku_ring_bell (void)
+{
+  beep ();
+}
+
+/* Create a BAlert with TEXT.  */
+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);
+}
+
+/* Add a button to ALERT and return the button.  */
+void *
+BAlert_add_button (void *alert, const char *text)
+{
+  BAlert *al = (BAlert *) alert;
+  al->AddButton (text);
+  return al->ButtonAt (al->CountButtons () - 1);
+}
+
+/* Run ALERT, returning the number of the button that was selected,
+   or -1 if no button was selected before the alert was closed.  */
+int32_t
+BAlert_go (void *alert)
+{
+  return ((BAlert *) alert)->Go ();
+}
+
+/* Enable or disable BUTTON depending on ENABLED_P.  */
+void
+BButton_set_enabled (void *button, int enabled_p)
+{
+  ((BButton *) button)->SetEnabled (enabled_p);
+}
+
+/* Set VIEW's tooltip to TOOLTIP.  */
+void
+BView_set_tooltip (void *view, const char *tooltip)
+{
+  ((BView *) view)->SetToolTip (tooltip);
+}
+
+/* Set VIEW's tooltip to a sticky tooltip at X by Y.  */
+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 ();
+}
+
+/* Delete ALERT.  */
+void
+BAlert_delete (void *alert)
+{
+  delete (BAlert *) alert;
+}
+
+/* Place the resolution of the monitor in DPI in RSSX and RSSY.  */
+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;
+    }
+}
+
+/* Add WINDOW to OTHER_WINDOW's subset and parent it to
+   OTHER_WINDOW.  */
+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 ();
+}
+
+/* Place text describing the current version of Haiku in VERSION,
+   which should be a buffer LEN bytes wide.  */
+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);
+    }
+}
+
+/* Return the amount of color planes in the current display.  */
+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;
+}
+
+/* Return the amount of colors the display can handle.  */
+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;
+}
+
+/* Warp the pointer to X by Y.  */
+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);
+}
+
+/* Update the position of CHILD in WINDOW without actually moving
+   it.  */
+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 ();
+}
+
+/* Find an appropriate view to draw onto.  If VW is double-buffered,
+   this will be the view used for double buffering instead of VW
+   itself.  */
+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;
+}
+
+/* Set up double buffering for 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 ();
+}
+
+/* Flip and invalidate the view VW.  */
+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 ();
+}
+
+/* Disable double buffering for VW.  */
+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 ();
+}
+
+/* Return non-0 if VW is double-buffered.  */
+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));
+}
+
+/* Popup a file dialog.  */
+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);
+    }
+}
+
+/* Temporarily fill VIEW with COLOR.  */
+void
+EmacsView_do_visible_bell (void *view, uint32_t color)
+{
+  EmacsView *vw = (EmacsView *) view;
+  vw->DoVisibleBell (color);
+}
+
+/* Zoom WINDOW.  */
+void
+BWindow_zoom (void *window)
+{
+  BWindow *w = (BWindow *) window;
+  w->Zoom ();
+}
+
+/* Make WINDOW fullscreen if FULLSCREEN_P.  */
+void
+EmacsWindow_make_fullscreen (void *window, int fullscreen_p)
+{
+  EmacsWindow *w = (EmacsWindow *) window;
+  w->MakeFullscreen (fullscreen_p);
+}
+
+/* Unzoom (maximize) WINDOW.  */
+void
+EmacsWindow_unzoom (void *window)
+{
+  EmacsWindow *w = (EmacsWindow *) window;
+  w->UnZoom ();
+}
+
+/* Move the pointer into MBAR and start tracking.  */
+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
+
+/* Return the size of BITMAP's data, in bytes.  */
+size_t
+BBitmap_bytes_length (void *bitmap)
+{
+  BBitmap *bm = (BBitmap *) bitmap;
+  return bm->BitsLength ();
+}
+
+/* Show VIEW's tooltip.  */
+void
+BView_show_tooltip (void *view)
+{
+  BView *vw = (BView *) view;
+  if (vw->LockLooper ())
+    {
+      vw->ShowToolTip (vw->ToolTip ());
+      vw->UnlockLooper ();
+    }
+}
+
+
+#ifdef USE_BE_CAIRO
+/* Return VIEW's cairo surface.  */
+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;
+}
+
+/* Transfer each clip rectangle in VIEW to the cairo context
+   CTX.  */
+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);
+}
+
+/* Lock WINDOW in preparation for drawing using Cairo.  */
+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");
+}
+
+/* Unlock WINDOW in preparation for drawing using Cairo.  */
+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
+
+/* Get the width of STR in the plain font.  */
+int
+be_string_width_with_plain_font (const char *str)
+{
+  return be_plain_font->StringWidth (str);
+}
+
+/* Get the ascent + descent of the plain font.  */
+int
+be_plain_font_height (void)
+{
+  struct font_height fheight;
+  be_plain_font->GetHeight (&fheight);
+
+  return fheight.ascent + fheight.descent;
+}
+
+/* Return the number of physical displays connected.  */
+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;
+}
+
+/* Set the minimum width the user can resize WINDOW to.  */
+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 ();
+}
+
+/* Set the alignment of WINDOW's dimensions.  */
+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..9f5f3c77e3
--- /dev/null
+++ b/src/haiku_support.h
@@ -0,0 +1,869 @@
+/* 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
+{
+  void *window;
+  int x, y;
+  /* 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
+
+  extern port_id port_application_to_emacs;
+
+  extern void haiku_io_init (void);
+  extern void haiku_io_init_in_app_thread (void);
+
+  extern void
+  haiku_read_size (ssize_t *len);
+
+  extern int
+  haiku_read (enum haiku_event_type *type, void *buf, ssize_t len);
+
+  extern int
+  haiku_read_with_timeout (enum haiku_event_type *type, void *buf, ssize_t len,
+			   time_t timeout);
+
+  extern int
+  haiku_write (enum haiku_event_type type, void *buf);
+
+  extern int
+  haiku_write_without_signal (enum haiku_event_type type, void *buf);
+
+  extern void
+  rgb_color_hsl (uint32_t rgb, double *h, double *s, double *l);
+
+  extern void
+  hsl_color_rgb (double h, double s, double l, uint32_t *rgb);
+
+  extern void *
+  BBitmap_new (int width, int height, int mono_p);
+
+  extern void *
+  BBitmap_data (void *bitmap);
+
+  extern int
+  BBitmap_convert (void *bitmap, void **new_bitmap);
+
+  extern void
+  BBitmap_free (void *bitmap);
+
+  extern void
+  BBitmap_dimensions (void *bitmap, int *left, int *top,
+		      int *right, int *bottom, int32_t *bytes_per_row,
+		      int *mono_p);
+
+  extern void *
+  BApplication_setup (void);
+
+  extern void *
+  BWindow_new (void *view);
+
+  extern void
+  BWindow_quit (void *window);
+
+  extern void
+  BWindow_set_offset (void *window, int x, int y);
+
+  extern void
+  BWindow_iconify (void *window);
+
+  extern void
+  BWindow_set_visible (void *window, int visible_p);
+
+  extern void
+  BFont_close (void *font);
+
+  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);
+
+  extern int
+  BFont_have_char_p (void *font, int32_t chr);
+
+  extern int
+  BFont_have_char_block (void *font, int32_t beg, int32_t end);
+
+  extern void
+  BFont_char_bounds (void *font, const char *mb_str, int *advance,
+		     int *lb, int *rb);
+
+  extern void
+  BFont_nchar_bounds (void *font, const char *mb_str, int *advance,
+		      int *lb, int *rb, int32_t n);
+
+  extern void
+  BWindow_retitle (void *window, const char *title);
+
+  extern void
+  BWindow_resize (void *window, int width, int height);
+
+  extern void
+  BWindow_activate (void *window);
+
+  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);
+
+  extern void
+  BScreen_px_dim (int *width, int *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);
+
+  extern void
+  BCursor_delete (void *cursor);
+
+  extern void
+  BView_set_view_cursor (void *view, void *cursor);
+
+  extern void
+  BWindow_Flush (void *window);
+
+  extern void
+  BMapKey (uint32_t kc, int *non_ascii_p, unsigned *code);
+
+  extern void *
+  BScrollBar_make_for_view (void *view, int horizontal_p,
+			    int x, int y, int x1, int y1,
+			    void *scroll_bar_ptr);
+
+  extern void
+  BScrollBar_delete (void *sb);
+
+  extern void
+  BView_move_frame (void *view, int x, int y, int x1, int y1);
+
+  extern void
+  BView_scroll_bar_update (void *sb, int portion, int whole, int position);
+
+  extern int
+  BScrollBar_default_size (int horizontal_p);
+
+  extern void
+  BView_invalidate (void *view);
+
+  extern void
+  BView_draw_lock (void *view);
+
+  extern void
+  BView_draw_unlock (void *view);
+
+  extern void
+  BWindow_center_on_screen (void *window);
+
+  extern void
+  BView_mouse_moved (void *view, int x, int y, uint32_t transit);
+
+  extern void
+  BView_mouse_down (void *view, int x, int y);
+
+  extern void
+  BView_mouse_up (void *view, int x, int y);
+
+  extern void
+  BBitmap_import_mono_bits (void *bitmap, void *bits, int wd, int h);
+
+  extern void
+  haiku_font_pattern_free (struct haiku_font_pattern *pt);
+
+  extern struct haiku_font_pattern *
+  BFont_find (struct haiku_font_pattern *pt);
+
+  extern int
+  BFont_open_pattern (struct haiku_font_pattern *pat, void **font, float size);
+
+  extern void
+  BFont_populate_fixed_family (struct haiku_font_pattern *ptn);
+
+  extern void
+  BFont_populate_plain_family (struct haiku_font_pattern *ptn);
+
+  extern void
+  BView_publish_scroll_bar (void *view, int x, int y, int width, int height);
+
+  extern void
+  BView_forget_scroll_bar (void *view, int x, int y, int width, int height);
+
+  extern void
+  BView_get_mouse (void *view, int *x, int *y);
+
+  extern void
+  BView_convert_to_screen (void *view, int *x, int *y);
+
+  extern void
+  BView_convert_from_screen (void *view, int *x, int *y);
+
+  extern void
+  BWindow_change_decoration (void *window, int decorate_p);
+
+  extern void
+  BWindow_set_tooltip_decoration (void *window);
+
+  extern void
+  BWindow_set_avoid_focus (void *window, int avoid_focus_p);
+
+  extern void
+  BView_emacs_delete (void *view);
+
+  extern uint32_t
+  haiku_current_workspace (void);
+
+  extern uint32_t
+  BWindow_workspaces (void *window);
+
+  extern void *
+  BPopUpMenu_new (const char *name);
+
+  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);
+
+  extern void
+  BMenu_add_separator (void *menu);
+
+  extern void *
+  BMenu_new_submenu (void *menu, const char *label, bool enabled_p);
+
+  extern void *
+  BMenu_new_menu_bar_submenu (void *menu, const char *label);
+
+  extern int
+  BMenu_count_items (void *menu);
+
+  extern void *
+  BMenu_item_at (void *menu, int idx);
+
+  extern void *
+  BMenu_run (void *menu, int x, int y);
+
+  extern void
+  BPopUpMenu_delete (void *menu);
+
+  extern void *
+  BMenuBar_new (void *view);
+
+  extern void
+  BMenu_delete_all (void *menu);
+
+  extern void
+  BMenuBar_delete (void *menubar);
+
+  extern void
+  BMenu_item_set_label (void *item, const char *label);
+
+  extern void *
+  BMenu_item_get_menu (void *item);
+
+  extern void
+  BMenu_delete_from (void *menu, int start, int count);
+
+  extern void
+  haiku_ring_bell (void);
+
+  extern void *
+  BAlert_new (const char *text, enum haiku_alert_type type);
+
+  extern void *
+  BAlert_add_button (void *alert, const char *text);
+
+  extern int32_t
+  BAlert_go (void *alert);
+
+  extern void
+  BButton_set_enabled (void *button, int enabled_p);
+
+  extern void
+  BView_set_tooltip (void *view, const char *tooltip);
+
+  extern void
+  BAlert_delete (void *alert);
+
+  extern void
+  BScreen_res (double *rrsx, double *rrsy);
+
+  extern void
+  EmacsWindow_parent_to (void *window, void *other_window);
+
+  extern void
+  EmacsWindow_unparent (void *window);
+
+  extern int
+  BFont_string_width (void *font, const char *utf8);
+
+  extern void
+  be_get_version_string (char *version, int len);
+
+  extern int
+  be_get_display_planes (void);
+
+  extern int
+  be_get_display_color_cells (void);
+
+  extern void
+  be_warp_pointer (int x, int y);
+
+  extern void
+  EmacsWindow_move_weak_child (void *window, void *child, int xoff, int yoff);
+
+  extern void
+  EmacsView_set_up_double_buffering (void *vw);
+
+  extern void
+  EmacsView_disable_double_buffering (void *vw);
+
+  extern void
+  EmacsView_flip_and_blit (void *vw);
+
+  extern int
+  EmacsView_double_buffered_p (void *vw);
+
+  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));
+
+  extern void
+  record_c_unwind_protect_from_cxx (void (*) (void *), void *);
+
+  extern ptrdiff_t
+  c_specpdl_idx_from_cxx (void);
+
+  extern void
+  c_unbind_to_nil_from_cxx (ptrdiff_t idx);
+
+  extern void
+  EmacsView_do_visible_bell (void *view, uint32_t color);
+
+  extern void
+  BWindow_zoom (void *window);
+
+  extern void
+  EmacsWindow_make_fullscreen (void *window, int fullscreen_p);
+
+  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
+
+  extern void
+  BMenuBar_start_tracking (void *mbar);
+
+  extern size_t
+  BBitmap_bytes_length (void *bitmap);
+
+  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
+
+  extern void
+  BView_set_and_show_sticky_tooltip (void *view, const char *tooltip,
+				     int x, int y);
+
+  extern void
+  BMenu_add_title (void *menu, const char *text);
+
+  extern int
+  be_plain_font_height (void);
+
+  extern int
+  be_string_width_with_plain_font (const char *str);
+
+  extern int
+  be_get_display_screens (void);
+
+  extern void
+  BWindow_set_min_size (void *window, int width, int height);
+
+  extern void
+  BWindow_set_size_alignment (void *window, int align_width, int align_height);
+
+#ifdef __cplusplus
+  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..868fc71f97
--- /dev/null
+++ b/src/haikufns.c
@@ -0,0 +1,2448 @@
+/* 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);
+}
+
+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 ();
+}
+
+\f
+
+DEFUN ("haiku-set-mouse-absolute-pixel-position",
+       Fhaiku_set_mouse_absolute_pixel_position,
+       Shaiku_set_mouse_absolute_pixel_position, 2, 2, 0,
+       doc: /* Move mouse pointer to a pixel position at (X, Y).  The
+coordinates X and Y are interpreted to start from the top-left
+corner of the screen.  */)
+  (Lisp_Object x, Lisp_Object y)
+{
+  int xval = check_integer_range (x, INT_MIN, INT_MAX);
+  int yval = check_integer_range (y, INT_MIN, INT_MAX);
+
+  if (!x_display_list)
+    error ("Window system not initialized");
+
+  block_input ();
+  be_warp_pointer (xval, yval);
+  unblock_input ();
+  return Qnil;
+}
+
+DEFUN ("haiku-mouse-absolute-pixel-position", Fhaiku_mouse_absolute_pixel_position,
+       Shaiku_mouse_absolute_pixel_position, 0, 0, 0,
+       doc: /* Return absolute position of mouse cursor in pixels.
+The position is returned as a cons cell (X . Y) of the coordinates of
+the mouse cursor position in pixels relative to a position (0, 0) of the
+selected frame's display.  */)
+  (void)
+{
+  if (!x_display_list)
+    return Qnil;
+
+  struct frame *f = SELECTED_FRAME ();
+
+  if (FRAME_INITIAL_P (f) || !FRAME_HAIKU_P (f)
+      || !FRAME_HAIKU_VIEW (f))
+    return Qnil;
+
+  block_input ();
+  void *view = FRAME_HAIKU_VIEW (f);
+
+  int x, y;
+  BView_get_mouse (view, &x, &y);
+  BView_convert_to_screen (view, &x, &y);
+  unblock_input ();
+
+  return Fcons (make_fixnum (x), make_fixnum (y));
+}
+
+DEFUN ("xw-display-color-p", Fxw_display_color_p, Sxw_display_color_p, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+     (Lisp_Object terminal)
+{
+  return Qt;
+}
+
+DEFUN ("xw-color-defined-p", Fxw_color_defined_p, Sxw_color_defined_p, 1, 2, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object color, Lisp_Object frame)
+{
+  Emacs_Color col;
+  CHECK_STRING (color);
+  decode_window_system_frame (frame);
+
+  return haiku_get_color (SSDATA (color), &col) ? Qnil : Qt;
+}
+
+DEFUN ("xw-color-values", Fxw_color_values, Sxw_color_values, 1, 2, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+     (Lisp_Object color, Lisp_Object frame)
+{
+  Emacs_Color col;
+  CHECK_STRING (color);
+  decode_window_system_frame (frame);
+
+  block_input ();
+  if (haiku_get_color (SSDATA (color), &col))
+    {
+      unblock_input ();
+      return Qnil;
+    }
+  unblock_input ();
+  return list3i (lrint (col.red), lrint (col.green), lrint (col.blue));
+}
+
+DEFUN ("x-display-grayscale-p", Fx_display_grayscale_p, Sx_display_grayscale_p,
+       0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  return Qnil;
+}
+
+DEFUN ("x-open-connection", Fx_open_connection, Sx_open_connection,
+       1, 3, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+     (Lisp_Object display, Lisp_Object resource_string, Lisp_Object must_succeed)
+{
+  struct haiku_display_info *dpy_info;
+  CHECK_STRING (display);
+
+  if (NILP (Fstring_equal (display, build_string ("be"))))
+    !NILP (must_succeed) ? fatal ("Bad display") : error ("Bad display");
+  dpy_info = haiku_term_init ();
+
+  if (!dpy_info)
+    !NILP (must_succeed) ? fatal ("Display not responding") :
+      error ("Display not responding");
+
+  return Qnil;
+}
+
+DEFUN ("x-display-pixel-width", Fx_display_pixel_width, Sx_display_pixel_width,
+       0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+
+{
+  check_haiku_display_info (terminal);
+
+  int width, height;
+  BScreen_px_dim (&width, &height);
+  return make_fixnum (width);
+}
+
+DEFUN ("x-display-pixel-height", Fx_display_pixel_height, Sx_display_pixel_height,
+       0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+
+{
+  check_haiku_display_info (terminal);
+
+  int width, height;
+  BScreen_px_dim (&width, &height);
+  return make_fixnum (width);
+}
+
+DEFUN ("x-display-mm-height", Fx_display_mm_height, Sx_display_mm_height, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  struct haiku_display_info *dpyinfo = check_haiku_display_info (terminal);
+
+  int width, height;
+  BScreen_px_dim (&width, &height);
+
+  return make_fixnum (height / (dpyinfo->resy / 25.4));
+}
+
+
+DEFUN ("x-display-mm-width", Fx_display_mm_width, Sx_display_mm_width, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  struct haiku_display_info *dpyinfo = check_haiku_display_info (terminal);
+
+  int width, height;
+  BScreen_px_dim (&width, &height);
+
+  return make_fixnum (height / (dpyinfo->resy / 25.4));
+}
+
+DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame,
+       1, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+     (Lisp_Object parms)
+{
+  return haiku_create_frame (parms, 0);
+}
+
+DEFUN ("x-display-visual-class", Fx_display_visual_class,
+       Sx_display_visual_class, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  check_haiku_display_info (terminal);
+
+  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..05fbd1021b
--- /dev/null
+++ b/src/haikuterm.c
@@ -0,0 +1,3608 @@
+/* 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;
+	    struct frame *f = haiku_window_to_frame (b->window);
+
+	    if (!f)
+	      continue;
+
+	    inev.kind = DRAG_N_DROP_EVENT;
+	    inev.arg = build_string_from_utf8 (b->ref);
+
+	    XSETINT (inev.x, b->x);
+	    XSETINT (inev.y, b->y);
+	    XSETFRAME (inev.frame_or_window, f);
+
+	    /* There should be no problem with calling free here.
+	       free on Haiku is thread-safe.  */
+	    free (b->ref);
+	    break;
+	  }
+	case APP_QUIT_REQUESTED_EVENT:
+	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..734ccdac31 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,30 @@ 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)
+    {
+      *pimg = NULL;
+      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 +3056,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 +3183,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 +4132,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 +4152,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 +4170,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 +4394,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 +4430,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 +4801,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 +5541,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 +5614,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 +5622,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 +5936,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 +5954,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 +6512,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 +6587,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 +9739,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 +11030,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 +11121,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 +11269,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 c3bc8307d7..c8ca3f920c 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)
@@ -6152,7 +6152,6 @@ make_lispy_event (struct input_event *event)
     case CONFIG_CHANGED_EVENT:
 	return list3 (Qconfig_changed_event,
 		      event->arg, event->frame_or_window);
-
       /* The 'kind' field of the event is something we don't recognize.  */
     default:
       emacs_abort ();
@@ -7243,7 +7242,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 +7258,7 @@ deliver_input_available_signal (int sig)
 {
   deliver_process_signal (sig, handle_input_available_signal);
 }
-#endif /* USABLE_SIGIO */
+#endif /* defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)  */
 
 \f
 /* User signal events.  */
@@ -7329,7 +7328,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 +11098,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 +11109,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 +11540,16 @@ init_keyboard (void)
       sigaction (SIGQUIT, &action, 0);
 #endif /* not DOS_NT */
     }
-#ifdef USABLE_SIGIO
+#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)
   if (!noninteractive)
     {
       struct sigaction action;
       emacs_sigaction_init (&action, deliver_input_available_signal);
+#ifdef USABLE_SIGIO
       sigaction (SIGIO, &action, 0);
+#else
+      sigaction (SIGPOLL, &action, 0);
+#endif
     }
 #endif
 
diff --git a/src/lisp.h b/src/lisp.h
index 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..ab01e1bfad 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
@@ -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 a00426795b..241ffe9a8d 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);
@@ -5730,7 +5730,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,
@@ -5738,7 +5738,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,
@@ -7732,7 +7736,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..5e13dd097e 100644
--- a/src/sysdep.c
+++ b/src/sysdep.c
@@ -678,6 +678,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 +791,7 @@ init_sigio (int fd)
 }
 
 #ifndef DOS_NT
+#ifdef F_SETOWN
 static void
 reset_sigio (int fd)
 {
@@ -795,12 +799,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 +815,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 +829,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 +839,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 +1269,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 +1690,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 +2790,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 +2878,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.,
@@ -3120,8 +3140,9 @@ list_system_processes (void)
 }
 
 /* The WINDOWSNT implementation is in w32.c.
-   The MSDOS implementation is in dosfns.c.  */
-#elif !defined (WINDOWSNT) && !defined (MSDOS)
+   The MSDOS implementation is in dosfns.c.
+   The Haiku implementation is in haiku.c.  */
+#elif !defined (WINDOWSNT) && !defined (MSDOS) && !defined (HAIKU)
 
 Lisp_Object
 list_system_processes (void)
@@ -4200,8 +4221,9 @@ system_process_attributes (Lisp_Object pid)
 }
 
 /* The WINDOWSNT implementation is in w32.c.
-   The MSDOS implementation is in dosfns.c.  */
-#elif !defined (WINDOWSNT) && !defined (MSDOS)
+   The MSDOS implementation is in dosfns.c.
+   The HAIKU implementation is in haiku.c.  */
+#elif !defined (WINDOWSNT) && !defined (MSDOS) && !defined (HAIKU)
 
 Lisp_Object
 system_process_attributes (Lisp_Object pid)
diff --git a/src/termhooks.h b/src/termhooks.h
index e7539bbce2..cf853e2885 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.  */
@@ -263,7 +264,6 @@ #define EMACS_TERMHOOKS_H
   /* File or directory was changed.  */
   , FILE_NOTIFY_EVENT
 #endif
-
 };
 
 /* Bit width of an enum event_kind tag at the start of structs and unions.  */
@@ -444,6 +444,7 @@ #define EVENT_INIT(event) memset (&(event), 0, sizeof (struct input_event))
     struct x_display_info *x;         /* xterm.h */
     struct w32_display_info *w32;     /* w32term.h */
     struct ns_display_info *ns;       /* nsterm.h */
+    struct haiku_display_info *haiku; /* haikuterm.h */
   } display_info;
 
 \f
@@ -832,6 +833,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 6c70ce60bb..8d34b7c4c3 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 d0d73eb828..fec6b2654b 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 816b6dc5a8..974a3fa906 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -13849,7 +13849,7 @@ syms_of_xterm (void)
 A value of nil means Emacs doesn't use toolkit scroll bars.
 With the X Window system, the value is a symbol describing the
 X toolkit.  Possible values are: gtk, motif, xaw, or xaw3d.
-With MS Windows or Nextstep, the value is t.  */);
+With MS Windows, Haiku windowing or Nextstep, the value is t.  */);
 #ifdef USE_TOOLKIT_SCROLL_BARS
 #ifdef USE_MOTIF
   Vx_toolkit_scroll_bars = intern_c_string ("motif");

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

* bug#51658: [PATCH] Haiku port (again)
  2021-11-20  9:34                                     ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2021-11-20  9:56                                       ` Eli Zaretskii
  2021-11-20 13:08                                         ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  0 siblings, 1 reply; 31+ messages in thread
From: Eli Zaretskii @ 2021-11-20  9:56 UTC (permalink / raw)
  To: Po Lu; +Cc: 51658

> From: Po Lu <luangruo@yahoo.com>
> Cc: 51658@debbugs.gnu.org
> Date: Sat, 20 Nov 2021 17:34:44 +0800
> 
> >>  (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))))
> 
> > This seems to contradict what you wrote in the user manual about
> > tooltip options?
> 
> This only disables tooltips on Haiku if a menu or popup is active, as
> Haiku doesn't let you map windows or tooltips above them.

But the manual doesn't mention that limitation, does it?

Does this limitation also affect tooltips produced by Emacs as special
frames?

> Here's a patch with the other issues you mentioned fixed.  Please see if
> it's better, thanks.

If you fixed everything and feel confident you did, just install it.
If there are some places where you want my opinion, please show only
those few places.

Thanks.





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

* bug#51658: [PATCH] Haiku port (again)
  2021-11-20  9:56                                       ` Eli Zaretskii
@ 2021-11-20 13:08                                         ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-11-20 13:30                                           ` Eli Zaretskii
  2021-11-20 13:51                                           ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  0 siblings, 2 replies; 31+ messages in thread
From: Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2021-11-20 13:08 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 51658

Eli Zaretskii <eliz@gnu.org> writes:

>> This only disables tooltips on Haiku if a menu or popup is active, as
>> Haiku doesn't let you map windows or tooltips above them.

> But the manual doesn't mention that limitation, does it?

Is that limitation important enough to mention?  It is not something
that is likely to happen at all.

> Does this limitation also affect tooltips produced by Emacs as special
> frames?

Yes, and all other windows as well.

> If you fixed everything and feel confident you did, just install it.
> If there are some places where you want my opinion, please show only
> those few places.

Thanks for the clarification.  I'll test again to see if it builds on
X-Windows and then install it.

Please let me know if it breaks the build on MS-Windows or some other
platform.





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

* bug#51658: [PATCH] Haiku port (again)
  2021-11-20 13:08                                         ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2021-11-20 13:30                                           ` Eli Zaretskii
  2021-11-20 13:33                                             ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-11-20 13:51                                           ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  1 sibling, 1 reply; 31+ messages in thread
From: Eli Zaretskii @ 2021-11-20 13:30 UTC (permalink / raw)
  To: Po Lu; +Cc: 51658

> From: Po Lu <luangruo@yahoo.com>
> Cc: 51658@debbugs.gnu.org
> Date: Sat, 20 Nov 2021 21:08:29 +0800
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> >> This only disables tooltips on Haiku if a menu or popup is active, as
> >> Haiku doesn't let you map windows or tooltips above them.
> 
> > But the manual doesn't mention that limitation, does it?
> 
> Is that limitation important enough to mention?  It is not something
> that is likely to happen at all.

maybe I don't understand something: tooltips for menu items are
routinely shown in Emacs, so how come you say it is unlikely to
happen?





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

* bug#51658: [PATCH] Haiku port (again)
  2021-11-20 13:30                                           ` Eli Zaretskii
@ 2021-11-20 13:33                                             ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-11-20 13:41                                               ` Eli Zaretskii
  0 siblings, 1 reply; 31+ messages in thread
From: Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2021-11-20 13:33 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 51658

Eli Zaretskii <eliz@gnu.org> writes:

>> Is that limitation important enough to mention?  It is not something
>> that is likely to happen at all.

> maybe I don't understand something: tooltips for menu items are
> routinely shown in Emacs, so how come you say it is unlikely to
> happen?

Basically, tooltips for menu bar items get shown in the echo area
instead, because it's impossible to display tooltips above the menu bar.

For ordinary popup menus, I implemented a hack that allows to display
tooltips correctly.

Thanks.





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

* bug#51658: [PATCH] Haiku port (again)
  2021-11-20 13:33                                             ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2021-11-20 13:41                                               ` Eli Zaretskii
  2021-11-20 13:45                                                 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  0 siblings, 1 reply; 31+ messages in thread
From: Eli Zaretskii @ 2021-11-20 13:41 UTC (permalink / raw)
  To: Po Lu; +Cc: 51658

> From: Po Lu <luangruo@yahoo.com>
> Cc: 51658@debbugs.gnu.org
> Date: Sat, 20 Nov 2021 21:33:29 +0800
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> >> Is that limitation important enough to mention?  It is not something
> >> that is likely to happen at all.
> 
> > maybe I don't understand something: tooltips for menu items are
> > routinely shown in Emacs, so how come you say it is unlikely to
> > happen?
> 
> Basically, tooltips for menu bar items get shown in the echo area
> instead, because it's impossible to display tooltips above the menu bar.

So this is not "unlikely" to happen, right?  As soon as the user tries
to use the menu-bar menus, the help-echo text will be shown in the
echo-area and not as a tooltip, right?  If so, I think this is very
important to mention in the Haiku appendix.





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

* bug#51658: [PATCH] Haiku port (again)
  2021-11-20 13:41                                               ` Eli Zaretskii
@ 2021-11-20 13:45                                                 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-11-20 13:55                                                   ` Eli Zaretskii
  0 siblings, 1 reply; 31+ messages in thread
From: Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2021-11-20 13:45 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 51658

Eli Zaretskii <eliz@gnu.org> writes:

> So this is not "unlikely" to happen, right?  As soon as the user tries
> to use the menu-bar menus, the help-echo text will be shown in the
> echo-area and not as a tooltip, right?  If so, I think this is very
> important to mention in the Haiku appendix.

Thanks, I added a short passage to haiku.texi explaining this:

  Both system tooltips and Emacs's own tooltips cannot display above
the menu bar, so help text in the menu bar will display in the echo
area instead.

WDYT?





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

* bug#51658: [PATCH] Haiku port (again)
  2021-11-20 13:08                                         ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-11-20 13:30                                           ` Eli Zaretskii
@ 2021-11-20 13:51                                           ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-11-20 14:15                                             ` Eli Zaretskii
  1 sibling, 1 reply; 31+ messages in thread
From: Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2021-11-20 13:51 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 51658-done

Po Lu <luangruo@yahoo.com> writes:

> Thanks for the clarification.  I'll test again to see if it builds on
> X-Windows and then install it.

It does, so installed with a change to the documentation.

> Please let me know if it breaks the build on MS-Windows or some other
> platform.

This still applies.  Many thanks in advance!





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

* bug#51658: [PATCH] Haiku port (again)
  2021-11-20 13:45                                                 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2021-11-20 13:55                                                   ` Eli Zaretskii
  0 siblings, 0 replies; 31+ messages in thread
From: Eli Zaretskii @ 2021-11-20 13:55 UTC (permalink / raw)
  To: Po Lu; +Cc: 51658

> From: Po Lu <luangruo@yahoo.com>
> Cc: 51658@debbugs.gnu.org
> Date: Sat, 20 Nov 2021 21:45:41 +0800
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> > So this is not "unlikely" to happen, right?  As soon as the user tries
> > to use the menu-bar menus, the help-echo text will be shown in the
> > echo-area and not as a tooltip, right?  If so, I think this is very
> > important to mention in the Haiku appendix.
> 
> Thanks, I added a short passage to haiku.texi explaining this:
> 
>   Both system tooltips and Emacs's own tooltips cannot display above
> the menu bar, so help text in the menu bar will display in the echo
> area instead.

OK.





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

* bug#51658: [PATCH] Haiku port (again)
  2021-11-20 13:51                                           ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2021-11-20 14:15                                             ` Eli Zaretskii
  0 siblings, 0 replies; 31+ messages in thread
From: Eli Zaretskii @ 2021-11-20 14:15 UTC (permalink / raw)
  To: Po Lu; +Cc: 51658-done

> From: Po Lu <luangruo@yahoo.com>
> Cc: 51658-done@debbugs.gnu.org
> Date: Sat, 20 Nov 2021 21:51:39 +0800
> 
> > Please let me know if it breaks the build on MS-Windows or some other
> > platform.
> 
> This still applies.  Many thanks in advance!

Nothing seems to be broken.  Good job!





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

end of thread, other threads:[~2021-11-20 14:15 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <87ee7surtv.fsf.ref@yahoo.com>
2021-11-07 11:29 ` bug#51658: [PATCH] Haiku port (again) Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-09 17:58   ` Eli Zaretskii
2021-11-10  0:00     ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-10  4:33       ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-10 12:38       ` Eli Zaretskii
2021-11-10 12:56         ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-10 14:19           ` Eli Zaretskii
2021-11-11  0:27             ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-11  6:51               ` Eli Zaretskii
2021-11-11  7:40                 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-13 18:31                   ` Eli Zaretskii
2021-11-14  1:08                     ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-14  7:54                       ` Eli Zaretskii
2021-11-14  9:36                         ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-14 10:28                           ` Eli Zaretskii
2021-11-14 10:39                             ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-14 10:54                               ` Eli Zaretskii
2021-11-14 11:06                                 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-15  2:59                               ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-20  7:03                                 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-20  8:33                                   ` Eli Zaretskii
2021-11-20  9:34                                     ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-20  9:56                                       ` Eli Zaretskii
2021-11-20 13:08                                         ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-20 13:30                                           ` Eli Zaretskii
2021-11-20 13:33                                             ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-20 13:41                                               ` Eli Zaretskii
2021-11-20 13:45                                                 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-20 13:55                                                   ` Eli Zaretskii
2021-11-20 13:51                                           ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-20 14:15                                             ` Eli Zaretskii

Code repositories for project(s) associated with this public inbox

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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).