unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Po Lu via "Emacs development discussions." <emacs-devel@gnu.org>
To: Eli Zaretskii <eliz@gnu.org>
Cc: emacs-devel@gnu.org
Subject: Re: Haiku port
Date: Thu, 26 Aug 2021 21:19:16 +0800	[thread overview]
Message-ID: <87czq04a3v.fsf@yahoo.com> (raw)
In-Reply-To: <83bl5ktm45.fsf@gnu.org> (Eli Zaretskii's message of "Thu, 26 Aug 2021 15:40:42 +0300")

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

Eli Zaretskii <eliz@gnu.org> writes:

> Is haikufont so different from the other font infrastructures that you
> couldn't do it?  Supporting HarfBuzz usually needs just a small number
> of functions, as the basics are already in hbfont.c.
>
> Or maybe you have specific questions about this, in which case please
> ask them.

BFont seems to be too basic.  Someone has given me details on using
FreeType and HarfBuzz in Haiku applications and I plan to look at that.

> OK, but then please add a comment there with this information, so this
> isn't forgotten if and when we change what we do for w32.

Done.

> AFAIU, cl, KCC, xlC, and aCC aren't GCC, right?

Yeah, I think I was a bit carried away at that time.

> I was talking about C source files and headers not specific to Haiku:
> those don't have 'extern "C"' guards anywhere.  They could fail to
> compile with a C++ compiler.

AFAIK, no Emacs header files are included by the haiku_*_support files,
which are the only files compiled with a C++ compiler.

The only interface between these files and the rest of Emacs is the
asynchronous IO mechanism in haiku_io.c, and the header haiku_support.h,
both of which are vetted to work.

> Hmm... maybe.  But then I guess I'm confused: what does the automatic
> detection of Haiku do, if one needs --with-be-app to have the
> windowing code?  IOW, why don't you assume --with-be-app if the
> automatic detection of Haiku succeeds?

I think it owes to the fact that the Haiku windowing backend is not
entirely complete.  Namely, the font driver needs work, and there are
some things I haven't quite worked out, such as child frames.

> Not all of them.

Fair enough.


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

diff --git a/configure.ac b/configure.ac
index 76f608ffb0..8ec4617f02 100644
--- a/configure.ac
+++ b/configure.ac
@@ -508,6 +508,9 @@ AC_DEFUN
 OPTION_DEFAULT_OFF([xwidgets],
   [enable use of xwidgets in Emacs buffers (requires gtk3 or macOS Cocoa)])
 
+OPTION_DEFAULT_OFF([be-app],
+  [enable use of Haiku's Application Kit as a window system])
+
 ## Makefile.in needs the cache file name.
 AC_SUBST(cache_file)
 
@@ -784,6 +787,10 @@ AC_DEFUN
     LDFLAGS="-N2M $LDFLAGS"
   ;;
 
+  *-haiku )
+    opsys=haiku
+  ;;
+
   ## Intel 386 machines where we don't care about the manufacturer.
   i[3456]86-*-* )
     case "${canonical}" in
@@ -905,7 +912,9 @@ AC_DEFUN
     if test $emacs_cv_prog_cc_g3 != yes; then
       CFLAGS=$emacs_save_CFLAGS
     fi
-    if test $opsys = mingw32; then
+    # Haiku also needs -gdwarf-2 because its GDB is too old
+    # to understand newer formats.
+    if test $opsys = mingw32 || test $opsys = haiku; then
       CFLAGS="$CFLAGS -gdwarf-2"
     fi
   fi
@@ -1570,6 +1579,8 @@ AC_DEFUN
 
   ## Motif needs -lgen.
   unixware) LIBS_SYSTEM="-lsocket -lnsl -lelf -lgen" ;;
+
+  haiku) LIBS_SYSTEM="-lnetwork" ;;
 esac
 
 AC_SUBST(LIBS_SYSTEM)
@@ -2075,6 +2086,20 @@ AC_DEFUN
    fi
 fi
 
+HAVE_BE_APP=no
+if test "${opsys}" = "haiku" && test "${with_be_app}" = "yes"; then
+   AC_PROG_CXX([gcc cl CC cxx cc++ c++ g++]) dnl Only GCC works for now
+   CXXFLAGS="$CXXFLAGS $emacs_g3_CFLAGS"
+   AC_LANG_PUSH([C++])
+   AC_CHECK_HEADER([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-ns'.])])
+   AC_LANG_POP([C++])
+fi
+
+AC_SUBST(HAVE_BE_APP)
+
 HAVE_W32=no
 W32_OBJ=
 W32_LIBS=
@@ -2196,6 +2221,27 @@ AC_DEFUN
   with_xft=no
 fi
 
+HAIKU_OBJ=
+HAIKU_CXX_OBJ=
+HAIKU_LIBS=
+
+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="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"
+fi
+
+AC_SUBST(HAIKU_LIBS)
+AC_SUBST(HAIKU_OBJ)
+AC_SUBST(HAIKU_CXX_OBJ)
+
 ## $window_system is now set to the window system we will
 ## ultimately use.
 
@@ -2235,6 +2281,9 @@ AC_DEFUN
   w32 )
     term_header=w32term.h
   ;;
+  haiku )
+    term_header=haikuterm.h
+  ;;
 esac
 
 if test "$window_system" = none && test "X$with_x" != "Xno"; then
@@ -2566,7 +2615,8 @@ AC_DEFUN
 
 ### Use -lrsvg-2 if available, unless '--with-rsvg=no' is specified.
 HAVE_RSVG=no
-if test "${HAVE_X11}" = "yes" || test "${HAVE_NS}" = "yes" || test "${opsys}" = "mingw32"; then
+if test "${HAVE_X11}" = "yes" || test "${HAVE_NS}" = "yes" \
+   || test "${opsys}" = "mingw32" || test "${HAVE_BE_APP}" = "yes"; then
   if test "${with_rsvg}" != "no"; then
     RSVG_REQUIRED=2.14.0
     RSVG_MODULE="librsvg-2.0 >= $RSVG_REQUIRED"
@@ -3237,6 +3287,9 @@ AC_DEFUN
   elif test "${HAVE_W32}" = "yes"; then
     AC_DEFINE(USE_TOOLKIT_SCROLL_BARS)
     USE_TOOLKIT_SCROLL_BARS=yes
+  elif test "${HAVE_BE_APP}" = "yes"; then
+    AC_DEFINE(USE_TOOLKIT_SCROLL_BARS)
+    USE_TOOLKIT_SCROLL_BARS=yes
   fi
 fi
 
@@ -3596,7 +3649,7 @@ AC_DEFUN
 HAVE_JPEG=no
 LIBJPEG=
 if test "${HAVE_X11}" = "yes" || test "${HAVE_W32}" = "yes" \
-   || test "${HAVE_NS}" = "yes"; then
+   || test "${HAVE_NS}" = "yes" || test "${HAVE_BE_APP}" = "yes"; then
   if test "${with_jpeg}" != "no"; then
     AC_CACHE_CHECK([for jpeglib 6b or later],
       [emacs_cv_jpeglib],
@@ -3894,7 +3947,7 @@ AC_DEFUN
   if test "$opsys" = mingw32; then
     AC_CHECK_HEADER([png.h], [HAVE_PNG=yes])
   elif test "${HAVE_X11}" = "yes" || test "${HAVE_W32}" = "yes" \
-       || test "${HAVE_NS}" = "yes"; then
+       || test "${HAVE_NS}" = "yes" || test "${HAVE_BE_APP}" = "yes"; then
     EMACS_CHECK_MODULES([PNG], [libpng >= 1.0.0])
     if test $HAVE_PNG = yes; then
       LIBPNG=$PNG_LIBS
@@ -3969,7 +4022,7 @@ AC_DEFUN
     AC_DEFINE(HAVE_TIFF, 1, [Define to 1 if you have the tiff library (-ltiff).])
   fi
 elif test "${HAVE_X11}" = "yes" || test "${HAVE_W32}" = "yes" \
-     || test "${HAVE_NS}" = "yes"; then
+     || test "${HAVE_NS}" = "yes" || test "${HAVE_BE_APP}" = "yes"; then
   if test "${with_tiff}" != "no"; then
     AC_CHECK_HEADER(tiffio.h,
       [tifflibs="-lz -lm"
@@ -5006,7 +5059,7 @@ AC_DEFUN
 dnl to read the input and send it to the true Emacs process
 dnl through a pipe.
 case $opsys in
-  darwin | gnu-linux | gnu-kfreebsd )
+  darwin | gnu-linux | gnu-kfreebsd)
     AC_DEFINE(INTERRUPT_INPUT, 1, [Define to read input using SIGIO.])
   ;;
 esac
@@ -5054,7 +5107,7 @@ AC_DEFUN
     AC_DEFINE(PTY_TTY_NAME_SPRINTF, [])
     ;;
 
-  gnu | qnxnto )
+  gnu | qnxnto | haiku )
     AC_DEFINE(FIRST_PTY_LETTER, ['p'])
     ;;
 
@@ -5367,6 +5420,22 @@ AC_DEFUN
       AC_DEFINE([USABLE_SIGIO], [1], [Define to 1 if SIGIO is usable.])
     fi
   fi
+
+  if 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 and FASYNC are not.])
+    fi
+  fi
 fi
 
 case $opsys in
@@ -5866,7 +5935,7 @@ AC_DEFUN
 #### Please respect alphabetical ordering when making additions.
 optsep=
 emacs_config_features=
-for opt in ACL CAIRO DBUS FREETYPE GCONF GIF GLIB GMP GNUTLS GPM GSETTINGS \
+for opt in ACL BE_APP CAIRO DBUS FREETYPE GCONF GIF GLIB GMP GNUTLS GPM GSETTINGS \
  HARFBUZZ IMAGEMAGICK JPEG JSON LCMS2 LIBOTF LIBSELINUX LIBSYSTEMD LIBXML2 \
  M17N_FLT MODULES NATIVE_COMP NOTIFY NS OLDXMENU PDUMPER PNG RSVG SECCOMP \
  SOUND THREADS TIFF \
diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi
index 7ab2896778..e733de54e5 100644
--- a/doc/lispref/display.texi
+++ b/doc/lispref/display.texi
@@ -2696,8 +2696,9 @@ Defining Faces
 @item type
 The kind of window system the terminal uses---either @code{graphic}
 (any graphics-capable display), @code{x}, @code{pc} (for the MS-DOS
-console), @code{w32} (for MS Windows 9X/NT/2K/XP), or @code{tty} (a
-non-graphics-capable display).  @xref{Window Systems, window-system}.
+console), @code{w32} (for MS Windows 9X/NT/2K/XP), @code{haiku} (for
+Haiku), or @code{tty} (a non-graphics-capable display).
+@xref{Window Systems, window-system}.
 
 @item class
 What kinds of colors the terminal supports---either @code{color},
@@ -8021,6 +8022,8 @@ Window Systems
 GNUstep and macOS).
 @item pc
 Emacs is displaying the frame using MS-DOS direct screen writes.
+@item haiku
+Emacs is displaying the frame using the Application Kit on Haiku.
 @item nil
 Emacs is displaying the frame on a character-based terminal.
 @end table
diff --git a/doc/lispref/frames.texi b/doc/lispref/frames.texi
index 477c105a95..81ff1236f0 100644
--- a/doc/lispref/frames.texi
+++ b/doc/lispref/frames.texi
@@ -213,7 +213,8 @@ Multiple Terminals
 @item
 The kind of display associated with the terminal.  This is the symbol
 returned by the function @code{terminal-live-p} (i.e., @code{x},
-@code{t}, @code{w32}, @code{ns}, or @code{pc}).  @xref{Frames}.
+@code{t}, @code{w32}, @code{ns}, @code{pc}, or @code{haiku}).
+@xref{Frames}.
 
 @item
 A list of terminal parameters.  @xref{Terminal Parameters}.
diff --git a/etc/NEWS b/etc/NEWS
index 04e482364a..3387f7ce9c 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -34,6 +34,13 @@ more details.
 ---
 ** Support for building with Motif has been removed.
 
+** 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.
+
 ** The Cairo graphics library is now used by default if present.
 '--with-cairo' is now the default, if the appropriate development files
 are found by 'configure'.  Note that building with Cairo means using
diff --git a/lib-src/emacsclient.c b/lib-src/emacsclient.c
index 018e81e422..ca81ac2b08 100644
--- a/lib-src/emacsclient.c
+++ b/lib-src/emacsclient.c
@@ -601,6 +601,8 @@ decode_options (int argc, char **argv)
       alt_display = "ns";
 #elif defined (HAVE_NTGUI)
       alt_display = "w32";
+#elif defined (HAVE_HAIKU)
+      alt_display = "be";
 #endif
 
       display = egetenv ("DISPLAY");
diff --git a/lisp/cus-edit.el b/lisp/cus-edit.el
index 7eae2e416b..e2c5ee35cf 100644
--- a/lisp/cus-edit.el
+++ b/lisp/cus-edit.el
@@ -2176,7 +2176,7 @@ custom-magic-reset
 ;;; The `custom' Widget.
 
 (defface custom-button
-  '((((type x w32 ns) (class color))	; Like default mode line
+  '((((type x w32 ns haiku) (class color))	; Like default mode line
      :box (:line-width 2 :style released-button)
      :background "lightgrey" :foreground "black"))
   "Face for custom buffer buttons if `custom-raised-buttons' is non-nil."
@@ -2184,7 +2184,7 @@ custom-button
   :group 'custom-faces)
 
 (defface custom-button-mouse
-  '((((type x w32 ns) (class color))
+  '((((type x w32 ns haiku) (class color))
      :box (:line-width 2 :style released-button)
      :background "grey90" :foreground "black")
     (t
@@ -2209,7 +2209,7 @@ custom-button-unraised
       (if custom-raised-buttons 'custom-button-mouse 'highlight))
 
 (defface custom-button-pressed
-  '((((type x w32 ns) (class color))
+  '((((type x w32 ns haiku) (class color))
      :box (:line-width 2 :style pressed-button)
      :background "lightgrey" :foreground "black")
     (t :inverse-video t))
diff --git a/lisp/faces.el b/lisp/faces.el
index a3a6f1b78d..f2e82506aa 100644
--- a/lisp/faces.el
+++ b/lisp/faces.el
@@ -1171,7 +1171,7 @@ face-valid-attribute-values
            (:height
             'integerp)
            (:stipple
-            (and (memq (window-system frame) '(x ns)) ; No stipple on w32
+            (and (memq (window-system frame) '(x ns)) ; No stipple on w32 or haiku
                  (mapcar #'list
                          (apply #'nconc
                                 (mapcar (lambda (dir)
@@ -2810,7 +2810,7 @@ tool-bar
   '((default
      :box (:line-width 1 :style released-button)
      :foreground "black")
-    (((type x w32 ns) (class color))
+    (((type x w32 ns haiku) (class color))
      :background "grey75")
     (((type x) (class mono))
      :background "grey"))
diff --git a/lisp/frame.el b/lisp/frame.el
index 146fe278b3..dbd5fc07c7 100644
--- a/lisp/frame.el
+++ b/lisp/frame.el
@@ -2040,8 +2040,8 @@ display-mouse-p
      ((eq frame-type 'w32)
       (with-no-warnings
        (> w32-num-mouse-buttons 0)))
-     ((memq frame-type '(x ns))
-      t)    ;; We assume X and NeXTstep *always* have a pointing device
+     ((memq frame-type '(x ns haiku))
+      t)    ;; We assume X, NeXTstep and Haiku *always* have a pointing device
      (t
       (or (and (featurep 'xt-mouse)
 	       xterm-mouse-mode)
@@ -2066,7 +2066,7 @@ display-graphic-p
 that use a window system such as X, and false for text-only terminals.
 DISPLAY can be a display name, a frame, or nil (meaning the selected
 frame's display)."
-  (not (null (memq (framep-on-display display) '(x w32 ns)))))
+  (not (null (memq (framep-on-display display) '(x w32 ns haiku)))))
 
 (defun display-images-p (&optional display)
   "Return non-nil if DISPLAY can display images.
@@ -2137,7 +2137,7 @@ display-pixel-height
 `display-monitor-attributes-list'."
   (let ((frame-type (framep-on-display display)))
     (cond
-     ((memq frame-type '(x w32 ns))
+     ((memq frame-type '(x w32 ns haiku))
       (x-display-pixel-height display))
      (t
       (frame-height (if (framep display) display (selected-frame)))))))
@@ -2157,7 +2157,7 @@ display-pixel-width
 `display-monitor-attributes-list'."
   (let ((frame-type (framep-on-display display)))
     (cond
-     ((memq frame-type '(x w32 ns))
+     ((memq frame-type '(x w32 ns haiku))
       (x-display-pixel-width display))
      (t
       (frame-width (if (framep display) display (selected-frame)))))))
@@ -2236,6 +2236,8 @@ display-backing-store
     (cond
      ((memq frame-type '(x w32 ns))
       (x-display-backing-store display))
+     ((eq frame-type 'haiku)
+      'when-mapped)
      (t
       'not-useful))))
 
@@ -2262,6 +2264,8 @@ display-planes
     (cond
      ((memq frame-type '(x w32 ns))
       (x-display-planes display))
+     ((eq frame-type 'haiku)
+      24)
      ((eq frame-type 'pc)
       4)
      (t
@@ -2277,6 +2281,8 @@ display-color-cells
     (cond
      ((memq frame-type '(x w32 ns))
       (x-display-color-cells display))
+     ((eq frame-type 'haiku)
+      (expt 2 24))
      ((eq frame-type 'pc)
       16)
      (t
@@ -2294,6 +2300,8 @@ display-visual-class
     (cond
      ((memq frame-type '(x w32 ns))
       (x-display-visual-class display))
+     ((eq frame-type 'haiku)
+      'direct-color)
      ((and (memq frame-type '(pc t))
 	   (tty-display-color-p display))
       'static-color)
diff --git a/lisp/loadup.el b/lisp/loadup.el
index 158c02ecea..deec90662a 100644
--- a/lisp/loadup.el
+++ b/lisp/loadup.el
@@ -303,6 +303,13 @@
       (load "term/common-win")
       (load "term/x-win")))
 
+(if (featurep 'haiku)
+    (progn
+      (load "term/common-win")
+      (load "term/haiku-win")
+      (load "international/mule-util")
+      (load "international/ucs-normalize")))
+
 (if (or (eq system-type 'windows-nt)
         (featurep 'w32))
     (progn
@@ -546,6 +553,7 @@
               (delete-file output)))))
       ;; Recompute NAME now, so that it isn't set when we dump.
       (if (not (or (eq system-type 'ms-dos)
+                   (eq system-type 'haiku) ;; BFS doesn't support hard links
                    ;; Don't bother adding another name if we're just
                    ;; building bootstrap-emacs.
                    (member dump-mode '("pbootstrap" "bootstrap"))))
diff --git a/lisp/mwheel.el b/lisp/mwheel.el
index def7758774..7c34931055 100644
--- a/lisp/mwheel.el
+++ b/lisp/mwheel.el
@@ -55,7 +55,8 @@ mouse-wheel-change-button
     (mouse-wheel-mode 1)))
 
 (defcustom mouse-wheel-down-event
-  (if (or (featurep 'w32-win) (featurep 'ns-win))
+  (if (or (featurep 'w32-win) (featurep 'ns-win)
+          (featurep 'haiku-win))
       'wheel-up
     'mouse-4)
   "Event used for scrolling down."
@@ -64,7 +65,8 @@ mouse-wheel-down-event
   :set 'mouse-wheel-change-button)
 
 (defcustom mouse-wheel-up-event
-  (if (or (featurep 'w32-win) (featurep 'ns-win))
+  (if (or (featurep 'w32-win) (featurep 'ns-win)
+          (featurep 'haiku-win))
       'wheel-down
     'mouse-5)
   "Event used for scrolling up."
diff --git a/lisp/term/haiku-win.el b/lisp/term/haiku-win.el
new file mode 100644
index 0000000000..a737d668a4
--- /dev/null
+++ b/lisp/term/haiku-win.el
@@ -0,0 +1,97 @@
+;;; 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 '(".*" . ext-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")
+
+(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)
+  (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)
+
+
+(provide 'haiku-win)
+(provide 'term/haiku-win)
+
+;;; haiku-win.el ends here
diff --git a/src/Makefile.in b/src/Makefile.in
index 732cd8f099..cdf8b29796 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -34,6 +34,7 @@ top_builddir =
 abs_top_srcdir=@abs_top_srcdir@
 VPATH = $(srcdir)
 CC = @CC@
+CXX = @CXX@
 CFLAGS = @CFLAGS@
 CPPFLAGS = @CPPFLAGS@
 LDFLAGS = @LDFLAGS@
@@ -339,10 +340,16 @@ BUILD_DETAILS =
 
 UNEXEC_OBJ = @UNEXEC_OBJ@
 
+HAIKU_OBJ = @HAIKU_OBJ@
+HAIKU_CXX_OBJ = @HAIKU_CXX_OBJ@
+HAIKU_LIBS = @HAIKU_LIBS@
+
 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@)
@@ -360,6 +367,9 @@ pdmp :=
 
 # Flags that might be in WARN_CFLAGS but are not valid for Objective C.
 NON_OBJC_CFLAGS = -Wignored-attributes -Wignored-qualifiers -Wopenmp-simd
+# Ditto, but for C++
+NON_CXX_CFLAGS = -Wmissing-prototypes -Wnested-externs -Wold-style-definition \
+  -Wstrict-prototypes -Wno-override-init
 
 # -Demacs makes some files produce the correct version for use in Emacs.
 # MYCPPFLAGS is for by-hand Emacs-specific overrides, e.g.,
@@ -380,12 +390,16 @@ ALL_CFLAGS =
 ALL_OBJC_CFLAGS = $(EMACS_CFLAGS) \
   $(filter-out $(NON_OBJC_CFLAGS),$(WARN_CFLAGS)) $(CFLAGS) \
   $(GNU_OBJC_CFLAGS)
+ALL_CXX_CFLAGS = $(EMACS_CFLAGS) \
+  $(filter-out $(NON_CXX_CFLAGS),$(WARN_CFLAGS)) $(CXXFLAGS)
 
-.SUFFIXES: .m
+.SUFFIXES: .m .cc
 .c.o:
 	$(AM_V_CC)$(CC) -c $(CPPFLAGS) $(ALL_CFLAGS) $(PROFILING_CFLAGS) $<
 .m.o:
 	$(AM_V_CC)$(CC) -c $(CPPFLAGS) $(ALL_OBJC_CFLAGS) $(PROFILING_CFLAGS) $<
+.cc.o:
+	$(AM_V_CXX)$(CXX) -c $(CPPFLAGS) $(ALL_CXX_CFLAGS) $(PROFILING_CFLAGS) $<
 
 ## lastfile must follow all files whose initialized data areas should
 ## be dumped as pure by dump-emacs.
@@ -407,8 +421,10 @@ base_obj =
 	thread.o systhread.o \
 	$(if $(HYBRID_MALLOC),sheap.o) \
 	$(MSDOS_OBJ) $(MSDOS_X_OBJ) $(NS_OBJ) $(CYGWIN_OBJ) $(FONT_OBJ) \
-	$(W32_OBJ) $(WINDOW_SYSTEM_OBJ) $(XGSELOBJ) $(JSON_OBJ)
-obj = $(base_obj) $(NS_OBJC_OBJ)
+	$(W32_OBJ) $(WINDOW_SYSTEM_OBJ) $(XGSELOBJ) $(JSON_OBJ) \
+	$(HAIKU_OBJ)
+doc_obj = $(base_obj) $(NS_OBJC_OBJ)
+obj = $(doc_obj) $(HAIKU_CXX_OBJ)
 
 ## Object files used on some machine or other.
 ## These go in the DOC file on all machines in case they are needed.
@@ -422,7 +438,8 @@ SOME_MACHINE_OBJECTS =
   w32.o w32console.o w32cygwinx.o w32fns.o w32heap.o w32inevt.o w32notify.o \
   w32menu.o w32proc.o w32reg.o w32select.o w32term.o w32xfns.o \
   w16select.o widget.o xfont.o ftfont.o xftfont.o gtkutil.o \
-  xsettings.o xgselect.o termcap.o hbfont.o
+  xsettings.o xgselect.o termcap.o hbfont.o \
+  haikuterm.o haikufns.o haikumenu.o haikufont.o
 
 ## gmalloc.o if !SYSTEM_MALLOC && !DOUG_LEA_MALLOC, else empty.
 GMALLOC_OBJ=@GMALLOC_OBJ@
@@ -517,7 +534,7 @@ LIBES =
    $(FREETYPE_LIBS) $(FONTCONFIG_LIBS) $(HARFBUZZ_LIBS) $(LIBOTF_LIBS) $(M17N_FLT_LIBS) \
    $(LIBGNUTLS_LIBS) $(LIB_PTHREAD) $(GETADDRINFO_A_LIBS) $(LCMS2_LIBS) \
    $(NOTIFY_LIBS) $(LIB_MATH) $(LIBZ) $(LIBMODULES) $(LIBSYSTEMD_LIBS) \
-   $(JSON_LIBS) $(LIBGMP) $(LIBGCCJIT_LIBS)
+   $(JSON_LIBS) $(LIBGMP) $(LIBGCCJIT_LIBS) $(HAIKU_LIBS)
 
 ## FORCE it so that admin/unidata can decide whether this file is
 ## up-to-date.  Although since charprop depends on bootstrap-emacs,
@@ -583,11 +600,11 @@ $(pdmp):
 ## for the first time, this prevents any variation between configurations
 ## in the contents of the DOC file.
 ##
-$(etc)/DOC: lisp.mk $(libsrc)/make-docfile$(EXEEXT) $(obj) $(lisp)
+$(etc)/DOC: lisp.mk $(libsrc)/make-docfile$(EXEEXT) $(doc_obj) $(lisp)
 	$(AM_V_GEN)$(MKDIR_P) $(etc)
 	$(AM_V_at)rm -f $(etc)/DOC
 	$(AM_V_at)$(libsrc)/make-docfile -d $(srcdir) \
-	  $(SOME_MACHINE_OBJECTS) $(obj) > $(etc)/DOC
+	  $(SOME_MACHINE_OBJECTS) $(doc_obj) > $(etc)/DOC
 	$(AM_V_at)$(libsrc)/make-docfile -a $(etc)/DOC -d $(lispsource) \
 	  $(shortlisp)
 
@@ -605,7 +622,7 @@ buildobj.h:
 GLOBAL_SOURCES = $(base_obj:.o=.c) $(NS_OBJC_OBJ:.o=.m)
 
 gl-stamp: $(libsrc)/make-docfile$(EXEEXT) $(GLOBAL_SOURCES)
-	$(AM_V_GLOBALS)$(libsrc)/make-docfile -d $(srcdir) -g $(obj) > globals.tmp
+	$(AM_V_GLOBALS)$(libsrc)/make-docfile -d $(srcdir) -g $(doc_obj) > globals.tmp
 	$(AM_V_at)$(top_srcdir)/build-aux/move-if-change globals.tmp globals.h
 	$(AM_V_at)echo timestamp > $@
 
@@ -630,9 +647,15 @@ $(LIBEGNU_ARCHIVE):
 ## to start if Vinstallation_directory has the wrong value.
 temacs$(EXEEXT): $(LIBXMENU) $(ALLOBJS) $(LIBEGNU_ARCHIVE) $(EMACSRES) \
   $(charsets) $(charscript) $(MAKE_PDUMPER_FINGERPRINT)
-	$(AM_V_CCLD)$(CC) -o $@.tmp \
+ifeq ($(HAVE_BE_APP),yes)
+	$(AM_V_CCLD)$(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)
diff --git a/src/alloc.c b/src/alloc.c
index 4ea337ddba..2c2434feaa 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -6141,6 +6141,10 @@ garbage_collect (void)
   xg_mark_data ();
 #endif
 
+#ifdef HAVE_HAIKU
+  mark_haiku_display ();
+#endif
+
 #ifdef HAVE_WINDOW_SYSTEM
   mark_fringe_data ();
 #endif
diff --git a/src/dispextern.h b/src/dispextern.h
index 33fcaa4c07..270375b897 100644
--- a/src/dispextern.h
+++ b/src/dispextern.h
@@ -134,6 +134,13 @@ #define FACE_COLOR_TO_PIXEL(face_color, frame) (FRAME_NS_P (frame) \
 #define FACE_COLOR_TO_PIXEL(face_color, frame) face_color
 #endif
 
+#ifdef HAVE_HAIKU
+#include "haikugui.h"
+typedef struct haiku_display_info Display_Info;
+typedef Emacs_Pixmap Emacs_Pix_Container;
+typedef Emacs_Pixmap Emacs_Pix_Context;
+#endif
+
 #ifdef HAVE_WINDOW_SYSTEM
 # include <time.h>
 # include "fontset.h"
@@ -3487,7 +3494,8 @@ #define TRY_WINDOW_IGNORE_FONTS_CHANGE	(1 << 1)
 void prepare_image_for_display (struct frame *, struct image *);
 ptrdiff_t lookup_image (struct frame *, Lisp_Object, int);
 
-#if defined HAVE_X_WINDOWS || defined USE_CAIRO || defined HAVE_NS
+#if defined HAVE_X_WINDOWS || defined USE_CAIRO || defined HAVE_NS \
+  || defined HAVE_HAIKU
 #define RGB_PIXEL_COLOR unsigned long
 #endif
 
diff --git a/src/dispnew.c b/src/dispnew.c
index 0c31319917..113174af04 100644
--- a/src/dispnew.c
+++ b/src/dispnew.c
@@ -6151,7 +6151,7 @@ sit_for (Lisp_Object timeout, bool reading, int display_option)
     wrong_type_argument (Qnumberp, timeout);
 
 
-#ifdef USABLE_SIGIO
+#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)
   gobble_input ();
 #endif
 
@@ -6458,6 +6458,15 @@ init_display_interactive (void)
     }
 #endif
 
+#ifdef HAVE_HAIKU
+  if (!inhibit_window_system && !will_dump_p ())
+    {
+      Vinitial_window_system = Qhaiku;
+      Vwindow_system_version = make_fixnum (1);
+      return;
+    }
+#endif
+
   /* If no window system has been specified, try to use the terminal.  */
   if (! isatty (STDIN_FILENO))
     fatal ("standard input is not a tty");
diff --git a/src/emacs.c b/src/emacs.c
index 866e43fda9..015512e629 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -2177,6 +2177,15 @@ main (int argc, char **argv)
       syms_of_fontset ();
 #endif /* HAVE_NS */
 
+#ifdef HAVE_HAIKU
+      syms_of_haikuterm ();
+      syms_of_haikufns ();
+      syms_of_haikumenu ();
+      syms_of_haikufont ();
+      syms_of_haikuselect ();
+      syms_of_fontset ();
+#endif /* HAVE_HAIKU */
+
       syms_of_gnutls ();
 
 #ifdef HAVE_INOTIFY
@@ -2231,6 +2240,10 @@ main (int argc, char **argv)
 #if defined WINDOWSNT || defined HAVE_NTGUI
       globals_of_w32select ();
 #endif
+
+#ifdef HAVE_HAIKU
+      init_haiku_select ();
+#endif
     }
 
   init_charset ();
@@ -2715,6 +2728,15 @@ shut_down_emacs (int sig, Lisp_Object stuff)
 	    }
 	}
     }
+#ifdef HAVE_HAIKU
+  /* Wait for the application thread to finish, or else we won't
+     be able to start the Haiku debugger. */
+  if (app_thread_id)
+    {
+      pthread_kill (app_thread_id, SIGKILL);
+      pthread_join (app_thread_id, NULL);
+    }
+#endif
 #else
   fflush (stdout);
   reset_all_sys_modes ();
diff --git a/src/frame.c b/src/frame.c
index 74ef2afdb1..cee7641df4 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -226,6 +226,7 @@ DEFUN ("framep", Fframep, Sframep, 1, 1, 0,
  `w32' for an Emacs frame that is a window on MS-Windows display,
  `ns' for an Emacs frame on a GNUstep or Macintosh Cocoa display,
  `pc' for a direct-write MS-DOS frame.
+ `haiku` for an Emacs frame running in Haiku.
 See also `frame-live-p'.  */)
   (Lisp_Object object)
 {
@@ -244,6 +245,8 @@ DEFUN ("framep", Fframep, Sframep, 1, 1, 0,
       return Qpc;
     case output_ns:
       return Qns;
+    case output_haiku:
+      return Qhaiku;
     default:
       emacs_abort ();
     }
@@ -6023,6 +6026,7 @@ syms_of_frame (void)
   DEFSYM (Qw32, "w32");
   DEFSYM (Qpc, "pc");
   DEFSYM (Qns, "ns");
+  DEFSYM (Qhaiku, "haiku");
   DEFSYM (Qvisible, "visible");
   DEFSYM (Qbuffer_predicate, "buffer-predicate");
   DEFSYM (Qbuffer_list, "buffer-list");
diff --git a/src/frame.h b/src/frame.h
index a8ad011889..a9b3042dcc 100644
--- a/src/frame.h
+++ b/src/frame.h
@@ -585,6 +585,7 @@ #define EMACS_FRAME_H
     struct x_output *x;         /* From xterm.h.  */
     struct w32_output *w32;     /* From w32term.h.  */
     struct ns_output *ns;       /* From nsterm.h.  */
+    struct haiku_output *haiku; /* From haikuterm.h. */
   }
   output_data;
 
@@ -852,6 +853,11 @@ #define FRAME_NS_P(f) false
 #else
 #define FRAME_NS_P(f) ((f)->output_method == output_ns)
 #endif
+#ifndef HAVE_HAIKU
+#define FRAME_HAIKU_P(f) false
+#else
+#define FRAME_HAIKU_P(f) ((f)->output_method == output_haiku)
+#endif
 
 /* FRAME_WINDOW_P tests whether the frame is a graphical window system
    frame.  */
@@ -864,6 +870,9 @@ #define FRAME_WINDOW_P(f) FRAME_W32_P (f)
 #ifdef HAVE_NS
 #define FRAME_WINDOW_P(f) FRAME_NS_P(f)
 #endif
+#ifdef HAVE_HAIKU
+#define FRAME_WINDOW_P(f) FRAME_HAIKU_P (f)
+#endif
 #ifndef FRAME_WINDOW_P
 #define FRAME_WINDOW_P(f) ((void) (f), false)
 #endif
diff --git a/src/haiku_draw_support.cc b/src/haiku_draw_support.cc
new file mode 100644
index 0000000000..7ba3a304b4
--- /dev/null
+++ b/src/haiku_draw_support.cc
@@ -0,0 +1,344 @@
+/* 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 <cmath>
+#include <math.h>
+
+#include "haiku_support.h"
+
+#define RGB_TO_UINT32(r, g, b) ((255 << 24) | ((r) << 16) | ((g) << 8) | (b))
+#define RED_FROM_ULONG(color)	(((color) >> 16) & 0xff)
+#define GREEN_FROM_ULONG(color)	(((color) >> 8) & 0xff)
+#define BLUE_FROM_ULONG(color)	((color) & 0xff)
+
+extern "C" void emacs_abort ();
+
+static void
+rgb32_to_rgb_color (uint32_t rgb, rgb_color *color)
+{
+  color->red = RED_FROM_ULONG (rgb);
+  color->green = GREEN_FROM_ULONG (rgb);
+  color->blue = BLUE_FROM_ULONG (rgb);
+  color->alpha = 255;
+}
+
+void
+BView_StartClip (void *view)
+{
+  BView *vw = (BView *) view;
+  BView_draw_lock (vw);
+  vw->PushState ();
+}
+
+void
+BView_EndClip (void *view)
+{
+  BView *vw = (BView *) view;
+  vw->PopState ();
+  BView_draw_unlock (vw);
+}
+
+void
+BView_SetHighColor (void *view, uint32_t color)
+{
+  BView *vw = (BView *) view;
+  rgb_color col;
+  rgb32_to_rgb_color (color, &col);
+
+  vw->SetHighColor (col);
+}
+
+void
+BView_SetPenSize (void *view, int u)
+{
+  BView *vw = (BView *) view;
+  vw->SetPenSize (u);
+}
+
+void
+BView_FillRectangle (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);
+}
+
+void
+BView_FillRectangleAbs (void *view, int x, int y, int x1, int y1)
+{
+  BView *vw = (BView *) 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 = (BView *) 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 = (BView *) view;
+  rgb_color col;
+  rgb32_to_rgb_color (color, &col);
+
+  vw->SetViewColor (col);
+}
+
+void
+BView_ClipToRect (void *view, int x, int y, int width, int height)
+{
+  BView *vw = (BView *) 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 = (BView *) 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 = (BView *) view;
+  BPoint from = BPoint (sx, sy);
+  BPoint to = BPoint (tx, ty);
+
+  vw->StrokeLine (from, to);
+}
+
+void
+BView_SetFont (void *view, void *font)
+{
+  BView *vw = (BView *) view;
+
+  vw->SetFont ((BFont *) font);
+}
+
+void
+BView_MovePenTo (void *view, int x, int y)
+{
+  BView *vw = (BView *) view;
+  BPoint pt = BPoint (x, y);
+
+  vw->MovePenTo (pt);
+}
+
+void
+BView_DrawString (void *view, const char *chr, ptrdiff_t len)
+{
+  BView *vw = (BView *) view;
+
+  vw->DrawString (chr, len);
+}
+
+void
+BView_DrawChar (void *view, char chr)
+{
+  BView *vw = (BView *) 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 = (BView *) view;
+
+  vw->CopyBits (BRect (x, y, x + width - 1, y + height - 1),
+		BRect (tox, toy, tox + towidth - 1, toy + toheight - 1));
+  vw->Sync ();
+}
+
+void
+rgb_color_hsl (uint32_t rgb, double *h, double *s, double *l)
+{
+  rgb_color col;
+  rgb32_to_rgb_color (rgb, &col);
+
+  double red = col.red / 255.0;
+  double green = col.green / 255.0;
+  double blue = col.blue / 255.0;
+
+  double max = std::fmax (std::fmax (red, blue), green);
+  double min = std::fmin (std::fmin (red, blue), green);
+  double delta = max - min;
+  *l = (max + min) / 2.0;
+
+  if (!delta)
+    {
+      *h = 0;
+      *s = 0;
+      return;
+    }
+
+  *s = (*l < 0.5) ? delta / (max + min) :
+    delta / (20 - max - min);
+  double rc = (max - red) / delta;
+  double gc = (max - green) / delta;
+  double bc = (max - blue) / delta;
+
+  if (red == max)
+    *h = bc - gc;
+  else if (green == max)
+    *h = 2.0 + rc + -bc;
+  else
+    *h = 4.0 + gc + -rc;
+  *h = std::fmod (*h / 6, 1.0);
+}
+
+static double
+hue_to_rgb (double v1, double v2, double h)
+{
+  if (h < 1 / 6)
+    return v1 + (v2 - v1) * h * 6.0;
+  else if (h < 0.5)
+    return v2;
+  else if (h < 2.0 / 3)
+    return v1 + (v2 - v1) * (2.0 / 3 - h) * 6.0;
+  return v1;
+}
+
+void
+hsl_color_rgb (double h, double s, double l, uint32_t *rgb)
+{
+  if (!s)
+    *rgb = RGB_TO_UINT32 (std::lrint (l * 255),
+			  std::lrint (l * 255),
+			  std::lrint (l * 255));
+  else
+    {
+      double m2 = l <= 0.5 ? l * (1 + s) : l + s - l * s;
+      double m1 = 2.0 * l - m2;
+
+      *rgb = RGB_TO_UINT32
+	(std::lrint (hue_to_rgb (m1, m2,
+				 std::fmod (h + 1 / 3.0, 1)) * 255),
+	 std::lrint (hue_to_rgb (m1, m2, h) * 255),
+	 std::lrint (hue_to_rgb (m1, m2,
+				 std::fmod (h - 1 / 3.0, 1)) * 255));
+    }
+}
+
+void
+BView_Draw (void *view, int x, int y, int width, int height)
+{
+  BView *vw = (BView *) view;
+  if (vw->LockLooper ())
+    {
+      vw->Draw (BRect (x, y, x + width, y + height));
+      vw->UnlockLooper ();
+    }
+}
+
+void
+BView_DrawBitmap (void *view, void *bitmap, int x, int y,
+		  int width, int height, int vx, int vy, int vwidth,
+		  int vheight)
+{
+  BView *vw = (BView *) view;
+  BBitmap *bm = (BBitmap *) bitmap;
+
+  vw->DrawBitmap (bm, BRect (x, y, x + width - 1, y + height - 1),
+		  BRect (vx, vy, vx + vwidth - 1, vy + vheight - 1));
+  vw->Flush ();
+  vw->Sync ();
+}
+
+void
+BView_DrawBitmapWithEraseOp (void *view, void *bitmap, int x, int y,
+			     int width, int height, int vx, int vy, int vwidth,
+			     int vheight)
+{
+  BView *vw = (BView *) view;
+  BBitmap *bm = (BBitmap *) bitmap;
+
+  drawing_mode dm = vw->DrawingMode ();
+  vw->SetDrawingMode (B_OP_ERASE);
+  vw->DrawBitmap (bm, BRect (x, y, x + width - 1, y + height - 1),
+		  BRect (vx, vy, vx + vwidth - 1, vy + vheight - 1));
+  vw->SetDrawingMode (dm);
+}
+
+void
+BView_BeginLayer (void *view, float opacity)
+{
+  BView *vw = (BView *) view;
+  vw->BeginLayer (opacity);
+}
+
+void
+BView_EndLayer (void *view)
+{
+  BView *vw = (BView *) view;
+  vw->EndLayer ();
+}
+
+void
+BView_DrawBitmapWithOverOp (void *view, void *bitmap, int x,
+			    int y, int width, int height)
+{
+  BView *vw = (BView *) view;
+  BBitmap *bm = (BBitmap *) bitmap;
+
+  drawing_mode dm = vw->DrawingMode ();
+  vw->SetDrawingMode (B_OP_OVER);
+  vw->DrawBitmap (bm, BRect (x, y, x + width - 1, y + height - 1));
+  vw->SetDrawingMode (dm);
+}
+
+void
+BView_BeginTransaction (void *view)
+{
+  BView *vw = (BView *) view;
+  if (!vw->Window ()->LockLooper ())
+    emacs_abort ();
+  vw->Window ()->BeginViewTransaction ();
+  vw->Window ()->UnlockLooper ();
+}
+
+void
+BView_EndTransaction (void *view)
+{
+  BView *vw = (BView *) view;
+  if (!vw->Window ()->LockLooper ())
+    emacs_abort ();
+  vw->Window ()->EndViewTransaction ();
+  vw->Window ()->UnlockLooper ();
+}
diff --git a/src/haiku_font_support.cc b/src/haiku_font_support.cc
new file mode 100644
index 0000000000..0dc1227907
--- /dev/null
+++ b/src/haiku_font_support.cc
@@ -0,0 +1,448 @@
+/* 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 <cstring>
+#include <cmath>
+
+#include "haiku_support.h"
+
+static void
+estimate_font_ascii (BFont *font, int *max_width,
+		     int *min_width, int *avg_width)
+{
+  char ch[2];
+  bool tems[1];
+  int total = 0;
+  int count = 0;
+  int min = 0;
+  int max = 0;
+
+  std::memset (ch, 0, sizeof ch);
+  for (ch[0] = 1; ch[0] < 127; ++ch[0])
+    {
+      tems[0] = false;
+      font->GetHasGlyphs (ch, 1, tems);
+      if (tems[0])
+	{
+	  int w = font->StringWidth (ch);
+	  ++count;
+	  total += w;
+
+	  if (!min || min > w)
+	    min = w;
+	  if (max < w)
+	    max = w;
+	}
+    }
+
+  *min_width = min;
+  *max_width = max;
+  *avg_width = total / count;
+}
+
+void *
+BFont_open_default (double size, int plain_or_bold_or_fixed)
+{
+  BFont *ft;
+  if (!plain_or_bold_or_fixed)
+    ft = new BFont (be_fixed_font);
+  else if (plain_or_bold_or_fixed > 0)
+    ft = new BFont (be_plain_font);
+  else
+    ft = new BFont (be_bold_font);
+  ft->SetSize (size);
+  ft->SetEncoding (B_UNICODE_UTF8);
+  return ft;
+}
+
+void
+BFont_close (void *font)
+{
+  if (font != (void *) be_fixed_font &&
+      font != (void *) be_plain_font &&
+      font != (void *) be_bold_font)
+    delete (BFont *) font;
+}
+
+void
+BFont_dat (void *font, int *px_size, int *min_width, int *max_width,
+	   int *avg_width, int *height, int *space_width, int *ascent,
+	   int *descent, int *underline_position, int *underline_thickness)
+{
+  BFont *ft = (BFont *) font;
+  struct font_height fheight;
+  bool have_space_p;
+
+  char atem[1];
+  bool otem[1];
+
+  ft->GetHeight (&fheight);
+  atem[0] = ' ';
+  otem[0] = false;
+  ft->GetHasGlyphs (atem, 1, otem);
+  have_space_p = otem[0];
+
+  estimate_font_ascii (ft, max_width, min_width, avg_width);
+  *ascent = std::lrint (fheight.ascent);
+  *descent = std::lrint (fheight.descent);
+  *height = *ascent + *descent;
+
+  *space_width = have_space_p ? ft->StringWidth (" ") : 0;
+
+  *px_size = std::lrint (ft->Size ());
+  *underline_position = 0;
+  *underline_thickness = 0;
+}
+
+int
+BFont_have_char_p (void *font, int32_t chr)
+{
+  BFont *ft = (BFont *) font;
+  return ft->IncludesBlock (chr, chr);
+}
+
+void
+BFont_char_bounds (void *font, const char *mb_str, int *advance,
+		   int *lb, int *rb)
+{
+  BRect rect;
+  BFont *ft = (BFont *) font;
+  edge_info edge_info;
+  float size, escapement;
+  size = ft->Size ();
+
+  ft->GetEdges (mb_str, 1, &edge_info);
+  *lb = (short) std::lrint (edge_info.left * size);
+  *rb = (short) std::lrint (edge_info.right * size);
+  ft->GetEscapements (mb_str, 1, &escapement);
+  *advance = std::lrint (escapement * size);
+}
+
+void *
+BFont_new (void)
+{
+  return new BFont ();
+}
+
+int
+BFont_family_has_style_p (haiku_font_family_or_style family,
+			  haiku_font_family_or_style style)
+{
+  int sc = count_font_styles (family);
+  for (int idx = 0; idx < sc; ++idx)
+    {
+      font_style s;
+      if (get_font_style (family, idx, &s) == B_OK)
+	{
+	  if (!strcmp (s, style))
+	    return 1;
+	}
+    }
+  return 0;
+}
+
+int
+BFont_family (int idx, char *f, int *fixed_p)
+{
+  uint32_t flags;
+  if (get_font_family (idx, (char (*)[64]) f, &flags) != B_OK)
+    return 1;
+  *fixed_p = flags & B_IS_FIXED;
+  return 0;
+}
+
+int
+BFont_set_family_and_style (void *font, haiku_font_family_or_style family,
+			    haiku_font_family_or_style style)
+{
+  BFont *ft = (BFont *) font;
+  return ft->SetFamilyAndStyle (family, style) != B_OK;
+}
+
+int
+BFont_set_family_face (void *font, haiku_font_family_or_style family,
+		       int italic_p, int bold_p)
+{
+  BFont *ft = (BFont *) font;
+  return ft->SetFamilyAndFace (family, (italic_p ? B_ITALIC_FACE : 0) |
+			       (bold_p == HAIKU_BOLD ? B_BOLD_FACE : 0));
+
+}
+
+static void
+font_style_to_flags (char *st, struct haiku_font_pattern *pattern)
+{
+  char *style = strdup (st);
+  char *token;
+  pattern->weight = -1;
+  int tok = 0;
+
+  while ((token = std::strtok (!tok ? style : NULL, " ")) && tok < 2)
+    {
+      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"))
+	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"))
+	pattern->weight = HAIKU_EXTRA_BOLD;
+      else if (token && !strcmp (token, "UltraBold"))
+	pattern->weight = HAIKU_ULTRA_BOLD;
+      else if (token && !strcmp (token, "Oblique"))
+	{
+	  pattern->specified |= FSPEC_SLANT;
+	  pattern->slant = SLANT_OBLIQUE;
+	}
+      else if (token && !strcmp (token, "Regular"))
+	{
+	  pattern->specified |= FSPEC_SLANT;
+	  pattern->slant = SLANT_REGULAR;
+	}
+      else if (token && !strcmp (token, "Italic"))
+	{
+	  pattern->specified |= FSPEC_SLANT;
+	  pattern->slant = SLANT_ITALIC;
+	}
+      else
+	{
+	  tok = 1000;
+	  break;
+	}
+      tok++;
+    }
+
+
+  if (pattern->weight != -1)
+    pattern->specified |= FSPEC_WEIGHT;
+
+  if (tok > 2)
+    {
+      pattern->specified &= ~FSPEC_SLANT;
+      pattern->specified &= ~FSPEC_WEIGHT;
+      pattern->specified |= FSPEC_STYLE;
+      std::strncpy ((char *) &pattern->style, style,
+		    sizeof pattern->style - 1);
+    }
+
+  free (style);
+}
+
+static bool
+font_family_style_matches_p (font_family family, char *style, uint32_t flags,
+			     struct haiku_font_pattern *pattern)
+{
+  struct haiku_font_pattern m;
+
+  if (style)
+    font_style_to_flags (style, &m);
+
+  enum haiku_font_slant slant;
+  int weight;
+
+
+  if (!(m.specified & FSPEC_SLANT))
+    slant = SLANT_REGULAR;
+  else
+    slant = m.slant;
+
+  if (!(m.specified & FSPEC_WEIGHT))
+    weight = HAIKU_REGULAR;
+  else
+    weight = m.weight;
+
+  if (pattern->specified & FSPEC_FAMILY &&
+      strcmp ((char *) &pattern->family, family))
+    return false;
+
+  if (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 != weight)
+    return false;
+
+  if (pattern->specified & FSPEC_SLANT && pattern->slant != slant)
+    return false;
+
+  return true;
+}
+
+static void
+haiku_font_fill_pattern (struct haiku_font_pattern *pattern,
+			 font_family family, char *style,
+			 uint32_t flags)
+{
+  if (style)
+    font_style_to_flags (style, pattern);
+
+  pattern->specified |= FSPEC_FAMILY;
+  std::strncpy (pattern->family, family,
+		sizeof pattern->family - 1);
+  pattern->specified |= FSPEC_SPACING;
+  pattern->mono_spacing_p = flags & B_IS_FIXED;
+}
+
+void
+haiku_font_pattern_free (struct haiku_font_pattern *pt)
+{
+  struct haiku_font_pattern *tem = pt;
+  while (tem)
+    {
+      struct haiku_font_pattern *t = tem;
+      tem = t->next;
+      delete t;
+    }
+}
+
+struct haiku_font_pattern *
+BFont_find (struct haiku_font_pattern *pt)
+{
+  struct haiku_font_pattern *r = NULL;
+  font_family name;
+  font_style sname;
+  uint32_t 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;
+	      haiku_font_fill_pattern (p, name, NULL, flags);
+	      p->next = r;
+	      r = p;
+	    }
+	  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, pt))
+		    {
+		      struct haiku_font_pattern *p = new struct haiku_font_pattern;
+		      p->specified = 0;
+		      haiku_font_fill_pattern (p, name, (char *) &sname, flags);
+		      p->next = r;
+		      r = p;
+		    }
+		}
+	    }
+	}
+    }
+
+  return r;
+}
+
+int
+BFont_open_pattern (struct haiku_font_pattern *pat, void **font, float size)
+{
+  int sty_count;
+  font_family name;
+  font_style sname;
+  uint32_t 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))
+    {
+      BFont *ft = new BFont;
+      if (ft->SetFamilyAndStyle (name, NULL) != B_OK)
+	{
+	  delete ft;
+	  return 1;
+	}
+      ft->SetSize (size);
+      ft->SetEncoding (B_UNICODE_UTF8);
+      *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);
+	      *font = (void *) ft;
+	      return 0;
+	    }
+	}
+    }
+
+  return 1;
+}
+
+void
+BFont_populate_fixed_family (struct haiku_font_pattern *ptn)
+{
+  font_family f;
+  font_style s;
+  be_fixed_font->GetFamilyAndStyle (&f, &s);
+
+  ptn->specified |= FSPEC_FAMILY;
+  strncpy (ptn->family, f, sizeof ptn->family);
+}
+
+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);
+}
diff --git a/src/haiku_io.c b/src/haiku_io.c
new file mode 100644
index 0000000000..c9a9983136
--- /dev/null
+++ b/src/haiku_io.c
@@ -0,0 +1,138 @@
+/* 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"
+
+#define PORT_CAP 1200
+
+port_id port_emacs_to_application;
+port_id port_application_to_emacs;
+
+void
+haiku_io_init (void)
+{
+  port_emacs_to_application = create_port (PORT_CAP, "emacs application port");
+  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);
+    }
+
+  emacs_abort ();
+}
+
+void
+haiku_read_size (ssize_t *len)
+{
+  port_id from = port_application_to_emacs;
+  ssize_t size;
+
+  size = port_buffer_size_etc (from, B_TIMEOUT, 0);
+
+  if (size < B_OK)
+    *len = -1;
+  else
+    *len = size;
+}
+
+int
+haiku_read (enum haiku_event_type *type, void *buf, ssize_t len)
+{
+  int32_t typ;
+  port_id from = port_application_to_emacs;
+
+  if (read_port (from, &typ, buf, len) < B_OK)
+    return -1;
+
+  *type = (enum haiku_event_type) typ;
+  eassert (len == haiku_len (typ));
+  return 0;
+}
+
+int
+haiku_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;
+}
+
+void
+haiku_io_init_in_app_thread (void)
+{
+  sigset_t set;
+  sigemptyset (&set);
+  sigaddset (&set, SIGUSR2);
+  sigaddset (&set, SIGUSR1);
+
+  if (pthread_sigmask (SIG_BLOCK, &set, NULL))
+    perror ("pthread_sigmask");
+}
diff --git a/src/haiku_select.cc b/src/haiku_select.cc
new file mode 100644
index 0000000000..8d345ca661
--- /dev/null
+++ b/src/haiku_select.cc
@@ -0,0 +1,155 @@
+/* Haiku window system selection support. Hey Emacs, this is -*- C++ -*-
+   Copyright (C) 2021 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+#include <Clipboard.h>
+
+#include <cstdlib>
+#include <cstring>
+
+#include "haikuselect.h"
+
+
+static BClipboard *primary = NULL;
+static BClipboard *secondary = NULL;
+static BClipboard *system_clipboard = NULL;
+
+int selection_state_flag;
+
+static char *
+BClipboard_find_data (BClipboard *cb, const char *type, ssize_t *len)
+{
+  if (!cb->Lock ())
+    return 0;
+
+  BMessage *dat = cb->Data ();
+  if (!dat)
+    {
+      cb->Unlock ();
+      return 0;
+    }
+
+  const char *ptr;
+  ssize_t bt;
+  dat->FindData (type, B_MIME_TYPE, (const void **) &ptr, &bt);
+
+  if (!ptr)
+    {
+      cb->Unlock ();
+      return NULL;
+    }
+
+  if (len)
+    *len = bt;
+
+  cb->Unlock ();
+
+  return strndup (ptr, bt);
+}
+
+static void
+BClipboard_set_data (BClipboard *cb, const char *type, const char *dat,
+		     ssize_t len)
+{
+  if (!cb->Lock ())
+    return;
+  cb->Clear ();
+  BMessage *mdat = cb->Data ();
+  if (!mdat)
+    {
+      cb->Unlock ();
+      return;
+    }
+
+  if (dat)
+    mdat->AddData (type, B_MIME_TYPE, dat, len);
+  cb->Commit ();
+  cb->Unlock ();
+}
+
+char *
+BClipboard_find_system_data (const char *type, ssize_t *len)
+{
+  if (!system_clipboard)
+    return 0;
+
+  return BClipboard_find_data (system_clipboard, type, len);
+}
+
+char *
+BClipboard_find_primary_selection_data (const char *type, ssize_t *len)
+{
+  if (!primary)
+    return 0;
+
+  return BClipboard_find_data (primary, type, len);
+}
+
+char *
+BClipboard_find_secondary_selection_data (const char *type, ssize_t *len)
+{
+  if (!secondary)
+    return 0;
+
+  return BClipboard_find_data (secondary, type, len);
+}
+
+void
+BClipboard_set_system_data (const char *type, const char *data,
+			    ssize_t len)
+{
+  if (!system_clipboard)
+    return;
+
+  BClipboard_set_data (system_clipboard, type, data, len);
+}
+
+void
+BClipboard_set_primary_selection_data (const char *type, const char *data,
+				       ssize_t len)
+{
+  if (!primary)
+    return;
+
+  BClipboard_set_data (primary, type, data, len);
+}
+
+void
+BClipboard_set_secondary_selection_data (const char *type, const char *data,
+					 ssize_t len)
+{
+  if (!secondary)
+    return;
+
+  BClipboard_set_data (secondary, type, data, len);
+}
+
+void
+BClipboard_free_data (void *ptr)
+{
+  std::free (ptr);
+}
+
+void
+init_haiku_select (void)
+{
+  system_clipboard = new BClipboard ("system");
+  primary = new BClipboard ("primary");
+  secondary = new BClipboard ("secondary");
+}
diff --git a/src/haiku_support.cc b/src/haiku_support.cc
new file mode 100644
index 0000000000..66ff12e117
--- /dev/null
+++ b/src/haiku_support.cc
@@ -0,0 +1,1180 @@
+/* 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 <Application.h>
+#include <GraphicsDefs.h>
+#include <InterfaceDefs.h>
+#include <Bitmap.h>
+#include <Window.h>
+#include <View.h>
+#include <Screen.h>
+#include <Cursor.h>
+#include <UnicodeChar.h>
+#include <ScrollBar.h>
+#include <Region.h>
+#include <Menu.h>
+#include <MenuItem.h>
+#include <PopUpMenu.h>
+#include <MenuBar.h>
+#include <Beep.h>
+#include <Alert.h>
+#include <Button.h>
+
+#include <cmath>
+#include <cstring>
+#include <pthread.h>
+#include <stdint.h>
+
+#include "haiku_support.h"
+
+extern "C"
+{
+  extern void emacs_abort (void);
+  pthread_t app_thread_id;
+}
+
+class Emacs : public BApplication
+{
+public:
+  Emacs () : BApplication ("application/emacs")
+  {
+  }
+};
+
+class EmacsWindow : public BWindow
+{
+public:
+  EmacsWindow () : BWindow (BRect (0, 0, 0, 0), "", B_TITLED_WINDOW_LOOK,
+			    B_NORMAL_WINDOW_FEEL, 0)
+  {
+
+  }
+
+  void
+  WindowActivated (bool activated)
+  {
+    struct haiku_activation_event rq;
+    rq.window = this;
+    rq.activated_p = activated;
+
+    haiku_write (ACTIVATION, &rq);
+  }
+
+  void
+  MessageReceived (BMessage *msg)
+  {
+    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);
+      }
+    BWindow::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;
+	if (modifiers () & B_SHIFT_KEY)
+	  rq.modifiers |= HAIKU_MODIFIER_SHIFT;
+
+	if (modifiers () & B_CONTROL_KEY)
+	  rq.modifiers |= HAIKU_MODIFIER_CTRL;
+
+	if (modifiers () & B_COMMAND_KEY)
+	  rq.modifiers |= HAIKU_MODIFIER_ALT;
+
+	rq.mb_char = code;
+	rq.kc = msg->GetInt32 ("key", -1);
+	rq.unraw_mb_char = BUnicodeChar::FromUTF8 (msg->GetString ("bytes"));
+
+	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;
+
+	if (modifiers () & B_SHIFT_KEY)
+	  rq.modifiers |= HAIKU_MODIFIER_SHIFT;
+
+	if (modifiers () & B_CONTROL_KEY)
+	  rq.modifiers |= HAIKU_MODIFIER_CTRL;
+
+	if (modifiers () & B_COMMAND_KEY)
+	  rq.modifiers |= HAIKU_MODIFIER_ALT;
+
+	float dx, dy;
+	if (msg->FindFloat ("be:wheel_delta_x", &dx) == B_OK &&
+	    msg->FindFloat ("be:wheel_delta_y", &dy) == B_OK)
+	  {
+	    rq.delta_x = dx;
+	    rq.delta_y = dy;
+
+	    haiku_write (WHEEL_MOVE_EVENT, &rq);
+	  };
+      }
+    else
+      BWindow::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);
+  }
+
+  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);
+  }
+
+  bool
+  QuitRequested ()
+  {
+    struct haiku_quit_requested_event rq;
+    rq.window = this;
+    haiku_write (QUIT_REQUESTED, &rq);
+    return false;
+  }
+
+  void
+  Minimize (bool minimized_p)
+  {
+    BWindow::Minimize (minimized_p);
+    struct haiku_iconification_event rq;
+    rq.window = this;
+    rq.iconified_p = minimized_p;
+
+    haiku_write (ICONIFICATION, &rq);
+  }
+};
+
+class EmacsMenuBar : 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
+{
+private:
+  uint32_t previous_buttons = 0;
+public:
+  int looper_locked_count = 0;
+  BRegion sb_region;
+
+  EmacsView () : BView (BRect (0, 0, 0, 0), "Emacs Content",
+			B_FOLLOW_ALL_SIDES, B_WILL_DRAW | B_SUBPIXEL_PRECISE)
+  {
+
+  }
+
+  void
+  Draw (BRect expose_bounds)
+  {
+    struct haiku_expose_event rq;
+    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
+  MouseMoved (BPoint point, uint32 transit, const BMessage *msg)
+  {
+    struct haiku_mouse_motion_event rq;
+
+    rq.just_exited_p = transit == B_EXITED_VIEW;
+    rq.x = point.x;
+    rq.y = point.y;
+    rq.be_code = transit;
+    rq.window = this->Window ();
+
+    haiku_write (MOUSE_MOTION, &rq);
+  }
+
+  void
+  MouseDown (BPoint point)
+  {
+    struct haiku_button_event rq;
+    uint32_t buttons;
+
+    this->GetMouse (&point, &buttons, false);
+
+    rq.window = this->Window ();
+    rq.btn_no = 0;
+
+    if (!(previous_buttons & B_PRIMARY_MOUSE_BUTTON) &&
+	(buttons & B_PRIMARY_MOUSE_BUTTON))
+      rq.btn_no = 0;
+    else if (!(previous_buttons & B_SECONDARY_MOUSE_BUTTON) &&
+	     (buttons & B_SECONDARY_MOUSE_BUTTON))
+      rq.btn_no = 2;
+    else if (!(previous_buttons & B_TERTIARY_MOUSE_BUTTON) &&
+	     (buttons & B_TERTIARY_MOUSE_BUTTON))
+      rq.btn_no = 1;
+    previous_buttons = buttons;
+
+    rq.x = point.x;
+    rq.y = point.y;
+
+    uint32_t mods = modifiers ();
+
+    rq.modifiers = 0;
+    if (mods & B_SHIFT_KEY)
+      rq.modifiers |= HAIKU_MODIFIER_SHIFT;
+
+    if (mods & B_CONTROL_KEY)
+      rq.modifiers |= HAIKU_MODIFIER_CTRL;
+
+    if (mods & B_COMMAND_KEY)
+      rq.modifiers |= HAIKU_MODIFIER_ALT;
+
+    haiku_write (BUTTON_DOWN, &rq);
+  }
+
+  void
+  MouseUp (BPoint point)
+  {
+    struct haiku_button_event rq;
+    uint32_t buttons;
+
+    this->GetMouse (&point, &buttons, false);
+
+    rq.window = this->Window ();
+    rq.btn_no = 0;
+
+    if ((previous_buttons & B_PRIMARY_MOUSE_BUTTON) &&
+	!(buttons & B_PRIMARY_MOUSE_BUTTON))
+      rq.btn_no = 0;
+    else if ((previous_buttons & B_SECONDARY_MOUSE_BUTTON) &&
+	     !(buttons & B_SECONDARY_MOUSE_BUTTON))
+      rq.btn_no = 2;
+    else if ((previous_buttons & B_TERTIARY_MOUSE_BUTTON) &&
+	     !(buttons & B_TERTIARY_MOUSE_BUTTON))
+      rq.btn_no = 1;
+    previous_buttons = buttons;
+
+    rq.x = point.x;
+    rq.y = point.y;
+
+    uint32_t mods = modifiers ();
+
+    rq.modifiers = 0;
+    if (mods & B_SHIFT_KEY)
+      rq.modifiers |= HAIKU_MODIFIER_SHIFT;
+
+    if (mods & B_CONTROL_KEY)
+      rq.modifiers |= HAIKU_MODIFIER_CTRL;
+
+    if (mods & B_COMMAND_KEY)
+      rq.modifiers |= HAIKU_MODIFIER_ALT;
+
+    haiku_write (BUTTON_UP, &rq);
+  }
+};
+
+class EmacsScrollBar : 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->SetFlags (vw->Flags () & ~B_FRAME_EVENTS);
+    vw->SetResizingMode (B_FOLLOW_NONE);
+  }
+
+  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);
+  }
+};
+
+static void *
+start_running_application (void *data)
+{
+  haiku_io_init_in_app_thread ();
+  ((Emacs *) data)->Lock ();
+  ((Emacs *) data)->Run ();
+  ((Emacs *) data)->Unlock ();
+  return NULL;
+}
+
+
+void *
+BBitmap_data (void *bitmap)
+{
+  return ((BBitmap *) bitmap)->Bits ();
+}
+
+int
+BBitmap_convert (void *_bitmap, void **new_bitmap)
+{
+  BBitmap *bitmap = (BBitmap *) _bitmap;
+  if (bitmap->ColorSpace () != B_RGBA32)
+    return 0;
+  BRect bounds = bitmap->Bounds ();
+  BBitmap *bmp = new (std::nothrow) BBitmap (bounds, B_RGBA32);
+  if (!bmp)
+    return 0;
+  if (bmp->ImportBits (bitmap) != B_OK)
+    {
+      delete bmp;
+      return 0;
+    }
+  **(BBitmap **) new_bitmap = bmp;
+  return 1;
+}
+
+void
+BBitmap_free (void *bitmap)
+{
+  delete (BBitmap *) bitmap;
+}
+
+void *
+BBitmap_new (int width, int height, int mono_p)
+{
+  return new (std::nothrow) BBitmap (BRect (0, 0, width, height),
+				     mono_p ? B_GRAY1 : B_RGB32);
+}
+
+void
+BBitmap_dimensions (void *bitmap, int *left, int *top,
+		    int *right, int *bottom,
+		    int32_t *bytes_per_row, int *mono_p)
+{
+  BRect rect = ((BBitmap *) bitmap)->Bounds ();
+  *left = rect.left;
+  *top = rect.top;
+  *right = rect.right;
+  *bottom = rect.bottom;
+
+  *bytes_per_row = ((BBitmap *) bitmap)->BytesPerRow ();
+  *mono_p = (((BBitmap *) bitmap)->ColorSpace () == B_GRAY1);
+}
+
+void *
+BApplication_setup (void)
+{
+  if (be_app)
+    return be_app;
+  Emacs *app;
+
+  app = new Emacs;
+  if (pthread_create (&app_thread_id, NULL, start_running_application, app))
+    {
+      perror ("pthread_create");
+      emacs_abort ();
+    }
+
+  return app;
+}
+
+void *
+BWindow_new (void *_view)
+{
+  BWindow *window = new (std::nothrow) EmacsWindow;
+  BView **v = (BView **) _view;
+  if (!window)
+    {
+      *v = NULL;
+      return window;
+    }
+
+  BView *vw = new (std::nothrow) EmacsView;
+  if (!vw)
+    {
+      *v = NULL;
+      window->Lock ();
+      window->Quit ();
+      return NULL;
+    }
+  window->BeginViewTransaction ();
+  window->AddChild (vw);
+  *v = vw;
+  return window;
+}
+
+void
+BWindow_quit (void *window)
+{
+  ((BWindow *) window)->Lock ();
+  ((BWindow *) window)->Quit ();
+}
+
+void
+BWindow_set_offset (void *window, int x, int y)
+{
+  ((BWindow *) window)->MoveTo (x, y);
+}
+
+void
+BWindow_iconify (void *window)
+{
+  if (((BWindow *) window)->IsHidden ())
+    BWindow_set_visible (window, true);
+  ((BWindow *) window)->Minimize (true);
+}
+
+void
+BWindow_set_visible (void *window, int visible_p)
+{
+  BWindow *win = (BWindow *) window;
+  if (visible_p)
+    {
+      if (win->IsMinimized ())
+	win->Minimize (false);
+      win->Show ();
+    }
+  else if (!win->IsHidden ())
+    {
+      if (win->IsMinimized ())
+	win->Minimize (false);
+      win->Hide ();
+    }
+}
+
+void
+BWindow_retitle (void *window, const char *title)
+{
+  ((BWindow *) window)->SetTitle (title);
+}
+
+void
+BWindow_resize (void *window, int width, int height)
+{
+  ((BWindow *) window)->ResizeTo (width, height);
+}
+
+void
+BWindow_activate (void *window)
+{
+  ((BWindow *) window)->Activate ();
+}
+
+void
+BScreen_px_dim (int *width, int *height)
+{
+  BScreen screen;
+  BRect frame = screen.Frame ();
+
+  *width = frame.right - frame.left;
+  *height = frame.bottom - frame.top;
+}
+
+void
+BView_resize_to (void *view, int width, int height)
+{
+  BView *vw = (BView *) view;
+  if (!vw->LockLooper ())
+    emacs_abort ();
+  vw->ResizeTo (width, height);
+  vw->UnlockLooper ();
+}
+
+void *
+BCursor_create_default (void)
+{
+  return new BCursor (B_CURSOR_ID_SYSTEM_DEFAULT);
+}
+
+void *
+BCursor_from_id (enum haiku_cursor cursor)
+{
+  return new BCursor ((enum BCursorID) cursor);
+}
+
+void *
+BCursor_create_i_beam (void)
+{
+  return new BCursor (B_CURSOR_ID_I_BEAM);
+}
+
+void *
+BCursor_create_progress_cursor (void)
+{
+  return new BCursor (B_CURSOR_ID_PROGRESS);
+}
+
+void *
+BCursor_create_grab (void)
+{
+  return new BCursor (B_CURSOR_ID_GRAB);
+}
+
+void
+BCursor_delete (void *cursor)
+{
+  delete (BCursor *) cursor;
+}
+
+void
+BView_set_view_cursor (void *view, void *cursor)
+{
+  if (!((BView *) view)->LockLooper ())
+    emacs_abort ();
+  ((BView *) view)->SetViewCursor ((BCursor *) cursor);
+  ((BView *) view)->UnlockLooper ();
+}
+
+void
+BWindow_Flush (void *window)
+{
+  ((BWindow *) window)->Flush ();
+}
+
+void
+BMapKey (uint32_t kc, int *non_ascii_p, unsigned *code)
+{
+  *code = 0;
+
+  switch (kc)
+    {
+    default:
+      *non_ascii_p = 0;
+      if (kc < 0xe && kc > 0x1)
+	{
+	  *code = XK_F1 + kc - 2;
+	  *non_ascii_p = 1;
+	}
+      return;
+    case 0x1e:
+      *code = XK_BackSpace;
+      break;
+    case 0x32:
+    case 0x5b:
+    case 0x47:
+      *code = XK_Return;
+      break;
+    case 0x61:
+      *code = XK_Left;
+      break;
+    case 0x63:
+      *code = XK_Right;
+      break;
+    case 0x57:
+      *code = XK_Up;
+      break;
+    case 0x62:
+      *code = XK_Down;
+      break;
+    case 0x64:
+      *code = XK_Insert;
+      break;
+    case 0x65:
+      *code = XK_Delete;
+      break;
+    case 0x37:
+      *code = XK_Home;
+      break;
+    case 0x58:
+      *code = XK_End;
+      break;
+    case 0x39:
+      *code = XK_Page_Up;
+      break;
+    case 0x5a:
+      *code = XK_Page_Down;
+      break;
+    case 0x1:
+      *code = XK_Escape;
+      break;
+    case 0x68:
+      *code = XK_Menu;
+      break;
+    }
+  *non_ascii_p = 1;
+}
+
+void *
+BScrollBar_make_for_view (void *view, int horizontal_p,
+			  int x, int y, int x1, int y1,
+			  void *scroll_bar_ptr)
+{
+  EmacsScrollBar *sb = new EmacsScrollBar (x, y, x1, y1, horizontal_p);
+  sb->scroll_bar = scroll_bar_ptr;
+  BView *vw = (BView *) view;
+  BView *sv = (BView *) sb;
+  if (!vw->LockLooper ())
+    emacs_abort ();
+  vw->AddChild ((BView *) sb);
+  sv->WindowActivated (vw->Window ()->IsActive ());
+  vw->UnlockLooper ();
+  return sb;
+}
+
+void
+BScrollBar_delete (void *sb)
+{
+  BView *view = (BView *) sb;
+  BView *pr = view->Parent ();
+
+  if (!pr->LockLooper ())
+    emacs_abort ();
+  pr->RemoveChild (view);
+  pr->UnlockLooper ();
+
+  delete (EmacsScrollBar *) sb;
+}
+
+void
+BView_move_frame (void *view, int x, int y, int x1, int y1)
+{
+  BView *vw = (BView *) view;
+
+  if (!vw->LockLooper ())
+    emacs_abort ();
+  vw->MoveTo (x, y);
+  vw->ResizeTo (x1 - x, y1 - y);
+  vw->UnlockLooper ();
+}
+
+void
+BView_scroll_bar_update (void *sb, int portion, int whole, int position)
+{
+  BScrollBar *bar = (BScrollBar *) sb;
+
+  if (!bar->LockLooper ())
+    emacs_abort ();
+  bar->SetRange (0, whole);
+  bar->SetValue (position);
+
+  bar->UnlockLooper ();
+}
+
+int
+BScrollBar_default_size (int horizontal_p)
+{
+  return horizontal_p ? B_H_SCROLL_BAR_HEIGHT : B_V_SCROLL_BAR_WIDTH;
+}
+
+void
+BView_hide (void *view)
+{
+  BView *vw = (BView *) view;
+  if (!vw->LockLooper ())
+    emacs_abort ();
+  vw->Hide ();
+  vw->UnlockLooper ();
+}
+
+void
+BView_show (void *view)
+{
+  BView *vw = (BView *) view;
+  if (!vw->LockLooper ())
+    emacs_abort ();
+  vw->Show ();
+  vw->UnlockLooper ();
+}
+
+void
+BView_invalidate (void *view)
+{
+  BView *vw = (BView *) view;
+  if (!vw->LockLooper ())
+    emacs_abort ();
+  vw->Invalidate ();
+  vw->UnlockLooper ();
+}
+
+void
+BView_draw_lock (void *view)
+{
+  EmacsView *vw = (EmacsView *) view;
+  if (vw->looper_locked_count)
+    {
+      vw->looper_locked_count++;
+      return;
+    }
+  if (!vw->LockLooper ())
+    emacs_abort ();
+  vw->looper_locked_count++;
+}
+
+void
+BView_draw_unlock (void *view)
+{
+  EmacsView *vw = (EmacsView *) view;
+  if (--vw->looper_locked_count)
+    return;
+
+  vw->UnlockLooper ();
+}
+
+void
+BWindow_center_on_screen (void *window)
+{
+  BWindow *w = (BWindow *) window;
+  w->CenterOnScreen ();
+}
+
+void
+BView_mouse_down (void *view, int x, int y)
+{
+  BView *vw = (BView *) view;
+  if (vw->LockLooper ())
+    {
+      vw->MouseDown (BPoint (x, y));
+      vw->UnlockLooper ();
+    }
+}
+
+void
+BView_mouse_up (void *view, int x, int y)
+{
+  BView *vw = (BView *) view;
+  if (vw->LockLooper ())
+    {
+      vw->MouseUp (BPoint (x, y));
+      vw->UnlockLooper ();
+    }
+}
+
+void
+BView_mouse_moved (void *view, int x, int y, uint32_t transit)
+{
+  BView *vw = (BView *) view;
+  if (vw->LockLooper ())
+    {
+      vw->MouseMoved (BPoint (x, y), transit, NULL);
+      vw->UnlockLooper ();
+    }
+}
+
+void
+BBitmap_import_mono_bits (void *bitmap, void *bits, int wd, int h)
+{
+  BBitmap *bmp = (BBitmap *) bitmap;
+  unsigned char *data = (unsigned char *) bmp->Bits ();
+  unsigned short *bts = (unsigned short *) bits;
+
+  for (int i = 0; i < h; i++)
+    {
+      *((unsigned short *) data) = bts[i];
+      data += bmp->BytesPerRow ();
+    }
+}
+
+void
+BView_publish_scroll_bar (void *view, int x, int y, int width, int height)
+{
+  EmacsView *vw = (EmacsView *) view;
+  if (vw->LockLooper ())
+    {
+      vw->sb_region.Include (BRect (x, y, x - 1 + width,
+				    y - 1 + height));
+      vw->UnlockLooper ();
+    }
+}
+
+void
+BView_forget_scroll_bar (void *view, int x, int y, int width, int height)
+{
+  EmacsView *vw = (EmacsView *) view;
+  if (vw->LockLooper ())
+    {
+      vw->sb_region.Exclude (BRect (x, y, x - 1 + width,
+				    y - 1 + height));
+      vw->UnlockLooper ();
+    }
+}
+
+int
+BFont_family_count (void)
+{
+  return count_font_families ();
+}
+
+void
+BView_get_mouse (void *view, int *x, int *y)
+{
+  BPoint l;
+  BView *vw = (BView *) view;
+  if (!vw->LockLooper ())
+    emacs_abort ();
+  vw->GetMouse (&l, NULL, 1);
+  vw->UnlockLooper ();
+
+  *x = std::lrint (l.x);
+  *y = std::lrint (l.y);
+}
+
+void
+BView_convert_to_screen (void *view, int *x, int *y)
+{
+  BPoint l = BPoint (*x, *y);
+  BView *vw = (BView *) view;
+  if (!vw->LockLooper ())
+    emacs_abort ();
+  vw->ConvertToScreen (&l);
+  vw->UnlockLooper ();
+
+  *x = std::lrint (l.x);
+  *y = std::lrint (l.y);
+}
+
+void
+BWindow_change_decoration (void *window, int decorate_p)
+{
+  BWindow *w = (BWindow *) window;
+  if (!w->LockLooper ())
+    emacs_abort ();
+  if (decorate_p)
+    w->SetLook (B_TITLED_WINDOW_LOOK);
+  else
+    w->SetLook (B_NO_BORDER_WINDOW_LOOK);
+  w->UnlockLooper ();
+}
+
+void
+BWindow_set_tooltip_decoration (void *window)
+{
+  BWindow *w = (BWindow *) window;
+  if (!w->LockLooper ())
+    emacs_abort ();
+  w->SetLook (B_BORDERED_WINDOW_LOOK);
+  w->UnlockLooper ();
+}
+
+void
+BWindow_set_avoid_focus (void *window, int avoid_focus_p)
+{
+  BWindow *w = (BWindow *) window;
+  if (!w->LockLooper ())
+    emacs_abort ();
+  if (!avoid_focus_p)
+    w->SetFlags (w->Flags () & ~B_AVOID_FOCUS);
+  else
+    w->SetFlags (B_AVOID_FOCUS);
+  w->UnlockLooper ();
+}
+
+void
+BView_emacs_delete (void *view)
+{
+  EmacsView *vw = (EmacsView *) view;
+  if (!vw->LockLooper ())
+    emacs_abort ();
+  vw->RemoveSelf ();
+  delete vw;
+}
+
+uint32_t
+haiku_current_workspace (void)
+{
+  return current_workspace ();
+}
+
+uint32_t
+BWindow_workspaces (void *window)
+{
+  return ((BWindow *) window)->Workspaces ();
+}
+
+void *
+BPopUpMenu_new (const char *name)
+{
+  BPopUpMenu *menu = new BPopUpMenu (name, false);
+  return menu;
+}
+
+void
+BMenu_add_item (void *menu, const char *label, void *ptr, bool enabled_p,
+		bool marked_p)
+{
+  BMenu *m = (BMenu *) menu;
+  BMessage *msg;
+  if (ptr)
+    msg = new BMessage ();
+  BMenuItem *it = new BMenuItem (label, ptr ? msg : NULL);
+  it->SetTarget (m->Window ());
+  it->SetEnabled (enabled_p);
+  it->SetMarked (marked_p);
+  if (ptr)
+    msg->AddPointer ("menuptr", ptr);
+  m->AddItem (it);
+}
+
+void
+BMenu_add_separator (void *menu)
+{
+  BMenu *m = (BMenu *) menu;
+
+  m->AddSeparatorItem ();
+}
+
+void *
+BMenu_new_submenu (void *menu, const char *label, bool enabled_p)
+{
+  BMenu *m = (BMenu *) menu;
+  BMenu *mn = new BMenu (label, B_ITEMS_IN_COLUMN);
+  mn->SetRadioMode (0);
+  BMenuItem *i = new BMenuItem (mn);
+  i->SetEnabled (enabled_p);
+  m->AddItem (i);
+  return mn;
+}
+
+void *
+BMenu_new_menu_bar_submenu (void *menu, const char *label)
+{
+  BMenu *m = (BMenu *) menu;
+  BMenu *mn = new BMenu (label, B_ITEMS_IN_COLUMN);
+  mn->SetRadioMode (0);
+  BMenuItem *i = new BMenuItem (mn);
+  i->SetEnabled (1);
+  m->AddItem (i);
+  return mn;
+}
+
+void *
+BMenu_run (void *menu, int x, int y)
+{
+  BPopUpMenu *mn = (BPopUpMenu *) menu;
+  BMenuItem *it = mn->Go (BPoint (x, y));
+  if (it)
+    {
+      BMessage *mg = it->Message ();
+      if (mg)
+	return (void *) mg->GetPointer ("menuptr");
+      else
+	return NULL;
+    }
+  return NULL;
+}
+
+void
+BPopUpMenu_delete (void *menu)
+{
+  delete (BPopUpMenu *) menu;
+}
+
+void *
+BMenuBar_new (void *view)
+{
+  BView *vw = (BView *) view;
+  EmacsMenuBar *bar = new EmacsMenuBar ();
+
+  if (!vw->LockLooper ())
+    emacs_abort ();
+  vw->AddChild ((BView *) bar);
+  vw->UnlockLooper ();
+
+  return bar;
+}
+
+void
+BMenuBar_delete (void *menubar)
+{
+  BView *vw = (BView *) menubar;
+  BView *p = vw->Parent ();
+  if (!p->LockLooper ())
+    emacs_abort ();
+  vw->RemoveSelf ();
+  p->UnlockLooper ();
+  delete vw;
+}
+
+void
+BMenu_delete_all (void *menu)
+{
+  BMenu *mn = (BMenu *) menu;
+  mn->RemoveItems (0, mn->CountItems (), true);
+}
+
+void
+BMenu_delete_from (void *menu, int start, int count)
+{
+  BMenu *mn = (BMenu *) menu;
+  mn->RemoveItems (start, count, true);
+}
+
+int
+BMenu_count_items (void *menu)
+{
+  return ((BMenu *) menu)->CountItems ();
+}
+
+void *
+BMenu_item_at (void *menu, int idx)
+{
+  return ((BMenu *) menu)->ItemAt (idx);
+}
+
+void
+BMenu_item_set_label (void *item, const char *label)
+{
+  ((BMenuItem *) item)->SetLabel (label);
+}
+
+void *
+BMenu_item_get_menu (void *item)
+{
+  return ((BMenuItem *) item)->Submenu ();
+}
+
+void
+haiku_ring_bell (void)
+{
+  beep ();
+}
+
+void *
+BAlert_new (const char *text, enum haiku_alert_type type)
+{
+  return new BAlert (NULL, text, NULL, NULL, NULL, B_WIDTH_AS_USUAL,
+		     (enum alert_type) type);
+}
+
+void *
+BAlert_add_button (void *alert, const char *text)
+{
+  BAlert *al = (BAlert *) alert;
+  al->AddButton (text);
+  return al->ButtonAt (al->CountButtons () - 1);
+}
+
+int32_t
+BAlert_go (void *alert)
+{
+  return ((BAlert *) alert)->Go ();
+}
+
+void
+BButton_set_enabled (void *button, int enabled_p)
+{
+  ((BButton *) button)->SetEnabled (enabled_p);
+}
+
+void
+BView_set_tooltip (void *view, const char *tooltip)
+{
+  ((BView *) view)->SetToolTip (tooltip);
+}
+
+void
+BAlert_delete (void *alert)
+{
+  delete (BAlert *) alert;
+}
diff --git a/src/haiku_support.h b/src/haiku_support.h
new file mode 100644
index 0000000000..e4161e8714
--- /dev/null
+++ b/src/haiku_support.h
@@ -0,0 +1,756 @@
+/* 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>
+
+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
+  };
+
+struct haiku_quit_requested_event
+{
+  void *window;
+};
+
+struct haiku_resize_event
+{
+  void *window;
+  float px_heightf;
+  float px_widthf;
+};
+
+struct haiku_expose_event
+{
+  void *window;
+  int x;
+  int y;
+  int width;
+  int height;
+};
+
+#define HAIKU_MODIFIER_ALT (1)
+#define HAIKU_MODIFIER_CTRL (1 << 1)
+#define HAIKU_MODIFIER_SHIFT (1 << 2)
+
+struct haiku_key_event
+{
+  void *window;
+  int modifiers;
+  uint32_t mb_char;
+  uint32_t unraw_mb_char;
+  short kc;
+};
+
+struct haiku_activation_event
+{
+  void *window;
+  int activated_p;
+};
+
+struct haiku_mouse_motion_event
+{
+  void *window;
+  bool just_exited_p;
+  int x;
+  int y;
+  uint32_t be_code;
+};
+
+struct haiku_button_event
+{
+  void *window;
+  int btn_no;
+  int modifiers;
+  int x;
+  int y;
+};
+
+struct haiku_iconification_event
+{
+  void *window;
+  int iconified_p;
+};
+
+struct haiku_move_event
+{
+  void *window;
+  int x;
+  int y;
+};
+
+struct haiku_wheel_move_event
+{
+  void *window;
+  int modifiers;
+  float delta_x;
+  float delta_y;
+};
+
+struct haiku_menu_bar_select_event
+{
+  void *window;
+  void *ptr;
+};
+
+#define FSPEC_FAMILY 1
+#define FSPEC_STYLE (1 << 1)
+#define FSPEC_SLANT (1 << 2)
+#define FSPEC_WEIGHT (1 << 3)
+#define FSPEC_SPACING (1 << 4)
+
+typedef char haiku_font_family_or_style[64];
+
+enum haiku_font_slant
+  {
+    SLANT_OBLIQUE,
+    SLANT_REGULAR,
+    SLANT_ITALIC
+  };
+
+struct haiku_font_pattern
+{
+  int specified;
+  struct haiku_font_pattern *next;
+  haiku_font_family_or_style family;
+  haiku_font_family_or_style style;
+  enum haiku_font_slant slant;
+  int weight;
+  int mono_spacing_p;
+};
+
+struct haiku_scroll_bar_value_event
+{
+  void *scroll_bar;
+  int position;
+};
+
+struct haiku_scroll_bar_drag_event
+{
+  void *scroll_bar;
+  int dragging_p;
+};
+
+struct haiku_menu_bar_resize_event
+{
+  void *window;
+  int width;
+  int height;
+};
+
+struct haiku_menu_bar_state_event
+{
+  void *window;
+};
+
+#define HAIKU_THIN 0
+#define HAIKU_ULTRALIGHT 20
+#define HAIKU_EXTRALIGHT 40
+#define HAIKU_LIGHT 50
+#define HAIKU_SEMI_LIGHT 75
+#define HAIKU_REGULAR 100
+#define HAIKU_SEMI_BOLD 180
+#define HAIKU_BOLD 200
+#define HAIKU_EXTRA_BOLD 205
+#define HAIKU_ULTRA_BOLD 210
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+#include <pthread.h>
+#include <OS.h>
+  /* The port used to send messages from Emacs to the application
+     thread. */
+  extern port_id port_emacs_to_application;
+  /* The port used to send messages from the application thread to
+     Emacs. */
+  extern port_id port_application_to_emacs;
+
+  extern void haiku_io_init (void);
+  extern void haiku_io_init_in_app_thread (void);
+
+  /* Read the size of the next message into len, returning -1 if the
+     query fails or there is no next message */
+  extern void
+  haiku_read_size (ssize_t *len);
+
+  /* Read the next message into buf, putting its type into type,
+     assuming the message is len long.  Return 0 if successful and
+     -1 if the read fails. */
+  extern int
+  haiku_read (enum haiku_event_type *type, void *buf, ssize_t len);
+
+  /* Write a message with type type into buf. */
+  extern int
+  haiku_write (enum haiku_event_type type, void *buf);
+
+  /* Convert RGB32 color color from RGB color space to its
+     HSL components pointed to by h, s and l. */
+  extern void
+  rgb_color_hsl (uint32_t rgb, double *h, double *s, double *l);
+
+  /* Ditto, but the other way round */
+  extern void
+  hsl_color_rgb (double h, double s, double l, uint32_t *rgb);
+
+  /* Create new bitmap in RGB32 format */
+  extern void *
+  BBitmap_new (int width, int height, int mono_p);
+
+  /* Take bitmap, a reference to a BBitmap, and return a pointer to
+     its data, in RGB32 format */
+  extern void *
+  BBitmap_data (void *bitmap);
+
+  /* Convert bitmap if required, placing the new bitmap in new_bitmap,
+     and return non-null if bitmap was successfully converted.
+     Bitmaps should be freed with `BBitmap_free' */
+  extern int
+  BBitmap_convert (void *bitmap, void **new_bitmap);
+
+  /* Delete bitmap */
+  extern void
+  BBitmap_free (void *bitmap);
+
+  /* Retrieve the dimensions of bitmap */
+  extern void
+  BBitmap_dimensions (void *bitmap, int *left, int *top,
+		      int *right, int *bottom, int32_t *bytes_per_row,
+		      int *mono_p);
+
+  /* Set up an application and return it.  If starting the application
+     thread fails, abort Emacs */
+  extern void *
+  BApplication_setup (void);
+
+  /* Set up and return a window with its view put in VIEW */
+  extern void *
+  BWindow_new (void *view);
+
+  /* Delete a window */
+  extern void
+  BWindow_quit (void *window);
+
+  /* Set window offset to X, Y */
+  extern void
+  BWindow_set_offset (void *window, int x, int y);
+
+  /* Iconify window */
+  extern void
+  BWindow_iconify (void *window);
+
+  /* Show or hide window */
+  extern void
+  BWindow_set_visible (void *window, int visible_p);
+
+  /* Open the default monospace font */
+  extern void *
+  BFont_open_default (double size, int plain_or_bold_or_fixed);
+
+  /* Close a font */
+  extern void
+  BFont_close (void *font);
+
+  /* Compute font data */
+  extern void
+  BFont_dat (void *font, int *px_size, int *min_width, int *max_width,
+	     int *avg_width, int *height, int *space_width, int *ascent,
+	     int *descent, int *underline_position, int *underline_thickness);
+
+  /* Return non-null if font contains chr, a Unicode code-point */
+  extern int
+  BFont_have_char_p (void *font, int32_t chr);
+
+  /* Compute bounds for mb_str, a character in multibyte encoding,
+     used with font.  The width (in pixels) is returned in advance,
+     the left bearing in lb, and the right bearing in rb */
+  extern void
+  BFont_char_bounds (void *font, const char *mb_str, int *advance,
+		     int *lb, int *rb);
+
+  /* Change the title of window to the multibyte string title */
+  extern void
+  BWindow_retitle (void *window, const char *title);
+
+  /* Resize window to width by height */
+  extern void
+  BWindow_resize (void *window, int width, int height);
+
+  /* Activate window */
+  extern void
+  BWindow_activate (void *window);
+
+  /* Drawing functions */
+  extern void
+  BView_StartClip (void *view);
+
+  extern void
+  BView_EndClip (void *view);
+
+  extern void
+  BView_SetHighColor (void *view, uint32_t color);
+
+  extern void
+  BView_SetPenSize (void *view, int u);
+
+  extern void
+  BView_SetFont (void *view, void *font);
+
+  extern void
+  BView_MovePenTo (void *view, int x, int y);
+
+  extern void
+  BView_DrawString (void *view, const char *chr, ptrdiff_t len);
+
+  extern void
+  BView_DrawChar (void *view, char chr);
+
+  extern void
+  BView_Draw (void *view, int x, int y, int width, int height);
+
+  extern void
+  BView_FillRectangle (void *view, int x, int y, int width, int height);
+
+  extern void
+  BView_FillRectangleAbs (void *view, int x, int y, int x1, int y1);
+
+  extern void
+  BView_StrokeRectangle (void *view, int x, int y, int width, int height);
+
+  extern void
+  BView_SetViewColor (void *view, uint32_t color);
+
+  extern void
+  BView_ClipToRect (void *view, int x, int y, int width, int height);
+
+  extern void
+  BView_ClipToInverseRect (void *view, int x, int y, int width, int height);
+
+  extern void
+  BView_StrokeLine (void *view, int sx, int sy, int tx, int ty);
+
+  extern void
+  BView_CopyBits (void *view, int x, int y, int width, int height,
+		  int tox, int toy, int towidth, int toheight);
+
+  extern void
+  BView_DrawBitmap (void *view, void *bitmap, int x, int y,
+		    int width, int height, int vx, int vy, int vwidth,
+		    int vheight);
+  extern void
+  BView_DrawBitmapWithEraseOp (void *view, void *bitmap, int x, int y,
+			       int width, int height, int vx, int vy, int vwidth,
+			       int vheight);
+
+  extern void
+  BView_DrawBitmapWithOverOp (void *view, void *bitmap,
+			      int x, int y, int width, int height);
+
+  extern void
+  BView_BeginLayer (void *view, float opacity);
+
+  extern void
+  BView_EndLayer (void *view);
+
+  /* Return the pixel dimensions of the main screen in width and height */
+  extern void
+  BScreen_px_dim (int *width, int *height);
+
+  /* Resize view to width, height */
+  extern void
+  BView_resize_to (void *view, int width, int height);
+
+  /* Functions for creating and freeing cursors */
+  extern void *
+  BCursor_create_default (void);
+
+  extern void *
+  BCursor_from_id (enum haiku_cursor cursor);
+
+  extern void *
+  BCursor_create_i_beam (void);
+
+  extern void *
+  BCursor_create_progress_cursor (void);
+
+  extern void *
+  BCursor_create_grab (void);
+
+  /* Delete cursor */
+  extern void
+  BCursor_delete (void *cursor);
+
+  /* Set view's cursor to cursor */
+  extern void
+  BView_set_view_cursor (void *view, void *cursor);
+
+  /* Flush window's connection to the App Server */
+  extern void
+  BWindow_Flush (void *window);
+
+  /* Map the keycode kc, storing the result in code and 1 in
+     non_ascii_p if it should be used */
+  extern void
+  BMapKey (uint32_t kc, int *non_ascii_p, unsigned *code);
+
+  /* Make a scrollbar, attach it to view's window, and return it */
+  extern void *
+  BScrollBar_make_for_view (void *view, int horizontal_p,
+			    int x, int y, int x1, int y1,
+			    void *scroll_bar_ptr);
+
+  /* Delete sb, a scrollbar */
+  extern void
+  BScrollBar_delete (void *sb);
+
+  /* Move view's frame to x, y, x1, y1 */
+  extern void
+  BView_move_frame (void *view, int x, int y, int x1, int y1);
+
+  /* Update sb with portion, whole and position */
+  extern void
+  BView_scroll_bar_update (void *sb, int portion, int whole, int position);
+
+  /* Return the default scrollbar size  */
+  extern int
+  BScrollBar_default_size (int horizontal_p);
+
+  /* Make view invisible */
+  extern void
+  BView_hide (void *view);
+
+  /* Make view visible */
+  extern void
+  BView_show (void *view);
+
+  /* Invalidate view */
+  extern void
+  BView_invalidate (void *view);
+
+  /* Lock view.  This is usually faster than calling LockLooper
+     directly. */
+  extern void
+  BView_draw_lock (void *view);
+
+  /* Ditto, but unlock instead. */
+  extern void
+  BView_draw_unlock (void *view);
+
+  /* Center window on its screen */
+  extern void
+  BWindow_center_on_screen (void *window);
+
+  /* Tell view that the mouse has moved to x by y */
+  extern void
+  BView_mouse_moved (void *view, int x, int y, uint32_t transit);
+
+  /* Tell view it has been clicked at x by y */
+  extern void
+  BView_mouse_down (void *view, int x, int y);
+
+  /* Tell view it has been released at x by y */
+  extern void
+  BView_mouse_up (void *view, int x, int y);
+
+  /* Import bits into bitmap using the B_GRAY1 colorspace */
+  extern void
+  BBitmap_import_mono_bits (void *bitmap, void *bits, int wd, int h);
+
+  /* Return the amount of font families */
+  extern int
+  BFont_family_count (void);
+
+  /* Query the font family at IDX, returning non-0 on failure */
+  extern int
+  BFont_family (int idx, char *f, int *fixed_p);
+
+  /* Return non-0 if the family contains style */
+  extern int
+  BFont_family_has_style_p (haiku_font_family_or_style family,
+			    haiku_font_family_or_style style);
+
+  /* Create a font */
+  extern void *
+  BFont_new (void);
+
+  /* Set font's family and style to FAMILY and STYLE respectively,
+     returning non-0 on failure.  */
+  extern int
+  BFont_set_family_and_style (void *font, haiku_font_family_or_style family,
+			      haiku_font_family_or_style style);
+
+  /* Set FONT's family along with its ITALIC and BOLD faces */
+  extern int
+  BFont_set_family_face (void *font, haiku_font_family_or_style family,
+			 int italic_p, int bold_p);
+
+  /* Delete every element of the font pattern PT */
+  extern void
+  haiku_font_pattern_free (struct haiku_font_pattern *pt);
+
+  /* Find all fonts matching the font pattern PT */
+  extern struct haiku_font_pattern *
+  BFont_find (struct haiku_font_pattern *pt);
+
+  /* Find and open a font matching the pattern PAT, which must have
+     its family set */
+  extern int
+  BFont_open_pattern (struct haiku_font_pattern *pat, void **font, float size);
+
+  /* Query the family of the default fixed font */
+  extern void
+  BFont_populate_fixed_family (struct haiku_font_pattern *ptn);
+
+  /* Ditto, but with the plain family */
+  extern void
+  BFont_populate_plain_family (struct haiku_font_pattern *ptn);
+
+  /* Make a scrollbar at x, y known to the view */
+  extern void
+  BView_publish_scroll_bar (void *view, int x, int y, int width, int height);
+
+  /* Forget the scrollbar at x, y by width, height */
+  extern void
+  BView_forget_scroll_bar (void *view, int x, int y, int width, int height);
+
+  /* Place the current coordinates of the mouse, relative to view, at x and y */
+  extern void
+  BView_get_mouse (void *view, int *x, int *y);
+
+  /* Perform an in-place conversion of x and y from view's coordinate
+     system to its screen's coordinate system */
+  extern void
+  BView_convert_to_screen (void *view, int *x, int *y);
+
+  /* Decorate or undecorate window depending on decorate_p */
+  extern void
+  BWindow_change_decoration (void *window, int decorate_p);
+
+  /* Decorate window appropriately for use as a tooltip */
+  extern void
+  BWindow_set_tooltip_decoration (void *window);
+
+  /* Set B_AVOID_FOCUS on window if avoid_focus_p is non-nil, or clear
+     it otherwise */
+  extern void
+  BWindow_set_avoid_focus (void *window, int avoid_focus_p);
+
+  /* Delete the frame view view */
+  extern void
+  BView_emacs_delete (void *view);
+
+  /* Begin a view transaction, during which drawing operations will
+     not be displayed */
+  extern void
+  BView_BeginTransaction (void *view);
+
+  /* End a view transaction and flush the view */
+  extern void
+  BView_EndTransaction (void *view);
+
+  /* Return the current workspace */
+  extern uint32_t
+  haiku_current_workspace (void);
+
+  /* Return a bitmask consisting of workspaces window is on */
+  extern uint32_t
+  BWindow_workspaces (void *window);
+
+  /* Create a popup menu */
+  extern void *
+  BPopUpMenu_new (const char *name);
+
+  /* Add an item to the menu */
+  extern void
+  BMenu_add_item (void *menu, const char *label, void *ptr, bool enabled_p,
+		  bool marked_p);
+
+  /* Add a separator to the menu */
+  extern void
+  BMenu_add_separator (void *menu);
+
+  /* Create a submenu and attach it to menu */
+  extern void *
+  BMenu_new_submenu (void *menu, const char *label, bool enabled_p);
+
+  /* Create a submenu that notifies Emacs upon opening */
+  extern void *
+  BMenu_new_menu_bar_submenu (void *menu, const char *label);
+
+  /* Count items in menu */
+  extern int
+  BMenu_count_items (void *menu);
+
+  /* Find the menu item at idx */
+  extern void *
+  BMenu_item_at (void *menu, int idx);
+
+  /* Run menu, waiting for it to close, and returning a pointer to the
+     data of the selected item (if one exists), or nil.  X, Y should
+     be in the screen coordinate system */
+  extern void *
+  BMenu_run (void *menu, int x, int y);
+
+  /* Delete the entire menu hierarchy of menu, and then delete menu
+     itself */
+  extern void
+  BPopUpMenu_delete (void *menu);
+
+  /* Create a menubar, attach it to view, and return it */
+  extern void *
+  BMenuBar_new (void *view);
+
+  /* Delete all items from menu */
+  extern void
+  BMenu_delete_all (void *menu);
+
+  /* Delete menubar along with all subitems */
+  extern void
+  BMenuBar_delete (void *menubar);
+
+  /* Set item's label to label */
+  extern void
+  BMenu_item_set_label (void *item, const char *label);
+
+  /* Get item's menu */
+  extern void *
+  BMenu_item_get_menu (void *item);
+
+  /* Delete count items from menu starting from start */
+  extern void
+  BMenu_delete_from (void *menu, int start, int count);
+
+  /* Emit a beep noise */
+  extern void
+  haiku_ring_bell (void);
+
+  /* Create a BAlert with text */
+  extern void *
+  BAlert_new (const char *text, enum haiku_alert_type type);
+
+  /* Add a button to alert and return the button */
+  extern void *
+  BAlert_add_button (void *alert, const char *text);
+
+  /* Run the alert, returning the number of the button that was
+     selected, or -1 if no button was selected before the alert was
+     closed */
+  extern int32_t
+  BAlert_go (void *alert);
+
+  /* Enable or disable button depending on enabled_p */
+  extern void
+  BButton_set_enabled (void *button, int enabled_p);
+
+  /* Set view's tooltip to tooltip */
+  extern void
+  BView_set_tooltip (void *view, const char *tooltip);
+
+  /* Delete alert */
+  extern void
+  BAlert_delete (void *alert);
+#ifdef __cplusplus
+}
+#endif /* _cplusplus */
+
+/* Borrowed from X.Org keysymdef.h */
+#define XK_BackSpace                     0xff08  /* Back space, back char */
+#define XK_Tab                           0xff09
+#define XK_Linefeed                      0xff0a  /* Linefeed, LF */
+#define XK_Clear                         0xff0b
+#define XK_Return                        0xff0d  /* Return, enter */
+#define XK_Pause                         0xff13  /* Pause, hold */
+#define XK_Scroll_Lock                   0xff14
+#define XK_Sys_Req                       0xff15
+#define XK_Escape                        0xff1b
+#define XK_Delete                        0xffff  /* Delete, rubout */
+#define XK_Home                          0xff50
+#define XK_Left                          0xff51  /* Move left, left arrow */
+#define XK_Up                            0xff52  /* Move up, up arrow */
+#define XK_Right                         0xff53  /* Move right, right arrow */
+#define XK_Down                          0xff54  /* Move down, down arrow */
+#define XK_Prior                         0xff55  /* Prior, previous */
+#define XK_Page_Up                       0xff55
+#define XK_Next                          0xff56  /* Next */
+#define XK_Page_Down                     0xff56
+#define XK_End                           0xff57  /* EOL */
+#define XK_Begin                         0xff58  /* BOL */
+#define XK_Select                        0xff60  /* Select, mark */
+#define XK_Print                         0xff61
+#define XK_Execute                       0xff62  /* Execute, run, do */
+#define XK_Insert                        0xff63  /* Insert, insert here */
+#define XK_Undo                          0xff65
+#define XK_Redo                          0xff66  /* Redo, again */
+#define XK_Menu                          0xff67
+#define XK_Find                          0xff68  /* Find, search */
+#define XK_Cancel                        0xff69  /* Cancel, stop, abort, exit */
+#define XK_Help                          0xff6a  /* Help */
+#define XK_Break                         0xff6b
+#define XK_Mode_switch                   0xff7e  /* Character set switch */
+#define XK_script_switch                 0xff7e  /* Alias for mode_switch */
+#define XK_Num_Lock                      0xff7f
+#define XK_F1                            0xffbe
+
+#endif /* _HAIKU_SUPPORT_H_ */
diff --git a/src/haikufns.c b/src/haikufns.c
new file mode 100644
index 0000000000..4bf2c091d3
--- /dev/null
+++ b/src/haikufns.c
@@ -0,0 +1,1769 @@
+/* Haiku window system support
+   Copyright (C) 2021 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+#include <math.h>
+
+#include "lisp.h"
+#include "frame.h"
+#include "blockinput.h"
+#include "termchar.h"
+#include "font.h"
+#include "keyboard.h"
+#include "buffer.h"
+#include "dispextern.h"
+
+#include "haikugui.h"
+#include "haikuterm.h"
+#include "haiku_support.h"
+#include "termhooks.h"
+
+#define RGB_TO_ULONG(r, g, b) \
+  (((r) << 16) | ((g) << 8) | (b));
+#define RED_FROM_ULONG(color)	(((color) >> 16) & 0xff)
+#define GREEN_FROM_ULONG(color)	(((color) >> 8) & 0xff)
+#define BLUE_FROM_ULONG(color)	((color) & 0xff)
+
+/* The frame of the currently visible tooltip.  */
+static Lisp_Object tip_frame;
+
+/* The window-system window corresponding to the frame of the
+   currently visible tooltip.  */
+static Window tip_window;
+
+/* A timer that hides or deletes the currently visible tooltip when it
+   fires.  */
+static Lisp_Object tip_timer;
+
+/* STRING argument of last `x-show-tip' call.  */
+static Lisp_Object tip_last_string;
+
+/* Normalized FRAME argument of last `x-show-tip' call.  */
+static Lisp_Object tip_last_frame;
+
+/* PARMS argument of last `x-show-tip' call.  */
+static Lisp_Object tip_last_parms;
+
+static void
+haiku_explicitly_set_name (struct frame *f, Lisp_Object arg, Lisp_Object oldval);
+static void
+haiku_set_title (struct frame *f, Lisp_Object name, Lisp_Object old_name);
+
+static ptrdiff_t image_cache_refcount;
+
+static Lisp_Object
+get_geometry_from_preferences (struct haiku_display_info *dpyinfo,
+                               Lisp_Object parms)
+{
+  struct {
+    const char *val;
+    const char *cls;
+    Lisp_Object tem;
+  } r[] = {
+    { "width",  "Width", Qwidth },
+    { "height", "Height", Qheight },
+    { "left", "Left", Qleft },
+    { "top", "Top", Qtop },
+  };
+
+  int i;
+  for (i = 0; i < ARRAYELTS (r); ++i)
+    {
+      if (NILP (Fassq (r[i].tem, parms)))
+        {
+          Lisp_Object value
+            = gui_display_get_arg (dpyinfo, parms, r[i].tem, r[i].val, r[i].cls,
+                                   RES_TYPE_NUMBER);
+          if (! EQ (value, Qunbound))
+            parms = Fcons (Fcons (r[i].tem, value), parms);
+        }
+    }
+
+  return parms;
+}
+
+void
+haiku_change_tool_bar_height (struct frame *f, int height)
+{
+  int unit = FRAME_LINE_HEIGHT (f);
+  int old_height = FRAME_TOOL_BAR_HEIGHT (f);
+  int lines = (height + unit - 1) / unit;
+  Lisp_Object fullscreen = get_frame_param (f, Qfullscreen);
+
+  /* Make sure we redisplay all windows in this frame.  */
+  fset_redisplay (f);
+
+  FRAME_TOOL_BAR_HEIGHT (f) = height;
+  FRAME_TOOL_BAR_LINES (f) = lines;
+  store_frame_param (f, Qtool_bar_lines, make_fixnum (lines));
+
+  if (FRAME_HAIKU_WINDOW (f) && FRAME_TOOL_BAR_HEIGHT (f) == 0)
+    {
+      clear_frame (f);
+      clear_current_matrices (f);
+    }
+
+  if ((height < old_height) && WINDOWP (f->tool_bar_window))
+    clear_glyph_matrix (XWINDOW (f->tool_bar_window)->current_matrix);
+
+  if (!f->tool_bar_resized)
+    {
+      /* As long as tool_bar_resized is false, effectively try to change
+	 F's native height.  */
+      if (NILP (fullscreen) || EQ (fullscreen, Qfullwidth))
+	adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
+			   1, false, Qtool_bar_lines);
+      else
+	adjust_frame_size (f, -1, -1, 4, false, Qtool_bar_lines);
+
+      f->tool_bar_resized =  f->tool_bar_redisplayed;
+    }
+  else
+    /* Any other change may leave the native size of F alone.  */
+    adjust_frame_size (f, -1, -1, 3, false, Qtool_bar_lines);
+
+  /* adjust_frame_size might not have done anything, garbage frame
+     here.  */
+  adjust_frame_glyphs (f);
+  SET_FRAME_GARBAGED (f);
+
+  if (FRAME_HAIKU_WINDOW (f))
+    haiku_clear_under_internal_border (f);
+}
+
+void
+haiku_change_tab_bar_height (struct frame *f, int height)
+{
+  int unit = FRAME_LINE_HEIGHT (f);
+  int old_height = FRAME_TAB_BAR_HEIGHT (f);
+  int lines = (height + unit - 1) / unit;
+  Lisp_Object fullscreen = get_frame_param (f, Qfullscreen);
+
+  /* Make sure we redisplay all windows in this frame.  */
+  fset_redisplay (f);
+
+  /* Recalculate tab bar and frame text sizes.  */
+  FRAME_TAB_BAR_HEIGHT (f) = height;
+  FRAME_TAB_BAR_LINES (f) = lines;
+  store_frame_param (f, Qtab_bar_lines, make_fixnum (lines));
+
+  if (FRAME_HAIKU_WINDOW (f) && FRAME_TAB_BAR_HEIGHT (f) == 0)
+    {
+      clear_frame (f);
+      clear_current_matrices (f);
+    }
+
+  if ((height < old_height) && WINDOWP (f->tab_bar_window))
+    clear_glyph_matrix (XWINDOW (f->tab_bar_window)->current_matrix);
+
+  if (!f->tab_bar_resized)
+    {
+      /* As long as tab_bar_resized is false, effectively try to change
+	 F's native height.  */
+      if (NILP (fullscreen) || EQ (fullscreen, Qfullwidth))
+	adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
+			   1, false, Qtab_bar_lines);
+      else
+	adjust_frame_size (f, -1, -1, 4, false, Qtab_bar_lines);
+
+      f->tab_bar_resized = f->tab_bar_redisplayed;
+    }
+  else
+    /* Any other change may leave the native size of F alone.  */
+    adjust_frame_size (f, -1, -1, 3, false, Qtab_bar_lines);
+
+  /* adjust_frame_size might not have done anything, garbage frame
+     here.  */
+  adjust_frame_glyphs (f);
+  SET_FRAME_GARBAGED (f);
+  if (FRAME_HAIKU_WINDOW (f))
+    haiku_clear_under_internal_border (f);
+}
+
+static void
+haiku_set_tool_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
+{
+  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)
+{
+  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) && !strcasecmp (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_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 Lisp_Object
+haiku_create_frame (Lisp_Object parms, int ttip_p)
+{
+  struct frame *f;
+  Lisp_Object frame, tem;
+  Lisp_Object name;
+  bool minibuffer_only = false;
+  long window_prompting = 0;
+  ptrdiff_t count = SPECPDL_INDEX ();
+  Lisp_Object display;
+  struct haiku_display_info *dpyinfo = NULL;
+  Lisp_Object parent_frame;
+  Lisp_Object font;
+  struct kboard *kb;
+
+  parms = Fcopy_alist (parms);
+
+  Vx_resource_name = Vinvocation_name;
+
+  display = gui_display_get_arg (dpyinfo, parms, Qterminal, 0, 0,
+                                 RES_TYPE_STRING);
+  if (EQ (display, Qunbound))
+    display = Qnil;
+  dpyinfo = check_haiku_display_info (display);
+  kb = dpyinfo->terminal->kboard;
+
+  if (!dpyinfo->terminal->name)
+    error ("Terminal is not live, can't create new frames on it");
+
+  name = gui_display_get_arg (dpyinfo, parms, Qname, 0, 0,
+                              RES_TYPE_STRING);
+  if (!STRINGP (name)
+      && ! EQ (name, Qunbound)
+      && ! NILP (name))
+    error ("Invalid frame name--not a string or nil");
+
+  if (STRINGP (name))
+    Vx_resource_name = name;
+
+  block_input ();
+
+  /* make_frame_without_minibuffer can run Lisp code and garbage collect.  */
+  /* No need to protect DISPLAY because that's not used after passing
+     it to make_frame_without_minibuffer.  */
+  frame = Qnil;
+  tem = gui_display_get_arg (dpyinfo, parms, Qminibuffer,
+                             "minibuffer", "Minibuffer",
+                             RES_TYPE_SYMBOL);
+  if (EQ (tem, Qnone) || NILP (tem))
+      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);
+
+  fset_icon_name (f, gui_display_get_arg (dpyinfo, parms, Qicon_name,
+                                          "iconName", "Title",
+                                          RES_TYPE_STRING));
+  if (! STRINGP (f->icon_name))
+    fset_icon_name (f, Qnil);
+
+  FRAME_DISPLAY_INFO (f) = dpyinfo;
+
+  /* With FRAME_DISPLAY_INFO set up, this unwind-protect is safe.  */
+  if (!ttip_p)
+    record_unwind_protect (unwind_create_frame, frame);
+  else
+    record_unwind_protect (unwind_create_tip_frame, frame);
+
+  FRAME_OUTPUT_DATA (f)->parent_desc = NULL;
+  FRAME_OUTPUT_DATA (f)->explicit_parent = 0;
+
+  /* Set the name; the functions to which we pass f expect the name to
+     be set.  */
+  if (EQ (name, Qunbound) || NILP (name) || ! STRINGP (name))
+    {
+      fset_name (f, Vinvocation_name);
+      f->explicit_name = 0;
+    }
+  else
+    {
+      fset_name (f, name);
+      f->explicit_name = 1;
+      specbind (Qx_resource_name, name);
+    }
+
+  register_font_driver (&haikufont_driver, f);
+
+  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);
+  Lisp_Object font_param = gui_display_get_arg (dpyinfo,
+                                                parms, Qfont, NULL, NULL,
+                                                RES_TYPE_STRING);
+  if (EQ (font_param, Qunbound))
+    font_param = Qnil;
+  font = !NILP (font_param) ? font_param
+    : gui_display_get_arg (dpyinfo, parms, Qfont, "font", "Font",
+                           RES_TYPE_STRING);
+
+  font = font_open_by_name (f, STRINGP (font) ? font : build_string
+			    (ttip_p ? "Sans Serif" : "monospace"));
+
+
+  if (!NILP (font_param))
+    gui_set_frame_parameters (f, Fcons (Fcons (Qfont_parameter, font_param),
+					Qnil));
+
+  gui_default_parameter (f, parms, Qfont, font, "font", "Font", RES_TYPE_STRING);
+
+  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);
+
+  tem = gui_display_get_arg (dpyinfo, parms, Qundecorated, NULL, NULL,
+                             RES_TYPE_BOOLEAN);
+  FRAME_UNDECORATED (f) = !NILP (tem) && !EQ (tem, Qunbound);
+  store_frame_param (f, Qundecorated, FRAME_UNDECORATED (f) ? Qt : Qnil);
+
+  parent_frame = gui_display_get_arg (dpyinfo, parms, Qparent_frame, NULL, NULL,
+                                      RES_TYPE_SYMBOL);
+
+   if (!NILP (parent_frame)
+      || 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);
+
+  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);
+  if (!ttip_p)
+    gui_default_parameter (f, parms, Qno_accept_focus, Qnil,
+			   NULL, NULL, RES_TYPE_BOOLEAN);
+
+  /* The resources controlling the menu-bar and tool-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) || ttip_p
+                         ? make_fixnum (0) : make_fixnum (1),
+                         NULL, NULL, RES_TYPE_NUMBER);
+
+  gui_default_parameter (f, parms, Qtool_bar_lines,
+                         NILP (Vtool_bar_mode) || ttip_p
+                         ? 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;
+
+      f->tooltip = 1;
+    }
+
+  tem = gui_display_get_arg (dpyinfo, parms, Qunsplittable, 0, 0,
+                             RES_TYPE_BOOLEAN);
+  f->no_split = minibuffer_only || (!EQ (tem, Qunbound) && !NILP (tem));
+
+  /* Add `tooltip' frame parameter's default value. */
+  if (NILP (Fframe_parameter (frame, Qtooltip)) && ttip_p)
+    Fmodify_frame_parameters (frame, Fcons (Fcons (Qtooltip, Qt), Qnil));
+
+#define ASSIGN_CURSOR(cursor, be_cursor) \
+  (FRAME_OUTPUT_DATA (f)->cursor = be_cursor)
+
+  ASSIGN_CURSOR (text_cursor, BCursor_create_i_beam ());
+  ASSIGN_CURSOR (nontext_cursor, BCursor_create_default ());
+  ASSIGN_CURSOR (modeline_cursor, BCursor_create_default ());
+  ASSIGN_CURSOR (hand_cursor, BCursor_create_grab ());
+  ASSIGN_CURSOR (hourglass_cursor, BCursor_create_progress_cursor ());
+  ASSIGN_CURSOR (horizontal_drag_cursor,
+		 BCursor_from_id (CURSOR_ID_RESIZE_EAST_WEST));
+  ASSIGN_CURSOR (vertical_drag_cursor,
+		 BCursor_from_id (CURSOR_ID_RESIZE_NORTH_SOUTH));
+  ASSIGN_CURSOR (left_edge_cursor,
+		 BCursor_from_id (CURSOR_ID_RESIZE_WEST));
+  ASSIGN_CURSOR (top_left_corner_cursor,
+		 BCursor_from_id (CURSOR_ID_RESIZE_NORTH_WEST));
+  ASSIGN_CURSOR (top_edge_cursor,
+		 BCursor_from_id (CURSOR_ID_RESIZE_NORTH));
+  ASSIGN_CURSOR (top_right_corner_cursor,
+		 BCursor_from_id (CURSOR_ID_RESIZE_NORTH_EAST));
+  ASSIGN_CURSOR (right_edge_cursor,
+		 BCursor_from_id (CURSOR_ID_RESIZE_EAST));
+  ASSIGN_CURSOR (bottom_right_corner_cursor,
+		 BCursor_from_id (CURSOR_ID_RESIZE_SOUTH_EAST));
+  ASSIGN_CURSOR (bottom_edge_cursor,
+		 BCursor_from_id (CURSOR_ID_RESIZE_SOUTH));
+  ASSIGN_CURSOR (bottom_left_corner_cursor,
+		 BCursor_from_id (CURSOR_ID_RESIZE_SOUTH_WEST));
+  ASSIGN_CURSOR (no_cursor,
+		 BCursor_from_id (CURSOR_ID_NO_CURSOR));
+
+  ASSIGN_CURSOR (current_cursor, FRAME_OUTPUT_DATA (f)->text_cursor);
+#undef ASSIGN_CURSOR
+
+
+  if (ttip_p)
+    f->no_split = true;
+  f->terminal->reference_count++;
+
+  FRAME_OUTPUT_DATA (f)->window = BWindow_new (&FRAME_OUTPUT_DATA (f)->view);
+  if (!FRAME_OUTPUT_DATA (f)->window)
+    xsignal1 (Qerror, build_unibyte_string ("Could not create window"));
+
+  if (!minibuffer_only && !ttip_p && FRAME_EXTERNAL_MENU_BAR (f))
+    initialize_frame_menubar (f);
+
+  FRAME_OUTPUT_DATA (f)->window_desc = FRAME_OUTPUT_DATA (f)->window;
+  Vframe_list = Fcons (frame, Vframe_list);
+
+  gui_default_parameter (f, parms, Qicon_type, Qnil,
+                         "bitmapIcon", "BitmapIcon", RES_TYPE_SYMBOL);
+  if (ttip_p)
+    {
+      gui_default_parameter (f, parms, Qundecorated, Qt, NULL, NULL, RES_TYPE_BOOLEAN);
+      gui_default_parameter (f, parms, Qno_accept_focus, Qt, NULL, NULL,
+			     RES_TYPE_BOOLEAN);
+    }
+  gui_default_parameter (f, parms, Qauto_raise, Qnil,
+                         "autoRaise", "AutoRaiseLower", RES_TYPE_BOOLEAN);
+  gui_default_parameter (f, parms, Qauto_lower, Qnil,
+                         "autoLower", "AutoLower", RES_TYPE_BOOLEAN);
+  gui_default_parameter (f, parms, Qcursor_type, Qbox,
+                         "cursorType", "CursorType", RES_TYPE_SYMBOL);
+  gui_default_parameter (f, parms, Qscroll_bar_width, Qnil,
+                         "scrollBarWidth", "ScrollBarWidth",
+                         RES_TYPE_NUMBER);
+  gui_default_parameter (f, parms, Qscroll_bar_height, Qnil,
+                         "scrollBarHeight", "ScrollBarHeight",
+                         RES_TYPE_NUMBER);
+  gui_default_parameter (f, parms, Qalpha, Qnil,
+                         "alpha", "Alpha", RES_TYPE_NUMBER);
+  gui_default_parameter (f, parms, Qfullscreen, Qnil,
+                         "fullscreen", "Fullscreen", RES_TYPE_SYMBOL);
+
+  f->can_set_window_size = true;
+
+  adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
+		     0, true, ttip_p ? Qtip_frame : Qx_create_frame_2);
+
+  if (!FRAME_OUTPUT_DATA (f)->explicit_parent && !ttip_p)
+    {
+      Lisp_Object visibility;
+
+      visibility = gui_display_get_arg (dpyinfo, parms, Qvisibility, 0, 0,
+                                        RES_TYPE_SYMBOL);
+      if (EQ (visibility, Qunbound))
+	visibility = Qt;
+      if (EQ (visibility, Qicon))
+	haiku_iconify_frame (f);
+      else if (!NILP (visibility))
+	haiku_visualize_frame (f);
+      else /* Qnil */
+	{
+	  f->was_invisible = true;
+	}
+    }
+
+  if (FRAME_HAS_MINIBUF_P (f)
+      && (!FRAMEP (KVAR (kb, Vdefault_minibuffer_frame))
+          || !FRAME_LIVE_P (XFRAME (KVAR (kb, Vdefault_minibuffer_frame)))))
+    kset_default_minibuffer_frame (kb, frame);
+
+  for (tem = parms; CONSP (tem); tem = XCDR (tem))
+    if (CONSP (XCAR (tem)) && !NILP (XCAR (XCAR (tem))))
+      fset_param_alist (f, Fcons (XCAR (tem), f->param_alist));
+
+  if (window_prompting & USPosition)
+    haiku_set_offset (f, f->left_pos, f->top_pos, 1);
+
+  /* Make sure windows on this frame appear in calls to next-window
+     and similar functions.  */
+  Vwindow_list = Qnil;
+
+  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;
+    }
+
+  if (NILP (tip_frame)
+      || (!delete && FRAMEP (tip_frame)
+	  && !FRAME_VISIBLE_P (XFRAME (tip_frame))))
+    return Qnil;
+  else
+    {
+      ptrdiff_t count;
+      Lisp_Object was_open = Qnil;
+
+      count = SPECPDL_INDEX ();
+      specbind (Qinhibit_redisplay, Qt);
+      specbind (Qinhibit_quit, Qt);
+
+      if (FRAMEP (tip_frame))
+	{
+	  if (FRAME_LIVE_P (XFRAME (tip_frame)))
+	    {
+	      if (delete)
+		{
+		  delete_frame (tip_frame, Qnil);
+		  tip_frame = Qnil;
+		}
+	      else
+		haiku_unvisualize_frame (XFRAME (tip_frame));
+
+	      was_open = Qt;
+	    }
+	  else
+	    tip_frame = Qnil;
+	}
+      else
+	tip_frame = Qnil;
+
+      return unbind_to (count, was_open);
+    }
+}
+
+static void
+haiku_set_undecorated (struct frame *f, Lisp_Object new_value,
+		       Lisp_Object old_value)
+{
+  if (EQ (new_value, old_value))
+    return;
+
+  block_input ();
+  FRAME_UNDECORATED (f) = !NILP (new_value);
+  BWindow_change_decoration (FRAME_HAIKU_WINDOW (f), NILP (new_value));
+  unblock_input ();
+}
+
+static void
+haiku_set_menu_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
+{
+  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);
+}
+
+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
+XGetPixel (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);
+
+  return mono_p ? data[(y * bytes_per_row + x / 8)] & (0x80 >> x % 8) :
+    ((uint32_t *) (data + (bytes_per_row * y)))[x];
+}
+
+void
+XPutPixel (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 (mono_p)
+    {
+      ptrdiff_t off = y * bytes_per_row;
+      ptrdiff_t bit = x % 8;
+      ptrdiff_t xoff = x / 8;
+
+      unsigned char *byte = ((unsigned char *) 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 (drawable)
+    BView_emacs_delete (drawable);
+
+  if (window)
+    BWindow_quit (window);
+
+  /* Free cursors */
+
+  BCursor_delete (f->output_data.haiku->text_cursor);
+  BCursor_delete (f->output_data.haiku->nontext_cursor);
+  BCursor_delete (f->output_data.haiku->modeline_cursor);
+  BCursor_delete (f->output_data.haiku->hand_cursor);
+  BCursor_delete (f->output_data.haiku->hourglass_cursor);
+  BCursor_delete (f->output_data.haiku->horizontal_drag_cursor);
+  BCursor_delete (f->output_data.haiku->vertical_drag_cursor);
+  BCursor_delete (f->output_data.haiku->left_edge_cursor);
+  BCursor_delete (f->output_data.haiku->top_left_corner_cursor);
+  BCursor_delete (f->output_data.haiku->top_edge_cursor);
+  BCursor_delete (f->output_data.haiku->top_right_corner_cursor);
+  BCursor_delete (f->output_data.haiku->right_edge_cursor);
+  BCursor_delete (f->output_data.haiku->bottom_right_corner_cursor);
+  BCursor_delete (f->output_data.haiku->bottom_edge_cursor);
+  BCursor_delete (f->output_data.haiku->bottom_left_corner_cursor);
+  BCursor_delete (f->output_data.haiku->no_cursor);
+
+  xfree (FRAME_OUTPUT_DATA (f));
+  FRAME_OUTPUT_DATA (f) = NULL;
+
+  unblock_input ();
+}
+
+void
+haiku_iconify_frame (struct frame *frame)
+{
+  if (FRAME_ICONIFIED_P (frame))
+    return;
+
+  block_input ();
+
+  SET_FRAME_VISIBLE (frame, false);
+  SET_FRAME_ICONIFIED (frame, true);
+
+  BWindow_iconify (FRAME_HAIKU_WINDOW (frame));
+
+  unblock_input ();
+}
+
+void
+haiku_set_offset (struct frame *frame, int x, int y,
+		  int change_gravity)
+{
+  if (change_gravity > 0)
+    {
+      frame->top_pos = y;
+      frame->left_pos = x;
+      frame->size_hint_flags &= ~ (XNegative | YNegative);
+      if (x < 0)
+	frame->size_hint_flags |= XNegative;
+      if (y < 0)
+	frame->size_hint_flags |= YNegative;
+      frame->win_gravity = NorthWestGravity;
+    }
+
+  block_input ();
+  if (change_gravity)
+    BWindow_set_offset (FRAME_HAIKU_WINDOW (frame), x, y);
+
+  unblock_input ();
+}
+
+void
+haiku_visualize_frame (struct frame *f)
+{
+  block_input ();
+
+  if (!FRAME_VISIBLE_P (f))
+    {
+      BWindow_set_visible (FRAME_HAIKU_WINDOW (f), 1);
+      haiku_set_offset (f, f->left_pos, f->top_pos, 1);
+
+      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);
+
+  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)
+{
+
+}
+
+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);
+}
+
+\f
+
+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-create-frame", Fx_create_frame, Sx_create_frame,
+       1, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+     (Lisp_Object parms)
+{
+  return haiku_create_frame (parms, 0);
+}
+
+DEFUN ("x-display-visual-class", Fx_display_visual_class,
+       Sx_display_visual_class, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  check_haiku_display_info (terminal);
+
+  return intern ("direct-color");
+}
+
+DEFUN ("x-show-tip", Fx_show_tip, Sx_show_tip, 1, 6, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object string, Lisp_Object frame, Lisp_Object parms,
+   Lisp_Object timeout, Lisp_Object dx, Lisp_Object dy)
+{
+  struct frame *tip_f;
+  struct window *w;
+  int root_x, root_y;
+  struct buffer *old_buffer;
+  struct text_pos pos;
+  int width, height;
+  int old_windows_or_buffers_changed = windows_or_buffers_changed;
+  ptrdiff_t count = SPECPDL_INDEX ();
+  ptrdiff_t count_1;
+  Lisp_Object window, size, tip_buf;
+
+  AUTO_STRING (tip, " *tip*");
+
+  specbind (Qinhibit_redisplay, Qt);
+
+  CHECK_STRING (string);
+
+  if (NILP (frame))
+    frame = selected_frame;
+  decode_window_system_frame (frame);
+
+  if (NILP (timeout))
+    timeout = make_fixnum (5);
+  else
+    CHECK_FIXNAT (timeout);
+
+  if (NILP (dx))
+    dx = make_fixnum (5);
+  else
+    CHECK_FIXNUM (dx);
+
+  if (NILP (dy))
+    dy = make_fixnum (-10);
+  else
+    CHECK_FIXNUM (dy);
+
+  if (FRAMEP (tip_frame) && FRAME_LIVE_P (XFRAME (tip_frame)))
+    {
+      if (FRAME_VISIBLE_P (XFRAME (tip_frame))
+	  && EQ (frame, tip_last_frame)
+	  && !NILP (Fequal_including_properties (string, tip_last_string))
+	  && !NILP (Fequal (parms, tip_last_parms)))
+	{
+	  /* Only DX and DY have changed.  */
+	  tip_f = XFRAME (tip_frame);
+	  if (!NILP (tip_timer))
+	    {
+	      Lisp_Object timer = tip_timer;
+
+	      tip_timer = Qnil;
+	      call1 (Qcancel_timer, timer);
+	    }
+
+	  block_input ();
+	  compute_tip_xy (tip_f, parms, dx, dy, FRAME_PIXEL_WIDTH (tip_f),
+			  FRAME_PIXEL_HEIGHT (tip_f), &root_x, &root_y);
+	  haiku_set_offset (tip_f, root_x, root_y, 1);
+	  haiku_visualize_frame (tip_f);
+	  unblock_input ();
+
+	  goto start_timer;
+	}
+      else if (tooltip_reuse_hidden_frame && EQ (frame, tip_last_frame))
+	{
+	  bool delete = false;
+	  Lisp_Object tail, elt, parm, last;
+
+	  /* Check if every parameter in PARMS has the same value in
+	     tip_last_parms.  This may destruct tip_last_parms
+	     which, however, will be recreated below.  */
+	  for (tail = parms; CONSP (tail); tail = XCDR (tail))
+	    {
+	      elt = XCAR (tail);
+	      parm = Fcar (elt);
+	      /* The left, top, right and bottom parameters are handled
+		 by compute_tip_xy so they can be ignored here.  */
+	      if (!EQ (parm, Qleft) && !EQ (parm, Qtop)
+		  && !EQ (parm, Qright) && !EQ (parm, Qbottom))
+		{
+		  last = Fassq (parm, tip_last_parms);
+		  if (NILP (Fequal (Fcdr (elt), Fcdr (last))))
+		    {
+		      /* We lost, delete the old tooltip.  */
+		      delete = true;
+		      break;
+		    }
+		  else
+		    tip_last_parms =
+		      call2 (Qassq_delete_all, parm, tip_last_parms);
+		}
+	      else
+		tip_last_parms =
+		  call2 (Qassq_delete_all, parm, tip_last_parms);
+	    }
+
+	  /* Now check if there's a parameter left in tip_last_parms with a
+	     non-nil value.  */
+	  for (tail = tip_last_parms; CONSP (tail); tail = XCDR (tail))
+	    {
+	      elt = XCAR (tail);
+	      parm = Fcar (elt);
+	      if (!EQ (parm, Qleft) && !EQ (parm, Qtop) && !EQ (parm, Qright)
+		  && !EQ (parm, Qbottom) && !NILP (Fcdr (elt)))
+		{
+		  /* We lost, delete the old tooltip.  */
+		  delete = true;
+		  break;
+		}
+	    }
+
+	  haiku_hide_tip (delete);
+	}
+      else
+	haiku_hide_tip (true);
+    }
+  else
+    haiku_hide_tip (true);
+
+  tip_last_frame = frame;
+  tip_last_string = string;
+  tip_last_parms = parms;
+
+  /* Block input until the tip has been fully drawn, to avoid crashes
+     when drawing tips in menus.  */
+  block_input ();
+
+  if (!FRAMEP (tip_frame) || !FRAME_LIVE_P (XFRAME (tip_frame)))
+    {
+      /* Add default values to frame parameters.  */
+      if (NILP (Fassq (Qname, parms)))
+	parms = Fcons (Fcons (Qname, build_string ("tooltip")), parms);
+      if (NILP (Fassq (Qinternal_border_width, parms)))
+	parms = Fcons (Fcons (Qinternal_border_width, make_fixnum (3)), parms);
+      if (NILP (Fassq (Qborder_width, parms)))
+	parms = Fcons (Fcons (Qborder_width, make_fixnum (1)), parms);
+      if (NILP (Fassq (Qborder_color, parms)))
+	parms = Fcons (Fcons (Qborder_color, build_string ("lightyellow")),
+		       parms);
+      if (NILP (Fassq (Qbackground_color, parms)))
+	parms = Fcons (Fcons (Qbackground_color, build_string ("lightyellow")),
+		       parms);
+
+      /* Create a frame for the tooltip and record it in the global
+	 variable tip_frame.  */
+
+      if (NILP (tip_frame = haiku_create_frame (parms, 1)))
+	{
+	  /* Creating the tip frame failed.  */
+	  unblock_input ();
+	  return unbind_to (count, Qnil);
+	}
+    }
+
+  tip_f = XFRAME (tip_frame);
+  window = FRAME_ROOT_WINDOW (tip_f);
+  tip_buf = Fget_buffer_create (tip, Qnil);
+  /* We will mark the tip window a "pseudo-window" below, and such
+     windows cannot have display margins.  */
+  bset_left_margin_cols (XBUFFER (tip_buf), make_fixnum (0));
+  bset_right_margin_cols (XBUFFER (tip_buf), make_fixnum (0));
+  set_window_buffer (window, tip_buf, false, false);
+  w = XWINDOW (window);
+  w->pseudo_window_p = true;
+  /* Try to avoid that `other-window' select us (Bug#47207).  */
+  Fset_window_parameter (window, Qno_other_window, Qt);
+
+  /* Set up the frame's root window.  Note: The following code does not
+     try to size the window or its frame correctly.  Its only purpose is
+     to make the subsequent text size calculations work.  The right
+     sizes should get installed when the toolkit gets back to us.  */
+  w->left_col = 0;
+  w->top_line = 0;
+  w->pixel_left = 0;
+  w->pixel_top = 0;
+
+  if (CONSP (Vx_max_tooltip_size)
+      && RANGED_FIXNUMP (1, XCAR (Vx_max_tooltip_size), INT_MAX)
+      && RANGED_FIXNUMP (1, XCDR (Vx_max_tooltip_size), INT_MAX))
+    {
+      w->total_cols = XFIXNAT (XCAR (Vx_max_tooltip_size));
+      w->total_lines = XFIXNAT (XCDR (Vx_max_tooltip_size));
+    }
+  else
+    {
+      w->total_cols = 80;
+      w->total_lines = 40;
+    }
+
+  w->pixel_width = w->total_cols * FRAME_COLUMN_WIDTH (tip_f);
+  w->pixel_height = w->total_lines * FRAME_LINE_HEIGHT (tip_f);
+  FRAME_TOTAL_COLS (tip_f) = WINDOW_TOTAL_COLS (w);
+  adjust_frame_glyphs (tip_f);
+
+  /* Insert STRING into the root window's buffer and fit the frame to
+     the buffer.  */
+  count_1 = SPECPDL_INDEX ();
+  old_buffer = current_buffer;
+  set_buffer_internal_1 (XBUFFER (w->contents));
+  bset_truncate_lines (current_buffer, Qnil);
+  specbind (Qinhibit_read_only, Qt);
+  specbind (Qinhibit_modification_hooks, Qt);
+  specbind (Qinhibit_point_motion_hooks, Qt);
+  Ferase_buffer ();
+  Finsert (1, &string);
+  clear_glyph_matrix (w->desired_matrix);
+  clear_glyph_matrix (w->current_matrix);
+  SET_TEXT_POS (pos, BEGV, BEGV_BYTE);
+  try_window (window, pos, TRY_WINDOW_IGNORE_FONTS_CHANGE);
+  /* Calculate size of tooltip window.  */
+  size = Fwindow_text_pixel_size (window, Qnil, Qnil, Qnil,
+				  make_fixnum (w->pixel_height), Qnil);
+  /* Add the frame's internal border to calculated size.  */
+  width = XFIXNUM (Fcar (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f);
+  height = XFIXNUM (Fcdr (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f);
+  /* Calculate position of tooltip frame.  */
+  compute_tip_xy (tip_f, parms, dx, dy, width, height, &root_x, &root_y);
+
+  BWindow_resize (FRAME_HAIKU_WINDOW (tip_f), width, height);
+  haiku_set_offset (tip_f, root_x, root_y, 1);
+  BWindow_set_tooltip_decoration (FRAME_HAIKU_WINDOW (tip_f));
+  SET_FRAME_VISIBLE (tip_f, 1);
+  BWindow_set_visible (FRAME_HAIKU_WINDOW (tip_f), 1);
+
+  w->must_be_updated_p = true;
+  update_single_window (w);
+  set_buffer_internal_1 (old_buffer);
+  unbind_to (count_1, Qnil);
+  unblock_input ();
+  windows_or_buffers_changed = old_windows_or_buffers_changed;
+
+ start_timer:
+  /* Let the tip disappear after timeout seconds.  */
+  tip_timer = call3 (intern ("run-at-time"), timeout, Qnil,
+		     intern ("x-hide-tip"));
+
+  return unbind_to (count, Qnil);
+}
+
+DEFUN ("x-hide-tip", Fx_hide_tip, Sx_hide_tip, 0, 0, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (void)
+{
+  return haiku_hide_tip (!tooltip_reuse_hidden_frame);
+}
+
+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 */
+    NULL, /* 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 */
+    NULL, /* set inhibit double buffering */
+    haiku_set_undecorated,
+    NULL, /* set parent frame */
+    NULL, /* set skip taskbar */
+    NULL, /* 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");
+
+  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);
+
+  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));
+
+  return;
+}
diff --git a/src/haikufont.c b/src/haikufont.c
new file mode 100644
index 0000000000..55def48902
--- /dev/null
+++ b/src/haikufont.c
@@ -0,0 +1,628 @@
+/* Font support for Haiku windowing
+
+Copyright (C) 2021 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+#include "lisp.h"
+#include "dispextern.h"
+#include "composite.h"
+#include "blockinput.h"
+#include "charset.h"
+#include "frame.h"
+#include "window.h"
+#include "fontset.h"
+#include "haikuterm.h"
+#include "character.h"
+#include "font.h"
+#include "termchar.h"
+#include "pdumper.h"
+#include "haiku_support.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+static Lisp_Object
+haikufont_get_fallback_entity (void)
+{
+  Lisp_Object ent = font_make_entity ();
+  ASET (ent, FONT_TYPE_INDEX, Qhaiku);
+  ASET (ent, FONT_FOUNDRY_INDEX, Qhaiku);
+  ASET (ent, FONT_FAMILY_INDEX, Qnil);
+  ASET (ent, FONT_ADSTYLE_INDEX, Qnil);
+  ASET (ent, FONT_REGISTRY_INDEX, Qiso8859_1);
+  ASET (ent, FONT_SIZE_INDEX, make_fixnum (0));
+  ASET (ent, FONT_AVGWIDTH_INDEX, make_fixnum (0));
+  ASET (ent, FONT_SPACING_INDEX, make_fixnum (FONT_SPACING_MONO));
+  FONT_SET_STYLE (ent, FONT_WIDTH_INDEX, Qnil);
+  FONT_SET_STYLE (ent, FONT_WEIGHT_INDEX, Qnil);
+  FONT_SET_STYLE (ent, FONT_SLANT_INDEX, Qnil);
+
+  return ent;
+}
+
+static Lisp_Object
+haikufont_get_cache (struct frame *frame)
+{
+  return FRAME_DISPLAY_INFO (frame)->name_list_element;
+}
+
+static Lisp_Object
+haikufont_weight_to_lisp (int weight)
+{
+  switch (weight)
+    {
+    case HAIKU_THIN:
+      return Qthin;
+    case HAIKU_ULTRALIGHT:
+      return Qultra_light;
+    case HAIKU_EXTRALIGHT:
+      return Qextra_light;
+    case HAIKU_LIGHT:
+      return Qlight;
+    case HAIKU_SEMI_LIGHT:
+      return Qsemi_light;
+    case HAIKU_REGULAR:
+      return Qnormal;
+    case HAIKU_SEMI_BOLD:
+      return Qsemi_bold;
+    case HAIKU_BOLD:
+      return Qbold;
+    case HAIKU_EXTRA_BOLD:
+      return Qextra_bold;
+    case HAIKU_ULTRA_BOLD:
+      return Qultra_bold;
+    }
+  emacs_abort ();
+}
+
+static int
+haikufont_lisp_to_weight (Lisp_Object weight)
+{
+  if (EQ (weight, Qthin))
+    return HAIKU_THIN;
+  if (EQ (weight, Qultra_light))
+    return HAIKU_ULTRALIGHT;
+  if (EQ (weight, Qextra_light))
+    return HAIKU_EXTRALIGHT;
+  if (EQ (weight, Qlight))
+    return HAIKU_LIGHT;
+  if (EQ (weight, Qsemi_light))
+    return HAIKU_SEMI_LIGHT;
+  if (EQ (weight, Qnormal))
+    return HAIKU_REGULAR;
+  if (EQ (weight, Qsemi_bold))
+    return HAIKU_SEMI_BOLD;
+  if (EQ (weight, Qbold))
+    return HAIKU_BOLD;
+  if (EQ (weight, Qextra_bold))
+    return HAIKU_EXTRA_BOLD;
+  if (EQ (weight, Qultra_bold))
+    return HAIKU_ULTRA_BOLD;
+
+  emacs_abort ();
+}
+
+static Lisp_Object
+haikufont_slant_to_lisp (enum haiku_font_slant slant)
+{
+  switch (slant)
+    {
+    case 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 int
+haikufont_maybe_handle_special_family (Lisp_Object family,
+				       struct haiku_font_pattern *ptn)
+{
+  CHECK_SYMBOL (family);
+
+  if (EQ (family, Qmonospace) || EQ (family, Qfixed))
+    {
+      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, Qiso8859_1);
+  ASET (ent, FONT_SIZE_INDEX, make_fixnum (0));
+  ASET (ent, FONT_AVGWIDTH_INDEX, make_fixnum (0));
+  ASET (ent, FONT_SPACING_INDEX, make_fixnum (FONT_SPACING_MONO));
+  FONT_SET_STYLE (ent, FONT_WIDTH_INDEX, Qnormal);
+  FONT_SET_STYLE (ent, FONT_WEIGHT_INDEX, Qnormal);
+  FONT_SET_STYLE (ent, FONT_SLANT_INDEX, Qnormal);
+
+  if (ptn->specified & FSPEC_FAMILY)
+    ASET (ent, FONT_FAMILY_INDEX, intern (ptn->family));
+  else
+    ASET (ent, FONT_FAMILY_INDEX, Qdefault);
+
+  if (ptn->specified & FSPEC_STYLE)
+    ASET (ent, FONT_ADSTYLE_INDEX, intern (ptn->style));
+  else
+    {
+      if (ptn->specified & FSPEC_WEIGHT)
+	FONT_SET_STYLE (ent, FONT_WEIGHT_INDEX,
+			haikufont_weight_to_lisp (ptn->weight));
+      if (ptn->specified & FSPEC_SLANT)
+	FONT_SET_STYLE (ent, FONT_SLANT_INDEX,
+			haikufont_slant_to_lisp (ptn->slant));
+    }
+
+  if (ptn->specified & FSPEC_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 = 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);
+    }
+}
+
+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, 1, &ptn);
+  ptn.specified &= ~FSPEC_FAMILY;
+  struct haiku_font_pattern *found = BFont_find (&ptn);
+  if (found)
+    {
+      tem = haikufont_pattern_to_entity (found);
+      haiku_font_pattern_free (found);
+    }
+  unblock_input ();
+  return !NILP (tem) ? tem : haikufont_get_fallback_entity ();
+}
+
+static Lisp_Object
+haikufont_list (struct frame *f, Lisp_Object font_spec)
+{
+  block_input ();
+  Lisp_Object lst = Qnil;
+  struct haiku_font_pattern ptn;
+  haikufont_spec_or_entity_to_pattern (font_spec, 1, &ptn);
+  struct haiku_font_pattern *found = BFont_find (&ptn);
+  if (found)
+    {
+      for (struct haiku_font_pattern *pt = found;
+	   pt; pt = pt->next)
+	lst = Fcons (haikufont_pattern_to_entity (pt), lst);
+      haiku_font_pattern_free (found);
+    }
+  unblock_input ();
+  return lst;
+}
+
+static unsigned int
+haikufont_encode_char (struct font *font, int c)
+{
+  struct haikufont_info *info = (struct haikufont_info *) font;
+
+  if (c < HAVE_CHAR_CACHE_MAX && info->have_char_cache[c])
+    return info->have_char_cache[c] < 0 ? FONT_INVALID_CODE : c;
+
+  if (!BFont_have_char_p (((struct haikufont_info *) font)->be_font, c))
+    {
+      if (c < HAVE_CHAR_CACHE_MAX)
+	info->have_char_cache[c] = -1;
+      return FONT_INVALID_CODE;
+    }
+  if (c < HAVE_CHAR_CACHE_MAX)
+    info->have_char_cache[c] = 1;
+
+  return c;
+}
+
+static int
+haikufont_have_char (Lisp_Object font, int c)
+{
+  if (FONT_ENTITY_P (font))
+    return -1;
+
+  return haikufont_encode_char (XFONT_OBJECT (font), c)
+    != FONT_INVALID_CODE;
+}
+
+static Lisp_Object
+haikufont_open (struct frame *f, Lisp_Object font_entity, int x)
+{
+  struct haikufont_info *font_info;
+  struct haiku_font_pattern ptn;
+  struct font *font;
+  void *be_font;
+  Lisp_Object font_object;
+  Lisp_Object tem;
+
+  block_input ();
+  if (x <= 0)
+    {
+      /* Get pixel size from frame instead */
+      tem = get_frame_param (f, Qfontsize);
+      x = NILP (tem) ? 0 : XFIXNAT (tem);
+    }
+
+  haikufont_spec_or_entity_to_pattern (font_entity, 0, &ptn);
+
+  if (BFont_open_pattern (&ptn, &be_font, x))
+    {
+      unblock_input ();
+      return Qnil;
+    }
+
+  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;
+
+  memset (font_info->have_char_cache, 0,
+	  HAVE_CHAR_CACHE_MAX * sizeof (int));
+  memset (font_info->met_cache, 0,
+	  HAVE_CHAR_CACHE_MAX * sizeof (struct font_metrics));
+
+  if (!font)
+    {
+      unblock_input ();
+      return Qnil;
+    }
+
+  font_info->be_font = be_font;
+  font->pixel_size = 0;
+  font->driver = &haikufont_driver;
+  font->encoding_charset = -1;
+  font->repertory_charset = -1;
+  font->default_ascent = 0;
+  font->vertical_centering = 0;
+  font->baseline_offset = 0;
+  font->relative_compose = 0;
+
+  font->props[FONT_FULLNAME_INDEX] =
+    build_unibyte_string ("fixed");
+
+  int px_size, min_width, max_width,
+    avg_width, height, space_width, ascent,
+    descent, underline_pos, underline_thickness;
+
+  BFont_dat (be_font, &px_size, &min_width,
+	     &max_width, &avg_width, &height,
+	     &space_width, &ascent, &descent,
+	     &underline_pos, &underline_thickness);
+
+  font->pixel_size = px_size;
+  font->min_width = min_width;
+  font->max_width = max_width;
+  font->average_width = avg_width;
+  font->height = height;
+  font->space_width = space_width;
+  font->ascent = ascent;
+  font->descent = descent;
+  font->default_ascent = ascent;
+  font->underline_position = underline_pos;
+  font->underline_thickness = underline_thickness;
+
+  font->vertical_centering = 0;
+  font->baseline_offset = 0;
+  font->relative_compose = 0;
+
+  font->props[FONT_NAME_INDEX] = Ffont_xlfd_name (font_object, Qnil);
+
+  unblock_input ();
+  return font_object;
+}
+
+static void
+haikufont_close (struct font *font)
+{
+  struct haikufont_info *info = (struct haikufont_info *) font;
+
+  block_input ();
+  if (info && info->be_font)
+    BFont_close (info->be_font);
+  unblock_input ();
+}
+
+static void
+haikufont_prepare_face (struct frame *f, struct face *face)
+{
+
+}
+
+static void
+haikufont_glyph_extents (struct font *font, unsigned code,
+			 struct font_metrics *metrics)
+{
+  struct haikufont_info *info = (struct haikufont_info *) font;
+
+  if (code < HAVE_CHAR_CACHE_MAX &&
+      info->met_cache[code].ascent != 0)
+    {
+      *metrics = info->met_cache[code];
+      return;
+    }
+
+  unsigned char utf8[MAX_MULTIBYTE_LENGTH];
+  memset (utf8, 0, MAX_MULTIBYTE_LENGTH);
+  CHAR_STRING (code, utf8);
+  int advance, lb, rb;
+  BFont_char_bounds (info->be_font, (const char *) utf8, &advance, &lb, &rb);
+
+  metrics->lbearing = lb;
+  metrics->rbearing = rb;
+  metrics->width = advance;
+  metrics->ascent = font->ascent;
+  metrics->descent = font->descent;
+
+  if (code < HAVE_CHAR_CACHE_MAX)
+    info->met_cache[code] = *metrics;
+}
+
+static void
+haikufont_text_extents (struct font *font, const unsigned int *code,
+			int nglyphs, struct font_metrics *metrics)
+{
+  int totalwidth = 0;
+  memset (metrics, 0, sizeof (struct font_metrics));
+
+  block_input ();
+  for (int i = 0; i < nglyphs; i++)
+    {
+      struct font_metrics m;
+      haikufont_glyph_extents (font, code[i], &m);
+      if (metrics)
+	{
+	  if (totalwidth + m.lbearing < metrics->lbearing)
+	    metrics->lbearing = totalwidth + m.lbearing;
+	  if (totalwidth + m.rbearing > metrics->rbearing)
+	    metrics->rbearing = totalwidth + m.rbearing;
+	  if (m.ascent > metrics->ascent)
+	    metrics->ascent = m.ascent;
+	  if (m.descent > metrics->descent)
+	    metrics->descent = m.descent;
+	}
+      totalwidth += m.width;
+    }
+
+  unblock_input ();
+
+  if (metrics)
+    metrics->width = totalwidth;
+}
+
+static int
+haikufont_draw (struct glyph_string *s, int from, int to,
+		int x, int y, bool with_background)
+{
+  struct frame *f = s->f;
+  struct face *face = s->face;
+  struct font_info *info = (struct font_info *) s->font;
+  unsigned char mb[MAX_MULTIBYTE_LENGTH];
+  void *view = FRAME_HAIKU_VIEW (f);
+
+  if (s->hl == DRAW_MOUSE_FACE)
+    face = FACE_FROM_ID_OR_NULL (s->f,
+				 MOUSE_HL_INFO (s->f)->mouse_face_face_id);
+  if (!face)
+    face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
+
+  block_input ();
+  prepare_face_for_display (s->f, face);
+
+  BView_draw_lock (view);
+  BView_StartClip (view);
+  if (with_background)
+    {
+      int fibw = FRAME_INTERNAL_BORDER_WIDTH (s->f);
+      int vx, vy, width, height;
+      vx = s->x;
+      vy = s->y;
+      width = s->width;
+      height = FONT_HEIGHT (face->font);
+      if (s->face->box != FACE_NO_BOX && s->first_glyph->left_box_line_p)
+	vx += max (s->face->box_vertical_line_width, 0);
+
+
+      int mbox_line_width = max (s->face->box_vertical_line_width, 0);
+
+      if (s->row->full_width_p)
+        {
+          if (vx <= fibw + 1 + mbox_line_width)
+            {
+              width += vx - mbox_line_width;
+              vx = mbox_line_width;
+            }
+          if (FRAME_PIXEL_WIDTH (s->f) - (vx + width)
+                <= fibw+1)
+            width += fibw;
+        }
+      if (s->face->box == FACE_NO_BOX)
+        {
+          /* Expand unboxed top row over internal border.  */
+          if (vy <= fibw + 1 + mbox_line_width)
+            {
+              height += vy;
+              vy = 0;
+            }
+        }
+      else
+        {
+          int correction = abs (s->face->box_horizontal_line_width) + 1;
+          vy += correction;
+          height -= 2 * correction;
+          correction = abs (s->face->box_vertical_line_width) + 1;
+          vx += correction;
+          width -= 2 * correction;
+        }
+      BView_SetHighColor (view, s->hl == DRAW_CURSOR ?
+			  FRAME_CURSOR_COLOR (s->f).pixel : face->background);
+
+      BView_FillRectangle (view, vx, vy, width, height);
+      s->background_filled_p = 1;
+    }
+
+  if (s->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) && s->char2b[from] <= 127)
+    BView_DrawChar (view, (char) s->char2b[from]);
+  else if (from == to)
+    {
+      int len = CHAR_STRING (s->char2b[from], mb);
+      BView_DrawString (view, (char *) mb, len);
+    }
+  else
+    {
+      ptrdiff_t b_len = 0;
+      char *b = xmalloc (b_len);
+
+      for (int idx = from; idx < to; ++idx)
+	{
+	  int len = CHAR_STRING (s->char2b[idx], mb);
+	  b = xrealloc (b, b_len = (b_len + len));
+	  if (len == 1)
+	    b[b_len - len] = mb[0];
+	  else
+	    memcpy (b + b_len - len, mb, len);
+	}
+
+      BView_DrawString (view, b, b_len);
+      xfree (b);
+    }
+  BView_EndClip (view);
+  BView_draw_unlock (view);
+  unblock_input ();
+  return 1;
+}
+
+struct font_driver const haikufont_driver =
+  {
+    .type = LISPSYM_INITIALLY (Qhaiku),
+    .case_sensitive = true,
+    .get_cache = haikufont_get_cache,
+    .has_char = haikufont_have_char,
+    .list = haikufont_list,
+    .match = haikufont_match,
+    .draw = haikufont_draw,
+    .open_font = haikufont_open,
+    .close_font = haikufont_close,
+    .prepare_face = haikufont_prepare_face,
+    .encode_char = haikufont_encode_char,
+    .text_extents = haikufont_text_extents
+  };
+
+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");
+}
diff --git a/src/haikugui.h b/src/haikugui.h
new file mode 100644
index 0000000000..cfc693fb55
--- /dev/null
+++ b/src/haikugui.h
@@ -0,0 +1,106 @@
+/* Haiku window system support
+   Copyright (C) 2021 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
+
+#ifndef _HAIKU_GUI_H_
+#define _HAIKU_GUI_H_
+
+#ifdef _cplusplus
+extern "C"
+{
+#endif
+
+typedef struct haiku_char_struct
+{
+  int rbearing;
+  int lbearing;
+  int width;
+  int ascent;
+  int descent;
+} XCharStruct;
+
+struct haiku_rect
+{
+  int x, y;
+  int width, height;
+};
+
+typedef void *haiku;
+
+typedef haiku Emacs_Pixmap;
+typedef haiku Emacs_Window;
+typedef haiku Emacs_Cursor;
+typedef haiku Drawable;
+
+#define NativeRectangle struct haiku_rect
+#define CONVERT_TO_EMACS_RECT(xr, nr)	\
+  ((xr).x     = (nr).x,			\
+   (xr).y     = (nr).y,			\
+   (xr).width = (nr).width,		\
+   (xr).height = (nr).height)
+
+#define CONVERT_FROM_EMACS_RECT(xr, nr)	\
+  ((nr).x    = (xr).x,			\
+   (nr).y    = (xr).y,			\
+   (nr).width  = (xr).width,		\
+   (nr).height = (xr).height)
+
+#define STORE_NATIVE_RECT(nr, px, py, pwidth, pheight)	\
+  ((nr).x    = (px),					\
+   (nr).y    = (py),					\
+   (nr).width  = (pwidth),				\
+   (nr).height = (pheight))
+
+#define ForgetGravity		0
+#define NorthWestGravity	1
+#define NorthGravity		2
+#define NorthEastGravity	3
+#define WestGravity		4
+#define CenterGravity		5
+#define EastGravity		6
+#define SouthWestGravity	7
+#define SouthGravity		8
+#define SouthEastGravity	9
+#define StaticGravity		10
+
+#define NoValue		0x0000
+#define XValue  	0x0001
+#define YValue		0x0002
+#define WidthValue  	0x0004
+#define HeightValue  	0x0008
+#define AllValues 	0x000F
+#define XNegative 	0x0010
+#define YNegative 	0x0020
+
+#define USPosition	(1L << 0) /* user specified x, y */
+#define USSize		(1L << 1) /* user specified width, height */
+#define PPosition	(1L << 2) /* program specified position */
+#define PSize		(1L << 3) /* program specified size */
+#define PMinSize	(1L << 4) /* program specified minimum size */
+#define PMaxSize	(1L << 5) /* program specified maximum size */
+#define PResizeInc	(1L << 6) /* program specified resize increments */
+#define PAspect		(1L << 7) /* program specified min, max aspect ratios */
+#define PBaseSize	(1L << 8) /* program specified base for incrementing */
+#define PWinGravity	(1L << 9) /* program specified window gravity */
+
+typedef haiku Window;
+typedef int Display;
+
+#ifdef _cplusplus
+};
+#endif
+#endif /* _HAIKU_GUI_H_ */
diff --git a/src/haikumenu.c b/src/haikumenu.c
new file mode 100644
index 0000000000..c1cdab6ee3
--- /dev/null
+++ b/src/haikumenu.c
@@ -0,0 +1,566 @@
+/* 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 "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;
+
+  while (i < menu_items_used)
+    {
+      if (NILP (AREF (menu_items, i)))
+	{
+	  menus[++menu_depth] = menu;
+	  i++;
+	}
+      else if (EQ (AREF (menu_items, i), Qlambda))
+	{
+	  panes[menu_depth] = NULL;
+	  menu = panes[--menu_depth] ? panes[menu_depth] : menus[menu_depth];
+	  i++;
+	}
+      else if (EQ (AREF (menu_items, i), Qquote))
+	i += 1;
+      else if (EQ (AREF (menu_items, i), Qt))
+	{
+	  Lisp_Object pane_name, prefix;
+	  const char *pane_string;
+
+	  if (menu_items_n_panes == 1)
+	    {
+	      i += MENU_ITEMS_PANE_LENGTH;
+	      continue;
+	    }
+
+	  pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
+	  prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
+
+	  if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
+	    {
+	      pane_name = ENCODE_UTF_8 (pane_name);
+	      ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name);
+	    }
+
+	  pane_string = (NILP (pane_name)
+			 ? "" : SSDATA (pane_name));
+	  if (!NILP (prefix))
+	    pane_string++;
+
+	  if (strcmp (pane_string, ""))
+	    {
+	      panes[menu_depth] =
+		menu = BMenu_new_submenu (menus[menu_depth], pane_string, 1);
+	    }
+
+	  i += MENU_ITEMS_PANE_LENGTH;
+	}
+      else
+	{
+	  Lisp_Object item_name, enable, descrip, def, selected;
+	  item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
+	  enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
+	  descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
+	  def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
+	  selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
+
+	  if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
+	    {
+	      item_name = ENCODE_UTF_8 (item_name);
+	      ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name);
+	    }
+
+	  if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
+	    {
+	      descrip = ENCODE_UTF_8 (descrip);
+	      ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip);
+	    }
+
+	  if (i + MENU_ITEMS_ITEM_LENGTH < menu_items_used &&
+	      NILP (AREF (menu_items, i + MENU_ITEMS_ITEM_LENGTH)))
+	    menu = BMenu_new_submenu (menu, SSDATA (item_name), !NILP (enable));
+	  else if (NILP (def) && menu_separator_name_p (SSDATA (item_name)))
+	    BMenu_add_separator (menu);
+	  else if (!mbar_p)
+	    BMenu_add_item (menu, SSDATA (item_name),
+			    !NILP (def) ? aref_addr (menu_items, i) : NULL,
+			    !NILP (enable), !NILP (selected));
+	  else
+	    BMenu_add_item (menu, SSDATA (item_name),
+			    !NILP (def) ? (void *) (intptr_t) i : NULL,
+			    !NILP (enable), !NILP (selected));
+
+	  i += MENU_ITEMS_ITEM_LENGTH;
+	}
+    }
+}
+
+static Lisp_Object
+haiku_dialog_show (struct frame *f, Lisp_Object title,
+		   Lisp_Object header, const char **error_name)
+{
+  int i, nb_buttons = 0;
+
+  *error_name = NULL;
+
+  if (menu_items_n_panes > 1)
+    {
+      *error_name = "Multiple panes in dialog box";
+      return Qnil;
+    }
+
+  Lisp_Object pane_name = AREF (menu_items, MENU_ITEMS_PANE_NAME);
+  i = MENU_ITEMS_PANE_LENGTH;
+
+  if (STRING_MULTIBYTE (pane_name))
+    pane_name = ENCODE_UTF_8 (pane_name);
+
+  block_input ();
+  void *alert = BAlert_new (SSDATA (pane_name), NILP (header) ? HAIKU_INFO_ALERT :
+			    HAIKU_IDEA_ALERT);
+
+  Lisp_Object vals[10];
+
+  while (i < menu_items_used)
+    {
+      Lisp_Object item_name, enable, descrip, value;
+      item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
+      enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
+      descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
+      value = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE);
+
+      if (NILP (item_name))
+	{
+	  BAlert_delete (alert);
+	  *error_name = "Submenu in dialog items";
+	  unblock_input ();
+	  return Qnil;
+	}
+
+      if (EQ (item_name, Qquote))
+	{
+	  i++;
+	}
+
+      if (nb_buttons >= 9)
+	{
+	  BAlert_delete (alert);
+	  *error_name = "Too many dialog items";
+	  unblock_input ();
+	  return Qnil;
+	}
+
+      if (STRING_MULTIBYTE (item_name))
+	item_name = ENCODE_UTF_8 (item_name);
+      if (!NILP (descrip) && STRING_MULTIBYTE (descrip))
+	descrip = ENCODE_UTF_8 (descrip);
+
+      void *button = BAlert_add_button (alert, SSDATA (item_name));
+
+      BButton_set_enabled (button, !NILP (enable));
+      if (!NILP (descrip))
+	BView_set_tooltip (button, SSDATA (descrip));
+
+      vals[nb_buttons] = value;
+      ++nb_buttons;
+      i += MENU_ITEMS_ITEM_LENGTH;
+    }
+
+  int32_t val = BAlert_go (alert);
+  unblock_input ();
+
+  if (val < 0)
+    quit ();
+  else
+    return vals[val];
+
+  return Qnil;
+}
+
+Lisp_Object
+haiku_popup_dialog (struct frame *f, Lisp_Object header, Lisp_Object contents)
+{
+  Lisp_Object title;
+  const char *error_name = NULL;
+  Lisp_Object selection;
+  ptrdiff_t specpdl_count = SPECPDL_INDEX ();
+
+  check_window_system (f);
+
+  /* Decode the dialog items from what was specified.  */
+  title = Fcar (contents);
+  CHECK_STRING (title);
+  record_unwind_protect_void (unuse_menu_items);
+
+  if (NILP (Fcar (Fcdr (contents))))
+    /* No buttons specified, add an "Ok" button so users can pop down
+       the dialog.  Also, the lesstif/motif version crashes if there are
+       no buttons.  */
+    contents = list2 (title, Fcons (build_string ("Ok"), Qt));
+
+  list_of_panes (list1 (contents));
+
+  /* Display them in a dialog box.  */
+  block_input ();
+  selection = haiku_dialog_show (f, title, header, &error_name);
+  unblock_input ();
+
+  unbind_to (specpdl_count, Qnil);
+  discard_menu_items ();
+
+  if (error_name)
+    error ("%s", error_name);
+  return selection;
+}
+
+Lisp_Object
+haiku_menu_show (struct frame *f, int x, int y, int menuflags,
+		 Lisp_Object title, const char **error_name)
+{
+  int i = 0, submenu_depth = 0;
+  void *view = FRAME_HAIKU_VIEW (f);
+  void *menu;
+
+  Lisp_Object *subprefix_stack =
+    alloca (menu_items_used * sizeof (Lisp_Object));
+
+  eassert (FRAME_HAIKU_P (f));
+
+  *error_name = NULL;
+
+  if (menu_items_used <= MENU_ITEMS_PANE_LENGTH)
+    {
+      *error_name = "Empty menu";
+      return Qnil;
+    }
+
+  block_input ();
+  if (STRINGP (title) && STRING_MULTIBYTE (title))
+    title = ENCODE_UTF_8 (title);
+
+  menu = BPopUpMenu_new (STRINGP (title) ? SSDATA (title) : NULL);
+  digest_menu_items (menu, 0, menu_items_used, 0);
+  BView_convert_to_screen (view, &x, &y);
+  unblock_input ();
+
+  ++popup_activated_p;
+  menu_item_selection = BMenu_run (menu, x, y);
+  --popup_activated_p;
+  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);
+  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);
+}
+
+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;
+}
+
+void
+syms_of_haikumenu (void)
+{
+  DEFSYM (Qdebug_on_next_call, "debug-on-next-call");
+
+  defsubr (&Smenu_or_popup_active_p);
+  return;
+}
diff --git a/src/haikuselect.c b/src/haikuselect.c
new file mode 100644
index 0000000000..00fab3bba6
--- /dev/null
+++ b/src/haikuselect.c
@@ -0,0 +1,134 @@
+/* Haiku window system selection support. Hey Emacs, this is -*- C++ -*-
+   Copyright (C) 2021 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+#include "lisp.h"
+#include "blockinput.h"
+#include "coding.h"
+#include "haikuselect.h"
+#include "haikuterm.h"
+
+DEFUN ("haiku-selection-data", Fhaiku_selection_data, Shaiku_selection_data,
+       2, 2, 0,
+       doc: /* Retrieve content typed as NAME from the clipboard
+CLIPBOARD.  CLIPBOARD is the symbol `PRIMARY', `SECONDARY' or
+`CLIPBOARD'.  NAME is a MIME type denoting the type of the data to
+fetch. */)
+  (Lisp_Object clipboard, Lisp_Object name)
+{
+  CHECK_SYMBOL (clipboard);
+  CHECK_STRING (name);
+  char *dat;
+  ssize_t len;
+
+  block_input ();
+  if (EQ (clipboard, QPRIMARY))
+    dat = BClipboard_find_primary_selection_data (SSDATA (name), &len);
+  else if (EQ (clipboard, QSECONDARY))
+    dat = BClipboard_find_secondary_selection_data (SSDATA (name), &len);
+  else if (EQ (clipboard, QCLIPBOARD))
+    dat = BClipboard_find_system_data (SSDATA (name), &len);
+  else
+    {
+      unblock_input ();
+      signal_error ("Bad clipboard", clipboard);
+    }
+  unblock_input ();
+
+  if (!dat)
+    return Qnil;
+
+  Lisp_Object str = make_unibyte_string (dat, len);
+  Lisp_Object lispy_type = Qnil;
+
+  if (!strcmp (SSDATA (name), "text/utf-8") ||
+      !strcmp (SSDATA (name), "text/plain"))
+    {
+      if (string_ascii_p (str))
+	lispy_type = QSTRING;
+      else
+	lispy_type = QUTF8_STRING;
+    }
+
+  if (lispy_type)
+    Fput_text_property (make_fixnum (0), make_fixnum (len),
+			Qforeign_selection, lispy_type, str);
+
+  block_input ();
+  BClipboard_free_data (dat);
+  unblock_input ();
+
+  return str;
+}
+
+DEFUN ("haiku-selection-put", Fhaiku_selection_put, Shaiku_selection_put,
+       3, 3, 0,
+       doc: /* Add or remove content from the clipboard CLIPBOARD.
+CLIPBOARD is the symbol `PRIMARY', `SECONDARY' or `CLIPBOARD'.  NAME
+is a MIME type denoting the type of the data to add.  DATA is the
+string that will be placed in the clipboard, or nil if the content is
+to be removed.  If NAME is the string `text/utf-8' or the string
+`text/plain', encode it as UTF-8 before storing it into the
+clipboard. */)
+  (Lisp_Object clipboard, Lisp_Object name, Lisp_Object data)
+{
+  CHECK_SYMBOL (clipboard);
+  CHECK_STRING (name);
+  if (!NILP (data))
+    CHECK_STRING (data);
+
+  block_input ();
+  /* It seems that Haiku applications counter-intuitively expect
+     UTF-8 data in both text/utf-8 and text/plain. */
+  if (!NILP (data) && STRING_MULTIBYTE (data) &&
+      (!strcmp (SSDATA (name), "text/utf-8") ||
+       !strcmp (SSDATA (name), "text/plain")))
+    data = ENCODE_UTF_8 (data);
+
+  char *dat = !NILP (data) ? SSDATA (data) : NULL;
+  ptrdiff_t len = !NILP (data) ? SBYTES (data) : 0;
+
+  if (EQ (clipboard, QPRIMARY))
+    BClipboard_set_primary_selection_data (SSDATA (name), dat, len);
+  else if (EQ (clipboard, QSECONDARY))
+    BClipboard_set_secondary_selection_data (SSDATA (name), dat, len);
+  else if (EQ (clipboard, QCLIPBOARD))
+    BClipboard_set_system_data (SSDATA (name), dat, len);
+  else
+    {
+      unblock_input ();
+      signal_error ("Bad clipboard", clipboard);
+    }
+  unblock_input ();
+
+  return Qnil;
+}
+
+void
+syms_of_haikuselect (void)
+{
+  DEFSYM (QSECONDARY, "SECONDARY");
+  DEFSYM (QCLIPBOARD, "CLIPBOARD");
+  DEFSYM (QSTRING, "STRING");
+  DEFSYM (QUTF8_STRING, "UTF8_STRING");
+  DEFSYM (Qforeign_selection, "foreign-selection");
+
+  defsubr (&Shaiku_selection_data);
+  defsubr (&Shaiku_selection_put);
+}
diff --git a/src/haikuselect.h b/src/haikuselect.h
new file mode 100644
index 0000000000..000c83b4dd
--- /dev/null
+++ b/src/haikuselect.h
@@ -0,0 +1,64 @@
+/* Haiku window system selection support. Hey Emacs, this is -*- C++ -*-
+   Copyright (C) 2021 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
+
+#ifndef _HAIKU_SELECT_H_
+#define _HAIKU_SELECT_H_
+
+#ifdef __cplusplus
+#include <cstdio>
+#endif
+
+#ifdef __cplusplus
+#include <stdio.h>
+extern "C"
+{
+  extern void init_haiku_select (void);
+#endif
+
+  /* Whether or not the selection was recently changed. */
+  extern int selection_state_flag;
+
+  /* Find a string with the MIME type TYPE in the system clipboard. */
+  extern char *
+  BClipboard_find_system_data (const char *type, ssize_t *len);
+
+  /* Ditto, but for the primary selection and not clipboard. */
+  extern char *
+  BClipboard_find_primary_selection_data (const char *type, ssize_t *len);
+
+  /* Ditto, this time for the secondary selection. */
+  extern char *
+  BClipboard_find_secondary_selection_data (const char *type, ssize_t *len);
+
+  extern void
+  BClipboard_set_system_data (const char *type, const char *data, ssize_t len);
+
+  extern void
+  BClipboard_set_primary_selection_data (const char *type, const char *data,
+					 ssize_t len);
+
+  extern void
+  BClipboard_set_secondary_selection_data (const char *type, const char *data,
+					   ssize_t len);
+
+  /* Free the returned data. */
+  extern void BClipboard_free_data (void *ptr);
+#ifdef __cplusplus
+};
+#endif
+#endif /* _HAIKU_SELECT_H_ */
diff --git a/src/haikuterm.c b/src/haikuterm.c
new file mode 100644
index 0000000000..f2681d0798
--- /dev/null
+++ b/src/haikuterm.c
@@ -0,0 +1,2925 @@
+/* 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 "window.h"
+
+#include <math.h>
+
+struct haiku_display_info *x_display_list = NULL;
+extern frame_parm_handler haiku_frame_parm_handlers[];
+
+static void **fringe_bmps;
+static int fringe_bitmap_fillptr = 0;
+
+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_delete_terminal (struct terminal *terminal)
+{
+  emacs_abort ();
+}
+
+static const char *
+get_string_resource (void *ignored, const char *name, const char *class)
+{
+  return NULL;
+}
+
+static void
+haiku_clip_to_string (struct glyph_string *s)
+{
+  struct haiku_rect r[2];
+  int n = get_glyph_string_clip_rects (s, (struct haiku_rect *) &r, 2);
+
+  if (n)
+    BView_ClipToRect (FRAME_HAIKU_VIEW (s->f), r[0].x, r[0].y,
+		      r[0].width, r[0].height);
+  if (n > 1)
+    {
+      BView_ClipToRect (FRAME_HAIKU_VIEW (s->f), r[1].x, r[1].y,
+			r[1].width, r[1].height);
+    }
+}
+
+static void
+haiku_flush_transaction (struct frame *f)
+{
+  void *view = FRAME_OUTPUT_DATA (f)->view;
+  BView_draw_lock (view);
+  BView_EndTransaction (view);
+  FRAME_DIRTY_P (f) = 0;
+  BView_BeginTransaction (view);
+  BView_draw_unlock (view);
+}
+
+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_flush_transaction (f);
+  unblock_input ();
+}
+
+static void
+haiku_buffer_flipping_unblocked_hook (struct frame *f)
+{
+  if (FRAME_DIRTY_P (f))
+    haiku_flush_transaction (f);
+}
+
+/* Set the clipping region to the inverse of all scroll bars in the
+   frame F.  This ensures we never clear a scrollbar by accident. */
+static void
+haiku_inverse_clip_to_scroll_bars (struct frame *f)
+{
+  Lisp_Object tem;
+  void *view = FRAME_HAIKU_VIEW (f);
+
+  for (tem = FRAME_SCROLL_BARS (f); VECTORLIKEP (tem);
+       tem = XSCROLL_BAR (tem)->next)
+    {
+      struct scroll_bar *sb = XSCROLL_BAR (tem);
+      if (sb->scroll_bar)
+	{
+	  BView_ClipToInverseRect (view, sb->left, sb->top,
+				   sb->width, sb->height);
+	}
+    }
+}
+
+static void
+haiku_clear_frame_area (struct frame *f, int x, int y,
+			int width, int height)
+{
+  void *vw = FRAME_HAIKU_VIEW (f);
+  block_input ();
+  BView_draw_lock (vw);
+  BView_StartClip (vw);
+  haiku_inverse_clip_to_scroll_bars (f);
+  BView_SetHighColor (vw, FRAME_BACKGROUND_PIXEL (f));
+  BView_FillRectangle (vw, x, y, width, height);
+  BView_EndClip (vw);
+  BView_draw_unlock (vw);
+  unblock_input ();
+}
+
+static void
+haiku_clear_frame (struct frame *f)
+{
+  void *view = FRAME_HAIKU_VIEW (f);
+  BView_StartClip (view);
+  BView_draw_lock (view);
+  haiku_inverse_clip_to_scroll_bars (f);
+  BView_SetHighColor (view, FRAME_BACKGROUND_PIXEL (f));
+  BView_FillRectangle (view, 0, 0, FRAME_PIXEL_WIDTH (f),
+		       FRAME_PIXEL_HEIGHT (f));
+  BView_draw_unlock (view);
+  BView_EndClip (view);
+}
+
+/* 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);
+
+  return font_object;
+}
+
+static void
+haiku_rehighlight (void)
+{
+  eassert (x_display_list && !x_display_list->next);
+
+  block_input ();
+
+  struct frame *old_hl = x_display_list->highlight_frame;
+
+  if (x_display_list->focused_frame)
+    {
+      x_display_list->highlight_frame
+	= ((FRAMEP (FRAME_FOCUS_FRAME (x_display_list->focused_frame)))
+	   ? XFRAME (FRAME_FOCUS_FRAME (x_display_list->focused_frame))
+	   : x_display_list->focused_frame);
+      if (!FRAME_LIVE_P (x_display_list->highlight_frame))
+	{
+	  fset_focus_frame (x_display_list->focused_frame, Qnil);
+	  x_display_list->highlight_frame = x_display_list->focused_frame;
+	}
+    }
+  else
+    x_display_list->highlight_frame = 0;
+
+  if (old_hl)
+    gui_update_cursor (old_hl, true);
+
+  if (x_display_list->highlight_frame)
+    gui_update_cursor (x_display_list->highlight_frame, true);
+  unblock_input ();
+}
+
+static void
+haiku_frame_raise_lower (struct frame *f, bool raise_p)
+{
+  if (raise_p)
+    {
+      BWindow_activate (FRAME_HAIKU_WINDOW (f));
+      flush_frame (f);
+    }
+}
+
+static void
+haiku_new_focus_frame (struct frame *frame)
+{
+  eassert (x_display_list && !x_display_list->next);
+
+  block_input ();
+  if (frame != x_display_list->focused_frame)
+    {
+      if (x_display_list->focused_frame &&
+	  x_display_list->focused_frame->auto_lower)
+	haiku_frame_raise_lower (x_display_list->focused_frame, 0);
+
+      x_display_list->focused_frame = frame;
+
+      if (frame && frame->auto_raise)
+	haiku_frame_raise_lower (frame, 1);
+    }
+  unblock_input ();
+
+  haiku_rehighlight ();
+}
+
+static void
+haiku_implicitly_set_name (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
+{
+  haiku_set_name (f, arg, 0);
+}
+
+static void
+haiku_query_frame_background_color (struct frame *f, Emacs_Color *bgcolor)
+{
+  haiku_query_color (FRAME_BACKGROUND_PIXEL (f), bgcolor);
+}
+
+static bool
+haiku_defined_color (struct frame *f,
+		     const char *name,
+		     Emacs_Color *color,
+		     bool alloc,
+		     bool make_index)
+{
+  return !haiku_get_color (name, color);
+}
+
+static void
+haiku_draw_plain_background (struct glyph_string *s, struct face *face,
+			     int box_line_width)
+{
+  void *view = FRAME_HAIKU_VIEW (s->f);
+  BView_StartClip (view);
+  if (s->hl == DRAW_CURSOR)
+    BView_SetHighColor (view, FRAME_CURSOR_COLOR (s->f).pixel);
+  else
+    BView_SetHighColor (view, face->background_defaulted_p ?
+			FRAME_BACKGROUND_PIXEL (s->f) :
+		      face->background);
+  BView_FillRectangle (view, s->x, s->y + box_line_width,
+		       s->background_width,
+		       s->height - 2 * box_line_width);
+  BView_EndClip (view);
+}
+
+static void
+haiku_draw_stipple_background (struct glyph_string *s, struct face *face,
+			       int box_line_width)
+{
+}
+
+static void
+haiku_maybe_draw_background (struct glyph_string *s, int force_p)
+{
+  if ((s->first_glyph->type != IMAGE_GLYPH) &&
+      (!s->background_filled_p || s->hl == DRAW_MOUSE_FACE))
+    {
+      int box_line_width = max (s->face->box_horizontal_line_width, 0);
+      if (FONT_HEIGHT (s->font) < s->height - 2 * box_line_width
+	  || FONT_TOO_HIGH (s->font)
+          || s->font_not_found_p || s->extends_to_end_of_line_p || force_p)
+	{
+	  struct face *face;
+          if (s->hl == DRAW_MOUSE_FACE)
+            {
+              face = FACE_FROM_ID_OR_NULL (s->f,
+					   MOUSE_HL_INFO (s->f)->mouse_face_face_id);
+              if (!face)
+                face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
+            }
+          else
+            face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
+	  if (!face->stipple)
+	    haiku_draw_plain_background (s, face, box_line_width);
+	  else
+	    haiku_draw_stipple_background (s, face, box_line_width);
+	  s->background_filled_p = 1;
+	}
+    }
+}
+
+/* Adapted from xterm `x_draw_box_rect' */
+static void
+haiku_draw_box_rect (struct glyph_string *s,
+		     int left_x, int top_y, int right_x, int bottom_y, int hwidth,
+		     int vwidth, bool left_p, bool right_p, struct haiku_rect *clip_rect)
+{
+  void *view = FRAME_HAIKU_VIEW (s->f);
+
+  BView_StartClip (view);
+  BView_SetHighColor (view, s->face->box_color);
+  if (clip_rect)
+    BView_ClipToRect (view, clip_rect->x, clip_rect->y, clip_rect->width,
+		      clip_rect->height);
+  BView_FillRectangle (view, left_x, top_y, right_x - left_x, hwidth);
+  if (left_p)
+    BView_FillRectangle (view, left_x, top_y, vwidth, bottom_y - top_y);
+
+  BView_FillRectangle (view, left_x, bottom_y - hwidth,
+		       right_x - left_x, hwidth);
+  if (right_p)
+    BView_FillRectangle (view, right_x - vwidth, top_y, vwidth, bottom_y - top_y);
+  BView_EndClip (view);
+}
+
+static void
+haiku_calculate_relief_colors (struct glyph_string *s,
+			       uint32_t *rgbout_w, uint32_t *rgbout_b)
+{
+  struct face *face = s->face;
+
+  if (s->hl == DRAW_MOUSE_FACE)
+    face = FACE_FROM_ID_OR_NULL (s->f,
+				 MOUSE_HL_INFO (s->f)->mouse_face_face_id);
+  if (!face && s->hl == DRAW_MOUSE_FACE)
+    face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
+
+  if (!face)
+    face = s->face;
+
+  prepare_face_for_display (s->f, s->face);
+
+  uint32_t rgbin = face->use_box_color_for_shadows_p ?
+    face->box_color :
+    (s->first_glyph->type == IMAGE_GLYPH
+     && s->img->pixmap
+     && !IMAGE_BACKGROUND_TRANSPARENT (s->img, s->f, 0)) ?
+    image_background (s->img, s->f, 0) : face->background;
+
+  double h, cs, l;
+  rgb_color_hsl (rgbin, &h, &cs, &l);
+
+  hsl_color_rgb (h, cs, fmin (1.0, l * 0.7), rgbout_b);
+  hsl_color_rgb (h, cs, fmin (1.0, l * 1.1), rgbout_w);
+}
+
+static void
+haiku_draw_relief_rect (struct glyph_string *s,
+			int left_x, int top_y, int right_x, int bottom_y,
+			int hwidth, int vwidth, bool raised_p, bool top_p, bool bot_p,
+			bool left_p, bool right_p,
+			struct haiku_rect *clip_rect)
+{
+  uint32_t color_white;
+  uint32_t color_black;
+
+  haiku_calculate_relief_colors (s, &color_white, &color_black);
+
+  void *view = FRAME_HAIKU_VIEW (s->f);
+  BView_StartClip (view);
+  BView_SetHighColor (view, raised_p ? color_white : color_black);
+
+  if (clip_rect)
+    BView_ClipToRect (view, clip_rect->x, clip_rect->y, clip_rect->width,
+		      clip_rect->height);
+  if (top_p)
+    BView_FillRectangle (view, left_x, top_y, right_x - left_x, hwidth);
+  if (left_p)
+    BView_FillRectangle (view, left_x, top_y, vwidth, bottom_y - top_y);
+  BView_SetHighColor (view, !raised_p ? color_white : color_black);
+  if (bot_p)
+    BView_FillRectangle (view, left_x, bottom_y - hwidth,
+			 right_x - left_x, hwidth);
+  if (right_p)
+    BView_FillRectangle (view, right_x - vwidth, top_y, vwidth, bottom_y - top_y);
+
+  BView_EndClip (view);
+}
+
+static void
+haiku_mouse_face_colors (struct glyph_string *s, uint32_t *fg,
+			 uint32_t *bg)
+{
+  int face_id;
+  struct face *face;
+
+  /* What face has to be used last for the mouse face?  */
+  face_id = MOUSE_HL_INFO (s->f)->mouse_face_face_id;
+  face = FACE_FROM_ID_OR_NULL (s->f, face_id);
+  if (face == NULL)
+    face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
+
+  if (s->first_glyph->type == CHAR_GLYPH)
+    face_id = FACE_FOR_CHAR (s->f, face, s->first_glyph->u.ch, -1, Qnil);
+  else
+    face_id = FACE_FOR_CHAR (s->f, face, 0, -1, Qnil);
+  s->face = FACE_FROM_ID (s->f, face_id);
+  prepare_face_for_display (s->f, s->face);
+
+  if (fg)
+    *fg = face->foreground;
+  if (bg)
+    *bg = face->background;
+}
+
+static void
+haiku_draw_underwave (struct glyph_string *s, int width, int x)
+{
+  int wave_height = 3, wave_length = 2;
+  int y, dx, dy, odd, xmax;
+  dx = wave_length;
+  dy = wave_height - 1;
+  y = s->ybase - wave_height + 3;
+
+  float ax, ay, bx, by;
+  xmax = x + width;
+
+  void *view = FRAME_HAIKU_VIEW (s->f);
+
+  BView_StartClip (view);
+  BView_ClipToRect (view, x, y, width, wave_height);
+  ax = x - ((int) (x) % dx) + (float) 0.5;
+  bx = ax + dx;
+  odd = (int) (ax / dx) % 2;
+  ay = by = y + 0.5;
+
+  if (odd)
+    ay += dy;
+  else
+    by += dy;
+
+  while (ax <= xmax)
+    {
+      BView_StrokeLine (view, ax, ay, bx, by);
+      ax = bx, ay = by;
+      bx += dx, by = y + 0.5 + odd * dy;
+      odd = !odd;
+    }
+  BView_EndClip (view);
+}
+
+static void
+haiku_draw_text_decoration (struct glyph_string *s, struct face *face,
+			    uint8_t dcol, int width, int x)
+{
+  if (s->for_overlaps)
+    return;
+
+  void *view = FRAME_HAIKU_VIEW (s->f);
+  BView_draw_lock (view);
+  BView_StartClip (view);
+
+  if (s->face->underline)
+    {
+      if (s->hl == DRAW_CURSOR)
+	BView_SetHighColor (view, FRAME_OUTPUT_DATA (s->f)->cursor_fg);
+      else if (!face->underline_defaulted_p)
+	BView_SetHighColor (view, face->underline_color);
+      else
+	BView_SetHighColor (view, dcol);
+
+      if (s->face->underline == FACE_UNDER_WAVE)
+	haiku_draw_underwave (s, width, x);
+      else if (s->face->underline == FACE_UNDER_LINE)
+	{
+	  unsigned long thickness, position;
+	  int y;
+
+	  if (s->prev &&
+	      s->prev->face->underline == FACE_UNDER_LINE)
+	    {
+	      /* We use the same underline style as the previous one.  */
+	      thickness = s->prev->underline_thickness;
+	      position = s->prev->underline_position;
+	    }
+	  else
+	    {
+	      struct font *font = font_for_underline_metrics (s);
+	      unsigned long minimum_offset;
+	      bool underline_at_descent_line;
+	      bool use_underline_position_properties;
+	      Lisp_Object val = (WINDOW_BUFFER_LOCAL_VALUE
+				 (Qunderline_minimum_offset, s->w));
+
+	      if (FIXNUMP (val))
+		minimum_offset = max (0, XFIXNUM (val));
+	      else
+		minimum_offset = 1;
+
+	      val = (WINDOW_BUFFER_LOCAL_VALUE
+		     (Qx_underline_at_descent_line, s->w));
+	      underline_at_descent_line
+		= !(NILP (val) || EQ (val, Qunbound));
+
+	      val = (WINDOW_BUFFER_LOCAL_VALUE
+		     (Qx_use_underline_position_properties, s->w));
+	      use_underline_position_properties
+		= !(NILP (val) || EQ (val, Qunbound));
+
+	      /* Get the underline thickness.  Default is 1 pixel.  */
+	      if (font && font->underline_thickness > 0)
+		thickness = font->underline_thickness;
+	      else
+		thickness = 1;
+	      if (underline_at_descent_line)
+		position = (s->height - thickness) - (s->ybase - s->y);
+	      else
+		{
+		  /* Get the underline position.  This is the
+		     recommended vertical offset in pixels from
+		     the baseline to the top of the underline.
+		     This is a signed value according to the
+		     specs, and its default is
+
+		     ROUND ((maximum descent) / 2), with
+		     ROUND(x) = floor (x + 0.5)  */
+
+		  if (use_underline_position_properties
+		      && font && font->underline_position >= 0)
+		    position = font->underline_position;
+		  else if (font)
+		    position = (font->descent + 1) / 2;
+		  else
+		    position = minimum_offset;
+		}
+	      position = max (position, minimum_offset);
+	    }
+	  /* Check the sanity of thickness and position.  We should
+	     avoid drawing underline out of the current line area.  */
+	  if (s->y + s->height <= s->ybase + position)
+	    position = (s->height - 1) - (s->ybase - s->y);
+	  if (s->y + s->height < s->ybase + position + thickness)
+	    thickness = (s->y + s->height) - (s->ybase + position);
+	  s->underline_thickness = thickness;
+	  s->underline_position = position;
+	  y = s->ybase + position;
+
+	  BView_FillRectangle (view, s->x, y, s->width, thickness);
+	}
+    }
+
+  if (s->face->overline_p)
+    {
+      unsigned long dy = 0, h = 1;
+      if (s->hl == DRAW_CURSOR)
+	BView_SetHighColor (view, FRAME_OUTPUT_DATA (s->f)->cursor_fg);
+      else if (!face->overline_color_defaulted_p)
+	BView_SetHighColor (view, face->overline_color);
+      else
+	BView_SetHighColor (view, dcol);
+
+      BView_FillRectangle (view, s->x, s->y + dy, s->width, h);
+    }
+
+  if (s->face->strike_through_p)
+    {
+      /* Y-coordinate and height of the glyph string's first
+	 glyph.  We cannot use s->y and s->height because those
+	 could be larger if there are taller display elements
+	 (e.g., characters displayed with a larger font) in the
+	 same glyph row.  */
+      int glyph_y = s->ybase - s->first_glyph->ascent;
+      int glyph_height = s->first_glyph->ascent + s->first_glyph->descent;
+      /* Strike-through width and offset from the glyph string's
+	 top edge.  */
+      unsigned long h = 1;
+      unsigned long dy = (glyph_height - h) / 2;
+
+      if (s->hl == DRAW_CURSOR)
+	BView_SetHighColor (view, FRAME_OUTPUT_DATA (s->f)->cursor_fg);
+      else if (!face->overline_color_defaulted_p)
+	BView_SetHighColor (view, face->overline_color);
+      else
+	BView_SetHighColor (view, dcol);
+
+      BView_FillRectangle (view, s->x, glyph_y + dy, s->width, h);
+    }
+
+  BView_EndClip (view);
+  BView_draw_unlock (view);
+}
+
+static void
+haiku_draw_string_box (struct glyph_string *s)
+{
+  int hwidth, vwidth, left_x, right_x, top_y, bottom_y, last_x;
+  bool raised_p, left_p, right_p;
+  struct glyph *last_glyph;
+  struct haiku_rect clip_rect;
+
+  last_x = ((s->row->full_width_p && !s->w->pseudo_window_p)
+	    ? WINDOW_RIGHT_EDGE_X (s->w)
+	    : window_box_right (s->w, s->area));
+
+  /* The glyph that may have a right box line.  For static
+     compositions and images, the right-box flag is on the first glyph
+     of the glyph string; for other types it's on the last glyph.  */
+  if (s->cmp || s->img)
+    last_glyph = s->first_glyph;
+  else if (s->first_glyph->type == COMPOSITE_GLYPH
+	   && s->first_glyph->u.cmp.automatic)
+    {
+      /* For automatic compositions, we need to look up the last glyph
+	 in the composition.  */
+        struct glyph *end = s->row->glyphs[s->area] + s->row->used[s->area];
+	struct glyph *g = s->first_glyph;
+	for (last_glyph = g++;
+	     g < end && g->u.cmp.automatic && g->u.cmp.id == s->cmp_id
+	       && g->slice.cmp.to < s->cmp_to;
+	     last_glyph = g++)
+	  ;
+    }
+  else
+    last_glyph = s->first_glyph + s->nchars - 1;
+
+  vwidth = eabs (s->face->box_vertical_line_width);
+  hwidth = eabs (s->face->box_horizontal_line_width);
+  raised_p = s->face->box == FACE_RAISED_BOX;
+  left_x = s->x;
+  right_x = (s->row->full_width_p && s->extends_to_end_of_line_p
+	     ? last_x
+	     : min (last_x, s->x + s->background_width));
+  top_y = s->y;
+  bottom_y = top_y + s->height;
+
+  left_p = (s->first_glyph->left_box_line_p
+	    || (s->hl == DRAW_MOUSE_FACE
+		&& (s->prev == NULL
+		    || s->prev->hl != s->hl)));
+  right_p = (last_glyph->right_box_line_p
+	     || (s->hl == DRAW_MOUSE_FACE
+		 && (s->next == NULL
+		     || s->next->hl != s->hl)));
+
+  get_glyph_string_clip_rect (s, &clip_rect);
+
+  if (s->face->box == FACE_SIMPLE_BOX)
+    haiku_draw_box_rect (s, left_x, top_y, right_x, bottom_y, hwidth,
+			 vwidth, left_p, right_p, &clip_rect);
+  else
+    haiku_draw_relief_rect (s, left_x, top_y, right_x, bottom_y, hwidth,
+			    vwidth, raised_p, true, true, left_p, right_p,
+			    &clip_rect);
+}
+
+static void
+haiku_draw_glyph_string_foreground (struct glyph_string *s)
+{
+  int i, x;
+  if (s->face->box != FACE_NO_BOX
+      && s->first_glyph->left_box_line_p)
+    x = s->x + max (s->face->box_vertical_line_width, 0);
+  else
+    x = s->x;
+
+  void *view = FRAME_HAIKU_VIEW (s->f);
+
+  if (s->font_not_found_p)
+    {
+      BView_StartClip (view);
+      if (s->hl == DRAW_CURSOR)
+	BView_SetHighColor (view, FRAME_OUTPUT_DATA (s->f)->cursor_fg);
+      else
+	BView_SetHighColor (view, s->face->foreground);
+      for (i = 0; i < s->nchars; ++i)
+	{
+	  struct glyph *g = s->first_glyph + i;
+	  BView_StrokeRectangle (view, x, s->y, g->pixel_width,
+				 s->height);
+	  x += g->pixel_width;
+	}
+      BView_EndClip (view);
+    }
+  else
+    {
+      struct font *ft = s->font;
+      int off = ft->baseline_offset;
+      int y;
+
+      if (ft->vertical_centering)
+	off = VCENTER_BASELINE_OFFSET (ft, s->f) - off;
+      y = s->ybase - off;
+      if (s->for_overlaps || (s->background_filled_p && s->hl != DRAW_CURSOR))
+	ft->driver->draw (s, 0, s->nchars, x, y, false);
+      else
+	ft->driver->draw (s, 0, s->nchars, x, y, true);
+
+      if (s->face->overstrike)
+	ft->driver->draw (s, 0, s->nchars, x + 1, y, false);
+    }
+}
+
+static void
+haiku_draw_glyphless_glyph_string_foreground (struct glyph_string *s)
+{
+  struct glyph *glyph = s->first_glyph;
+  unsigned char2b[8];
+  int x, i, j;
+
+  /* If first glyph of S has a left box line, start drawing the text
+     of S to the right of that box line.  */
+  if (s->face && s->face->box != FACE_NO_BOX
+      && s->first_glyph->left_box_line_p)
+    x = s->x + max (s->face->box_vertical_line_width, 0);
+  else
+    x = s->x;
+
+  s->char2b = char2b;
+
+  for (i = 0; i < s->nchars; i++, glyph++)
+    {
+#ifdef GCC_LINT
+      enum { PACIFY_GCC_BUG_81401 = 1 };
+#else
+      enum { PACIFY_GCC_BUG_81401 = 0 };
+#endif
+      char buf[7 + PACIFY_GCC_BUG_81401];
+      char *str = NULL;
+      int len = glyph->u.glyphless.len;
+
+      if (glyph->u.glyphless.method == GLYPHLESS_DISPLAY_ACRONYM)
+	{
+	  if (len > 0
+	      && CHAR_TABLE_P (Vglyphless_char_display)
+	      && (CHAR_TABLE_EXTRA_SLOTS (XCHAR_TABLE (Vglyphless_char_display))
+		  >= 1))
+	    {
+	      Lisp_Object acronym
+		= (! glyph->u.glyphless.for_no_font
+		   ? CHAR_TABLE_REF (Vglyphless_char_display,
+				     glyph->u.glyphless.ch)
+		   : XCHAR_TABLE (Vglyphless_char_display)->extras[0]);
+	      if (STRINGP (acronym))
+		str = SSDATA (acronym);
+	    }
+	}
+      else if (glyph->u.glyphless.method == GLYPHLESS_DISPLAY_HEX_CODE)
+	{
+	  unsigned int ch = glyph->u.glyphless.ch;
+	  eassume (ch <= MAX_CHAR);
+	  sprintf (buf, "%0*X", ch < 0x10000 ? 4 : 6, ch);
+	  str = buf;
+	}
+
+      if (str)
+	{
+	  int upper_len = (len + 1) / 2;
+
+	  /* It is assured that all LEN characters in STR is ASCII.  */
+	  for (j = 0; j < len; j++)
+            char2b[j] = s->font->driver->encode_char (s->font, str[j]) & 0xFFFF;
+
+	  s->font->driver->draw (s, 0, upper_len,
+				 x + glyph->slice.glyphless.upper_xoff,
+				 s->ybase + glyph->slice.glyphless.upper_yoff,
+				 false);
+	  s->font->driver->draw (s, upper_len, len,
+				 x + glyph->slice.glyphless.lower_xoff,
+				 s->ybase + glyph->slice.glyphless.lower_yoff,
+				 false);
+	}
+      BView_StartClip (FRAME_HAIKU_VIEW (s->f));
+      if (glyph->u.glyphless.method != GLYPHLESS_DISPLAY_THIN_SPACE)
+	BView_FillRectangle (FRAME_HAIKU_VIEW (s->f),
+			     x, s->ybase - glyph->ascent,
+			     glyph->pixel_width - 1,
+			     glyph->ascent + glyph->descent - 1);
+      BView_EndClip (FRAME_HAIKU_VIEW (s->f));
+      x += glyph->pixel_width;
+   }
+}
+
+static void
+haiku_draw_stretch_glyph_string (struct glyph_string *s)
+{
+  eassert (s->first_glyph->type == STRETCH_GLYPH);
+
+  if (s->hl == DRAW_CURSOR && !x_stretch_cursor_p)
+    {
+      int width, background_width = s->background_width;
+      int x = s->x;
+
+      if (!s->row->reversed_p)
+	{
+	  int left_x = window_box_left_offset (s->w, TEXT_AREA);
+
+	  if (x < left_x)
+	    {
+	      background_width -= left_x - x;
+	      x = left_x;
+	    }
+	}
+      else
+	{
+	  /* In R2L rows, draw the cursor on the right edge of the
+	     stretch glyph.  */
+	  int right_x = window_box_right (s->w, TEXT_AREA);
+	  if (x + background_width > right_x)
+	    background_width -= x - right_x;
+	  x += background_width;
+	}
+
+      width = min (FRAME_COLUMN_WIDTH (s->f), background_width);
+
+      haiku_maybe_draw_background (s, 1);
+
+      if (width < background_width)
+	{
+	  int y = s->y;
+	  int w = background_width - width, h = s->height;
+
+	  if (!s->face->stipple)
+	    {
+	      uint32_t bkg;
+	      if (s->hl == DRAW_MOUSE_FACE)
+		haiku_mouse_face_colors (s, NULL, &bkg);
+	      else
+		bkg = s->face->background;
+
+	      void *view = FRAME_HAIKU_VIEW (s->f);
+
+	      BView_StartClip (view);
+	      BView_SetHighColor (view, bkg);
+	      BView_FillRectangle (view, x, y, w, h);
+	      BView_EndClip (view);
+	    }
+	}
+    }
+  else if (!s->background_filled_p)
+    {
+      int background_width = s->background_width;
+      int x = s->x, text_left_x = window_box_left_offset (s->w, TEXT_AREA);
+      if (x < text_left_x && !s->row->mode_line_p)
+	{
+	  int left_x = WINDOW_LEFT_SCROLL_BAR_AREA_WIDTH (s->w);
+	  int right_x = text_left_x;
+
+	  if (WINDOW_HAS_FRINGES_OUTSIDE_MARGINS (s->w))
+	    left_x += WINDOW_LEFT_FRINGE_WIDTH (s->w);
+	  else
+	    right_x -= WINDOW_LEFT_FRINGE_WIDTH (s->w);
+
+	  /* Adjust X and BACKGROUND_WIDTH to fit inside the space
+	     between LEFT_X and RIGHT_X.  */
+	  if (x < left_x)
+	    {
+	      background_width -= left_x - x;
+	      x = left_x;
+	    }
+	  if (x + background_width > right_x)
+	    background_width = right_x - x;
+	}
+
+      if (background_width > 0)
+	{
+	  void *view = FRAME_HAIKU_VIEW (s->f);
+	  BView_StartClip (view);
+	  uint32_t bkg;
+	  if (s->hl == DRAW_MOUSE_FACE)
+	    haiku_mouse_face_colors (s, NULL, &bkg);
+	  else
+	    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_draw_unlock (view);
+  BView_EndClip (view);
+}
+
+static void
+haiku_clip_to_row (struct window *w, struct glyph_row *row,
+		   enum glyph_row_area area)
+{
+  struct frame *f = WINDOW_XFRAME (w);
+  int window_x, window_y, window_width;
+  int x, y, width, height;
+
+  window_box (w, area, &window_x, &window_y, &window_width, 0);
+
+  x = window_x;
+  y = WINDOW_TO_FRAME_PIXEL_Y (w, max (0, row->y));
+  y = max (y, window_y);
+  width = window_width;
+  height = row->visible_height;
+
+  BView_ClipToRect (FRAME_HAIKU_VIEW (f), x, y, width, height);
+}
+
+static void
+haiku_update_begin (struct frame *f)
+{
+  void *view = FRAME_HAIKU_VIEW (f);
+  BView_draw_lock (view);
+}
+
+static void
+haiku_update_end (struct frame *f)
+{
+  void *view = FRAME_HAIKU_VIEW (f);
+  BView_draw_unlock (view);
+  MOUSE_HL_INFO (f)->mouse_face_defer = false;
+  flush_frame (f);
+}
+
+static void
+haiku_draw_composite_glyph_string_foreground (struct glyph_string *s)
+{
+  int i, j, x;
+  struct font *font = s->font;
+  void *view = FRAME_HAIKU_VIEW (s->f);
+
+  /* If first glyph of S has a left box line, start drawing the text
+     of S to the right of that box line.  */
+  if (s->face && s->face->box != FACE_NO_BOX
+      && s->first_glyph->left_box_line_p)
+    x = s->x + max (s->face->box_vertical_line_width, 0);
+  else
+    x = s->x;
+
+  /* S is a glyph string for a composition.  S->cmp_from is the index
+     of the first character drawn for glyphs of this composition.
+     S->cmp_from == 0 means we are drawing the very first character of
+     this composition.  */
+
+  /* Draw a rectangle for the composition if the font for the very
+     first character of the composition could not be loaded.  */
+
+  if (s->font_not_found_p && !s->cmp_from)
+    {
+      BView_StartClip (view);
+      if (s->hl == DRAW_CURSOR)
+	BView_SetHighColor (view, FRAME_OUTPUT_DATA (s->f)->cursor_fg);
+      else
+	BView_SetHighColor (view, s->face->foreground);
+      BView_StrokeRectangle (view, s->x, s->y, s->width - 1, s->height - 1);
+      BView_EndClip (view);
+    }
+  else if (!s->first_glyph->u.cmp.automatic)
+    {
+      int y = s->ybase;
+
+      for (i = 0, j = s->cmp_from; i < s->nchars; i++, j++)
+	/* TAB in a composition means display glyphs with padding
+	   space on the left or right.  */
+	if (COMPOSITION_GLYPH (s->cmp, j) != '\t')
+	  {
+	    int xx = x + s->cmp->offsets[j * 2];
+	    int yy = y - s->cmp->offsets[j * 2 + 1];
+
+	    font->driver->draw (s, j, j + 1, xx, yy, false);
+	    if (s->face->overstrike)
+	      font->driver->draw (s, j, j + 1, xx + 1, yy, false);
+	  }
+    }
+  else
+    {
+      Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
+      Lisp_Object glyph;
+      int y = s->ybase;
+      int width = 0;
+
+      for (i = j = s->cmp_from; i < s->cmp_to; i++)
+	{
+	  glyph = LGSTRING_GLYPH (gstring, i);
+	  if (NILP (LGLYPH_ADJUSTMENT (glyph)))
+	    width += LGLYPH_WIDTH (glyph);
+	  else
+	    {
+	      int xoff, yoff, wadjust;
+
+	      if (j < i)
+		{
+		  font->driver->draw (s, j, i, x, y, false);
+		  if (s->face->overstrike)
+		    font->driver->draw (s, j, i, x + 1, y, false);
+		  x += width;
+		}
+	      xoff = LGLYPH_XOFF (glyph);
+	      yoff = LGLYPH_YOFF (glyph);
+	      wadjust = LGLYPH_WADJUST (glyph);
+	      font->driver->draw (s, i, i + 1, x + xoff, y + yoff, false);
+	      if (s->face->overstrike)
+		font->driver->draw (s, i, i + 1, x + xoff + 1, y + yoff,
+				    false);
+	      x += wadjust;
+	      j = i + 1;
+	      width = 0;
+	    }
+	}
+      if (j < i)
+	{
+	  font->driver->draw (s, j, i, x, y, false);
+	  if (s->face->overstrike)
+	    font->driver->draw (s, j, i, x + 1, y, false);
+	}
+    }
+}
+
+static void
+haiku_draw_image_relief (struct glyph_string *s)
+{
+  int x1, y1, thick;
+  bool raised_p, top_p, bot_p, left_p, right_p;
+  int extra_x, extra_y;
+  struct haiku_rect r;
+  int x = s->x;
+  int y = s->ybase - image_ascent (s->img, s->face, &s->slice);
+
+  /* If first glyph of S has a left box line, start drawing it to the
+     right of that line.  */
+  if (s->face->box != FACE_NO_BOX
+      && s->first_glyph->left_box_line_p
+      && s->slice.x == 0)
+    x += max (s->face->box_vertical_line_width, 0);
+
+  /* If there is a margin around the image, adjust x- and y-position
+     by that margin.  */
+  if (s->slice.x == 0)
+    x += s->img->hmargin;
+  if (s->slice.y == 0)
+    y += s->img->vmargin;
+
+  if (s->hl == DRAW_IMAGE_SUNKEN
+      || s->hl == DRAW_IMAGE_RAISED)
+    {
+      thick = (tab_bar_button_relief < 0
+	       ? DEFAULT_TAB_BAR_BUTTON_RELIEF
+	       : (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));
+	  extra_y = XFIXNUM (XCDR (Vtab_bar_button_margin));
+	}
+      else if (FIXNUMP (Vtab_bar_button_margin))
+	extra_x = extra_y = XFIXNUM (Vtab_bar_button_margin);
+    }
+
+  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 = false;
+
+  if (s->slice.x == 0)
+    x -= thick + extra_x, left_p = true;
+  if (s->slice.y == 0)
+    y -= thick + extra_y, top_p = true;
+  if (s->slice.x + s->slice.width == s->img->width)
+    x1 += thick + extra_x, right_p = true;
+  if (s->slice.y + s->slice.height == s->img->height)
+    y1 += thick + extra_y, bot_p = true;
+
+  get_glyph_string_clip_rect (s, &r);
+  haiku_draw_relief_rect (s, x, y, x1, y1, thick, thick, raised_p,
+			  top_p, bot_p, left_p, right_p, &r);
+}
+
+static void
+haiku_draw_image_glyph_string (struct glyph_string *s)
+{
+  int box_line_hwidth = max (s->face->box_vertical_line_width, 0);
+  int box_line_vwidth = max (s->face->box_horizontal_line_width, 0);
+
+  int x, y;
+  int height, width;
+
+  height = s->height;
+  if (s->slice.y == 0)
+    height -= box_line_vwidth;
+  if (s->slice.y + s->slice.height >= s->img->height)
+    height -= box_line_vwidth;
+
+  width = s->background_width;
+  x = s->x;
+  if (s->first_glyph->left_box_line_p
+      && s->slice.x == 0)
+    {
+      x += box_line_hwidth;
+      width -= box_line_hwidth;
+    }
+
+  y = s->y;
+  if (s->slice.y == 0)
+    y += box_line_vwidth;
+
+  void *view = FRAME_HAIKU_VIEW (s->f);
+  void *bitmap = s->img->pixmap;
+  /* Fill background with face under the image.  Do it only if row is
+     taller than image or if image has a clip mask to reduce
+     flickering.  */
+  s->stippled_p = s->face->stipple != 0;
+  if (height > s->slice.height
+      || s->img->hmargin
+      || s->img->vmargin
+      || s->img->mask
+      || s->img->pixmap == 0
+      || s->width != s->background_width)
+    {
+      BView_draw_lock (view);
+      BView_StartClip (view);
+      BView_SetHighColor (view, s->face->background);
+      BView_FillRectangle (view, x, y, width, height);
+      BView_EndClip (view);
+      BView_draw_unlock (view);
+
+      if (!s->for_overlaps && s->face->box != FACE_NO_BOX)
+	haiku_draw_string_box (s);
+    }
+
+  if (bitmap)
+    {
+      struct haiku_rect nr;
+      Emacs_Rectangle cr, ir, r;
+
+      get_glyph_string_clip_rect (s, &nr);
+      CONVERT_TO_EMACS_RECT (cr, nr);
+      if (s->slice.x == 0)
+	x += s->img->hmargin;
+      if (s->slice.y == 0)
+	y += s->img->vmargin;
+      ir.x = x;
+      ir.y = y;
+      ir.width = s->slice.width;
+      ir.height = s->slice.height;
+      r = ir;
+
+      if (gui_intersect_rectangles (&cr, &ir, &r))
+	{
+	  BView_draw_lock (view);
+	  BView_StartClip (view);
+
+	  haiku_clip_to_string (s);
+	  if (s->img->mask)
+	    BView_BeginLayer (view, 1.0);
+	  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 (s->img->mask)
+	    {
+	      BView_DrawBitmapWithEraseOp (view, s->img->mask,
+					   s->slice.x + r.x - x,
+					   s->slice.y + r.y - y,
+					   r.width, r.height,
+					   r.x, r.y, r.width, r.height);
+	      BView_EndLayer (view);
+	    }
+	  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);
+
+  if (s->next && s->right_overhang && !s->for_overlaps)
+    {
+      int width;
+      struct glyph_string *next;
+
+      for (width = 0, next = s->next;
+	   next && width < s->right_overhang;
+	   width += next->width, next = next->next)
+	if (next->first_glyph->type != IMAGE_GLYPH)
+          {
+	    prepare_face_for_display (s->f, s->next->face);
+	    haiku_start_clip (s->next);
+	    haiku_clip_to_string (s->next);
+            if (next->first_glyph->type != STRETCH_GLYPH)
+	      haiku_maybe_draw_background (s->next, 1);
+            else
+	      haiku_draw_stretch_glyph_string (s->next);
+            next->num_clips = 0;
+	    haiku_end_clip (s);
+          }
+    }
+
+  haiku_start_clip (s);
+  haiku_inverse_clip_to_scroll_bars (s->f);
+
+  if (!s->for_overlaps && s->face->box != FACE_NO_BOX
+      && (s->first_glyph->type == CHAR_GLYPH
+	  || s->first_glyph->type == COMPOSITE_GLYPH))
+    {
+      haiku_clip_to_string (s);
+      haiku_maybe_draw_background (s, 1);
+      haiku_draw_string_box (s);
+    }
+  else
+    haiku_clip_to_string (s);
+
+  if (s->for_overlaps)
+    s->background_filled_p = true;
+
+  switch (s->first_glyph->type)
+    {
+    case COMPOSITE_GLYPH:
+      if (s->for_overlaps || (s->cmp_from > 0
+			      && ! s->first_glyph->u.cmp.automatic))
+	s->background_filled_p = 1;
+      else
+	haiku_maybe_draw_background (s, 1);
+      haiku_draw_composite_glyph_string_foreground (s);
+      break;
+    case CHAR_GLYPH:
+      if (s->for_overlaps)
+	s->background_filled_p = 1;
+      else
+	haiku_maybe_draw_background (s, 0);
+      haiku_draw_glyph_string_foreground (s);
+      break;
+    case STRETCH_GLYPH:
+      haiku_draw_stretch_glyph_string (s);
+      break;
+    case IMAGE_GLYPH:
+      haiku_draw_image_glyph_string (s);
+      break;
+    case GLYPHLESS_GLYPH:
+      if (s->for_overlaps)
+	s->background_filled_p = 1;
+      else
+	haiku_maybe_draw_background (s, 1);
+      haiku_draw_glyphless_glyph_string_foreground (s);
+      break;
+    }
+
+  if (!s->for_overlaps)
+    {
+      uint32_t dcol;
+      struct face *face;
+      if (s->hl == DRAW_MOUSE_FACE)
+	{
+	  face = FACE_FROM_ID_OR_NULL (s->f,
+				       MOUSE_HL_INFO (s->f)->mouse_face_face_id);
+	  if (!face)
+	    face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
+	}
+      else
+	face = s->face;
+      dcol = face->foreground;
+
+      haiku_draw_text_decoration (s, face, dcol, s->width, s->x);
+    }
+
+  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_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);
+	}
+      else
+	{
+	  haiku_clear_frame_area (f, 0, y, width, height);
+	  haiku_clear_frame_area (f, FRAME_PIXEL_WIDTH (f) - width,
+				  y, width, height);
+	}
+      unblock_input ();
+    }
+}
+
+static void
+haiku_set_window_size (struct frame *f, bool change_gravity,
+		       int width, int height)
+{
+  if (FRAME_HAIKU_WINDOW (f))
+    {
+      block_input ();
+      BWindow_resize (FRAME_HAIKU_WINDOW (f), width, height);
+      unblock_input ();
+    }
+}
+
+static void
+haiku_draw_window_cursor (struct window *w,
+			  struct glyph_row *glyph_row,
+			  int x, int y,
+			  enum text_cursor_kinds cursor_type,
+			  int cursor_width, bool on_p, bool active_p)
+{
+  struct frame *f = XFRAME (WINDOW_FRAME (w));
+
+  struct glyph *phys_cursor_glyph;
+  struct glyph *cursor_glyph;
+
+  void *view = FRAME_HAIKU_VIEW (f);
+
+  int fx, fy, h, cursor_height;
+
+  if (!on_p)
+    return;
+
+  if (cursor_type == NO_CURSOR)
+    {
+      w->phys_cursor_width = 0;
+      return;
+    }
+
+  w->phys_cursor_on_p = true;
+  w->phys_cursor_type = cursor_type;
+
+  phys_cursor_glyph = get_phys_cursor_glyph (w);
+
+  if (!phys_cursor_glyph)
+    {
+      if (glyph_row->exact_window_width_line_p
+          && w->phys_cursor.hpos >= glyph_row->used[TEXT_AREA])
+        {
+          glyph_row->cursor_in_fringe_p = 1;
+          draw_fringe_bitmap (w, glyph_row, 0);
+        }
+      return;
+    }
+
+  get_phys_cursor_geometry (w, glyph_row, phys_cursor_glyph, &fx, &fy, &h);
+
+  if (cursor_type == BAR_CURSOR)
+    {
+      if (cursor_width < 1)
+	cursor_width = max (FRAME_CURSOR_WIDTH (f), 1);
+      if (cursor_width < w->phys_cursor_width)
+        w->phys_cursor_width = cursor_width;
+    }
+  else if (cursor_type == HBAR_CURSOR)
+    {
+      cursor_height = (cursor_width < 1) ? lrint (0.25 * h) : cursor_width;
+      if (cursor_height > glyph_row->height)
+        cursor_height = glyph_row->height;
+      if (h > cursor_height)
+        fy += h - cursor_height;
+      h = cursor_height;
+    }
+
+  if (glyph_row->exact_window_width_line_p
+      && (glyph_row->reversed_p
+	  ? (w->phys_cursor.hpos < 0)
+	  : (w->phys_cursor.hpos >= glyph_row->used[TEXT_AREA])))
+    {
+      glyph_row->cursor_in_fringe_p = true;
+      draw_fringe_bitmap (w, glyph_row, glyph_row->reversed_p);
+      return;
+    }
+
+  if (cursor_type == HOLLOW_BOX_CURSOR &&
+      phys_cursor_glyph->type != IMAGE_GLYPH)
+    draw_phys_cursor_glyph (w, glyph_row, DRAW_NORMAL_TEXT);
+
+  BView_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);
+}
+
+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)
+{
+  struct font *font = s->font;
+
+  if (s->char2b)
+    {
+      struct font_metrics metrics;
+      unsigned int codes[2];
+      codes[0] = *(s->char2b);
+      codes[1] = *(s->char2b + s->nchars - 1);
+
+      font->driver->text_extents (font, codes, 2, &metrics);
+      s->left_overhang = -metrics.lbearing;
+      s->right_overhang
+	= metrics.rbearing > metrics.width
+	? metrics.rbearing - metrics.width : 0;
+    }
+  else
+    {
+      s->left_overhang = 0;
+      s->right_overhang = 0;
+    }
+}
+
+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_StartClip (view);
+  if (face)
+    BView_SetHighColor (view, face->foreground);
+  BView_StrokeLine (view, x, y_0, x, y_1);
+  BView_EndClip (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_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);
+      BView_SetHighColor (view, color);
+      BView_StrokeLine (view, x0 + 1, y0, x0 + 1, y1);
+      BView_SetHighColor (view, color_last);
+      BView_StrokeLine (view, x1 - 1, y0, x1 - 1, y1);
+    }
+  BView_EndClip (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->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;
+    }
+  else
+    {
+      bar = XSCROLL_BAR (w->horizontal_scroll_bar);
+
+      if (!bar->dragging)
+	{
+	  BView_scroll_bar_update (bar->scroll_bar, portion, whole, position);
+	  BView_invalidate (bar->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;
+	}
+    }
+
+  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);
+    }
+  else
+    {
+      bar = XSCROLL_BAR (w->vertical_scroll_bar);
+
+      if (!bar->dragging)
+	{
+	  BView_scroll_bar_update (bar->scroll_bar, portion, whole, position);
+	  bar->update = position;
+	  BView_invalidate (bar->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;
+	}
+    }
+
+  bar->position = position;
+  bar->total = whole;
+
+  XSETVECTOR (barobj, bar);
+  wset_vertical_scroll_bar (w, barobj);
+  unblock_input ();
+}
+
+static void
+haiku_draw_fringe_bitmap (struct window *w, struct glyph_row *row,
+			  struct draw_fringe_bitmap_params *p)
+{
+  void *view = FRAME_HAIKU_VIEW (XFRAME (WINDOW_FRAME (w)));
+  struct face *face = p->face;
+
+  BView_draw_lock (view);
+  BView_StartClip (view);
+  haiku_clip_to_row (w, row, ANY_AREA);
+  if (p->bx >= 0)
+    {
+      BView_SetHighColor (view, face->background);
+      BView_FillRectangle (view, p->bx, p->by, p->nx, p->ny);
+    }
+
+  if (p->which && p->which < fringe_bitmap_fillptr)
+    {
+      void *bitmap = fringe_bmps[p->which];
+
+      uint32_t col;
+      if (!p->cursor_p)
+	col = face->foreground;
+      else if (p->overlay_p)
+	col = face->background;
+      else
+	col = FRAME_CURSOR_COLOR (XFRAME (WINDOW_FRAME (w))).pixel;
+      BView_SetHighColor (view, col);
+      BView_DrawBitmapWithOverOp (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);
+  BView_StartClip (view);
+  BView_CopyBits (view, x, from_y, width, height,
+		  x, to_y, width, height);
+  BView_EndClip (view);
+  BView_draw_unlock (view);
+
+  unblock_input ();
+}
+
+static void
+haiku_mouse_position (struct frame **fp, int insist, Lisp_Object *bar_window,
+		      enum scroll_bar_part *part, Lisp_Object *x, Lisp_Object *y,
+		      Time *timestamp)
+{
+  block_input ();
+  if (!fp)
+    return;
+  Lisp_Object frame, tail;
+  struct frame *f1 = NULL;
+  FOR_EACH_FRAME (tail, frame)
+    XFRAME (frame)->mouse_moved = false;
+
+  if (gui_mouse_grabbed (x_display_list) && !EQ (track_mouse, Qdropping))
+    f1 = x_display_list->last_mouse_frame;
+
+  if (!f1 || FRAME_TOOLTIP_P (f1))
+    f1 = ((EQ (track_mouse, Qdropping) && gui_mouse_grabbed (x_display_list))
+	  ? x_display_list->last_mouse_frame
+	  : NULL);
+
+  if (!f1 && insist > 0)
+    f1 = SELECTED_FRAME ();
+
+  if (f1)
+    {
+      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)
+{
+  block_input ();
+  if (!f->pointer_invisible && FRAME_HAIKU_VIEW (f))
+    {
+      BView_set_view_cursor (FRAME_HAIKU_VIEW (f), cursor);
+    }
+  unblock_input ();
+  FRAME_OUTPUT_DATA (f)->current_cursor = cursor;
+}
+
+static void
+haiku_update_window_end (struct window *w, bool cursor_on_p,
+			 bool mouse_face_overwritten_p)
+{
+
+}
+
+static 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,
+    0 /* default font parameter */
+  };
+
+static int
+haiku_read_socket (struct terminal *terminal, struct input_event *hold_quit)
+{
+  block_input ();
+  int message_count = 0;
+  void *buf;
+  ssize_t b_size;
+
+  buf = alloca (200);
+  haiku_read_size (&b_size);
+  while (b_size >= 0)
+    {
+      enum haiku_event_type type;
+      struct input_event inev, inev2;
+
+      if (b_size > 200)
+	emacs_abort ();
+
+      EVENT_INIT (inev);
+      EVENT_INIT (inev2);
+      inev.kind = NO_EVENT;
+      inev2.kind = NO_EVENT;
+      inev.arg = Qnil;
+      inev2.arg = Qnil;
+
+      haiku_read (&type, buf, b_size);
+
+      switch (type)
+	{
+	case QUIT_REQUESTED:
+	  {
+	    struct haiku_quit_requested_event *b = buf;
+	    struct frame *f = haiku_window_to_frame (b->window);
+
+	    if (!f)
+	      continue;
+
+	    inev.kind = DELETE_WINDOW_EVENT;
+	    XSETFRAME (inev.frame_or_window, f);
+	    break;
+	  }
+	case FRAME_RESIZED:
+	  {
+	    struct haiku_resize_event *b = buf;
+	    struct frame *f = haiku_window_to_frame (b->window);
+
+	    if (!f)
+	      continue;
+
+	    int width = (int) b->px_widthf;
+	    int height = (int) b->px_heightf;
+
+	    BView_resize_to (FRAME_HAIKU_VIEW (f), width, height);
+	    if (width != FRAME_PIXEL_WIDTH (f)
+		|| height != FRAME_PIXEL_HEIGHT (f)
+		|| (f->new_size_p
+		    && ((f->new_width >= 0 && width != f->new_width)
+			|| (f->new_height >= 0 && height != f->new_height))))
+	      {
+		change_frame_size (f, width, height, false, true, false);
+		SET_FRAME_GARBAGED (f);
+		cancel_mouse_face (f);
+	      }
+	    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);
+	    break;
+	  }
+	case KEY_DOWN:
+	  {
+	    struct haiku_key_event *b = buf;
+	    struct frame *f = haiku_window_to_frame (b->window);
+	    int non_ascii_p;
+	    if (!f)
+	      continue;
+
+	    BMapKey (b->kc, &non_ascii_p, &inev.code);
+
+	    if (!non_ascii_p && !(b->modifiers &~ HAIKU_MODIFIER_SHIFT))
+	      inev.code = b->unraw_mb_char;
+	    else if (b->modifiers && !non_ascii_p)
+	      inev.code = b->mb_char;
+
+	    if (non_ascii_p)
+	      inev.kind = NON_ASCII_KEYSTROKE_EVENT;
+	    else
+	      inev.kind = inev.code > 127 ? MULTIBYTE_CHAR_KEYSTROKE_EVENT :
+		ASCII_KEYSTROKE_EVENT;
+
+	    if (b->modifiers & HAIKU_MODIFIER_SHIFT)
+	      inev.modifiers |= shift_modifier;
+	    if (b->modifiers & HAIKU_MODIFIER_CTRL)
+	      inev.modifiers |= ctrl_modifier;
+	    if (b->modifiers & HAIKU_MODIFIER_ALT)
+	      inev.modifiers |= meta_modifier;
+
+	    XSETFRAME (inev.frame_or_window, f);
+	    break;
+	  }
+	case ACTIVATION:
+	  {
+	    struct haiku_activation_event *b = buf;
+	    struct frame *f = haiku_window_to_frame (b->window);
+
+	    if (!f)
+	      continue;
+
+	    if ((x_display_list->focus_event_frame != f && b->activated_p) ||
+		(x_display_list->focus_event_frame == f && !b->activated_p))
+	      {
+		haiku_new_focus_frame (b->activated_p ? f : NULL);
+		if (b->activated_p)
+		  x_display_list->focus_event_frame = f;
+		else
+		  x_display_list->focus_event_frame = NULL;
+		inev.kind = b->activated_p ? FOCUS_IN_EVENT : FOCUS_OUT_EVENT;
+		XSETFRAME (inev.frame_or_window, f);
+	      }
+
+	    break;
+	  }
+	case MOUSE_MOTION:
+	  {
+	    struct haiku_mouse_motion_event *b = buf;
+	    struct frame *f = haiku_window_to_frame (b->window);
+
+	    if (!f)
+	      continue;
+
+	    Lisp_Object frame;
+	    XSETFRAME (frame, f);
+
+	    if (b->just_exited_p)
+	      {
+		Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f);
+		if (f == hlinfo->mouse_face_mouse_frame)
+		  {
+		    /* If we move outside the frame, then we're
+		       certainly no longer on any text in the frame.  */
+		    clear_mouse_face (hlinfo);
+		    hlinfo->mouse_face_mouse_frame = 0;
+		  }
+
+		haiku_new_focus_frame (x_display_list->focused_frame);
+		help_echo_string = Qnil;
+		gen_help_event (Qnil, frame, Qnil, Qnil, 0);
+	      }
+	    else
+	      {
+		struct haiku_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
+		struct haiku_rect r = dpyinfo->last_mouse_glyph;
+
+		dpyinfo->last_mouse_motion_x = b->x;
+		dpyinfo->last_mouse_motion_y = b->y;
+		dpyinfo->last_mouse_motion_frame = f;
+
+		previous_help_echo_string = help_echo_string;
+		help_echo_string = Qnil;
+
+		if (f != dpyinfo->last_mouse_glyph_frame ||
+		    b->x < r.x || b->x >= r.x + r.width || b->y < r.y ||
+		    b->y >= r.y + r.height)
+		  {
+		    f->mouse_moved = true;
+		    dpyinfo->last_mouse_scroll_bar = NULL;
+		    note_mouse_highlight (f, b->x, b->y);
+		    remember_mouse_glyph (f, b->x, b->y,
+					  &FRAME_DISPLAY_INFO (f)->last_mouse_glyph);
+		    dpyinfo->last_mouse_glyph_frame = f;
+		    gen_help_event (help_echo_string, frame, help_echo_window,
+				    help_echo_object, help_echo_pos);
+		  }
+
+		if (MOUSE_HL_INFO (f)->mouse_face_hidden)
+		  {
+		    MOUSE_HL_INFO (f)->mouse_face_hidden = 0;
+		    clear_mouse_face (MOUSE_HL_INFO (f));
+		  }
+
+		if (!NILP (Vmouse_autoselect_window))
+		  {
+		    static Lisp_Object last_mouse_window;
+		    Lisp_Object window = window_from_coordinates (f, b->x, b->y, 0, 0, 0);
+
+		    if (WINDOWP (window)
+			&& !EQ (window, last_mouse_window)
+			&& !EQ (window, selected_window)
+			&& (!NILP (focus_follows_mouse)
+				|| (EQ (XWINDOW (window)->frame,
+					XWINDOW (selected_window)->frame))))
+		      {
+			inev.kind = SELECT_WINDOW_EVENT;
+			inev.frame_or_window = window;
+		      }
+
+		    last_mouse_window = window;
+		  }
+	      }
+	    break;
+	  }
+	case BUTTON_UP:
+	case BUTTON_DOWN:
+	  {
+	    struct haiku_button_event *b = buf;
+	    struct frame *f = haiku_window_to_frame (b->window);
+	    int tab_bar_p = 0, tool_bar_p = 0;
+
+	    if (!f)
+	      continue;
+
+	    struct haiku_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
+
+	    if (b->modifiers & HAIKU_MODIFIER_SHIFT)
+	      inev.modifiers |= shift_modifier;
+	    if (b->modifiers & HAIKU_MODIFIER_CTRL)
+	      inev.modifiers |= ctrl_modifier;
+	    if (b->modifiers & HAIKU_MODIFIER_ALT)
+	      inev.modifiers |= meta_modifier;
+
+	    /* 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)
+		  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 && !tool_bar_p)
+	      inev.kind = MOUSE_CLICK_EVENT;
+	    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. */
+		SET_FRAME_GARBAGED (f);
+		expose_frame (f, 0, 0, 0, 0);
+	      }
+	    else
+	      {
+		SET_FRAME_VISIBLE (f, 1);
+		SET_FRAME_ICONIFIED (f, 0);
+		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;
+
+	    inev.kind = MOVE_FRAME_EVENT;
+	    XSETINT (inev.x, b->x);
+	    XSETINT (inev.y, b->y);
+
+	    XSETFRAME (inev.frame_or_window, f);
+	    break;
+	  }
+	case SCROLL_BAR_VALUE_EVENT:
+	  {
+	    struct haiku_scroll_bar_value_event *b = buf;
+	    struct scroll_bar *bar = b->scroll_bar;
+
+	    struct window *w = XWINDOW (bar->window);
+
+	    if (bar->update != -1)
+	      {
+		bar->update = -1;
+		break;
+	      }
+
+	    if (bar->position != b->position)
+	      {
+		inev.kind = bar->horizontal ? HORIZONTAL_SCROLL_BAR_CLICK_EVENT :
+		  SCROLL_BAR_CLICK_EVENT;
+		inev.part = bar->horizontal ?
+		  scroll_bar_horizontal_handle : scroll_bar_handle;
+
+		XSETINT (inev.x, b->position);
+		XSETINT (inev.y, bar->total);
+		XSETWINDOW (inev.frame_or_window, w);
+	      }
+	    break;
+	  }
+	case SCROLL_BAR_DRAG_EVENT:
+	  {
+	    struct haiku_scroll_bar_drag_event *b = buf;
+	    struct scroll_bar *bar = b->scroll_bar;
+
+	    bar->dragging = b->dragging_p;
+	    if (!b->dragging_p && bar->horizontal)
+	      set_horizontal_scroll_bar (XWINDOW (bar->window));
+	    else if (!b->dragging_p)
+	      set_vertical_scroll_bar (XWINDOW (bar->window));
+	    break;
+	  }
+	case WHEEL_MOVE_EVENT:
+	  {
+	    struct haiku_wheel_move_event *b = buf;
+	    struct frame *f = haiku_window_to_frame (b->window);
+	    int x, y;
+
+	    if (!f)
+	      continue;
+	    BView_get_mouse (FRAME_HAIKU_VIEW (f), &x, &y);
+
+	    if (b->modifiers & HAIKU_MODIFIER_SHIFT)
+	      inev.modifiers |= shift_modifier;
+	    if (b->modifiers & HAIKU_MODIFIER_CTRL)
+	      inev.modifiers |= ctrl_modifier;
+	    if (b->modifiers & HAIKU_MODIFIER_ALT)
+	      inev.modifiers |= meta_modifier;
+	    inev2.modifiers = inev.modifiers;
+
+	    if (b->delta_y)
+	      {
+		inev.kind = WHEEL_EVENT;
+		inev.code = 0;
+
+		XSETINT (inev.x, x);
+		XSETINT (inev.y, y);
+		XSETFRAME (inev.frame_or_window, f);
+
+		inev.modifiers |= signbit (b->delta_y) ?
+		  up_modifier : down_modifier;
+	      }
+
+	    if (b->delta_x)
+	      {
+		inev2.kind = HORIZ_WHEEL_EVENT;
+		inev2.code = 0;
+
+		XSETINT (inev2.x, x);
+		XSETINT (inev2.y, y);
+		XSETFRAME (inev2.frame_or_window, f);
+
+		inev2.modifiers |= signbit (b->delta_x) ?
+		  up_modifier : down_modifier;
+	      }
+
+	    break;
+	  }
+
+	case MENU_BAR_RESIZE:
+	  {
+	    struct haiku_menu_bar_resize_event *b = buf;
+	    struct frame *f = haiku_window_to_frame (b->window);
+
+	    if (!f || !FRAME_EXTERNAL_MENU_BAR (f))
+	      continue;
+
+	    int old_height = FRAME_MENU_BAR_HEIGHT (f);
+
+	    FRAME_MENU_BAR_HEIGHT (f) = b->height + 1;
+	    FRAME_MENU_BAR_LINES (f) =
+	      (b->height + FRAME_LINE_HEIGHT (f)) / FRAME_LINE_HEIGHT (f);
+	    if (FRAME_HAIKU_WINDOW (f))
+	      haiku_clear_under_internal_border (f);
+
+	    if (old_height != b->height)
+	      adjust_frame_size (f, -1, -1, 3, true, Qmenu_bar_lines);
+	    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));
+		    set_frame_menubar (f, 1);
+		    BView_draw_unlock (FRAME_HAIKU_VIEW (f));
+		  }
+		popup_activated_p += 1;
+	      }
+	    else
+	      {
+		if (!popup_activated_p)
+		  emacs_abort ();
+		popup_activated_p -= 1;
+	      }
+	    break;
+	  }
+	case MENU_BAR_SELECT_EVENT:
+	  {
+	    struct haiku_menu_bar_select_event *b = buf;
+	    struct frame *f = haiku_window_to_frame (b->window);
+
+	    if (!f || !FRAME_EXTERNAL_MENU_BAR (f))
+	      continue;
+
+	    find_and_call_menu_selection (f, f->menu_bar_items_used,
+					  f->menu_bar_vector, b->ptr);
+	    break;
+	  }
+	case 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;
+	}
+    }
+
+  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);
+	  BView_SetHighColor (view, FRAME_FOREGROUND_PIXEL (f));
+	  BView_FillRectangle (view, 0, 0, FRAME_PIXEL_WIDTH (f),
+			       FRAME_PIXEL_HEIGHT (f));
+	  BView_EndTransaction (view);
+	  flush_frame (f);
+	  BView_draw_lock (f);
+	  BView_draw_unlock (view);
+
+	  SET_FRAME_GARBAGED (f);
+	  expose_frame (f, 0, 0, 0, 0);
+	  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 struct terminal *
+haiku_create_terminal (struct haiku_display_info *dpyinfo)
+{
+  struct terminal *terminal;
+
+  terminal = create_terminal (output_haiku, &haiku_redisplay_interface);
+
+  terminal->display_info.haiku = dpyinfo;
+  dpyinfo->terminal = terminal;
+  terminal->kboard = allocate_kboard (Qhaiku);
+
+  terminal->iconify_frame_hook = haiku_iconify_frame;
+  terminal->ring_bell_hook = haiku_beep;
+  terminal->popup_dialog_hook = haiku_popup_dialog;
+  terminal->frame_visible_invisible_hook = haiku_set_frame_visible_invisible;
+  terminal->set_frame_offset_hook = haiku_set_offset;
+  terminal->delete_terminal_hook = haiku_delete_terminal;
+  terminal->get_string_resource_hook = get_string_resource;
+  terminal->set_new_font_hook = haiku_new_font;
+  terminal->defined_color_hook = haiku_defined_color;
+  terminal->set_window_size_hook = haiku_set_window_size;
+  terminal->read_socket_hook = haiku_read_socket;
+  terminal->implicit_set_name_hook = haiku_implicitly_set_name;
+  terminal->mouse_position_hook = haiku_mouse_position;
+  terminal->delete_frame_hook = haiku_delete_window;
+  terminal->frame_up_to_date_hook = haiku_frame_up_to_date;
+  terminal->buffer_flipping_unblocked_hook = haiku_buffer_flipping_unblocked_hook;
+  terminal->clear_frame_hook = haiku_clear_frame;
+  terminal->change_tab_bar_height_hook = haiku_change_tab_bar_height;
+  terminal->change_tool_bar_height_hook = haiku_change_tool_bar_height;
+  terminal->set_vertical_scroll_bar_hook = haiku_set_vertical_scroll_bar;
+  terminal->set_horizontal_scroll_bar_hook = haiku_set_horizontal_scroll_bar;
+  terminal->set_scroll_bar_default_height_hook = haiku_set_scroll_bar_default_height;
+  terminal->set_scroll_bar_default_width_hook = haiku_set_scroll_bar_default_width;
+  terminal->judge_scroll_bars_hook = haiku_judge_scroll_bars;
+  terminal->condemn_scroll_bars_hook = haiku_condemn_scroll_bars;
+  terminal->redeem_scroll_bar_hook = haiku_redeem_scroll_bar;
+  terminal->update_begin_hook = haiku_update_begin;
+  terminal->update_end_hook = haiku_update_end;
+  terminal->frame_rehighlight_hook = haiku_frame_rehighlight;
+  terminal->query_frame_background_color = haiku_query_frame_background_color;
+  terminal->free_pixmap = haiku_free_pixmap;
+  terminal->frame_raise_lower_hook = haiku_frame_raise_lower;
+  terminal->menu_show_hook = haiku_menu_show;
+  terminal->toggle_invisible_pointer_hook = haiku_toggle_invisible_pointer;
+
+  return terminal;
+}
+
+struct haiku_display_info *
+haiku_term_init (void)
+{
+  struct haiku_display_info *dpyinfo;
+  struct terminal *terminal;
+
+  Lisp_Object color_file, color_map;
+
+  block_input ();
+  Fset_input_interrupt_mode (Qnil);
+
+  baud_rate = 19200;
+
+  dpyinfo = xzalloc (sizeof *dpyinfo);
+  bzero (dpyinfo, sizeof *dpyinfo);
+
+  haiku_io_init ();
+
+  if (port_emacs_to_application < B_OK ||
+      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 ();
+  dpyinfo->resx = 72.27;
+  dpyinfo->resy = 72.27;
+  dpyinfo->next = x_display_list;
+  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;
+  unblock_input ();
+
+  return dpyinfo;
+}
+
+void
+haiku_clear_under_internal_border (struct frame *f)
+{
+  if (FRAME_INTERNAL_BORDER_WIDTH (f) > 0)
+    {
+      int border = FRAME_INTERNAL_BORDER_WIDTH (f);
+      int width = FRAME_PIXEL_WIDTH (f);
+      int height = FRAME_PIXEL_HEIGHT (f);
+      int margin = FRAME_TOP_MARGIN_HEIGHT (f);
+      int face_id =
+	(FRAME_PARENT_FRAME (f)
+	 ? (!NILP (Vface_remapping_alist)
+	    ? lookup_basic_face (NULL, f, CHILD_FRAME_BORDER_FACE_ID)
+	    : CHILD_FRAME_BORDER_FACE_ID)
+	 : (!NILP (Vface_remapping_alist)
+	    ? lookup_basic_face (NULL, f, INTERNAL_BORDER_FACE_ID)
+	    : INTERNAL_BORDER_FACE_ID));
+      struct face *face = FACE_FROM_ID_OR_NULL (f, face_id);
+      void *view = FRAME_HAIKU_VIEW (f);
+      block_input ();
+      BView_draw_lock (view);
+      BView_StartClip (view);
+
+      if (face)
+	BView_SetHighColor (view, face->background);
+      else
+	BView_SetHighColor (view, FRAME_BACKGROUND_PIXEL (f));
+
+      BView_FillRectangle (view, 0, margin, width, border);
+      BView_FillRectangle (view, 0, 0, border, height);
+      BView_FillRectangle (view, 0, margin, width, border);
+      BView_FillRectangle (view, width - border, 0, border, height);
+      BView_FillRectangle (view, 0, height - border, width, border);
+      BView_EndClip (view);
+      BView_draw_unlock (view);
+      unblock_input ();
+    }
+}
+
+void
+mark_haiku_display (void)
+{
+  if (x_display_list)
+    mark_object (x_display_list->color_map);
+}
+
+void
+haiku_scroll_bar_remove (struct scroll_bar *bar)
+{
+  block_input ();
+  void *view = FRAME_HAIKU_VIEW (WINDOW_XFRAME (XWINDOW (bar->window)));
+  BView_forget_scroll_bar (view, bar->left, bar->top, bar->width, bar->height);
+  BScrollBar_delete (bar->scroll_bar);
+  expose_frame (WINDOW_XFRAME (XWINDOW (bar->window)),
+		bar->left, bar->top, bar->width, bar->height);
+
+  if (bar->horizontal)
+    wset_horizontal_scroll_bar (XWINDOW (bar->window), Qnil);
+  else
+    wset_vertical_scroll_bar (XWINDOW (bar->window), Qnil);
+
+  unblock_input ();
+};
+
+void
+syms_of_haikuterm (void)
+{
+  DEFVAR_BOOL ("haiku-initialized", ext_initialized, doc: /* Non-nil if the Haiku
+ terminal backend has been initialized. */);
+
+  DEFVAR_BOOL ("x-use-underline-position-properties",
+	       x_use_underline_position_properties,
+     doc: /* SKIP: real doc in xterm.c.  */);
+  x_use_underline_position_properties = 0;
+
+  DEFVAR_BOOL ("x-underline-at-descent-line",
+	       x_underline_at_descent_line,
+     doc: /* SKIP: real doc in xterm.c.  */);
+  x_underline_at_descent_line = 0;
+
+  DEFVAR_LISP ("x-toolkit-scroll-bars", Vx_toolkit_scroll_bars,
+	       doc: /* SKIP: real doc in xterm.c.  */);
+  Vx_toolkit_scroll_bars = Qt;
+
+  DEFSYM (Qx_use_underline_position_properties,
+	  "x-use-underline-position-properties");
+
+  DEFSYM (Qx_underline_at_descent_line, "x-underline-at-descent-line");
+
+  Fprovide (Qhaiku, Qnil);
+}
diff --git a/src/haikuterm.h b/src/haikuterm.h
new file mode 100644
index 0000000000..f20dba14e1
--- /dev/null
+++ b/src/haikuterm.h
@@ -0,0 +1,273 @@
+/* Haiku window system support
+   Copyright (C) 2021 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
+
+#ifndef _HAIKU_TERM_H_
+#define _HAIKU_TERM_H_
+
+#include <pthread.h>
+
+#include "haikugui.h"
+#include "frame.h"
+#include "character.h"
+#include "dispextern.h"
+#include "font.h"
+
+#ifdef _cplusplus
+extern "C"
+{
+#endif
+
+#ifndef _cplusplus
+#include "lisp.h"
+#include "dispextern.h"
+#include "frame.h"
+#endif
+
+#ifndef _cplusplus
+#define C_FRAME struct frame *
+#define C_FONT struct font *
+#define C_TERMINAL struct terminal *
+#else
+#define C_FRAME void *
+#define C_FONT void *
+#define C_TERMINAL void *
+#endif
+
+#define HAVE_CHAR_CACHE_MAX 256
+
+extern int popup_activated_p ;
+extern pthread_t app_thread_id;
+
+struct haikufont_info
+{
+  struct font font;
+  haiku be_font;
+
+  int have_char_cache[HAVE_CHAR_CACHE_MAX];
+  struct font_metrics met_cache[HAVE_CHAR_CACHE_MAX];
+};
+
+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;
+
+  C_FONT font;
+
+  int hourglass_p;
+  uint32_t cursor_fg;
+  bool dirty_p;
+};
+
+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)
+
+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 XGetPixel (haiku bitmap, int x, int y);
+extern void XPutPixel (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);
+
+#ifdef _cplusplus
+};
+#endif
+#endif /* _HAIKU_TERM_H_ */
diff --git a/src/image.c b/src/image.c
index bcd45eb451..5efe4139aa 100644
--- a/src/image.c
+++ b/src/image.c
@@ -135,6 +135,27 @@ #define PIX_MASK_DRAW	1
 # define COLOR_TABLE_SUPPORT 1
 #endif
 
+#ifdef HAVE_HAIKU
+#include "haiku_support.h"
+typedef struct haiku_bitmap_record Bitmap_Record;
+
+#define GET_PIXEL(ximg, x, y) XGetPixel (ximg, x, y)
+#define PUT_PIXEL XPutPixel
+#define NO_PIXMAP 0
+
+#define PIX_MASK_RETAIN	1
+#define PIX_MASK_DRAW	0
+
+#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);
@@ -465,7 +486,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 +582,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);
@@ -2820,6 +2845,29 @@ image_create_x_image_and_pixmap_1 (struct frame *f, int width, int height, int d
   return 1;
 #endif /* HAVE_X_WINDOWS */
 
+#ifdef HAVE_HAIKU
+  if (depth == 0)
+    depth = 24;
+
+  if (depth != 24)
+    {
+      image_error ("Invalid image bit depth specified");
+      return 0;
+    }
+
+  *pixmap = BBitmap_new (width, height, depth == 1);
+
+  if (*pixmap == NO_PIXMAP)
+    {
+      *pimg = NULL;
+      image_error ("Unable to create pixmap", Qnil, Qnil);
+      return 0;
+    }
+
+  *pimg = *pixmap;
+  return 1;
+#endif
+
 #ifdef HAVE_NTGUI
 
   BITMAPINFOHEADER *header;
@@ -3087,7 +3135,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;
@@ -5446,7 +5494,7 @@ lookup_rgb_color (struct frame *f, int r, int g, int b)
 {
 #ifdef HAVE_NTGUI
   return PALETTERGB (r >> 8, g >> 8, b >> 8);
-#elif defined USE_CAIRO || defined HAVE_NS
+#elif defined USE_CAIRO || defined HAVE_NS || defined HAVE_HAIKU
   return RGB_TO_ULONG (r >> 8, g >> 8, b >> 8);
 #else
   xsignal1 (Qfile_error,
@@ -5519,7 +5567,7 @@ image_to_emacs_colors (struct frame *f, struct image *img, bool rgb_p)
   p = colors;
   for (y = 0; y < img->height; ++y)
     {
-#if !defined USE_CAIRO && !defined HAVE_NS
+#if !defined USE_CAIRO && !defined HAVE_NS && !defined HAVE_HAIKU
       Emacs_Color *row = p;
       for (x = 0; x < img->width; ++x, ++p)
 	p->pixel = GET_PIXEL (ximg, x, y);
@@ -5841,6 +5889,7 @@ image_disable_image (struct frame *f, struct image *img)
     {
 #ifndef HAVE_NTGUI
 #ifndef HAVE_NS  /* TODO: NS support, however this not needed for toolbars */
+#ifndef HAVE_HAIKU
 
 #ifndef USE_CAIRO
 #define CrossForeground(f) BLACK_PIX_DEFAULT (f)
@@ -5858,6 +5907,7 @@ #define MaskForeground(f)  PIX_MASK_DRAW
       if (img->mask)
 	image_pixmap_draw_cross (f, img->mask, 0, 0, img->width, img->height,
 				 MaskForeground (f));
+#endif /* !HAVE_HAIKU */
 #endif /* !HAVE_NS */
 #else
       HDC hdc, bmpdc;
@@ -7060,6 +7110,7 @@ png_load_body (struct frame *f, struct image *img, struct png_load_context *c)
 	  g = *p++ << 8;
 	  b = *p++ << 8;
 	  PUT_PIXEL (ximg, x, y, lookup_rgb_color (f, r, g, b));
+
 	  /* An alpha channel, aka mask channel, associates variable
 	     transparency with an image.  Where other image formats
 	     support binary transparency---fully transparent or fully
diff --git a/src/keyboard.c b/src/keyboard.c
index 2e4c4e6aab..5c0061102a 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -3869,7 +3869,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)
@@ -7138,7 +7138,7 @@ tty_read_avail_input (struct terminal *terminal,
 static void
 handle_async_input (void)
 {
-#ifdef USABLE_SIGIO
+#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)
   while (1)
     {
       int nread = gobble_input ();
@@ -7201,7 +7201,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)
@@ -7217,7 +7217,7 @@ deliver_input_available_signal (int sig)
 {
   deliver_process_signal (sig, handle_input_available_signal);
 }
-#endif /* USABLE_SIGIO */
+#endif /* defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)  */
 
 \f
 /* User signal events.  */
@@ -7287,7 +7287,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
@@ -11053,7 +11053,7 @@ DEFUN ("set-input-interrupt-mode", Fset_input_interrupt_mode,
   (Lisp_Object interrupt)
 {
   bool new_interrupt_input;
-#ifdef USABLE_SIGIO
+#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)
 #ifdef HAVE_X_WINDOWS
   if (x_display_list != NULL)
     {
@@ -11064,9 +11064,9 @@ DEFUN ("set-input-interrupt-mode", Fset_input_interrupt_mode,
   else
 #endif /* HAVE_X_WINDOWS */
     new_interrupt_input = !NILP (interrupt);
-#else /* not USABLE_SIGIO */
+#else /* not USABLE_SIGIO || USABLE_SIGPOLL */
   new_interrupt_input = false;
-#endif /* not USABLE_SIGIO */
+#endif /* not USABLE_SIGIO || USABLE_SIGPOLL */
 
   if (new_interrupt_input != interrupt_input)
     {
@@ -11493,12 +11493,16 @@ init_keyboard (void)
       sigaction (SIGQUIT, &action, 0);
 #endif /* not DOS_NT */
     }
-#ifdef USABLE_SIGIO
+#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)
   if (!noninteractive)
     {
       struct sigaction action;
       emacs_sigaction_init (&action, deliver_input_available_signal);
+#ifdef USABLE_SIGIO
       sigaction (SIGIO, &action, 0);
+#else
+      sigaction (SIGPOLL, &action, 0);
+#endif
     }
 #endif
 
diff --git a/src/lisp.h b/src/lisp.h
index 7bfc69b647..4b4d97e959 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -3316,7 +3316,7 @@ rarely_quit (unsigned short int count)
 
 /* Define if the windowing system provides a menu bar.  */
 #if defined (USE_X_TOOLKIT) || defined (HAVE_NTGUI) \
-  || defined (HAVE_NS) || defined (USE_GTK)
+  || defined (HAVE_NS) || defined (USE_GTK) || defined (HAVE_HAIKU)
 #define HAVE_EXT_MENU_BAR true
 #endif
 
@@ -4411,7 +4411,7 @@ fast_string_match_ignore_case (Lisp_Object regexp, Lisp_Object string)
 extern Lisp_Object tab_bar_items (Lisp_Object, int *);
 extern Lisp_Object tool_bar_items (Lisp_Object, int *);
 extern void discard_mouse_events (void);
-#ifdef USABLE_SIGIO
+#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)
 void handle_input_available_signal (int);
 #endif
 extern Lisp_Object pending_funcalls;
diff --git a/src/menu.c b/src/menu.c
index d43360ec4e..51d6a98a67 100644
--- a/src/menu.c
+++ b/src/menu.c
@@ -50,7 +50,8 @@ Copyright (C) 1986, 1988, 1993-1994, 1996, 1999-2021 Free Software
 static bool
 have_boxes (void)
 {
-#if defined (USE_X_TOOLKIT) || defined (USE_GTK) || defined (HAVE_NTGUI) || defined(HAVE_NS)
+#if defined (USE_X_TOOLKIT) || defined (USE_GTK) || defined (HAVE_NTGUI) || defined (HAVE_NS) \
+  || defined (HAVE_HAIKU)
   if (FRAME_WINDOW_P (XFRAME (Vmenu_updating_frame)))
     return 1;
 #endif
@@ -169,7 +170,7 @@ ensure_menu_items (int items)
     }
 }
 
-#ifdef HAVE_EXT_MENU_BAR
+#if defined (HAVE_EXT_MENU_BAR) || defined (HAVE_HAIKU)
 
 /* Begin a submenu.  */
 
@@ -422,7 +423,8 @@ single_menu_item (Lisp_Object key, Lisp_Object item, Lisp_Object dummy, void *sk
 		  AREF (item_properties, ITEM_PROPERTY_SELECTED),
 		  AREF (item_properties, ITEM_PROPERTY_HELP));
 
-#if defined (USE_X_TOOLKIT) || defined (USE_GTK) || defined (HAVE_NS) || defined (HAVE_NTGUI)
+#if defined (USE_X_TOOLKIT) || defined (USE_GTK) || defined (HAVE_NS) \
+  || defined (HAVE_NTGUI) || defined (HAVE_HAIKU)
   /* Display a submenu using the toolkit.  */
   if (FRAME_WINDOW_P (XFRAME (Vmenu_updating_frame))
       && ! (NILP (map) || NILP (enabled)))
@@ -872,6 +874,9 @@ update_submenu_strings (widget_value *first_wv)
     }
 }
 
+#endif /* USE_X_TOOLKIT || USE_GTK || HAVE_NS || HAVE_NTGUI */
+#ifdef HAVE_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,8 +964,7 @@ find_and_call_menu_selection (struct frame *f, int menu_bar_items_used,
   SAFE_FREE ();
 }
 
-#endif /* USE_X_TOOLKIT || USE_GTK || HAVE_NS || HAVE_NTGUI */
-
+#endif /* HAVE_HAIKU */
 #ifdef HAVE_NS
 /* As above, but return the menu selection instead of storing in kb buffer.
    If KEYMAPS, return full prefixes to selection. */
diff --git a/src/process.c b/src/process.c
index bfca165fca..901fd5e32e 100644
--- a/src/process.c
+++ b/src/process.c
@@ -258,7 +258,7 @@ #define READ_OUTPUT_DELAY_MAX_MAX   (READ_OUTPUT_DELAY_INCREMENT * 7)
 
 static void start_process_unwind (Lisp_Object);
 static void create_process (Lisp_Object, char **, Lisp_Object);
-#ifdef USABLE_SIGIO
+#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)
 static bool keyboard_bit_set (fd_set *);
 #endif
 static void deactivate_process (Lisp_Object);
@@ -5700,7 +5700,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
       if (! NILP (wait_for_cell) && ! NILP (XCAR (wait_for_cell)))
 	break;
 
-#ifdef USABLE_SIGIO
+#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)
       /* If we think we have keyboard input waiting, but didn't get SIGIO,
 	 go read it.  This can happen with X on BSD after logging out.
 	 In that case, there really is no input and no SIGIO,
@@ -5708,7 +5708,11 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
 
       if (read_kbd && interrupt_input
 	  && keyboard_bit_set (&Available) && ! noninteractive)
+#ifdef USABLE_SIGIO
 	handle_input_available_signal (SIGIO);
+#else
+	handle_input_available_signal (SIGPOLL);
+#endif
 #endif
 
       /* If checking input just got us a size-change event from X,
@@ -7702,7 +7706,7 @@ delete_gpm_wait_descriptor (int desc)
 
 # endif
 
-# ifdef USABLE_SIGIO
+#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)
 
 /* Return true if *MASK has a bit set
    that corresponds to one of the keyboard input descriptors.  */
diff --git a/src/sound.c b/src/sound.c
index 9041076bdc..d42bc8550d 100644
--- a/src/sound.c
+++ b/src/sound.c
@@ -299,11 +299,15 @@ sound_perror (const char *msg)
   int saved_errno = errno;
 
   turn_on_atimers (1);
-#ifdef USABLE_SIGIO
+#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)
   {
     sigset_t unblocked;
     sigemptyset (&unblocked);
+#ifdef USABLE_SIGIO
     sigaddset (&unblocked, SIGIO);
+#else
+    sigaddset (&unblocked, SIGPOLL);
+#endif
     pthread_sigmask (SIG_UNBLOCK, &unblocked, 0);
   }
 #endif
@@ -698,7 +702,7 @@ vox_open (struct sound_device *sd)
 vox_configure (struct sound_device *sd)
 {
   int val;
-#ifdef USABLE_SIGIO
+#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)
   sigset_t oldset, blocked;
 #endif
 
@@ -708,9 +712,13 @@ vox_configure (struct sound_device *sd)
      interrupted by a signal.  Block the ones we know to cause
      troubles.  */
   turn_on_atimers (0);
-#ifdef USABLE_SIGIO
+#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)
   sigemptyset (&blocked);
+#ifdef USABLE_SIGIO
   sigaddset (&blocked, SIGIO);
+#else
+  sigaddset (&blocked, SIGPOLL);
+#endif
   pthread_sigmask (SIG_BLOCK, &blocked, &oldset);
 #endif
 
@@ -744,7 +752,7 @@ vox_configure (struct sound_device *sd)
     }
 
   turn_on_atimers (1);
-#ifdef USABLE_SIGIO
+#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)
   pthread_sigmask (SIG_SETMASK, &oldset, 0);
 #endif
 }
@@ -760,10 +768,14 @@ vox_close (struct sound_device *sd)
       /* On GNU/Linux, it seems that the device driver doesn't like to
 	 be interrupted by a signal.  Block the ones we know to cause
 	 troubles.  */
-#ifdef USABLE_SIGIO
+#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)
       sigset_t blocked, oldset;
       sigemptyset (&blocked);
+#ifdef USABLE_SIGIO
       sigaddset (&blocked, SIGIO);
+#else
+      sigaddset (&blocked, SIGPOLL);
+#endif
       pthread_sigmask (SIG_BLOCK, &blocked, &oldset);
 #endif
       turn_on_atimers (0);
@@ -772,7 +784,7 @@ vox_close (struct sound_device *sd)
       ioctl (sd->fd, SNDCTL_DSP_SYNC, NULL);
 
       turn_on_atimers (1);
-#ifdef USABLE_SIGIO
+#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)
       pthread_sigmask (SIG_SETMASK, &oldset, 0);
 #endif
 
diff --git a/src/sysdep.c b/src/sysdep.c
index 8eaee22498..eec5b80ddc 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
@@ -1674,6 +1687,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
     }
 
diff --git a/src/termhooks.h b/src/termhooks.h
index 1d3cdc8fe8..3d6bfd89d6 100644
--- a/src/termhooks.h
+++ b/src/termhooks.h
@@ -60,7 +60,8 @@ #define EMACS_TERMHOOKS_H
   output_x_window,
   output_msdos_raw,
   output_w32,
-  output_ns
+  output_ns,
+  output_haiku
 };
 
 /* Input queue declarations and hooks.  */
@@ -442,6 +443,7 @@ #define EVENT_INIT(event) memset (&(event), 0, sizeof (struct input_event))
     struct x_display_info *x;         /* xterm.h */
     struct w32_display_info *w32;     /* w32term.h */
     struct ns_display_info *ns;       /* nsterm.h */
+    struct haiku_display_info *haiku; /* haikuterm.h */
   } display_info;
 
 \f
@@ -830,6 +832,9 @@ #define TERMINAL_FONT_CACHE(t)						\
 #elif defined (HAVE_NS)
 #define TERMINAL_FONT_CACHE(t)						\
   (t->type == output_ns ? t->display_info.ns->name_list_element : Qnil)
+#elif defined (HAVE_HAIKU)
+#define TERMINAL_FONT_CACHE(t)						\
+  (t->type == output_haiku ? t->display_info.haiku->name_list_element : Qnil)
 #endif
 
 extern struct terminal *decode_live_terminal (Lisp_Object);
diff --git a/src/terminal.c b/src/terminal.c
index b83adc596b..b5f244ee31 100644
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -445,6 +445,8 @@ DEFUN ("terminal-live-p", Fterminal_live_p, Sterminal_live_p, 1, 1, 0,
       return Qpc;
     case output_ns:
       return Qns;
+    case output_haiku:
+      return Qhaiku;
     default:
       emacs_abort ();
     }
diff --git a/src/verbose.mk.in b/src/verbose.mk.in
index 50d6ea3200..35f96209ff 100644
--- a/src/verbose.mk.in
+++ b/src/verbose.mk.in
@@ -23,6 +23,7 @@ ifeq (${V},1)
 AM_V_AR =
 AM_V_at =
 AM_V_CC =
+AM_V_CXX =
 AM_V_CCLD =
 AM_V_ELC =
 AM_V_GEN =
@@ -33,6 +34,7 @@ else
 AM_V_AR = @echo "  AR      " $@;
 AM_V_at = @
 AM_V_CC = @echo "  CC      " $@;
+AM_V_CXX = @echo "  CXX     " $@;
 AM_V_CCLD = @echo "  CCLD    " $@;
 ifeq ($(HAVE_NATIVE_COMP),yes)
 ifeq ($(NATIVE_DISABLED),1)
diff --git a/src/xdisp.c b/src/xdisp.c
index e853c8c223..037a7700cc 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -13852,7 +13852,6 @@ note_tab_bar_highlight (struct frame *f, int x, int y)
   else if (rc == 0)
     /* On same tab-bar item as before.  */
     goto set_help_echo;
-
   clear_mouse_face (hlinfo);
 
   bool mouse_down_p = false;
@@ -14801,7 +14800,6 @@ note_tool_bar_highlight (struct frame *f, int x, int y)
   else if (rc == 0)
     /* On same tool-bar item as before.  */
     goto set_help_echo;
-
   clear_mouse_face (hlinfo);
 
   /* Mouse is down, but on different tool-bar item?  */
@@ -15629,6 +15627,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;
@@ -25213,6 +25216,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);
@@ -33535,6 +33543,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 207f0d6a36..f995a62a21 100644
--- a/src/xfaces.c
+++ b/src/xfaces.c
@@ -246,6 +246,10 @@ #define GCGraphicsExposures 0
 #ifdef HAVE_NS
 #define GCGraphicsExposures 0
 #endif /* HAVE_NS */
+
+#ifdef HAVE_HAIKU
+#define GCGraphicsExposures 0
+#endif /* HAVE_HAIKU */
 #endif /* HAVE_WINDOW_SYSTEM */
 
 #include "buffer.h"
@@ -555,8 +559,8 @@ x_free_gc (struct frame *f, Emacs_GC *gc)
 
 #endif  /* HAVE_NTGUI */
 
-#ifdef HAVE_NS
-/* NS emulation of GCs */
+#if defined (HAVE_NS) || defined (HAVE_HAIKU)
+/* NS and Haiku emulation of GCs */
 
 static Emacs_GC *
 x_create_gc (struct frame *f,
diff --git a/src/xterm.c b/src/xterm.c
index 1887c3255d..a75ca56a65 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -13685,7 +13685,7 @@ syms_of_xterm (void)
 A value of nil means Emacs doesn't use toolkit scroll bars.
 With the X Window system, the value is a symbol describing the
 X toolkit.  Possible values are: gtk, motif, xaw, or xaw3d.
-With MS Windows or Nextstep, the value is t.  */);
+With MS Windows, Haiku windowing or Nextstep, the value is t.  */);
 #ifdef USE_TOOLKIT_SCROLL_BARS
 #ifdef USE_MOTIF
   Vx_toolkit_scroll_bars = intern_c_string ("motif");

  reply	other threads:[~2021-08-26 13:19 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <87a6l47j32.fsf.ref@yahoo.com>
2021-08-26  7:37 ` Haiku port Po Lu via Emacs development discussions.
2021-08-26  7:51   ` Po Lu via Emacs development discussions.
2021-08-26  8:01     ` Po Lu via Emacs development discussions.
2021-08-26  9:13       ` Eli Zaretskii
2021-08-26  9:50         ` Po Lu via Emacs development discussions.
2021-08-26 11:26           ` Po Lu
2021-08-26 12:40           ` Eli Zaretskii
2021-08-26 13:19             ` Po Lu via Emacs development discussions. [this message]
2021-08-26 13:50               ` Eli Zaretskii
2021-08-27  0:14                 ` Po Lu
2021-08-27  6:36                   ` Eli Zaretskii
2021-08-28  6:19                     ` Po Lu via Emacs development discussions.
2021-08-26  8:39   ` Werner LEMBERG
2021-08-26  8:49     ` Po Lu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87czq04a3v.fsf@yahoo.com \
    --to=emacs-devel@gnu.org \
    --cc=eliz@gnu.org \
    --cc=luangruo@yahoo.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

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

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