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: emacs-devel@gnu.org
Subject: Haiku port
Date: Thu, 26 Aug 2021 15:37:05 +0800	[thread overview]
Message-ID: <87a6l47j32.fsf@yahoo.com> (raw)
In-Reply-To: 87a6l47j32.fsf.ref@yahoo.com


I've spent the past week or so working on a port to Haiku, a free (I
think) derivative of BeOS (including a window system interface to the
Haiku Application Kit, itself a development of the original BeOS
Application Kit).

I was initially sceptical about the popularity of this operating system,
but it seems to be gaining enough popularity to warrant official support
from Vim and Qt.

So far, basic stuff works, though it is missing some functionality here
and there, and I would not deem this port ready for serious work.
However I haven't had time to work on tests and documentation; the
tests, I can definitely work on, but I'm not sure from where I should
begin updating the documentation, and would appreciate some advice.

I also think the BFont font driver needs work -- it has artifacting with
RTL text and when drawing condensed characters, but font rendering is
quite certainly outside my field of expertise, and I would appreciate
help from an expert.  (Though, ideally, we would have a FreeType-based
font driver, but I think FreeType is not easily usable within Haiku
applications).

At some point I hope this port can make its way into GNU Emacs.

The patch is attached below and should apply cleanly.  I would
appreciate any suggestions or comments.  I think I have already signed
the copyright assignment papers, but if not, I would be happy to sign
again.

===File
/tmp/tramp.sshfs.user@shredder/boot/home/Desktop/Emacs/haiku-emacs.patch===
diff --git a/configure.ac b/configure.ac
index 76f608ffb0..059640b4f1 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,7 @@ AC_DEFUN
     if test $emacs_cv_prog_cc_g3 != yes; then
       CFLAGS=$emacs_save_CFLAGS
     fi
-    if test $opsys = mingw32; then
+    if test $opsys = mingw32 || test $opsys = haiku; then
       CFLAGS="$CFLAGS -gdwarf-2"
     fi
   fi
@@ -1570,6 +1577,8 @@ AC_DEFUN
 
   ## Motif needs -lgen.
   unixware) LIBS_SYSTEM="-lsocket -lnsl -lelf -lgen" ;;
+
+  haiku) LIBS_SYSTEM="-lnetwork" ;;
 esac
 
 AC_SUBST(LIBS_SYSTEM)
@@ -2075,6 +2084,20 @@ AC_DEFUN
    fi
 fi
 
+HAVE_BE_APP=no
+if test "${opsys}" = "haiku" && test "${with_be_app}" = "yes"; then
+   AC_PROG_CXX([gcc cl KCC CC cxx cc++ xlC aCC c++ g++])
+   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 +2219,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 +2279,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 +2613,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"
@@ -3216,7 +3264,7 @@ AC_DEFUN
 dnl --with-toolkit-scroll-bars=no was specified.
 
 AH_TEMPLATE(USE_TOOLKIT_SCROLL_BARS,
-	    [Define to 1 if we should use toolkit scroll bars.])dnl
+	    [Define to 1 if we should use toolkit scroll bars.])
 USE_TOOLKIT_SCROLL_BARS=no
 if test "${with_toolkit_scroll_bars}" != "no"; then
   if test "${USE_X_TOOLKIT}" != "none"; then
@@ -3237,6 +3285,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 +3647,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 +3945,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 +4020,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,8 +5057,8 @@ 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 )
-    AC_DEFINE(INTERRUPT_INPUT, 1, [Define to read input using SIGIO.])
+  darwin | gnu-linux | gnu-kfreebsd)
+    AC_DEFINE(INTERRUPT_INPUT, 1, [Define to read input using SIGIO or SIGPOLL.])
   ;;
 esac
 
@@ -5054,7 +5105,7 @@ AC_DEFUN
     AC_DEFINE(PTY_TTY_NAME_SPRINTF, [])
     ;;
 
-  gnu | qnxnto )
+  gnu | qnxnto | haiku )
     AC_DEFINE(FIRST_PTY_LETTER, ['p'])
     ;;
 
@@ -5367,6 +5418,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 +5933,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..ba3165dccb 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..7f7affef59 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..cd62357ce4
--- /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 ext-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..f445a9ce80
--- /dev/null
+++ b/src/haiku_select.cc
@@ -0,0 +1,148 @@
+/* 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)
+{
+  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;
+    }
+
+  cb->Unlock ();
+
+  return strndup (ptr, bt);
+}
+
+static void
+BClipboard_set_data (BClipboard *cb, const char *type, const char *dat)
+{
+  if (!cb->Lock ())
+    return;
+  cb->Clear ();
+  BMessage *mdat = cb->Data ();
+  if (!mdat)
+    {
+      cb->Unlock ();
+      return;
+    }
+
+  if (dat)
+    mdat->AddData ("text/plain", B_MIME_TYPE, dat, strlen (dat));
+  cb->Commit ();
+  cb->Unlock ();
+}
+
+char *
+BClipboard_find_system_data (const char *type)
+{
+  if (!system_clipboard)
+    return 0;
+
+  return BClipboard_find_data (system_clipboard, type);
+}
+
+char *
+BClipboard_find_primary_selection_data (const char *type)
+{
+  if (!primary)
+    return 0;
+
+  return BClipboard_find_data (primary, type);
+}
+
+char *
+BClipboard_find_secondary_selection_data (const char *type)
+{
+  if (!secondary)
+    return 0;
+
+  return BClipboard_find_data (secondary, type);
+}
+
+void
+BClipboard_set_system_data (const char *type, const char *data)
+{
+  if (!system_clipboard)
+    return;
+
+  BClipboard_set_data (system_clipboard, type, data);
+}
+
+void
+BClipboard_set_primary_selection_data (const char *type, const char *data)
+{
+  if (!primary)
+    return;
+
+  BClipboard_set_data (primary, type, data);
+}
+
+void
+BClipboard_set_secondary_selection_data (const char *type, const char *data)
+{
+  if (!secondary)
+    return;
+
+  BClipboard_set_data (secondary, type, data);
+}
+
+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..3b0af04c24
--- /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_DOCUMENT_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_DOCUMENT_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..06ad9d028a
--- /dev/null
+++ b/src/haikufns.c
@@ -0,0 +1,1757 @@
+/* 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)
+{
+  if (!EQ (new_value, old_value))
+    FRAME_NO_ACCEPT_FOCUS (f) = !NILP (new_value);
+}
+
+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 ? Qright : 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);
+  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, 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_avoid_focus (FRAME_HAIKU_WINDOW (tip_f), 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..223d7e7992
--- /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);
+  menu_item_selection = BMenu_run (menu, x, y);
+
+  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);
+		  unblock_input ();
+		  return entry;
+		}
+	      i += MENU_ITEMS_ITEM_LENGTH;
+	    }
+	}
+    }
+  else if (!(menuflags & MENU_FOR_CLICK))
+    {
+      BPopUpMenu_delete (menu);
+      unblock_input ();
+      quit ();
+    }
+  BPopUpMenu_delete (menu);
+  unblock_input ();
+  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..4d95817787
--- /dev/null
+++ b/src/haikuselect.c
@@ -0,0 +1,104 @@
+/* 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;
+
+  block_input ();
+  if (EQ (clipboard, QPRIMARY))
+    dat = BClipboard_find_primary_selection_data (SSDATA (name));
+  else if (EQ (clipboard, QSECONDARY))
+    dat = BClipboard_find_secondary_selection_data (SSDATA (name));
+  else if (EQ (clipboard, QCLIPBOARD))
+    dat = BClipboard_find_system_data (SSDATA (name));
+  else
+    {
+      unblock_input ();
+      signal_error ("Bad clipboard", clipboard);
+    }
+  unblock_input ();
+
+  if (!dat)
+    return Qnil;
+  Lisp_Object str = build_string (dat);
+
+  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. */)
+  (Lisp_Object clipboard, Lisp_Object name, Lisp_Object data)
+{
+  CHECK_SYMBOL (clipboard);
+  CHECK_STRING (name);
+  if (!NILP (data))
+    CHECK_STRING (data);
+
+  block_input ();
+  char *dat = !NILP (data) ? SSDATA (ENCODE_UTF_8 (data)) : NULL;
+  if (EQ (clipboard, QPRIMARY))
+    BClipboard_set_primary_selection_data (SSDATA (name), dat);
+  else if (EQ (clipboard, QSECONDARY))
+    BClipboard_set_secondary_selection_data (SSDATA (name), dat);
+  else if (EQ (clipboard, QCLIPBOARD))
+    BClipboard_set_system_data (SSDATA (name), dat);
+  else
+    {
+      unblock_input ();
+      signal_error ("Bad clipboard", clipboard);
+    }
+  unblock_input ();
+
+  return Qnil;
+}
+
+void
+syms_of_haikuselect (void)
+{
+  DEFSYM (QSECONDARY, "SECONDARY");
+  DEFSYM (QCLIPBOARD, "CLIPBOARD");
+
+  defsubr (&Shaiku_selection_data);
+  defsubr (&Shaiku_selection_put);
+}
diff --git a/src/haikuselect.h b/src/haikuselect.h
new file mode 100644
index 0000000000..e5e52f7ffa
--- /dev/null
+++ b/src/haikuselect.h
@@ -0,0 +1,57 @@
+/* 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
+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);
+
+  /* Ditto, but for the primary selection and not clipboard. */
+  extern char *
+  BClipboard_find_primary_selection_data (const char *type);
+
+  /* Ditto, this time for the secondary selection. */
+  extern char *
+  BClipboard_find_secondary_selection_data (const char *type);
+
+  extern void
+  BClipboard_set_system_data (const char *type, const char *data);
+
+  extern void
+  BClipboard_set_primary_selection_data (const char *type, const char *data);
+
+  extern void
+  BClipboard_set_secondary_selection_data (const char *type, const char *data);
+
+  /* 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/nsfns.m b/src/nsfns.m
index c40367703d..cd2b648dc2 100644
--- a/src/nsfns.m
+++ b/src/nsfns.m
@@ -905,62 +905,60 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side.
 
 /* Note: see frame.c for template, also where generic functions are
    implemented.  */
-frame_parm_handler ns_frame_parm_handlers[] =
-{
-  gui_set_autoraise, /* generic OK */
-  gui_set_autolower, /* generic OK */
-  ns_set_background_color,
-  0, /* x_set_border_color,  may be impossible under Nextstep */
-  0, /* x_set_border_width,  may be impossible under Nextstep */
-  ns_set_cursor_color,
-  ns_set_cursor_type,
-  gui_set_font, /* generic OK */
-  ns_set_foreground_color,
-  ns_set_icon_name,
-  ns_set_icon_type,
-  ns_set_child_frame_border_width,
-  ns_set_internal_border_width,
-  gui_set_right_divider_width, /* generic OK */
-  gui_set_bottom_divider_width, /* generic OK */
-  ns_set_menu_bar_lines,
-  ns_set_mouse_color,
-  ns_explicitly_set_name,
-  gui_set_scroll_bar_width, /* generic OK */
-  gui_set_scroll_bar_height, /* generic OK */
-  ns_set_title,
-  gui_set_unsplittable, /* generic OK */
-  gui_set_vertical_scroll_bars, /* generic OK */
-  gui_set_horizontal_scroll_bars, /* generic OK */
-  gui_set_visibility, /* generic OK */
-  ns_set_tab_bar_lines,
-  ns_set_tool_bar_lines,
-  0, /* x_set_scroll_bar_foreground, will ignore (not possible on NS) */
-  0, /* x_set_scroll_bar_background,  will ignore (not possible on NS) */
-  gui_set_screen_gamma, /* generic OK */
-  gui_set_line_spacing, /* generic OK, sets f->extra_line_spacing to int */
-  gui_set_left_fringe, /* generic OK */
-  gui_set_right_fringe, /* generic OK */
-  0, /* x_set_wait_for_wm, will ignore */
-  gui_set_fullscreen, /* generic OK */
-  gui_set_font_backend, /* generic OK */
-  gui_set_alpha,
-  0, /* x_set_sticky */
-  0, /* x_set_tool_bar_position */
-  0, /* x_set_inhibit_double_buffering */
-  ns_set_undecorated,
-  ns_set_parent_frame,
-  0, /* x_set_skip_taskbar */
-  ns_set_no_focus_on_map,
-  ns_set_no_accept_focus,
-  ns_set_z_group,
-  0, /* x_set_override_redirect */
-  gui_set_no_special_glyphs,
+frame_parm_handler ns_frame_parm_handlers[] = {
+    gui_set_autoraise, /* generic OK */
+    gui_set_autolower, /* generic OK */
+    ns_set_background_color,
+    0, /* x_set_border_color,  may be impossible under Nextstep */
+    0, /* x_set_border_width,  may be impossible under Nextstep */
+    ns_set_cursor_color,
+    ns_set_cursor_type,
+    gui_set_font, /* generic OK */
+    ns_set_foreground_color,
+    ns_set_icon_name,
+    ns_set_icon_type,
+    ns_set_child_frame_border_width,
+    ns_set_internal_border_width,
+    gui_set_right_divider_width,  /* generic OK */
+    gui_set_bottom_divider_width, /* generic OK */
+    ns_set_menu_bar_lines,
+    ns_set_mouse_color,
+    ns_explicitly_set_name,
+    gui_set_scroll_bar_width,  /* generic OK */
+    gui_set_scroll_bar_height, /* generic OK */
+    ns_set_title,
+    gui_set_unsplittable,           /* generic OK */
+    gui_set_vertical_scroll_bars,   /* generic OK */
+    gui_set_horizontal_scroll_bars, /* generic OK */
+    gui_set_visibility,             /* generic OK */
+    ns_set_tab_bar_lines,
+    ns_set_tool_bar_lines,
+    0, /* x_set_scroll_bar_foreground, will ignore (not possible on NS) */
+    0, /* x_set_scroll_bar_background,  will ignore (not possible on NS) */
+    gui_set_screen_gamma, /* generic OK */
+    gui_set_line_spacing, /* generic OK, sets f->extra_line_spacing to int */
+    gui_set_left_fringe,  /* generic OK */
+    gui_set_right_fringe, /* generic OK */
+    0,                    /* x_set_wait_for_wm, will ignore */
+    gui_set_fullscreen,   /* generic OK */
+    gui_set_font_backend, /* generic OK */
+    gui_set_alpha,
+    0, /* x_set_sticky */
+    0, /* x_set_tool_bar_position */
+    0, /* x_set_inhibit_double_buffering */
+    ns_set_undecorated,
+    ns_set_parent_frame,
+    0, /* x_set_skip_taskbar */
+    ns_set_no_focus_on_map,
+    ns_set_no_accept_focus,
+    ns_set_z_group,
+    0, /* x_set_override_redirect */
+    gui_set_no_special_glyphs,
 #ifdef NS_IMPL_COCOA
-  ns_set_appearance,
-  ns_set_transparent_titlebar,
-#endif
+    ns_set_appearance,
+    ns_set_transparent_titlebar,
 };
-
+#endif
 
 /* Handler for signals raised during x_create_frame.
    FRAME is the frame which is partially constructed.  */
diff --git a/src/nsterm.m b/src/nsterm.m
index 1c1f0c8f23..db48af1e6d 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -1671,38 +1671,34 @@ Hide the window (X11 semantics)
   unblock_input ();
 }
 
-
-static void
-ns_set_window_size (struct frame *f,
-                    bool change_gravity,
-                    int width,
-                    int height)
+static void ns_set_window_size(struct frame *f, bool change_gravity, int width,
+                               int height)
 /* --------------------------------------------------------------------------
      Adjust window pixel size based on native sizes WIDTH and HEIGHT.
      Impl is a bit more complex than other terms, need to do some
      internal clipping.
    -------------------------------------------------------------------------- */
 {
-  EmacsView *view = FRAME_NS_VIEW (f);
+  EmacsView *view = FRAME_NS_VIEW(f);
   NSWindow *window = [view window];
   NSRect frameRect;
 
-  NSTRACE ("ns_set_window_size");
+  NSTRACE("ns_set_window_size");
 
   if (view == nil)
     return;
 
-  NSTRACE_RECT ("current", [window frame]);
-  NSTRACE_MSG ("Width:%d Height:%d", width, height);
-  NSTRACE_MSG ("Font %d x %d", FRAME_COLUMN_WIDTH (f), FRAME_LINE_HEIGHT (f));
+  NSTRACE_RECT("current", [window frame]);
+  NSTRACE_MSG("Width:%d Height:%d", width, height);
+  NSTRACE_MSG("Font %d x %d", FRAME_COLUMN_WIDTH(f), FRAME_LINE_HEIGHT(f));
 
-  block_input ();
+  block_input();
 
-  frameRect = [window frameRectForContentRect:NSMakeRect (0, 0, width, height)];
+  frameRect = [window frameRectForContentRect:NSMakeRect(0, 0, width, height)];
 
   /* Set the origin so the top left of the frame doesn't move.  */
   frameRect.origin = [window frame].origin;
-  frameRect.origin.y += NSHeight ([view frame]) - height;
+  frameRect.origin.y += NSHeight([view frame]) - height;
 
   if (f->output_data.ns->zooming)
     f->output_data.ns->zooming = 0;
@@ -1710,12 +1706,13 @@ Hide the window (X11 semantics)
   /* Usually it seems safe to delay changing the frame size, but when a
      series of actions are taken with no redisplay between them then we
      can end up using old values so don't delay here.  */
-  change_frame_size (f, width, height, false, NO, false);
+  change_frame_size(f, width, height, false, NO, false);
 
   [window setFrame:frameRect display:NO];
 
-  unblock_input ();
+  unblock_input
 }
+();
 
 void
 ns_set_undecorated (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
diff --git a/src/process.c b/src/process.c
index bfca165fca..b7e8d14e48 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..3580055dbe 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -25213,6 +25213,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);
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  7:37 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 ` Po Lu via Emacs development discussions. [this message]
2021-08-26  7:51   ` Haiku port 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.
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=87a6l47j32.fsf@yahoo.com \
    --to=emacs-devel@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).