emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

master 85a078e: Add support for the Haiku operating system and its windo


From: Po Lu
Subject: master 85a078e: Add support for the Haiku operating system and its window system
Date: Sat, 20 Nov 2021 08:52:31 -0500 (EST)

branch: master
commit 85a078e7853d708e599f97a3de06aed3a1c090ea
Author: Po Lu <luangruo@yahoo.com>
Commit: Po Lu <luangruo@yahoo.com>

    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 meaning of `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 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, and causes compiler warnings.
    (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.
---
 .gitignore                      |    2 +
 Makefile.in                     |    8 +
 configure.ac                    |  222 ++-
 doc/emacs/Makefile.in           |    1 +
 doc/emacs/emacs.texi            |    7 +
 doc/emacs/haiku.texi            |  136 ++
 doc/lispref/display.texi        |    8 +-
 doc/lispref/frames.texi         |   44 +-
 doc/lispref/os.texi             |    3 +
 etc/MACHINES                    |   28 +
 etc/NEWS                        |   21 +
 etc/PROBLEMS                    |   16 +
 lib-src/Makefile.in             |   21 +
 lib-src/be_resources.cc         |  144 ++
 lib-src/emacsclient.c           |    2 +
 lisp/cus-edit.el                |    6 +-
 lisp/cus-start.el               |    8 +-
 lisp/faces.el                   |    4 +-
 lisp/frame.el                   |   45 +-
 lisp/international/mule-cmds.el |    2 +-
 lisp/loadup.el                  |    6 +
 lisp/menu-bar.el                |    9 +-
 lisp/mwheel.el                  |   12 +-
 lisp/net/browse-url.el          |   20 +
 lisp/net/eww.el                 |    8 +-
 lisp/term/haiku-win.el          |  134 ++
 lisp/tooltip.el                 |    7 +-
 lisp/version.el                 |    4 +
 src/Makefile.in                 |   64 +-
 src/alloc.c                     |    4 +
 src/dispextern.h                |   20 +-
 src/dispnew.c                   |   11 +-
 src/emacs.c                     |   28 +
 src/fileio.c                    |    2 +-
 src/filelock.c                  |    2 +-
 src/floatfns.c                  |   15 +
 src/font.c                      |    3 +
 src/font.h                      |    4 +-
 src/frame.c                     |    4 +
 src/frame.h                     |    9 +
 src/ftcrfont.c                  |   56 +-
 src/ftfont.c                    |    4 +
 src/ftfont.h                    |    6 +-
 src/haiku.c                     |  286 ++++
 src/haiku_draw_support.cc       |  488 ++++++
 src/haiku_font_support.cc       |  596 +++++++
 src/haiku_io.c                  |  207 +++
 src/haiku_select.cc             |  155 ++
 src/haiku_support.cc            | 2930 +++++++++++++++++++++++++++++++
 src/haiku_support.h             |  869 ++++++++++
 src/haikufns.c                  | 2448 ++++++++++++++++++++++++++
 src/haikufont.c                 | 1072 ++++++++++++
 src/haikugui.h                  |  106 ++
 src/haikuimage.c                |  109 ++
 src/haikumenu.c                 |  656 +++++++
 src/haikuselect.c               |  134 ++
 src/haikuselect.h               |   64 +
 src/haikuterm.c                 | 3608 +++++++++++++++++++++++++++++++++++++++
 src/haikuterm.h                 |  293 ++++
 src/image.c                     |  142 +-
 src/keyboard.c                  |   21 +-
 src/lisp.h                      |   14 +-
 src/menu.c                      |   12 +-
 src/process.c                   |   10 +-
 src/sound.c                     |   24 +-
 src/sysdep.c                    |   34 +-
 src/termhooks.h                 |    8 +-
 src/terminal.c                  |    2 +
 src/verbose.mk.in               |    4 +
 src/xdisp.c                     |   15 +
 src/xfaces.c                    |    8 +-
 src/xfns.c                      |    5 +-
 src/xterm.c                     |    2 +-
 73 files changed, 15344 insertions(+), 138 deletions(-)

diff --git a/.gitignore b/.gitignore
index ea1662c..f1abb2a 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 ccb5d93..3c092fa 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -102,6 +102,8 @@ HAVE_NATIVE_COMP = @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: src install-arch-indep install-etcdoc 
install-$(NTDIR)
        $(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 82661c9..90a487f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -511,6 +511,12 @@ otherwise for the first of 'inotify', 'kqueue' or 'gfile' 
that is usable.])
 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)
 
@@ -787,6 +793,10 @@ case "${canonical}" in
     LDFLAGS="-N2M $LDFLAGS"
   ;;
 
+  *-haiku )
+    opsys=haiku
+  ;;
+
   ## Intel 386 machines where we don't care about the manufacturer.
   i[3456]86-*-* )
     case "${canonical}" in
@@ -908,7 +918,9 @@ if test "$ac_test_CFLAGS" != set; then
     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
@@ -1575,6 +1587,8 @@ case "$opsys" in
 
   ## Motif needs -lgen.
   unixware) LIBS_SYSTEM="-lsocket -lnsl -lelf -lgen" ;;
+
+  haiku) LIBS_SYSTEM="-lnetwork" ;;
 esac
 
 AC_SUBST(LIBS_SYSTEM)
@@ -2080,6 +2094,22 @@ if test "${HAVE_NS}" = yes; then
    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=
@@ -2201,6 +2231,39 @@ if test "${HAVE_W32}" = "yes"; then
   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.
 
@@ -2240,6 +2303,9 @@ dnl use the toolkit if we have gtk, or X11R5 or newer.
   w32 )
     term_header=w32term.h
   ;;
+  haiku )
+    term_header=haikuterm.h
+  ;;
 esac
 
 if test "$window_system" = none && test "X$with_x" != "Xno"; then
@@ -2571,7 +2637,8 @@ fi
 
 ### 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"
@@ -2595,7 +2662,8 @@ fi
 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"
 
@@ -2614,7 +2682,8 @@ if test "${with_webp}" != "no"; then
 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
@@ -3264,6 +3333,9 @@ if test "${with_toolkit_scroll_bars}" != "no"; then
   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
 
@@ -3353,6 +3425,22 @@ if test "${HAVE_X11}" = "yes"; then
     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)
@@ -3502,6 +3590,58 @@ if test "${HAVE_X11}" = "yes" && test "${HAVE_FREETYPE}" 
= "yes" \
   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)
@@ -3623,7 +3763,7 @@ AC_SUBST(LIBXPM)
 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],
@@ -3941,7 +4081,7 @@ if test "${with_png}" != no; then
   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
@@ -4016,7 +4156,7 @@ if test "${opsys}" = "mingw32"; then
     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"
@@ -4045,7 +4185,8 @@ if test "${opsys}" = "mingw32"; then
     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.
@@ -4482,6 +4623,13 @@ case $with_unexec,$canonical in
       [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])
 
@@ -4835,7 +4983,7 @@ CFLAGS="$OLDCFLAGS"
 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
@@ -5090,7 +5238,7 @@ dnl It would have Emacs fork off a separate process
 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
@@ -5186,6 +5334,14 @@ case $opsys in
     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, P [...]
     ;;
+
+  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, "% [...]
+    ;;
 esac
 
 
@@ -5407,8 +5563,25 @@ case $opsys in
     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)
@@ -5451,6 +5624,22 @@ if test $emacs_cv_usable_FIONREAD = yes; then
       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
@@ -5563,6 +5752,17 @@ if test "${HAVE_X_WINDOWS}" = "yes" ; then
     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
@@ -5951,7 +6151,7 @@ Configured for '${canonical}'.
 #### 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 69d39ef..dde3ae8 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 83847fb..ce92435 100644
--- a/doc/emacs/emacs.texi
+++ b/doc/emacs/emacs.texi
@@ -221,6 +221,7 @@ Appendices
 * 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 @@ Emacs and macOS / GNUstep
 * 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 @@ Lisp programming.
 
 @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 0000000..a2dc6e1
--- /dev/null
+++ b/doc/emacs/haiku.texi
@@ -0,0 +1,136 @@
+@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.
+
+  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.
+
+  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.
+
+@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 c093901..b948aff0 100644
--- a/doc/lispref/display.texi
+++ b/doc/lispref/display.texi
@@ -2767,8 +2767,9 @@ apply to.  Here are the possible values of 
@var{characteristic}:
 @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},
@@ -8274,6 +8275,8 @@ Emacs is displaying the frame using the Nextstep 
interface (used on
 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
@@ -8320,6 +8323,7 @@ area.  On text-mode (a.k.a.@: ``TTY'') frames, tooltips 
are always
 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 31ad82b..923ff19 100644
--- a/doc/lispref/frames.texi
+++ b/doc/lispref/frames.texi
@@ -214,7 +214,8 @@ The terminal and keyboard coding systems used on the 
terminal.
 @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 @@ indicate that position for the various builds:
 @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 @@ fit will be clipped by the window manager.
 @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 @@ either via @code{focus-follows-mouse} (@pxref{Input 
Focus}) or
 @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 @@ driver for OTF and TTF fonts with text shaping by the 
Uniscribe
 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 @@ raises @var{frame} above all other child frames of its 
parent.
 @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 @@ that if both frames are visible and their display areas 
overlap,
 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 @@ parent frame's window-system window.
 
 @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 1fbd664..fb0f25f 100644
--- a/doc/lispref/os.texi
+++ b/doc/lispref/os.texi
@@ -947,6 +947,9 @@ actually Linux is just the kernel, not the whole system.)
 @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 d8d0b86..d883f1a 100644
--- a/etc/MACHINES
+++ b/etc/MACHINES
@@ -103,6 +103,34 @@ the list at the end of this file.
     ./configure CC='gcc -m64'  # GCC
     ./configure CC='cc -m64'   # Oracle Developer Studio
 
+** Haiku
+
+  On 32-bit Haiku it is required that the newer GCC 8 be used, instead
+  of the legacy GCC 2 used by default.  This can be achieved by
+  invoking configure inside a shell launched by the 'setarch' program
+  invoked as 'setarch x86'.
+
+  When building with packages discovered through pkg-config, such as
+  libpng, on a GCC 2/GCC 8 hybrid system, simply evaluating 'setarch
+  x86' is insufficient to ensure that all required libraries are found
+  at their correct locations.  To avoid this problem, set the
+  environment variable 'PKG_CONFIG_PATH' to the GCC 8 pkg-config
+  directory at '/system/develop/lib/x86/pkgconfig/' before configuring
+  Emacs.
+
+  If GCC complains about not being able to resolve symbols such as
+  "BHandler::LockLooper", you are almost certainly experiencing this
+  problem.
+
+  Haiku running on non-x86 systems has not been tested.  It is
+  anticipated that Haiku running on big-endian systems will experience
+  problems when Emacs is built with Haiku windowing support, but there
+  doesn't seem to be any reliable way to get Haiku running on a
+  big-endian system at present.
+
+  The earliest release of Haiku that will successfully compile Emacs
+  is R1/Beta2.  For windowing support, R1/Beta3 or later is required.
+
 
 * Obsolete platforms
 
diff --git a/etc/NEWS b/etc/NEWS
index 3cceac5..bfea4da 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -24,6 +24,27 @@ applies, and please also update docstrings as needed.
 
 * Installation Changes in Emacs 29.1
 
+** Emacs has been ported to the Haiku operating system.
+The configuration process should automatically detect and build for
+Haiku.  There is also an optional window-system port to Haiku, which
+can be enabled by configuring Emacs with the option '--with-be-app',
+which will require the Haiku Application Kit development headers and a
+C++ compiler to be present on your system.  If Emacs is not built with
+the option '--with-be-app', the resulting Emacs will only run in
+text-mode terminals.
+
++++
+*** Cairo drawing support has been enabled for Haiku builds.
+To enable Cairo support, ensure that the Cairo and FreeType
+development files are present on your system, and configure Emacs with
+'--with-be-cairo'.
+
+---
+*** Double buffering is now enabled on the Haiku operating system.
+Unlike X, there is no compile-time option to enable or disable
+double-buffering.  If you wish to disable double-buffering, change the
+frame parameter `inhibit-double-buffering' instead.
+
 ** Emacs now installs the ".pdmp" file using a unique fingerprint in the name.
 The file is typically installed using a file name akin to
 "...dir/libexec/emacs/29.1/x86_64-pc-linux-gnu/emacs-<fingerprint>.pdmp".
diff --git a/etc/PROBLEMS b/etc/PROBLEMS
index f506881..acff3be 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 e6cda73..d062e78 100644
--- a/lib-src/Makefile.in
+++ b/lib-src/Makefile.in
@@ -27,7 +27,9 @@ EMACSOPT = -batch --no-site-file --no-site-lisp
 # ==================== Things 'configure' will edit ====================
 
 CC=@CC@
+CXX=@CXX@
 CFLAGS=@CFLAGS@
+CXXFLAGS=@CXXFLAGS@
 CPPFLAGS = @CPPFLAGS@
 LDFLAGS = @LDFLAGS@
 
@@ -130,6 +132,11 @@ MKDIR_P = @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 = hexl${EXEEXT}                           \
            $(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 = @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 = $(C_SWITCH_SYSTEM) $(C_SWITCH_MACHINE) \
 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}: ${srcdir}/emacsclient.c $(NTLIB) 
$(CLIENTRES) $(config_h)
           $(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 0000000..e6a14f0
--- /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 0e800dd..c55b298 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 6c353b0..b7c53a4 100644
--- a/lisp/cus-edit.el
+++ b/lisp/cus-edit.el
@@ -2176,7 +2176,7 @@ and `face'."
 ;;; 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 @@ and `face'."
   :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 @@ and `face'."
       (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 a46107a..68019c0 100644
--- a/lisp/cus-start.el
+++ b/lisp/cus-start.el
@@ -829,7 +829,11 @@ since it could result in memory overflow and make Emacs 
crash."
             ;; 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 @@ since it could result in memory overflow and make Emacs 
crash."
                       (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 9ec20c4..b2498cd 100644
--- a/lisp/faces.el
+++ b/lisp/faces.el
@@ -1172,7 +1172,7 @@ an integer value."
            (: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 @@ Note: Other faces cannot inherit from the cursor face."
   '((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 2c73737..1319759 100644
--- a/lisp/frame.el
+++ b/lisp/frame.el
@@ -1633,6 +1633,7 @@ live frame and defaults to the selected one."
 (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 @@ and width values are in pixels.
       (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 @@ of frames like calls to map a frame or change its 
visibility."
 (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."
       (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 @@ position (0, 0) of the selected frame's terminal."
       (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 @@ position (0, 0) of the selected frame's terminal."
      ((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 @@ workarea attribute."
 (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 @@ Return nil if DISPLAY contains no Emacs frame."
      ((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 @@ frame's display)."
      ((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 @@ frames and several different fonts at once.  This is true 
for displays
 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 should be either a frame or a display name (a 
string).
 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 @@ with DISPLAY.  To get information for each physical 
monitor, use
 `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 @@ with DISPLAY.  To get information for each physical 
monitor, use
 `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 @@ For graphical terminals, note that on \"multi-monitor\" 
setups this
 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 @@ For graphical terminals, note that on \"multi-monitor\" 
setups this
 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 can be a display name or a frame.
 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 can be a display name or a frame.
 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 can be a display name or a frame.
 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 can be a display name or a frame.
 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 can be a display name or a frame.
 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 089decb..b922f19 100644
--- a/lisp/international/mule-cmds.el
+++ b/lisp/international/mule-cmds.el
@@ -88,7 +88,7 @@
     (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 15a71ef..ed1570e 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 @@ lost after dumping")))
               (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 94e75ef..274f594 100644
--- a/lisp/menu-bar.el
+++ b/lisp/menu-bar.el
@@ -2540,6 +2540,7 @@ See `menu-bar-mode' for more information."
 
 (declare-function x-menu-bar-open "term/x-win" (&optional frame))
 (declare-function w32-menu-bar-open "term/w32-win" (&optional frame))
+(declare-function haiku-menu-bar-open "haikumenu.c" (&optional frame))
 
 (defun lookup-key-ignore-too-long (map key)
   "Call `lookup-key' and convert numeric values to nil."
@@ -2665,9 +2666,10 @@ first TTY menu-bar menu to be dropped down.  
Interactively,
 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 +2678,7 @@ If FRAME is nil or not given, use the selected frame."
     (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 3d0b8f0..cd84a10 100644
--- a/lisp/mwheel.el
+++ b/lisp/mwheel.el
@@ -55,7 +55,8 @@
     (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."
@@ -71,7 +72,8 @@
   :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."
@@ -235,7 +237,8 @@ Also see `mouse-wheel-tilt-scroll'."
   "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.")
@@ -245,7 +248,8 @@ Also see `mouse-wheel-tilt-scroll'."
   "Alternative wheel left event to consider.")
 
 (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 3af37e4..687bf6c 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 @@
     (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 @@ Defaults to the value of `browse-url-epiphany-arguments' 
at the time
 `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 @@ instead of `browse-url-new-window-flag'."
     ((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
@@ -1377,6 +1385,18 @@ used instead of `browse-url-new-window-flag'."
 (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.
 Optional argument SAME-WINDOW non-nil means show the URL in the
diff --git a/lisp/net/eww.el b/lisp/net/eww.el
index 031a731..e86d21f 100644
--- a/lisp/net/eww.el
+++ b/lisp/net/eww.el
@@ -239,7 +239,7 @@ parameter, and should return the (possibly) transformed 
URL."
   :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 @@ parameter, and should return the (possibly) transformed 
URL."
   :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 @@ parameter, and should return the (possibly) transformed 
URL."
   :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 @@ parameter, and should return the (possibly) transformed 
URL."
   :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 0000000..36af10d
--- /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 23b67ee..6cc482d 100644
--- a/lisp/tooltip.el
+++ b/lisp/tooltip.el
@@ -368,10 +368,15 @@ It is also called if Tooltip mode is on, for text-only 
displays."
      ((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 3a3093f..5d0a1ae 100644
--- a/lisp/version.el
+++ b/lisp/version.el
@@ -53,6 +53,8 @@ developing Emacs.")
 (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 @@ to the system configuration; look at `system-configuration' 
instead."
                       ((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 0aaaf91..d276df2 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -34,6 +34,7 @@ top_builddir = @top_builddir@
 abs_top_srcdir=@abs_top_srcdir@
 VPATH = $(srcdir)
 CC = @CC@
+CXX = @CXX@
 CFLAGS = @CFLAGS@
 CPPFLAGS = @CPPFLAGS@
 LDFLAGS = @LDFLAGS@
@@ -346,10 +347,17 @@ BUILD_DETAILS = @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@)
@@ -367,6 +375,9 @@ endif
 
 # 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.,
@@ -382,17 +393,21 @@ EMACS_CFLAGS=-Demacs $(MYCPPFLAGS) -I. -I$(srcdir) \
   $(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.
@@ -414,8 +429,10 @@ base_obj = dispnew.o frame.o scroll.o xdisp.o menu.o 
$(XMENU_OBJ) window.o \
        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.
@@ -429,7 +446,8 @@ SOME_MACHINE_OBJECTS = dosfns.o msdos.o \
   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@
@@ -455,7 +473,11 @@ FIRSTFILE_OBJ=@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
@@ -527,7 +549,7 @@ LIBES = $(LIBS) $(W32_LIBS) $(LIBS_GNUSTEP) $(LIBX_BASE) 
$(LIBIMAGE) \
    $(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) $(XINPUT_LIBS)
+   $(JSON_LIBS) $(LIBGMP) $(LIBGCCJIT_LIBS) $(XINPUT_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,
@@ -584,6 +606,18 @@ else
        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 
\
@@ -602,11 +636,11 @@ endif
 ## 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)
 
@@ -624,7 +658,7 @@ buildobj.h: Makefile
 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 > $@
 
@@ -649,9 +683,15 @@ endif
 ## 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)
@@ -736,6 +776,7 @@ ${ETAGS}: FORCE
 # 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).
@@ -750,7 +791,8 @@ TAGS: ${ETAGS} $(ctagsfiles1) $(ctagsfiles2)
          $(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 aa790d3..f8908c9 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 f17f095..a698f65 100644
--- a/src/dispextern.h
+++ b/src/dispextern.h
@@ -134,6 +134,13 @@ typedef Emacs_Pixmap Emacs_Pix_Context;
 #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 @@ struct redisplay_interface
 #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 @@ struct image
 #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 @@ bool valid_image_p (Lisp_Object);
 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 632eec2..f3f110a 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 032b27f..63f2a39 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -109,6 +109,10 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #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 @@ Using an Emacs configured with --with-x-toolkit=lucid 
does not have this problem
       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 @@ Using an Emacs configured with --with-x-toolkit=lucid 
does not have this problem
 #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 @@ Special values:
   `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 4015448..859b305 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -6190,7 +6190,7 @@ before any other event (mouse or keypress) is handled.  
*/)
   (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 cc185d9..c127762 100644
--- a/src/filelock.c
+++ b/src/filelock.c
@@ -65,7 +65,7 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #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 aadae4f..f52dae4 100644
--- a/src/floatfns.c
+++ b/src/floatfns.c
@@ -347,6 +347,21 @@ int
 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 b503123..d423fd4 100644
--- a/src/font.c
+++ b/src/font.c
@@ -5751,6 +5751,9 @@ match.  */);
 #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 6694164..2da5ec4 100644
--- a/src/font.h
+++ b/src/font.h
@@ -965,7 +965,7 @@ extern struct font_driver const nsfont_driver;
 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 @@ extern void font_deferred_log (const char *, Lisp_Object, 
Lisp_Object);
 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 79a7c89..a21dd0d 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -226,6 +226,7 @@ Value is:
  `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 @@ See also `frame-live-p'.  */)
       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 3dd7680..cb2bad7 100644
--- a/src/frame.h
+++ b/src/frame.h
@@ -585,6 +585,7 @@ struct frame
     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 @@ default_pixels_per_inch_y (void)
 #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 @@ default_pixels_per_inch_y (void)
 #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 db417b3..5d75f18 100644
--- a/src/ftcrfont.c
+++ b/src/ftcrfont.c
@@ -22,7 +22,13 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #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 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #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 03e44ec..cf59275 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 f771dc1..0e0bebb 100644
--- a/src/ftfont.h
+++ b/src/ftfont.h
@@ -29,6 +29,10 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 # 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 @@ struct font_info
   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)
diff --git a/src/haiku.c b/src/haiku.c
new file mode 100644
index 0000000..485d869
--- /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 0000000..5b1eccf
--- /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 0000000..9ac0400
--- /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 0000000..c152d9b
--- /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 0000000..8d345ca
--- /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 0000000..99d4ee7
--- /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 0000000..9f5f3c7
--- /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 0000000..868fc71
--- /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 ();
+}
+
+
+
+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 0000000..811fa62
--- /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 0000000..cfc693f
--- /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 0000000..138e5b8
--- /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 0000000..698da9d
--- /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 0000000..3f0441e
--- /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 0000000..542d550
--- /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 0000000..05fbd10
--- /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 0000000..af55f68
--- /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 6769e49..734ccda 100644
--- a/src/image.c
+++ b/src/image.c
@@ -20,6 +20,7 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #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 @@ typedef struct ns_bitmap_record Bitmap_Record;
 # 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 @@ static void
 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 @@ xbm_load (struct frame *f, struct image *img)
 #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 @@ enum xpm_keyword_index
   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 @@ static const struct image_keyword xpm_format[XPM_LAST] =
   {":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 @@ image_disable_image (struct frame *f, struct image *img)
       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 @@ The list of capabilities can include one or more of the 
following:
   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 @@ static struct image_type const image_types[] =
  { 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 @@ non-numeric, there is no explicit limit on the size of 
images.  */);
   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 0c48790..3722ba1 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)
@@ -6156,7 +6156,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 ();
@@ -7247,7 +7246,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)
@@ -7263,7 +7262,7 @@ deliver_input_available_signal (int sig)
 {
   deliver_process_signal (sig, handle_input_available_signal);
 }
-#endif /* USABLE_SIGIO */
+#endif /* defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)  */
 
 
 /* User signal events.  */
@@ -7333,7 +7332,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
@@ -11103,7 +11102,7 @@ See also `current-input-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)
     {
@@ -11114,9 +11113,9 @@ See also `current-input-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)
     {
@@ -11545,12 +11544,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 31656bb..19caba4 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 @@ struct frame;
 
 /* 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 @@ extern Lisp_Object menu_bar_items (Lisp_Object);
 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 1aafa78..ab01e1b 100644
--- a/src/menu.c
+++ b/src/menu.c
@@ -50,7 +50,8 @@ extern AppendMenuW_Proc unicode_append_menu;
 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 a004267..241ffe9 100644
--- a/src/process.c
+++ b/src/process.c
@@ -259,7 +259,7 @@ static bool process_output_skip;
 
 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 9041076..d42bc85 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 @@ static void
 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 8eaee22..5e13dd0 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 @@ static const struct speed_struct speeds[] =
 #ifdef B150
     { 150, B150 },
 #endif
+#ifndef HAVE_TINY_SPEED_T
 #ifdef B200
     { 200, B200 },
 #endif
@@ -2859,6 +2878,7 @@ static const struct speed_struct speeds[] =
 #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 b274be9..1cf9863 100644
--- a/src/termhooks.h
+++ b/src/termhooks.h
@@ -60,7 +60,8 @@ enum output_method
   output_x_window,
   output_msdos_raw,
   output_w32,
-  output_ns
+  output_ns,
+  output_haiku
 };
 
 /* Input queue declarations and hooks.  */
@@ -266,7 +267,6 @@ enum event_kind
   /* File or directory was changed.  */
   , FILE_NOTIFY_EVENT
 #endif
-
 };
 
 /* Bit width of an enum event_kind tag at the start of structs and unions.  */
@@ -447,6 +447,7 @@ struct terminal
     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;
 
 
@@ -835,6 +836,9 @@ extern struct terminal *terminal_list;
 #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 b83adc5..b5f244e 100644
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -445,6 +445,8 @@ possible return values.  */)
       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 a5ff931..9252971 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 6c70ce6..8d34b7c 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 d0d73eb..fec6b26 100644
--- a/src/xfaces.c
+++ b/src/xfaces.c
@@ -246,6 +246,10 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #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 0ea43d1..a142f55 100644
--- a/src/xfns.c
+++ b/src/xfns.c
@@ -4461,7 +4461,8 @@ For GNU and Unix system, the first 2 numbers are the 
version of the X
 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'.
 
@@ -7419,7 +7420,7 @@ Use a file selection dialog.  Select DEFAULT-FILENAME in 
the dialog's file
 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 18f8a60..9e5aed9 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -14959,7 +14959,7 @@ selected window or cursor position is preserved.  */);
 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");



reply via email to

[Prev in Thread] Current Thread [Next in Thread]