summaryrefslogtreecommitdiff
path: root/src/haiku_support.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/haiku_support.cc')
-rw-r--r--src/haiku_support.cc5556
1 files changed, 5556 insertions, 0 deletions
diff --git a/src/haiku_support.cc b/src/haiku_support.cc
new file mode 100644
index 00000000000..0f8e26d0db4
--- /dev/null
+++ b/src/haiku_support.cc
@@ -0,0 +1,5556 @@
+/* Haiku window system support. Hey, Emacs, this is -*- C++ -*-
+ Copyright (C) 2021-2022 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 <attribute.h>
+
+#include <app/Application.h>
+#include <app/Cursor.h>
+#include <app/Clipboard.h>
+#include <app/Messenger.h>
+#include <app/Roster.h>
+
+#include <interface/GraphicsDefs.h>
+#include <interface/InterfaceDefs.h>
+#include <interface/Bitmap.h>
+#include <interface/Window.h>
+#include <interface/View.h>
+#include <interface/Screen.h>
+#include <interface/ScrollBar.h>
+#include <interface/Region.h>
+#include <interface/Menu.h>
+#include <interface/MenuItem.h>
+#include <interface/PopUpMenu.h>
+#include <interface/MenuBar.h>
+#include <interface/Alert.h>
+#include <interface/Button.h>
+#include <interface/ControlLook.h>
+#include <interface/Deskbar.h>
+#include <interface/ListView.h>
+#include <interface/StringItem.h>
+#include <interface/SplitView.h>
+#include <interface/ScrollView.h>
+#include <interface/StringView.h>
+#include <interface/TextControl.h>
+#include <interface/CheckBox.h>
+
+#include <locale/UnicodeChar.h>
+
+#include <game/WindowScreen.h>
+#include <game/DirectWindow.h>
+
+#include <storage/FindDirectory.h>
+#include <storage/Entry.h>
+#include <storage/Path.h>
+#include <storage/FilePanel.h>
+#include <storage/AppFileInfo.h>
+#include <storage/Path.h>
+#include <storage/PathFinder.h>
+#include <storage/Node.h>
+
+#include <support/Beep.h>
+#include <support/DataIO.h>
+#include <support/Locker.h>
+#include <support/ObjectList.h>
+
+#include <translation/TranslatorRoster.h>
+#include <translation/TranslationDefs.h>
+#include <translation/TranslationUtils.h>
+
+#include <kernel/OS.h>
+#include <kernel/fs_attr.h>
+#include <kernel/scheduler.h>
+
+#include <private/interface/ToolTip.h>
+#include <private/interface/WindowPrivate.h>
+
+#include <cmath>
+#include <cstring>
+#include <cstdint>
+#include <cstdio>
+#include <csignal>
+#include <cfloat>
+
+#ifdef USE_BE_CAIRO
+#include <cairo.h>
+#endif
+
+#include "haiku_support.h"
+
+/* Some messages that Emacs sends to itself. */
+enum
+ {
+ SCROLL_BAR_UPDATE = 3000,
+ WAIT_FOR_RELEASE = 3001,
+ RELEASE_NOW = 3002,
+ CANCEL_DROP = 3003,
+ SHOW_MENU_BAR = 3004,
+ BE_MENU_BAR_OPEN = 3005,
+ QUIT_APPLICATION = 3006,
+ REPLAY_MENU_BAR = 3007,
+ FONT_FAMILY_SELECTED = 3008,
+ FONT_STYLE_SELECTED = 3009,
+ FILE_PANEL_SELECTION = 3010,
+ QUIT_PREVIEW_DIALOG = 3011,
+ SET_FONT_INDICES = 3012,
+ SET_PREVIEW_DIALOG = 3013,
+ UPDATE_PREVIEW_DIALOG = 3014,
+ SEND_MOVE_FRAME_EVENT = 3015,
+ SET_DISABLE_ANTIALIASING = 3016,
+ };
+
+/* X11 keysyms that we use. */
+enum
+ {
+ KEY_BACKSPACE = 0xff08,
+ KEY_TAB = 0xff09,
+ KEY_RETURN = 0xff0d,
+ KEY_PAUSE = 0xff13,
+ KEY_ESCAPE = 0xff1b,
+ KEY_DELETE = 0xffff,
+ KEY_HOME = 0xff50,
+ KEY_LEFT_ARROW = 0xff51,
+ KEY_UP_ARROW = 0xff52,
+ KEY_RIGHT_ARROW = 0xff53,
+ KEY_DOWN_ARROW = 0xff54,
+ KEY_PAGE_UP = 0xff55,
+ KEY_PAGE_DOWN = 0xff56,
+ KEY_END = 0xff57,
+ KEY_PRINT = 0xff61,
+ KEY_INSERT = 0xff63,
+ /* This is used to indicate the first function key. */
+ KEY_F1 = 0xffbe,
+ /* These are found on some multilingual keyboards. */
+ KEY_HANGUL = 0xff31,
+ KEY_HANGUL_HANJA = 0xff34,
+ KEY_HIRIGANA_KATAGANA = 0xff27,
+ KEY_ZENKAKU_HANKAKU = 0xff2a,
+ };
+
+struct font_selection_dialog_message
+{
+ /* Whether or not font selection was canceled. */
+ bool_bf cancel : 1;
+
+ /* Whether or not a size was explicitly specified. */
+ bool_bf size_specified : 1;
+
+ /* Whether or not antialiasing should be disabled. */
+ bool_bf disable_antialias : 1;
+
+ /* The index of the selected font family. */
+ int family_idx;
+
+ /* The index of the selected font style. */
+ int style_idx;
+
+ /* The selected font size. */
+ int size;
+};
+
+/* The color space of the main screen. B_NO_COLOR_SPACE means it has
+ not yet been computed. */
+static color_space dpy_color_space = B_NO_COLOR_SPACE;
+
+/* The keymap, or NULL if it has not been initialized. */
+static key_map *key_map;
+
+/* Indices of characters into the keymap. */
+static char *key_chars;
+
+/* Lock around keymap data, since it's touched from different
+ threads. */
+static BLocker key_map_lock;
+
+/* The locking semantics of BWindows running in multiple threads are
+ so complex that child frame state (which is the only state that is
+ shared between different BWindows at runtime) does best with a
+ single global lock. */
+static BLocker child_frame_lock;
+
+/* Variable where the popup menu thread returns the chosen menu
+ item. */
+static BMessage volatile *popup_track_message;
+
+/* Variable in which alert dialog threads return the selected button
+ number. */
+static int32 volatile alert_popup_value;
+
+/* The view that has the passive grab. */
+static void *grab_view;
+
+/* The locker for that variable. */
+static BLocker grab_view_locker;
+
+/* Whether or not a drag-and-drop operation is in progress. */
+static bool drag_and_drop_in_progress;
+
+/* Many places require us to lock the child frame data, and then lock
+ the locker of some random window. Unfortunately, locking such a
+ window might be delayed due to an arriving message, which then
+ calls a callback inside that window that tries to lock the child
+ frame data but doesn't finish since the child frame lock is already
+ held, not letting the code that held the child frame lock proceed,
+ thereby causing a deadlock.
+
+ Rectifying that problem is simple: all code in a looper callback
+ must lock the child frame data with this macro instead.
+
+ IOW, if some other code is already running with the child frame
+ lock held, don't interfere: wait until it's finished before
+ continuing. */
+#define CHILD_FRAME_LOCK_INSIDE_LOOPER_CALLBACK \
+ if (child_frame_lock.LockWithTimeout (200) != B_OK) \
+ { \
+ /* The Haiku equivalent of XPutBackEvent. */ \
+ if (CurrentMessage ()) \
+ PostMessage (CurrentMessage ()); \
+ } \
+ else
+
+/* This could be a private API, but it's used by (at least) the Qt
+ port, so it's probably here to stay. */
+extern status_t get_subpixel_antialiasing (bool *);
+
+/* The ID of the thread the BApplication is running in. */
+static thread_id app_thread;
+
+_Noreturn void
+gui_abort (const char *msg)
+{
+ fprintf (stderr, "Abort in GUI code: %s\n", msg);
+ fprintf (stderr, "Under Haiku, Emacs cannot recover from errors in GUI code\n");
+ fprintf (stderr, "App Server disconnects usually manifest as bitmap "
+ "initialization failures or lock failures.");
+ abort ();
+}
+
+struct be_popup_menu_data
+{
+ int x, y;
+ BPopUpMenu *menu;
+};
+
+static int32
+be_popup_menu_thread_entry (void *thread_data)
+{
+ struct be_popup_menu_data *data;
+ struct haiku_dummy_event dummy;
+ BMenuItem *it;
+
+ data = (struct be_popup_menu_data *) thread_data;
+
+ it = data->menu->Go (BPoint (data->x, data->y));
+
+ if (it)
+ popup_track_message = it->Message ();
+ else
+ popup_track_message = NULL;
+
+ haiku_write (DUMMY_EVENT, &dummy);
+ return 0;
+}
+
+/* Convert a raw character RAW produced by the keycode KEY into a key
+ symbol and place it in KEYSYM.
+
+ If RAW cannot be converted into a keysym, value is 0. If RAW can
+ be converted into a keysym, but it should be ignored, value is -1.
+
+ Any other value means success, and that the keysym should be used
+ instead of mapping the keycode into a character. */
+
+static int
+keysym_from_raw_char (int32 raw, int32 key, unsigned *code)
+{
+ switch (raw)
+ {
+ case B_BACKSPACE:
+ *code = KEY_BACKSPACE;
+ break;
+ case B_RETURN:
+ *code = KEY_RETURN;
+ break;
+ case B_TAB:
+ *code = KEY_TAB;
+ break;
+ case B_ESCAPE:
+ *code = KEY_ESCAPE;
+ break;
+ case B_LEFT_ARROW:
+ *code = KEY_LEFT_ARROW;
+ break;
+ case B_RIGHT_ARROW:
+ *code = KEY_RIGHT_ARROW;
+ break;
+ case B_UP_ARROW:
+ *code = KEY_UP_ARROW;
+ break;
+ case B_DOWN_ARROW:
+ *code = KEY_DOWN_ARROW;
+ break;
+ case B_INSERT:
+ *code = KEY_INSERT;
+ break;
+ case B_DELETE:
+ *code = KEY_DELETE;
+ break;
+ case B_HOME:
+ *code = KEY_HOME;
+ break;
+ case B_END:
+ *code = KEY_END;
+ break;
+ case B_PAGE_UP:
+ *code = KEY_PAGE_UP;
+ break;
+ case B_PAGE_DOWN:
+ *code = KEY_PAGE_DOWN;
+ break;
+
+ case B_FUNCTION_KEY:
+ *code = KEY_F1 + key - 2;
+
+ if (*code - KEY_F1 == 12)
+ *code = KEY_PRINT;
+ else if (*code - KEY_F1 == 13)
+ /* Okay, Scroll Lock is a bit too much: keyboard.c doesn't
+ know about it yet, and it shouldn't, since that's a
+ modifier key.
+
+ *code = KEY_SCROLL_LOCK; */
+ return -1;
+ else if (*code - KEY_F1 == 14)
+ *code = KEY_PAUSE;
+
+ break;
+
+ case B_HANGUL:
+ *code = KEY_HANGUL;
+ break;
+ case B_HANGUL_HANJA:
+ *code = KEY_HANGUL_HANJA;
+ break;
+ case B_KATAKANA_HIRAGANA:
+ *code = KEY_HIRIGANA_KATAGANA;
+ break;
+ case B_HANKAKU_ZENKAKU:
+ *code = KEY_ZENKAKU_HANKAKU;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+static void
+map_key (char *chars, int32 offset, uint32_t *c)
+{
+ int size = chars[offset++];
+ switch (size)
+ {
+ case 0:
+ break;
+
+ case 1:
+ *c = chars[offset];
+ break;
+
+ default:
+ {
+ char str[5];
+ int i = (size <= 4) ? size : 4;
+ strncpy (str, &(chars[offset]), i);
+ str[i] = '0';
+ *c = BUnicodeChar::FromUTF8 ((char *) &str);
+ break;
+ }
+ }
+}
+
+static void
+map_shift (uint32_t kc, uint32_t *ch)
+{
+ if (!key_map_lock.Lock ())
+ gui_abort ("Failed to lock keymap");
+ if (!key_map)
+ get_key_map (&key_map, &key_chars);
+ if (!key_map)
+ return;
+ if (kc >= 128)
+ return;
+
+ int32_t m = key_map->shift_map[kc];
+ map_key (key_chars, m, ch);
+ key_map_lock.Unlock ();
+}
+
+static void
+map_caps (uint32_t kc, uint32_t *ch)
+{
+ if (!key_map_lock.Lock ())
+ gui_abort ("Failed to lock keymap");
+ if (!key_map)
+ get_key_map (&key_map, &key_chars);
+ if (!key_map)
+ return;
+ if (kc >= 128)
+ return;
+
+ int32_t m = key_map->caps_map[kc];
+ map_key (key_chars, m, ch);
+ key_map_lock.Unlock ();
+}
+
+static void
+map_caps_shift (uint32_t kc, uint32_t *ch)
+{
+ if (!key_map_lock.Lock ())
+ gui_abort ("Failed to lock keymap");
+ if (!key_map)
+ get_key_map (&key_map, &key_chars);
+ if (!key_map)
+ return;
+ if (kc >= 128)
+ return;
+
+ int32_t m = key_map->caps_shift_map[kc];
+ map_key (key_chars, m, ch);
+ key_map_lock.Unlock ();
+}
+
+static void
+map_normal (uint32_t kc, uint32_t *ch)
+{
+ if (!key_map_lock.Lock ())
+ gui_abort ("Failed to lock keymap");
+ if (!key_map)
+ get_key_map (&key_map, &key_chars);
+ if (!key_map)
+ return;
+ if (kc >= 128)
+ return;
+
+ int32_t m = key_map->normal_map[kc];
+ map_key (key_chars, m, ch);
+ key_map_lock.Unlock ();
+}
+
+static BRect
+get_zoom_rect (BWindow *window)
+{
+ BScreen screen;
+ BDeskbar deskbar;
+ BRect screen_frame;
+ BRect frame;
+ BRect deskbar_frame;
+ BRect window_frame;
+ BRect decorator_frame;
+
+ if (!screen.IsValid ())
+ gui_abort ("Failed to calculate screen rect");
+
+ screen_frame = frame = screen.Frame ();
+ deskbar_frame = deskbar.Frame ();
+
+ if (!(modifiers () & B_SHIFT_KEY) && !deskbar.IsAutoHide ())
+ {
+ switch (deskbar.Location ())
+ {
+ case B_DESKBAR_TOP:
+ frame.top = deskbar_frame.bottom + 2;
+ break;
+
+ case B_DESKBAR_BOTTOM:
+ case B_DESKBAR_LEFT_BOTTOM:
+ case B_DESKBAR_RIGHT_BOTTOM:
+ frame.bottom = deskbar_frame.top - 2;
+ break;
+
+ case B_DESKBAR_LEFT_TOP:
+ if (!deskbar.IsExpanded ())
+ frame.top = deskbar_frame.bottom + 2;
+ else if (!deskbar.IsAlwaysOnTop ()
+ && !deskbar.IsAutoRaise ())
+ frame.left = deskbar_frame.right + 2;
+ break;
+
+ default:
+ if (deskbar.IsExpanded ()
+ && !deskbar.IsAlwaysOnTop ()
+ && !deskbar.IsAutoRaise ())
+ frame.right = deskbar_frame.left - 2;
+ }
+ }
+
+ if (window)
+ {
+ window_frame = window->Frame ();
+ decorator_frame = window->DecoratorFrame ();
+
+ frame.top += (window_frame.top
+ - decorator_frame.top);
+ frame.bottom -= (decorator_frame.bottom
+ - window_frame.bottom);
+ frame.left += (window_frame.left
+ - decorator_frame.left);
+ frame.right -= (decorator_frame.right
+ - window_frame.right);
+
+ if (frame.top > deskbar_frame.bottom
+ || frame.bottom < deskbar_frame.top)
+ {
+ frame.left = screen_frame.left + (window_frame.left
+ - decorator_frame.left);
+ frame.right = screen_frame.right - (decorator_frame.right
+ - window_frame.right);
+ }
+ }
+
+ return frame;
+}
+
+/* Invisible window used to get B_SCREEN_CHANGED events. */
+class EmacsScreenChangeMonitor : public BWindow
+{
+ BRect previous_screen_frame;
+
+public:
+ EmacsScreenChangeMonitor (void) : BWindow (BRect (-100, -100, 0, 0), "",
+ B_NO_BORDER_WINDOW_LOOK,
+ B_FLOATING_ALL_WINDOW_FEEL,
+ B_AVOID_FRONT | B_AVOID_FOCUS)
+ {
+ BScreen screen (this);
+
+ if (!screen.IsValid ())
+ return;
+
+ previous_screen_frame = screen.Frame ();
+
+ /* Immediately show this window upon creation. It will not steal
+ the focus or become visible. */
+ Show ();
+
+ if (!LockLooper ())
+ return;
+
+ Hide ();
+ UnlockLooper ();
+ }
+
+ void
+ DispatchMessage (BMessage *msg, BHandler *handler)
+ {
+ struct haiku_screen_changed_event rq;
+ BRect frame;
+
+ if (msg->what == B_SCREEN_CHANGED)
+ {
+ if (msg->FindInt64 ("when", &rq.when) != B_OK)
+ rq.when = 0;
+
+ if (msg->FindRect ("frame", &frame) != B_OK
+ || frame != previous_screen_frame)
+ {
+ haiku_write (SCREEN_CHANGED_EVENT, &rq);
+
+ if (frame.IsValid ())
+ previous_screen_frame = frame;
+ }
+ }
+
+ BWindow::DispatchMessage (msg, handler);
+ }
+};
+
+class Emacs : public BApplication
+{
+public:
+ BMessage settings;
+ bool settings_valid_p;
+ EmacsScreenChangeMonitor *monitor;
+
+ Emacs (void) : BApplication ("application/x-vnd.GNU-emacs"),
+ settings_valid_p (false)
+ {
+ BPath settings_path;
+
+ if (find_directory (B_USER_SETTINGS_DIRECTORY, &settings_path) != B_OK)
+ return;
+
+ settings_path.Append (PACKAGE_NAME);
+
+ BEntry entry (settings_path.Path ());
+ BFile settings_file (&entry, B_READ_ONLY | B_CREATE_FILE);
+
+ if (settings.Unflatten (&settings_file) != B_OK)
+ return;
+
+ settings_valid_p = true;
+ monitor = new EmacsScreenChangeMonitor;
+ }
+
+ ~Emacs (void)
+ {
+ if (monitor->LockLooper ())
+ monitor->Quit ();
+ else
+ delete monitor;
+ }
+
+ void
+ AboutRequested (void)
+ {
+ BAlert *about = new BAlert (PACKAGE_NAME,
+ PACKAGE_STRING
+ "\nThe extensible, self-documenting, real-time display editor.",
+ "Close");
+ about->Go ();
+ }
+
+ bool
+ QuitRequested (void)
+ {
+ struct haiku_app_quit_requested_event rq;
+ struct haiku_session_manager_reply reply;
+ int32 reply_type;
+
+ haiku_write (APP_QUIT_REQUESTED_EVENT, &rq);
+
+ if (read_port (port_emacs_to_session_manager,
+ &reply_type, &reply, sizeof reply) < B_OK)
+ /* Return true so the system kills us, since there's no real
+ alternative if this read fails. */
+ return true;
+
+ return reply.quit_reply;
+ }
+
+ void
+ MessageReceived (BMessage *msg)
+ {
+ struct haiku_clipboard_changed_event rq;
+
+ if (msg->what == QUIT_APPLICATION)
+ Quit ();
+ else if (msg->what == B_CLIPBOARD_CHANGED)
+ haiku_write (CLIPBOARD_CHANGED_EVENT, &rq);
+ else
+ BApplication::MessageReceived (msg);
+ }
+};
+
+class EmacsWindow : public BWindow
+{
+public:
+ struct child_frame
+ {
+ struct child_frame *next;
+ int xoff, yoff;
+ EmacsWindow *window;
+ } *subset_windows;
+
+ EmacsWindow *parent;
+ BRect pre_fullscreen_rect;
+ BRect pre_zoom_rect;
+ int x_before_zoom;
+ int y_before_zoom;
+ bool shown_flag;
+ volatile bool was_shown_p;
+ bool menu_bar_active_p;
+ bool override_redirect_p;
+ window_look pre_override_redirect_look;
+ window_feel pre_override_redirect_feel;
+ uint32 pre_override_redirect_workspaces;
+ int window_id;
+ bool *menus_begun;
+ enum haiku_z_group z_group;
+ bool tooltip_p;
+ enum haiku_fullscreen_mode fullscreen_mode;
+
+ EmacsWindow () : BWindow (BRect (0, 0, 0, 0), "", B_TITLED_WINDOW_LOOK,
+ B_NORMAL_WINDOW_FEEL, B_NO_SERVER_SIDE_WINDOW_MODIFIERS),
+ subset_windows (NULL),
+ parent (NULL),
+ x_before_zoom (INT_MIN),
+ y_before_zoom (INT_MIN),
+ shown_flag (false),
+ was_shown_p (false),
+ menu_bar_active_p (false),
+ override_redirect_p (false),
+ menus_begun (NULL),
+ z_group (Z_GROUP_NONE),
+ tooltip_p (false),
+ fullscreen_mode (FULLSCREEN_MODE_NONE)
+ {
+ /* This pulse rate is used by scroll bars for repeating a button
+ action while a button is held down. */
+ SetPulseRate (30000);
+ }
+
+ ~EmacsWindow ()
+ {
+ if (!child_frame_lock.Lock ())
+ gui_abort ("Failed to lock child frame state lock");
+ struct child_frame *next;
+ for (struct child_frame *f = subset_windows; f; f = next)
+ {
+ if (f->window->LockLooper ())
+ gui_abort ("Failed to lock looper for unparent");
+ f->window->Unparent ();
+ f->window->UnlockLooper ();
+ next = f->next;
+ delete f;
+ }
+
+ if (this->parent)
+ UnparentAndUnlink ();
+ child_frame_lock.Unlock ();
+ }
+
+ void
+ RecomputeFeel (void)
+ {
+ if (override_redirect_p || tooltip_p)
+ SetFeel (kMenuWindowFeel);
+ else if (parent)
+ SetFeel (B_FLOATING_SUBSET_WINDOW_FEEL);
+ else if (z_group == Z_GROUP_ABOVE)
+ SetFeel (B_FLOATING_ALL_WINDOW_FEEL);
+ else
+ SetFeel (B_NORMAL_WINDOW_FEEL);
+ }
+
+ void
+ UpwardsSubset (EmacsWindow *w)
+ {
+ for (; w; w = w->parent)
+ AddToSubset (w);
+ }
+
+ void
+ UpwardsSubsetChildren (EmacsWindow *w)
+ {
+ if (!LockLooper ())
+ gui_abort ("Failed to lock looper for subset");
+ if (!child_frame_lock.Lock ())
+ gui_abort ("Failed to lock child frame state lock");
+ UpwardsSubset (w);
+ for (struct child_frame *f = subset_windows; f;
+ f = f->next)
+ f->window->UpwardsSubsetChildren (w);
+ child_frame_lock.Unlock ();
+ UnlockLooper ();
+ }
+
+ void
+ UpwardsUnSubset (EmacsWindow *w)
+ {
+ for (; w; w = w->parent)
+ RemoveFromSubset (w);
+ }
+
+ void
+ UpwardsUnSubsetChildren (EmacsWindow *w)
+ {
+ if (!LockLooper ())
+ gui_abort ("Failed to lock looper for unsubset");
+ if (!child_frame_lock.Lock ())
+ gui_abort ("Failed to lock child frame state lock");
+ UpwardsUnSubset (w);
+ for (struct child_frame *f = subset_windows; f;
+ f = f->next)
+ f->window->UpwardsUnSubsetChildren (w);
+ child_frame_lock.Unlock ();
+ UnlockLooper ();
+ }
+
+ void
+ Unparent (void)
+ {
+ EmacsWindow *parent;
+
+ if (!child_frame_lock.Lock ())
+ gui_abort ("Failed to lock child frame state lock");
+
+ parent = this->parent;
+ this->parent = NULL;
+ RecomputeFeel ();
+ UpwardsUnSubsetChildren (parent);
+ this->RemoveFromSubset (this);
+ child_frame_lock.Unlock ();
+ }
+
+ void
+ UnparentAndUnlink (void)
+ {
+ if (!child_frame_lock.Lock ())
+ gui_abort ("Failed to lock child frame state lock");
+ this->parent->UnlinkChild (this);
+ this->Unparent ();
+ child_frame_lock.Unlock ();
+ }
+
+ void
+ UnlinkChild (EmacsWindow *window)
+ {
+ struct child_frame *last = NULL;
+ struct child_frame *tem = subset_windows;
+
+ for (; tem; last = tem, tem = tem->next)
+ {
+ if (tem->window == window)
+ {
+ if (last)
+ last->next = tem->next;
+ else
+ subset_windows = tem->next;
+ delete tem;
+ return;
+ }
+ }
+
+ gui_abort ("Failed to unlink child frame");
+ }
+
+ void
+ ParentTo (EmacsWindow *window)
+ {
+ if (!child_frame_lock.Lock ())
+ gui_abort ("Failed to lock child frame state lock");
+
+ if (this->parent)
+ UnparentAndUnlink ();
+
+ this->parent = window;
+ RecomputeFeel ();
+ this->AddToSubset (this);
+ if (!IsHidden () && this->parent)
+ UpwardsSubsetChildren (parent);
+ window->LinkChild (this);
+
+ child_frame_lock.Unlock ();
+ }
+
+ void
+ LinkChild (EmacsWindow *window)
+ {
+ struct child_frame *f = new struct child_frame;
+
+ for (struct child_frame *f = subset_windows; f;
+ f = f->next)
+ {
+ if (window == f->window)
+ gui_abort ("Trying to link a child frame that is already present");
+ }
+
+ f->window = window;
+ f->next = subset_windows;
+ f->xoff = -1;
+ f->yoff = -1;
+
+ subset_windows = f;
+ }
+
+ void
+ MoveToIncludingFrame (int x, int y)
+ {
+ BRect decorator, frame;
+
+ decorator = DecoratorFrame ();
+ frame = Frame ();
+
+ MoveTo (x + frame.left - decorator.left,
+ y + frame.top - decorator.top);
+ }
+
+ void
+ DoMove (struct child_frame *f)
+ {
+ BRect frame = this->Frame ();
+ f->window->MoveToIncludingFrame (frame.left + f->xoff,
+ frame.top + f->yoff);
+ }
+
+ void
+ DoUpdateWorkspace (struct child_frame *f)
+ {
+ f->window->SetWorkspaces (this->Workspaces ());
+ }
+
+ void
+ MoveChild (EmacsWindow *window, int xoff, int yoff,
+ int weak_p)
+ {
+ if (!child_frame_lock.Lock ())
+ gui_abort ("Failed to lock child frame state lock");
+
+ for (struct child_frame *f = subset_windows; f;
+ f = f->next)
+ {
+ if (window == f->window)
+ {
+ f->xoff = xoff;
+ f->yoff = yoff;
+ if (!weak_p)
+ DoMove (f);
+
+ child_frame_lock.Unlock ();
+ return;
+ }
+ }
+
+ child_frame_lock.Unlock ();
+ gui_abort ("Trying to move a child frame that doesn't exist");
+ }
+
+ void
+ WindowActivated (bool activated)
+ {
+ struct haiku_activation_event rq;
+ rq.window = this;
+ rq.activated_p = activated;
+
+ haiku_write (ACTIVATION, &rq);
+ }
+
+ void
+ MessageReceived (BMessage *msg)
+ {
+ if (msg->WasDropped ())
+ {
+ BPoint whereto;
+ int64 threadid;
+ struct haiku_drag_and_drop_event rq;
+
+ if (msg->FindInt64 ("emacs:thread_id", &threadid) == B_OK
+ && threadid == find_thread (NULL))
+ return;
+
+ whereto = msg->DropPoint ();
+
+ this->ConvertFromScreen (&whereto);
+
+ rq.window = this;
+ rq.message = DetachCurrentMessage ();
+ rq.x = whereto.x;
+ rq.y = whereto.y;
+
+ haiku_write (DRAG_AND_DROP_EVENT, &rq);
+ }
+ else if (msg->GetPointer ("menuptr"))
+ {
+ struct haiku_menu_bar_select_event rq;
+
+ rq.window = this;
+ rq.ptr = (void *) msg->GetPointer ("menuptr");
+
+ haiku_write (MENU_BAR_SELECT_EVENT, &rq);
+ }
+ else
+ 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;
+
+ /* Pass through key events to the regular dispatch mechanism
+ if the menu bar active, so that key navigation can work. */
+ if (menu_bar_active_p)
+ {
+ BWindow::DispatchMessage (msg, handler);
+ return;
+ }
+
+ rq.window = this;
+
+ int32 raw, key;
+ int ret;
+ msg->FindInt32 ("raw_char", &raw);
+ msg->FindInt32 ("key", &key);
+ msg->FindInt64 ("when", &rq.time);
+
+ rq.modifiers = 0;
+ uint32_t mods = modifiers ();
+
+ if (mods & B_SHIFT_KEY)
+ rq.modifiers |= HAIKU_MODIFIER_SHIFT;
+
+ if (mods & B_CONTROL_KEY)
+ rq.modifiers |= HAIKU_MODIFIER_CTRL;
+
+ if (mods & B_COMMAND_KEY)
+ rq.modifiers |= HAIKU_MODIFIER_ALT;
+
+ if (mods & B_OPTION_KEY)
+ rq.modifiers |= HAIKU_MODIFIER_SUPER;
+
+ ret = keysym_from_raw_char (raw, key, &rq.keysym);
+
+ if (!ret)
+ rq.keysym = 0;
+
+ if (ret < 0)
+ return;
+
+ rq.multibyte_char = 0;
+
+ if (!rq.keysym)
+ {
+ if (mods & B_SHIFT_KEY)
+ {
+ if (mods & B_CAPS_LOCK)
+ map_caps_shift (key, &rq.multibyte_char);
+ else
+ map_shift (key, &rq.multibyte_char);
+ }
+ else
+ {
+ if (mods & B_CAPS_LOCK)
+ map_caps (key, &rq.multibyte_char);
+ else
+ map_normal (key, &rq.multibyte_char);
+ }
+ }
+
+ haiku_write (msg->what == B_KEY_DOWN ? KEY_DOWN : KEY_UP, &rq);
+ }
+ else if (msg->what == B_MOUSE_WHEEL_CHANGED)
+ {
+ struct haiku_wheel_move_event rq;
+ rq.window = this;
+ rq.modifiers = 0;
+
+ uint32_t mods = modifiers ();
+
+ if (mods & B_SHIFT_KEY)
+ rq.modifiers |= HAIKU_MODIFIER_SHIFT;
+
+ if (mods & B_CONTROL_KEY)
+ rq.modifiers |= HAIKU_MODIFIER_CTRL;
+
+ if (mods & B_COMMAND_KEY)
+ rq.modifiers |= HAIKU_MODIFIER_ALT;
+
+ if (mods & B_OPTION_KEY)
+ rq.modifiers |= HAIKU_MODIFIER_SUPER;
+
+ float dx, dy;
+ if (msg->FindFloat ("be:wheel_delta_x", &dx) == B_OK &&
+ msg->FindFloat ("be:wheel_delta_y", &dy) == B_OK)
+ {
+ rq.delta_x = dx;
+ rq.delta_y = dy;
+
+ haiku_write (WHEEL_MOVE_EVENT, &rq);
+ };
+ }
+ else if (msg->what == SEND_MOVE_FRAME_EVENT)
+ FrameMoved (Frame ().LeftTop ());
+ else if (msg->what == B_SCREEN_CHANGED)
+ {
+ if (fullscreen_mode != FULLSCREEN_MODE_NONE)
+ SetFullscreen (fullscreen_mode);
+
+ BWindow::DispatchMessage (msg, handler);
+ }
+ else
+ BWindow::DispatchMessage (msg, handler);
+ }
+
+ void
+ MenusBeginning (void)
+ {
+ struct haiku_menu_bar_state_event rq;
+
+ rq.window = this;
+ if (!menus_begun)
+ haiku_write (MENU_BAR_OPEN, &rq);
+ else
+ *menus_begun = true;
+
+ menu_bar_active_p = true;
+ }
+
+ void
+ MenusEnded ()
+ {
+ struct haiku_menu_bar_state_event rq;
+ rq.window = this;
+
+ haiku_write (MENU_BAR_CLOSE, &rq);
+ menu_bar_active_p = false;
+ }
+
+ void
+ FrameResized (float newWidth, float newHeight)
+ {
+ struct haiku_resize_event rq;
+ rq.window = this;
+ rq.width = newWidth + 1.0f;
+ rq.height = newHeight + 1.0f;
+
+ haiku_write (FRAME_RESIZED, &rq);
+ BWindow::FrameResized (newWidth, newHeight);
+ }
+
+ void
+ FrameMoved (BPoint new_position)
+ {
+ struct haiku_move_event rq;
+ BRect frame, decorator_frame;
+ struct child_frame *f;
+
+ if (fullscreen_mode == FULLSCREEN_MODE_WIDTH
+ && new_position.x != 0)
+ {
+ MoveTo (0, new_position.y);
+ return;
+ }
+
+ if (fullscreen_mode == FULLSCREEN_MODE_HEIGHT
+ && new_position.y != 0)
+ {
+ MoveTo (new_position.x, 0);
+ return;
+ }
+
+ rq.window = this;
+ rq.x = std::lrint (new_position.x);
+ rq.y = std::lrint (new_position.y);
+
+ frame = Frame ();
+ decorator_frame = DecoratorFrame ();
+
+ rq.decorator_width
+ = std::lrint (frame.left - decorator_frame.left);
+ rq.decorator_height
+ = std::lrint (frame.top - decorator_frame.top);
+
+ haiku_write (MOVE_EVENT, &rq);
+
+ CHILD_FRAME_LOCK_INSIDE_LOOPER_CALLBACK
+ {
+ for (f = subset_windows; f; f = f->next)
+ DoMove (f);
+ child_frame_lock.Unlock ();
+
+ BWindow::FrameMoved (new_position);
+ }
+ }
+
+ void
+ WorkspacesChanged (uint32_t old, uint32_t n)
+ {
+ struct child_frame *f;
+
+ CHILD_FRAME_LOCK_INSIDE_LOOPER_CALLBACK
+ {
+ for (f = subset_windows; f; f = f->next)
+ DoUpdateWorkspace (f);
+
+ child_frame_lock.Unlock ();
+ }
+ }
+
+ void
+ EmacsMoveTo (int x, int y)
+ {
+ if (!child_frame_lock.Lock ())
+ gui_abort ("Failed to lock child frame state lock");
+
+ if (!this->parent)
+ this->MoveToIncludingFrame (x, y);
+ else
+ this->parent->MoveChild (this, x, y, 0);
+ child_frame_lock.Unlock ();
+ }
+
+ bool
+ QuitRequested ()
+ {
+ struct haiku_quit_requested_event rq;
+ rq.window = this;
+ haiku_write (QUIT_REQUESTED, &rq);
+ return false;
+ }
+
+ void
+ Minimize (bool minimized_p)
+ {
+ struct haiku_iconification_event rq;
+
+ rq.window = this;
+ rq.iconified_p = !parent && minimized_p;
+ haiku_write (ICONIFICATION, &rq);
+
+ BWindow::Minimize (minimized_p);
+ }
+
+ void
+ EmacsHide (void)
+ {
+ if (this->IsHidden ())
+ return;
+ if (!child_frame_lock.Lock ())
+ gui_abort ("Failed to lock child frame state lock");
+
+ Hide ();
+ if (this->parent)
+ UpwardsUnSubsetChildren (this->parent);
+
+ child_frame_lock.Unlock ();
+ }
+
+ void
+ EmacsShow (void)
+ {
+ if (!this->IsHidden ())
+ return;
+
+ if (!child_frame_lock.Lock ())
+ gui_abort ("Failed to lock child frame state lock");
+
+ if (!was_shown_p)
+ {
+ /* This window is being shown for the first time, which means
+ Show will unlock the looper. In this case, it should be
+ locked again, since the looper is unlocked when the window
+ is first created. */
+
+ if (!LockLooper ())
+ gui_abort ("Failed to lock looper during first window show");
+ was_shown_p = true;
+ }
+
+ if (this->parent)
+ shown_flag = 1;
+ Show ();
+ if (this->parent)
+ UpwardsSubsetChildren (this->parent);
+
+ child_frame_lock.Unlock ();
+ }
+
+ BRect
+ ClearFullscreen (enum haiku_fullscreen_mode target_mode)
+ {
+ BRect original_frame;
+
+ switch (fullscreen_mode)
+ {
+ case FULLSCREEN_MODE_MAXIMIZED:
+ original_frame = pre_zoom_rect;
+
+ if (target_mode == FULLSCREEN_MODE_NONE)
+ BWindow::Zoom (pre_zoom_rect.LeftTop (),
+ BE_RECT_WIDTH (pre_zoom_rect) - 1,
+ BE_RECT_HEIGHT (pre_zoom_rect) - 1);
+ break;
+
+ case FULLSCREEN_MODE_BOTH:
+ case FULLSCREEN_MODE_HEIGHT:
+ case FULLSCREEN_MODE_WIDTH:
+ original_frame = pre_fullscreen_rect;
+ SetFlags (Flags () & ~(B_NOT_MOVABLE
+ | B_NOT_ZOOMABLE
+ | B_NOT_RESIZABLE));
+
+ if (target_mode != FULLSCREEN_MODE_NONE)
+ goto out;
+
+ MoveTo (pre_fullscreen_rect.LeftTop ());
+ ResizeTo (BE_RECT_WIDTH (pre_fullscreen_rect) - 1,
+ BE_RECT_HEIGHT (pre_fullscreen_rect) - 1);
+ break;
+
+ case FULLSCREEN_MODE_NONE:
+ original_frame = Frame ();
+ break;
+ }
+
+ out:
+ fullscreen_mode = FULLSCREEN_MODE_NONE;
+ return original_frame;
+ }
+
+ BRect
+ FullscreenRectForMode (enum haiku_fullscreen_mode mode)
+ {
+ BScreen screen (this);
+ BRect frame;
+
+ if (!screen.IsValid ())
+ return BRect (0, 0, 0, 0);
+
+ frame = screen.Frame ();
+
+ if (mode == FULLSCREEN_MODE_HEIGHT)
+ frame.right -= BE_RECT_WIDTH (frame) / 2;
+ else if (mode == FULLSCREEN_MODE_WIDTH)
+ frame.bottom -= BE_RECT_HEIGHT (frame) / 2;
+
+ return frame;
+ }
+
+ void
+ SetFullscreen (enum haiku_fullscreen_mode mode)
+ {
+ BRect zoom_rect, frame;
+
+ frame = ClearFullscreen (mode);
+
+ switch (mode)
+ {
+ case FULLSCREEN_MODE_MAXIMIZED:
+ pre_zoom_rect = frame;
+ zoom_rect = get_zoom_rect (this);
+ BWindow::Zoom (zoom_rect.LeftTop (),
+ BE_RECT_WIDTH (zoom_rect) - 1,
+ BE_RECT_HEIGHT (zoom_rect) - 1);
+ break;
+
+ case FULLSCREEN_MODE_BOTH:
+ SetFlags (Flags () | B_NOT_MOVABLE);
+ FALLTHROUGH;
+
+ case FULLSCREEN_MODE_HEIGHT:
+ case FULLSCREEN_MODE_WIDTH:
+ SetFlags (Flags () | B_NOT_ZOOMABLE | B_NOT_RESIZABLE);
+ pre_fullscreen_rect = frame;
+ zoom_rect = FullscreenRectForMode (mode);
+ ResizeTo (BE_RECT_WIDTH (zoom_rect) - 1,
+ BE_RECT_HEIGHT (zoom_rect) - 1);
+ MoveTo (zoom_rect.left, zoom_rect.top);
+ break;
+
+ case FULLSCREEN_MODE_NONE:
+ break;
+ }
+
+ fullscreen_mode = mode;
+ }
+
+ void
+ Zoom (BPoint origin, float width, float height)
+ {
+ struct haiku_zoom_event rq;
+
+ rq.window = this;
+ rq.fullscreen_mode = fullscreen_mode;
+ haiku_write (ZOOM_EVENT, &rq);
+ }
+
+ void
+ OffsetChildRect (BRect *r, EmacsWindow *c)
+ {
+ if (!child_frame_lock.Lock ())
+ gui_abort ("Failed to lock child frame state lock");
+
+ for (struct child_frame *f; f; f = f->next)
+ if (f->window == c)
+ {
+ r->top -= f->yoff;
+ r->bottom -= f->yoff;
+ r->left -= f->xoff;
+ r->right -= f->xoff;
+ child_frame_lock.Unlock ();
+ return;
+ }
+
+ child_frame_lock.Lock ();
+ gui_abort ("Trying to calculate offsets for a child frame that doesn't exist");
+ }
+};
+
+class EmacsMenuBar : public BMenuBar
+{
+ bool tracking_p;
+
+public:
+ EmacsMenuBar () : BMenuBar (BRect (0, 0, 0, 0), NULL)
+ {
+ }
+
+ void
+ AttachedToWindow (void)
+ {
+ BWindow *window = Window ();
+
+ window->SetKeyMenuBar (this);
+ }
+
+ void
+ FrameResized (float newWidth, float newHeight)
+ {
+ struct haiku_menu_bar_resize_event rq;
+ rq.window = this->Window ();
+ rq.height = std::lrint (newHeight + 1);
+ rq.width = std::lrint (newWidth + 1);
+
+ haiku_write (MENU_BAR_RESIZE, &rq);
+ BMenuBar::FrameResized (newWidth, newHeight);
+ }
+
+ void
+ MouseDown (BPoint point)
+ {
+ struct haiku_menu_bar_click_event rq;
+ EmacsWindow *ew = (EmacsWindow *) Window ();
+
+ rq.window = ew;
+ rq.x = std::lrint (point.x);
+ rq.y = std::lrint (point.y);
+
+ if (!ew->menu_bar_active_p)
+ haiku_write (MENU_BAR_CLICK, &rq);
+ else
+ BMenuBar::MouseDown (point);
+ }
+
+ void
+ MouseMoved (BPoint point, uint32 transit, const BMessage *msg)
+ {
+ struct haiku_menu_bar_left_event rq;
+
+ if (transit == B_EXITED_VIEW)
+ {
+ rq.x = std::lrint (point.x);
+ rq.y = std::lrint (point.y);
+ rq.window = this->Window ();
+
+ haiku_write (MENU_BAR_LEFT, &rq);
+ }
+
+ BMenuBar::MouseMoved (point, transit, msg);
+ }
+
+ void
+ MessageReceived (BMessage *msg)
+ {
+ BRect frame;
+ BPoint pt, l;
+ EmacsWindow *window;
+ bool menus_begun;
+
+ if (msg->what == SHOW_MENU_BAR)
+ {
+ window = (EmacsWindow *) Window ();
+ frame = Frame ();
+ pt = frame.LeftTop ();
+ l = pt;
+ menus_begun = false;
+ Parent ()->ConvertToScreen (&pt);
+
+ window->menus_begun = &menus_begun;
+ set_mouse_position (pt.x, pt.y);
+ BMenuBar::MouseDown (l);
+ window->menus_begun = NULL;
+
+ if (!menus_begun)
+ msg->SendReply (msg);
+ else
+ msg->SendReply (BE_MENU_BAR_OPEN);
+ }
+ else if (msg->what == REPLAY_MENU_BAR)
+ {
+ window = (EmacsWindow *) Window ();
+ menus_begun = false;
+ window->menus_begun = &menus_begun;
+
+ if (msg->FindPoint ("emacs:point", &pt) == B_OK)
+ BMenuBar::MouseDown (pt);
+
+ window->menus_begun = NULL;
+
+ if (!menus_begun)
+ msg->SendReply (msg);
+ else
+ msg->SendReply (BE_MENU_BAR_OPEN);
+ }
+ else
+ BMenuBar::MessageReceived (msg);
+ }
+};
+
+class EmacsView : public BView
+{
+public:
+ int looper_locked_count;
+ BRegion sb_region;
+ BRegion invalid_region;
+
+ BView *offscreen_draw_view;
+ BBitmap *offscreen_draw_bitmap_1;
+ BBitmap *copy_bitmap;
+
+#ifdef USE_BE_CAIRO
+ cairo_surface_t *cr_surface;
+ cairo_t *cr_context;
+ BLocker cr_surface_lock;
+#endif
+
+ BMessage *wait_for_release_message;
+ int64 grabbed_buttons;
+ BScreen screen;
+ bool use_frame_synchronization;
+
+ EmacsView () : BView (BRect (0, 0, 0, 0), "Emacs",
+ B_FOLLOW_NONE, B_WILL_DRAW),
+ looper_locked_count (0),
+ offscreen_draw_view (NULL),
+ offscreen_draw_bitmap_1 (NULL),
+ copy_bitmap (NULL),
+#ifdef USE_BE_CAIRO
+ cr_surface (NULL),
+ cr_context (NULL),
+#endif
+ wait_for_release_message (NULL),
+ grabbed_buttons (0),
+ use_frame_synchronization (false)
+ {
+
+ }
+
+ ~EmacsView ()
+ {
+ if (wait_for_release_message)
+ {
+ wait_for_release_message->SendReply (wait_for_release_message);
+ delete wait_for_release_message;
+ }
+
+ TearDownDoubleBuffering ();
+
+ if (!grab_view_locker.Lock ())
+ gui_abort ("Couldn't lock grab view locker");
+ if (grab_view == this)
+ grab_view = NULL;
+ grab_view_locker.Unlock ();
+ }
+
+ void
+ SetFrameSynchronization (bool sync)
+ {
+ if (LockLooper ())
+ {
+ use_frame_synchronization = sync;
+ UnlockLooper ();
+ }
+ }
+
+ void
+ MessageReceived (BMessage *msg)
+ {
+ uint32 buttons;
+ BLooper *looper = Looper ();
+
+ if (msg->what == WAIT_FOR_RELEASE)
+ {
+ if (wait_for_release_message)
+ gui_abort ("Wait for release message already exists");
+
+ GetMouse (NULL, &buttons, false);
+
+ if (!buttons)
+ msg->SendReply (msg);
+ else
+ wait_for_release_message = looper->DetachCurrentMessage ();
+ }
+ else if (msg->what == RELEASE_NOW)
+ {
+ if (wait_for_release_message)
+ wait_for_release_message->SendReply (msg);
+
+ delete wait_for_release_message;
+ wait_for_release_message = NULL;
+ }
+ else
+ BView::MessageReceived (msg);
+ }
+
+#ifdef USE_BE_CAIRO
+ void
+ DetachCairoSurface (void)
+ {
+ if (!cr_surface_lock.Lock ())
+ gui_abort ("Could not lock cr surface during detachment");
+ if (!cr_surface)
+ gui_abort ("Trying to detach window cr surface when none exists");
+ cairo_destroy (cr_context);
+ cairo_surface_destroy (cr_surface);
+ cr_surface = NULL;
+ cr_context = NULL;
+ cr_surface_lock.Unlock ();
+ }
+
+ void
+ AttachCairoSurface (void)
+ {
+ if (!cr_surface_lock.Lock ())
+ gui_abort ("Could not lock cr surface during attachment");
+ if (cr_surface)
+ gui_abort ("Trying to attach cr surface when one already exists");
+ BRect bounds = offscreen_draw_bitmap_1->Bounds ();
+
+ cr_surface = cairo_image_surface_create_for_data
+ ((unsigned char *) offscreen_draw_bitmap_1->Bits (),
+ CAIRO_FORMAT_ARGB32, BE_RECT_WIDTH (bounds),
+ BE_RECT_HEIGHT (bounds),
+ offscreen_draw_bitmap_1->BytesPerRow ());
+ if (!cr_surface)
+ gui_abort ("Cr surface allocation failed for double-buffered view");
+
+ cr_context = cairo_create (cr_surface);
+ if (!cr_context)
+ gui_abort ("cairo_t allocation failed for double-buffered view");
+ cr_surface_lock.Unlock ();
+ }
+#endif
+
+ void
+ TearDownDoubleBuffering (void)
+ {
+ if (offscreen_draw_view)
+ {
+ if (Window ())
+ ClearViewBitmap ();
+ if (copy_bitmap)
+ {
+ delete copy_bitmap;
+ copy_bitmap = NULL;
+ }
+ if (!looper_locked_count)
+ if (!offscreen_draw_view->LockLooper ())
+ gui_abort ("Failed to lock offscreen draw view");
+#ifdef USE_BE_CAIRO
+ if (cr_surface)
+ DetachCairoSurface ();
+#endif
+ offscreen_draw_view->RemoveSelf ();
+ delete offscreen_draw_view;
+ offscreen_draw_view = NULL;
+ delete offscreen_draw_bitmap_1;
+ offscreen_draw_bitmap_1 = NULL;
+ }
+ }
+
+ void
+ AfterResize (void)
+ {
+ if (offscreen_draw_view)
+ {
+ if (!LockLooper ())
+ gui_abort ("Failed to lock looper after resize");
+
+ if (!offscreen_draw_view->LockLooper ())
+ gui_abort ("Failed to lock offscreen draw view after resize");
+#ifdef USE_BE_CAIRO
+ DetachCairoSurface ();
+#endif
+ offscreen_draw_view->RemoveSelf ();
+ delete offscreen_draw_bitmap_1;
+ offscreen_draw_bitmap_1 = new BBitmap (Frame (), B_RGBA32, 1);
+ if (offscreen_draw_bitmap_1->InitCheck () != B_OK)
+ gui_abort ("Offscreen draw bitmap initialization failed");
+
+ BRect frame = Frame ();
+
+ offscreen_draw_view->MoveTo (frame.left, frame.top);
+ offscreen_draw_view->ResizeTo (BE_RECT_WIDTH (frame),
+ BE_RECT_HEIGHT (frame));
+ offscreen_draw_bitmap_1->AddChild (offscreen_draw_view);
+#ifdef USE_BE_CAIRO
+ AttachCairoSurface ();
+#endif
+
+ if (looper_locked_count)
+ offscreen_draw_bitmap_1->Lock ();
+
+ UnlockLooper ();
+ }
+ }
+
+ void
+ Draw (BRect expose_bounds)
+ {
+ struct haiku_expose_event rq;
+ EmacsWindow *w = (EmacsWindow *) Window ();
+
+ if (w->shown_flag && offscreen_draw_view)
+ {
+ PushState ();
+ SetDrawingMode (B_OP_ERASE);
+ FillRect (Frame ());
+ PopState ();
+ return;
+ }
+
+ if (!offscreen_draw_view)
+ {
+ if (sb_region.Contains (std::lrint (expose_bounds.left),
+ std::lrint (expose_bounds.top)) &&
+ sb_region.Contains (std::lrint (expose_bounds.right),
+ std::lrint (expose_bounds.top)) &&
+ sb_region.Contains (std::lrint (expose_bounds.left),
+ std::lrint (expose_bounds.bottom)) &&
+ sb_region.Contains (std::lrint (expose_bounds.right),
+ std::lrint (expose_bounds.bottom)))
+ return;
+
+ rq.x = std::floor (expose_bounds.left);
+ rq.y = std::floor (expose_bounds.top);
+ rq.width = std::ceil (expose_bounds.right - expose_bounds.left + 1);
+ rq.height = std::ceil (expose_bounds.bottom - expose_bounds.top + 1);
+ if (!rq.width)
+ rq.width = 1;
+ if (!rq.height)
+ rq.height = 1;
+ rq.window = this->Window ();
+
+ haiku_write (FRAME_EXPOSED, &rq);
+ }
+ }
+
+ void
+ FlipBuffers (void)
+ {
+ EmacsWindow *w;
+ if (!LockLooper ())
+ gui_abort ("Failed to lock looper during buffer flip");
+ if (!offscreen_draw_view)
+ gui_abort ("Failed to lock offscreen view during buffer flip");
+
+ offscreen_draw_view->Sync ();
+ w = (EmacsWindow *) Window ();
+ w->shown_flag = 0;
+
+ if (copy_bitmap &&
+ copy_bitmap->Bounds () != offscreen_draw_bitmap_1->Bounds ())
+ {
+ delete copy_bitmap;
+ copy_bitmap = NULL;
+ }
+ if (!copy_bitmap)
+ {
+ copy_bitmap = new BBitmap (offscreen_draw_bitmap_1);
+ SetViewBitmap (copy_bitmap, Frame (),
+ Frame (), B_FOLLOW_NONE, 0);
+ }
+ else
+ copy_bitmap->ImportBits (offscreen_draw_bitmap_1);
+
+ if (copy_bitmap->InitCheck () != B_OK)
+ gui_abort ("Failed to init copy bitmap during buffer flip");
+
+ /* Wait for VBLANK. If responding to the invalidation or buffer
+ flipping takes longer than the blanking period, we lose. */
+ if (use_frame_synchronization)
+ screen.WaitForRetrace ();
+
+ Invalidate (&invalid_region);
+ invalid_region.MakeEmpty ();
+ UnlockLooper ();
+ return;
+ }
+
+ void
+ SetUpDoubleBuffering (void)
+ {
+ if (!LockLooper ())
+ gui_abort ("Failed to lock self setting up double buffering");
+ if (offscreen_draw_view)
+ gui_abort ("Failed to lock offscreen view setting up double buffering");
+
+ offscreen_draw_bitmap_1 = new BBitmap (Frame (), B_RGBA32, 1);
+ if (offscreen_draw_bitmap_1->InitCheck () != B_OK)
+ gui_abort ("Failed to init offscreen bitmap");
+#ifdef USE_BE_CAIRO
+ AttachCairoSurface ();
+#endif
+ offscreen_draw_view = new BView (Frame (), NULL, B_FOLLOW_NONE, B_WILL_DRAW);
+ offscreen_draw_bitmap_1->AddChild (offscreen_draw_view);
+
+ if (looper_locked_count)
+ {
+ if (!offscreen_draw_bitmap_1->Lock ())
+ gui_abort ("Failed to lock bitmap after double buffering was set up");
+ }
+
+ invalid_region.MakeEmpty ();
+ UnlockLooper ();
+ Invalidate ();
+ }
+
+ void
+ MouseMoved (BPoint point, uint32 transit, const BMessage *drag_msg)
+ {
+ struct haiku_mouse_motion_event rq;
+ int64 threadid;
+ EmacsWindow *window;
+
+ window = (EmacsWindow *) Window ();
+
+ if (transit == B_EXITED_VIEW)
+ rq.just_exited_p = true;
+ else
+ rq.just_exited_p = false;
+
+ rq.x = point.x;
+ rq.y = point.y;
+ rq.window = window;
+ rq.time = system_time ();
+
+ if (drag_msg && (drag_msg->IsSourceRemote ()
+ || drag_msg->FindInt64 ("emacs:thread_id",
+ &threadid) != B_OK
+ || threadid != find_thread (NULL)))
+ rq.dnd_message = true;
+ else
+ rq.dnd_message = false;
+
+ if (!grab_view_locker.Lock ())
+ gui_abort ("Couldn't lock grab view locker");
+
+ if (grab_view && this != grab_view)
+ {
+ grab_view_locker.Unlock ();
+ return;
+ }
+
+ grab_view_locker.Unlock ();
+
+ haiku_write (MOUSE_MOTION, &rq);
+ }
+
+ void
+ BasicMouseDown (BPoint point, BView *scroll_bar, BMessage *message)
+ {
+ struct haiku_button_event rq;
+ int64 when;
+ int32 mods, buttons, button;
+
+ if (message->FindInt64 ("when", &when) != B_OK
+ || message->FindInt32 ("modifiers", &mods) != B_OK
+ || message->FindInt32 ("buttons", &buttons) != B_OK)
+ return;
+
+ /* Find which button was pressed by comparing the previous button
+ mask to the current one. This assumes that B_MOUSE_DOWN will
+ be sent for each button press. */
+ button = buttons & ~grabbed_buttons;
+ grabbed_buttons = buttons;
+
+ if (!scroll_bar)
+ {
+ if (!grab_view_locker.Lock ())
+ gui_abort ("Couldn't lock grab view locker");
+ grab_view = this;
+ grab_view_locker.Unlock ();
+ }
+
+ rq.window = this->Window ();
+ rq.scroll_bar = scroll_bar;
+
+ if (button == B_PRIMARY_MOUSE_BUTTON)
+ rq.btn_no = 0;
+ else if (button == B_SECONDARY_MOUSE_BUTTON)
+ rq.btn_no = 2;
+ else if (button == B_TERTIARY_MOUSE_BUTTON)
+ rq.btn_no = 1;
+ else
+ /* We don't know which button was pressed. This usually happens
+ when a B_MOUSE_UP is sent to a view that didn't receive a
+ corresponding B_MOUSE_DOWN event, so simply ignore the
+ message. */
+ return;
+
+ rq.x = point.x;
+ rq.y = point.y;
+ rq.modifiers = 0;
+
+ if (mods & B_SHIFT_KEY)
+ rq.modifiers |= HAIKU_MODIFIER_SHIFT;
+
+ if (mods & B_CONTROL_KEY)
+ rq.modifiers |= HAIKU_MODIFIER_CTRL;
+
+ if (mods & B_COMMAND_KEY)
+ rq.modifiers |= HAIKU_MODIFIER_ALT;
+
+ if (mods & B_OPTION_KEY)
+ rq.modifiers |= HAIKU_MODIFIER_SUPER;
+
+ if (!scroll_bar)
+ SetMouseEventMask (B_POINTER_EVENTS, (B_LOCK_WINDOW_FOCUS
+ | B_NO_POINTER_HISTORY));
+
+ rq.time = when;
+ haiku_write (BUTTON_DOWN, &rq);
+ }
+
+ void
+ MouseDown (BPoint point)
+ {
+ BMessage *msg;
+ BLooper *looper;
+
+ looper = Looper ();
+ msg = (looper
+ ? looper->CurrentMessage ()
+ : NULL);
+
+ if (msg)
+ BasicMouseDown (point, NULL, msg);
+ }
+
+ void
+ BasicMouseUp (BPoint point, BView *scroll_bar, BMessage *message)
+ {
+ struct haiku_button_event rq;
+ int64 when;
+ int32 mods, button, buttons;
+
+ if (message->FindInt64 ("when", &when) != B_OK
+ || message->FindInt32 ("modifiers", &mods) != B_OK
+ || message->FindInt32 ("buttons", &buttons) != B_OK)
+ return;
+
+ if (!scroll_bar)
+ {
+ if (!grab_view_locker.Lock ())
+ gui_abort ("Couldn't lock grab view locker");
+ if (!buttons)
+ grab_view = NULL;
+ grab_view_locker.Unlock ();
+ }
+
+ button = (grabbed_buttons & ~buttons);
+ grabbed_buttons = buttons;
+
+ if (wait_for_release_message)
+ {
+ if (!grabbed_buttons)
+ {
+ wait_for_release_message->SendReply (wait_for_release_message);
+ delete wait_for_release_message;
+ wait_for_release_message = NULL;
+ }
+
+ return;
+ }
+
+ rq.window = this->Window ();
+ rq.scroll_bar = scroll_bar;
+
+ if (button == B_PRIMARY_MOUSE_BUTTON)
+ rq.btn_no = 0;
+ else if (button == B_SECONDARY_MOUSE_BUTTON)
+ rq.btn_no = 2;
+ else if (button == B_TERTIARY_MOUSE_BUTTON)
+ rq.btn_no = 1;
+ else
+ return;
+
+ rq.x = point.x;
+ rq.y = point.y;
+
+ rq.modifiers = 0;
+ if (mods & B_SHIFT_KEY)
+ rq.modifiers |= HAIKU_MODIFIER_SHIFT;
+
+ if (mods & B_CONTROL_KEY)
+ rq.modifiers |= HAIKU_MODIFIER_CTRL;
+
+ if (mods & B_COMMAND_KEY)
+ rq.modifiers |= HAIKU_MODIFIER_ALT;
+
+ if (mods & B_OPTION_KEY)
+ rq.modifiers |= HAIKU_MODIFIER_SUPER;
+
+ rq.time = when;
+ haiku_write (BUTTON_UP, &rq);
+ }
+
+ void
+ MouseUp (BPoint point)
+ {
+ BMessage *msg;
+ BLooper *looper;
+
+ looper = Looper ();
+ msg = (looper
+ ? looper->CurrentMessage ()
+ : NULL);
+
+ if (msg)
+ BasicMouseUp (point, NULL, msg);
+ }
+};
+
+class EmacsScrollBar : public BScrollBar
+{
+public:
+ int dragging;
+ bool horizontal;
+ enum haiku_scroll_bar_part current_part;
+ float old_value;
+ scroll_bar_info info;
+
+ /* How many button events were passed to the parent without
+ release. */
+ int handle_button_count;
+ bool in_overscroll;
+ bool can_overscroll;
+ bool maybe_overscroll;
+ BPoint last_overscroll;
+ int last_reported_overscroll_value;
+ int max_value, real_max_value;
+ int overscroll_start_value;
+ bigtime_t repeater_start;
+ EmacsView *parent;
+
+ EmacsScrollBar (int x, int y, int x1, int y1, bool horizontal_p,
+ EmacsView *parent)
+ : BScrollBar (BRect (x, y, x1, y1), NULL, NULL, 0, 0, horizontal_p ?
+ B_HORIZONTAL : B_VERTICAL),
+ dragging (0),
+ handle_button_count (0),
+ in_overscroll (false),
+ can_overscroll (false),
+ maybe_overscroll (false),
+ parent (parent)
+ {
+ BView *vw = (BView *) this;
+ vw->SetResizingMode (B_FOLLOW_NONE);
+ horizontal = horizontal_p;
+ get_scroll_bar_info (&info);
+ SetSteps (5000, 10000);
+ }
+
+ void
+ MessageReceived (BMessage *msg)
+ {
+ int32 portion, range, dragging, value;
+ float proportion;
+
+ if (msg->what == SCROLL_BAR_UPDATE)
+ {
+ portion = msg->GetInt32 ("emacs:portion", 0);
+ range = msg->GetInt32 ("emacs:range", 0);
+ dragging = msg->GetInt32 ("emacs:dragging", 0);
+ proportion = ((range <= 0 || portion <= 0)
+ ? 1.0f : (float) portion / range);
+ value = msg->GetInt32 ("emacs:units", 0);
+ can_overscroll = msg->GetBool ("emacs:overscroll", false);
+
+ if (value < 0)
+ value = 0;
+
+ if (dragging != 1)
+ {
+ if (in_overscroll || dragging != -1)
+ {
+ /* Set the value to the smallest possible one.
+ Otherwise, the call to SetRange could lead to
+ spurious updates. */
+ old_value = 0;
+ SetValue (0);
+
+ /* Unlike on Motif, PORTION isn't included in the total
+ range of the scroll bar. */
+
+ SetRange (0, range - portion);
+ SetProportion (proportion);
+ max_value = range - portion;
+ real_max_value = range;
+
+ if (in_overscroll || value > max_value)
+ value = max_value;
+
+ old_value = roundf (value);
+ SetValue (old_value);
+ }
+ else
+ {
+ value = Value ();
+
+ old_value = 0;
+ SetValue (0);
+ SetRange (0, range - portion);
+ SetProportion (proportion);
+ old_value = value;
+ SetValue (value);
+ max_value = range - portion;
+ real_max_value = range;
+ }
+ }
+ }
+
+ BScrollBar::MessageReceived (msg);
+ }
+
+ void
+ Pulse (void)
+ {
+ struct haiku_scroll_bar_part_event rq;
+ BPoint point;
+ uint32 buttons;
+
+ if (!dragging)
+ {
+ SetFlags (Flags () & ~B_PULSE_NEEDED);
+ return;
+ }
+
+ if (repeater_start < system_time ())
+ {
+ GetMouse (&point, &buttons, false);
+
+ if (ButtonRegionFor (current_part).Contains (point))
+ {
+ rq.scroll_bar = this;
+ rq.window = Window ();
+ rq.part = current_part;
+ haiku_write (SCROLL_BAR_PART_EVENT, &rq);
+ }
+ }
+
+ BScrollBar::Pulse ();
+ }
+
+ void
+ ValueChanged (float new_value)
+ {
+ struct haiku_scroll_bar_value_event rq;
+
+ new_value = Value ();
+
+ if (dragging)
+ {
+ if (new_value != old_value)
+ {
+ if (dragging > 1)
+ {
+ SetValue (old_value);
+ SetFlags (Flags () | B_PULSE_NEEDED);
+ }
+ else
+ dragging++;
+ }
+
+ return;
+ }
+
+ if (new_value != old_value)
+ {
+ rq.scroll_bar = this;
+ rq.window = Window ();
+ rq.position = new_value;
+ old_value = new_value;
+
+ haiku_write (SCROLL_BAR_VALUE_EVENT, &rq);
+ }
+ }
+
+ BRegion
+ ButtonRegionFor (enum haiku_scroll_bar_part button)
+ {
+ BRegion region;
+ BRect bounds;
+ BRect rect;
+ float button_size;
+
+ bounds = Bounds ();
+ bounds.InsetBy (0.0, 0.0);
+
+ if (horizontal)
+ button_size = bounds.Height () + 1.0f;
+ else
+ button_size = bounds.Width () + 1.0f;
+
+ rect = BRect (bounds.left, bounds.top,
+ bounds.left + button_size - 1.0f,
+ bounds.top + button_size - 1.0f);
+
+ if (button == HAIKU_SCROLL_BAR_UP_BUTTON)
+ {
+ if (!horizontal)
+ {
+ region.Include (rect);
+ if (info.double_arrows)
+ region.Include (rect.OffsetToCopy (bounds.left,
+ bounds.bottom - 2 * button_size + 1));
+ }
+ else
+ {
+ region.Include (rect);
+ if (info.double_arrows)
+ region.Include (rect.OffsetToCopy (bounds.right - 2 * button_size,
+ bounds.top));
+ }
+ }
+ else
+ {
+ if (!horizontal)
+ {
+ region.Include (rect.OffsetToCopy (bounds.left, bounds.bottom - button_size));
+
+ if (info.double_arrows)
+ region.Include (rect.OffsetByCopy (0.0, button_size));
+ }
+ else
+ {
+ region.Include (rect.OffsetToCopy (bounds.right - button_size, bounds.top));
+
+ if (info.double_arrows)
+ region.Include (rect.OffsetByCopy (button_size, 0.0));
+ }
+ }
+
+ return region;
+ }
+
+ void
+ MouseDown (BPoint pt)
+ {
+ struct haiku_scroll_bar_drag_event rq;
+ struct haiku_scroll_bar_part_event part;
+ BRegion r;
+ BLooper *looper;
+ BMessage *message;
+ int32 buttons, mods;
+
+ looper = Looper ();
+ message = NULL;
+
+ if (!looper)
+ GetMouse (&pt, (uint32 *) &buttons, false);
+ else
+ {
+ message = looper->CurrentMessage ();
+
+ if (!message || message->FindInt32 ("buttons", &buttons) != B_OK)
+ GetMouse (&pt, (uint32 *) &buttons, false);
+ }
+
+ if (message && (message->FindInt32 ("modifiers", &mods)
+ == B_OK)
+ && mods & B_CONTROL_KEY)
+ {
+ /* Allow C-mouse-3 to split the window on a scroll bar. */
+ handle_button_count += 1;
+ SetMouseEventMask (B_POINTER_EVENTS, (B_SUSPEND_VIEW_FOCUS
+ | B_LOCK_WINDOW_FOCUS));
+ parent->BasicMouseDown (ConvertToParent (pt), this, message);
+
+ return;
+ }
+
+ repeater_start = system_time () + 300000;
+
+ if (buttons == B_PRIMARY_MOUSE_BUTTON)
+ {
+ r = ButtonRegionFor (HAIKU_SCROLL_BAR_UP_BUTTON);
+
+ if (r.Contains (pt))
+ {
+ part.scroll_bar = this;
+ part.window = Window ();
+ part.part = HAIKU_SCROLL_BAR_UP_BUTTON;
+ dragging = 1;
+ current_part = HAIKU_SCROLL_BAR_UP_BUTTON;
+
+ haiku_write (SCROLL_BAR_PART_EVENT, &part);
+ goto out;
+ }
+
+ r = ButtonRegionFor (HAIKU_SCROLL_BAR_DOWN_BUTTON);
+
+ if (r.Contains (pt))
+ {
+ part.scroll_bar = this;
+ part.window = Window ();
+ part.part = HAIKU_SCROLL_BAR_DOWN_BUTTON;
+ dragging = 1;
+ current_part = HAIKU_SCROLL_BAR_DOWN_BUTTON;
+
+ if (Value () == max_value)
+ {
+ SetFlags (Flags () | B_PULSE_NEEDED);
+ dragging = 2;
+ }
+
+ haiku_write (SCROLL_BAR_PART_EVENT, &part);
+ goto out;
+ }
+
+ maybe_overscroll = true;
+ }
+
+ rq.dragging_p = 1;
+ rq.window = Window ();
+ rq.scroll_bar = this;
+
+ SetMouseEventMask (B_POINTER_EVENTS, (B_SUSPEND_VIEW_FOCUS
+ | B_LOCK_WINDOW_FOCUS));
+
+ haiku_write (SCROLL_BAR_DRAG_EVENT, &rq);
+
+ out:
+ BScrollBar::MouseDown (pt);
+ }
+
+ void
+ MouseUp (BPoint pt)
+ {
+ struct haiku_scroll_bar_drag_event rq;
+ BMessage *msg;
+ BLooper *looper;
+
+ in_overscroll = false;
+ maybe_overscroll = false;
+
+ if (handle_button_count)
+ {
+ handle_button_count--;
+ looper = Looper ();
+ msg = (looper
+ ? looper->CurrentMessage ()
+ : NULL);
+
+ if (msg)
+ parent->BasicMouseUp (ConvertToParent (pt),
+ this, msg);
+
+ return;
+ }
+
+ rq.dragging_p = 0;
+ rq.scroll_bar = this;
+ rq.window = Window ();
+
+ haiku_write (SCROLL_BAR_DRAG_EVENT, &rq);
+ dragging = 0;
+
+ BScrollBar::MouseUp (pt);
+ }
+
+ void
+ MouseMoved (BPoint point, uint32 transit, const BMessage *msg)
+ {
+ struct haiku_menu_bar_left_event rq;
+ struct haiku_scroll_bar_value_event value_event;
+ int range, diff, value, trough_size;
+ BRect bounds;
+ BPoint conv;
+ uint32 buttons;
+
+ GetMouse (NULL, &buttons, false);
+
+ if (transit == B_EXITED_VIEW)
+ {
+ conv = ConvertToParent (point);
+
+ rq.x = std::lrint (conv.x);
+ rq.y = std::lrint (conv.y);
+ rq.window = this->Window ();
+
+ haiku_write (MENU_BAR_LEFT, &rq);
+ }
+
+ if (in_overscroll)
+ {
+ if (horizontal)
+ diff = point.x - last_overscroll.x;
+ else
+ diff = point.y - last_overscroll.y;
+
+ if (diff < 0)
+ {
+ in_overscroll = false;
+ goto allow;
+ }
+
+ range = real_max_value;
+ bounds = Bounds ();
+ bounds.InsetBy (1.0, 1.0);
+ value = overscroll_start_value;
+ trough_size = (horizontal
+ ? BE_RECT_WIDTH (bounds)
+ : BE_RECT_HEIGHT (bounds));
+ trough_size -= (horizontal
+ ? BE_RECT_HEIGHT (bounds)
+ : BE_RECT_WIDTH (bounds)) / 2;
+ if (info.double_arrows)
+ trough_size -= (horizontal
+ ? BE_RECT_HEIGHT (bounds)
+ : BE_RECT_WIDTH (bounds)) / 2;
+
+ value += ((double) range / trough_size) * diff;
+
+ if (value != last_reported_overscroll_value)
+ {
+ last_reported_overscroll_value = value;
+
+ value_event.scroll_bar = this;
+ value_event.window = Window ();
+ value_event.position = value;
+
+ haiku_write (SCROLL_BAR_VALUE_EVENT, &value_event);
+ return;
+ }
+ }
+ else if (can_overscroll
+ && (buttons == B_PRIMARY_MOUSE_BUTTON)
+ && maybe_overscroll)
+ {
+ value = Value ();
+
+ if (value >= max_value)
+ {
+ BScrollBar::MouseMoved (point, transit, msg);
+
+ if (value == Value ())
+ {
+ overscroll_start_value = value;
+ in_overscroll = true;
+ last_overscroll = point;
+ last_reported_overscroll_value = value;
+
+ MouseMoved (point, transit, msg);
+ return;
+ }
+ }
+ }
+
+ allow:
+ BScrollBar::MouseMoved (point, transit, msg);
+ }
+};
+
+class EmacsTitleMenuItem : public BMenuItem
+{
+public:
+ EmacsTitleMenuItem (const char *str) : BMenuItem (str, NULL)
+ {
+ SetEnabled (0);
+ }
+
+ void
+ DrawContent (void)
+ {
+ BMenu *menu = Menu ();
+
+ menu->PushState ();
+ menu->SetFont (be_bold_font);
+ menu->SetHighColor (ui_color (B_MENU_ITEM_TEXT_COLOR));
+ BMenuItem::DrawContent ();
+ menu->PopState ();
+ }
+};
+
+class EmacsMenuItem : public BMenuItem
+{
+public:
+ int menu_bar_id;
+ void *menu_ptr;
+ void *wind_ptr;
+ char *key;
+ char *help;
+
+ EmacsMenuItem (const char *key_label, const char *label,
+ const char *help, BMessage *message = NULL)
+ : BMenuItem (label, message),
+ menu_bar_id (-1),
+ menu_ptr (NULL),
+ wind_ptr (NULL),
+ key (NULL),
+ help (NULL)
+ {
+ if (key_label)
+ key = strdup (key_label);
+
+ if (help)
+ this->help = strdup (help);
+ }
+
+ ~EmacsMenuItem ()
+ {
+ if (key)
+ free (key);
+ if (help)
+ free (help);
+ }
+
+ void
+ DrawContent (void)
+ {
+ BMenu *menu = Menu ();
+
+ BMenuItem::DrawContent ();
+
+ if (key)
+ {
+ BRect r = Frame ();
+ int w;
+
+ menu->PushState ();
+ menu->ClipToRect (r);
+ menu->SetFont (be_plain_font);
+ w = menu->StringWidth (key);
+ menu->MovePenTo (BPoint (BE_RECT_WIDTH (r) - w - 4,
+ menu->PenLocation ().y));
+ menu->DrawString (key);
+ menu->PopState ();
+ }
+ }
+
+ void
+ GetContentSize (float *w, float *h)
+ {
+ BMenuItem::GetContentSize (w, h);
+ if (Menu () && key)
+ *w += 4 + Menu ()->StringWidth (key);
+ }
+
+ void
+ Highlight (bool highlight_p)
+ {
+ struct haiku_menu_bar_help_event rq;
+ struct haiku_dummy_event dummy;
+ BMenu *menu = Menu ();
+ BRect r;
+ BPoint pt;
+ uint32 buttons;
+
+ if (help)
+ menu->SetToolTip (highlight_p ? help : NULL);
+ else
+ {
+ rq.window = wind_ptr;
+ rq.mb_idx = highlight_p ? menu_bar_id : -1;
+ rq.highlight_p = highlight_p;
+ rq.data = menu_ptr;
+
+ r = Frame ();
+ menu->GetMouse (&pt, &buttons);
+
+ if (!highlight_p || r.Contains (pt))
+ {
+ if (menu_bar_id > 0)
+ haiku_write (MENU_BAR_HELP_EVENT, &rq);
+ else
+ {
+ haiku_write_without_signal (MENU_BAR_HELP_EVENT, &rq, true);
+ haiku_write (DUMMY_EVENT, &dummy);
+ }
+ }
+ }
+
+ BMenuItem::Highlight (highlight_p);
+ }
+};
+
+class EmacsFontPreviewDialog : public BWindow
+{
+ BStringView text_view;
+ BMessenger preview_source;
+ BFont *current_font;
+ bool is_visible;
+
+ void
+ DoLayout (void)
+ {
+ float width, height;
+
+ text_view.GetPreferredSize (&width, &height);
+ text_view.ResizeTo (width - 1, height - 1);
+
+ SetSizeLimits (width, width, height, height);
+ ResizeTo (width - 1, height - 1);
+ }
+
+ bool
+ QuitRequested (void)
+ {
+ preview_source.SendMessage (QUIT_PREVIEW_DIALOG);
+
+ return false;
+ }
+
+ void
+ MessageReceived (BMessage *message)
+ {
+ int32 family, style;
+ uint32 flags;
+ font_family name;
+ font_style sname;
+ status_t rc;
+ const char *size_name;
+ int size;
+
+ if (message->what == SET_FONT_INDICES)
+ {
+ size_name = message->FindString ("emacs:size");
+
+ if (message->FindInt32 ("emacs:family", &family) != B_OK
+ || message->FindInt32 ("emacs:style", &style) != B_OK)
+ return;
+
+ rc = get_font_family (family, &name, &flags);
+
+ if (rc != B_OK)
+ return;
+
+ rc = get_font_style (name, style, &sname, &flags);
+
+ if (rc != B_OK)
+ return;
+
+ if (current_font)
+ delete current_font;
+
+ current_font = new BFont;
+ current_font->SetFamilyAndStyle (name, sname);
+
+ if (message->GetBool ("emacs:disable_antialiasing", false))
+ current_font->SetFlags (B_DISABLE_ANTIALIASING);
+
+ if (size_name && strlen (size_name))
+ {
+ size = atoi (size_name);
+ current_font->SetSize (size);
+ }
+
+ text_view.SetFont (current_font);
+ DoLayout ();
+ return;
+ }
+
+ BWindow::MessageReceived (message);
+ }
+
+public:
+
+ EmacsFontPreviewDialog (BWindow *target)
+ : BWindow (BRect (45, 45, 500, 300),
+ "Preview font",
+ B_FLOATING_WINDOW_LOOK,
+ B_MODAL_APP_WINDOW_FEEL,
+ B_NOT_ZOOMABLE | B_NOT_RESIZABLE),
+ text_view (BRect (0, 0, 0, 0),
+ NULL, "The quick brown fox "
+ "jumped over the lazy dog"),
+ preview_source (target),
+ current_font (NULL)
+ {
+ AddChild (&text_view);
+ DoLayout ();
+ }
+
+ ~EmacsFontPreviewDialog (void)
+ {
+ text_view.RemoveSelf ();
+
+ if (current_font)
+ delete current_font;
+ }
+};
+
+class TripleLayoutView : public BView
+{
+ BScrollView *view_1;
+ BView *view_2, *view_3;
+
+ void
+ FrameResized (float new_width, float new_height)
+ {
+ BRect frame;
+ float width, height, height_1, width_1;
+ float basic_height;
+
+ frame = Frame ();
+
+ view_2->GetPreferredSize (&width, &height);
+ view_3->GetPreferredSize (&width_1, &height_1);
+
+ basic_height = height + height_1;
+
+ view_1->MoveTo (0, 0);
+ view_1->ResizeTo (BE_RECT_WIDTH (frame),
+ BE_RECT_HEIGHT (frame) - basic_height);
+ view_2->MoveTo (2, BE_RECT_HEIGHT (frame) - basic_height);
+ view_2->ResizeTo (BE_RECT_WIDTH (frame) - 4, height);
+ view_3->MoveTo (2, BE_RECT_HEIGHT (frame) - height_1);
+ view_3->ResizeTo (BE_RECT_WIDTH (frame) - 4, height_1);
+
+ BView::FrameResized (new_width, new_height);
+ }
+
+ /* This is called by the BSplitView. */
+ BSize
+ MinSize (void)
+ {
+ float width, height;
+ float width_1, height_1;
+ BSize size_1;
+
+ size_1 = view_1->MinSize ();
+ view_2->GetPreferredSize (&width, &height);
+ view_3->GetPreferredSize (&width_1, &height_1);
+
+ return BSize (std::max (size_1.width,
+ std::max (width_1, width)),
+ std::max (size_1.height, height + height_1));
+ }
+
+public:
+ TripleLayoutView (BScrollView *first, BView *second,
+ BView *third) : BView (NULL, B_FRAME_EVENTS),
+ view_1 (first),
+ view_2 (second),
+ view_3 (third)
+ {
+ FrameResized (801, 801);
+ }
+};
+
+class EmacsFontSelectionDialog : public BWindow
+{
+ BView basic_view;
+ BCheckBox antialias_checkbox;
+ BCheckBox preview_checkbox;
+ BSplitView split_view;
+ BListView font_family_pane;
+ BListView font_style_pane;
+ BScrollView font_family_scroller;
+ BScrollView font_style_scroller;
+ TripleLayoutView style_view;
+ BObjectList<BStringItem> all_families;
+ BObjectList<BStringItem> all_styles;
+ BButton cancel_button, ok_button;
+ BTextControl size_entry;
+ port_id comm_port;
+ bool allow_monospace_only;
+ int pending_selection_idx;
+ EmacsFontPreviewDialog *preview;
+
+ void
+ ShowPreview (void)
+ {
+ if (!preview)
+ {
+ preview = new EmacsFontPreviewDialog (this);
+ preview->Show ();
+
+ UpdatePreview ();
+ }
+ }
+
+ void
+ UpdatePreview (void)
+ {
+ int family, style;
+ BMessage message;
+ BMessenger messenger (preview);
+
+ family = font_family_pane.CurrentSelection ();
+ style = font_style_pane.CurrentSelection ();
+
+ message.what = SET_FONT_INDICES;
+ message.AddInt32 ("emacs:family", family);
+ message.AddInt32 ("emacs:style", style);
+
+ if (antialias_checkbox.Value () == B_CONTROL_ON)
+ message.AddBool ("emacs:disable_antialiasing", true);
+
+ message.AddString ("emacs:size",
+ size_entry.Text ());
+
+ messenger.SendMessage (&message);
+ }
+
+ void
+ HidePreview (void)
+ {
+ if (preview)
+ {
+ if (preview->LockLooper ())
+ preview->Quit ();
+ /* I hope this works. */
+ else
+ delete preview;
+
+ preview = NULL;
+ }
+ }
+
+ void
+ UpdateStylesForIndex (int idx)
+ {
+ int n, i, previous_selection;
+ uint32 flags;
+ font_family family;
+ font_style style;
+ BStringItem *item;
+ char *current_style;
+
+ n = all_styles.CountItems ();
+ current_style = NULL;
+ previous_selection = font_style_pane.CurrentSelection ();
+
+ if (previous_selection >= 0)
+ {
+ item = all_styles.ItemAt (previous_selection);
+ current_style = strdup (item->Text ());
+ }
+
+ font_style_pane.MakeEmpty ();
+ all_styles.MakeEmpty ();
+
+ if (get_font_family (idx, &family, &flags) == B_OK)
+ {
+ n = count_font_styles (family);
+
+ for (i = 0; i < n; ++i)
+ {
+ if (get_font_style (family, i, &style, &flags) == B_OK)
+ item = new BStringItem (style);
+ else
+ item = new BStringItem ("<error>");
+
+ if (current_style && pending_selection_idx < 0
+ && !strcmp (current_style, style))
+ pending_selection_idx = i;
+
+ font_style_pane.AddItem (item);
+ all_styles.AddItem (item);
+ }
+ }
+
+ if (pending_selection_idx >= 0)
+ {
+ font_style_pane.Select (pending_selection_idx);
+ font_style_pane.ScrollToSelection ();
+ }
+
+ pending_selection_idx = -1;
+ UpdateForSelectedStyle ();
+
+ if (current_style)
+ free (current_style);
+ }
+
+ bool
+ QuitRequested (void)
+ {
+ struct font_selection_dialog_message rq;
+
+ rq.cancel = true;
+ write_port (comm_port, 0, &rq, sizeof rq);
+
+ return false;
+ }
+
+ void
+ UpdateForSelectedStyle (void)
+ {
+ int style = font_style_pane.CurrentSelection ();
+
+ if (style < 0)
+ ok_button.SetEnabled (false);
+ else
+ ok_button.SetEnabled (true);
+
+ if (style >= 0 && preview)
+ UpdatePreview ();
+ }
+
+ void
+ MessageReceived (BMessage *msg)
+ {
+ const char *text;
+ int idx;
+ struct font_selection_dialog_message rq;
+
+ if (msg->what == FONT_FAMILY_SELECTED)
+ {
+ idx = font_family_pane.CurrentSelection ();
+ UpdateStylesForIndex (idx);
+ }
+ else if (msg->what == FONT_STYLE_SELECTED)
+ UpdateForSelectedStyle ();
+ else if (msg->what == B_OK
+ && font_style_pane.CurrentSelection () >= 0)
+ {
+ text = size_entry.Text ();
+
+ rq.cancel = false;
+ rq.family_idx = font_family_pane.CurrentSelection ();
+ rq.style_idx = font_style_pane.CurrentSelection ();
+ rq.size = atoi (text);
+ rq.size_specified = rq.size > 0 || strlen (text);
+
+ if (antialias_checkbox.Value () == B_CONTROL_ON)
+ rq.disable_antialias = true;
+ else
+ rq.disable_antialias = false;
+
+ write_port (comm_port, 0, &rq, sizeof rq);
+ }
+ else if (msg->what == B_CANCEL)
+ {
+ rq.cancel = true;
+
+ write_port (comm_port, 0, &rq, sizeof rq);
+ }
+ else if (msg->what == SET_PREVIEW_DIALOG)
+ {
+ if (preview_checkbox.Value () == B_CONTROL_OFF)
+ HidePreview ();
+ else
+ ShowPreview ();
+ }
+ else if (msg->what == QUIT_PREVIEW_DIALOG)
+ {
+ preview_checkbox.SetValue (B_CONTROL_OFF);
+ HidePreview ();
+ }
+ else if (msg->what == UPDATE_PREVIEW_DIALOG)
+ {
+ if (preview)
+ UpdatePreview ();
+ }
+ else if (msg->what == SET_DISABLE_ANTIALIASING)
+ {
+ if (preview)
+ UpdatePreview ();
+ }
+
+ BWindow::MessageReceived (msg);
+ }
+
+public:
+
+ ~EmacsFontSelectionDialog (void)
+ {
+ if (preview)
+ {
+ if (preview->LockLooper ())
+ preview->Quit ();
+ /* I hope this works. */
+ else
+ delete preview;
+ }
+
+ font_family_pane.MakeEmpty ();
+ font_style_pane.MakeEmpty ();
+
+ font_family_pane.RemoveSelf ();
+ font_style_pane.RemoveSelf ();
+ antialias_checkbox.RemoveSelf ();
+ preview_checkbox.RemoveSelf ();
+ style_view.RemoveSelf ();
+ font_family_scroller.RemoveSelf ();
+ font_style_scroller.RemoveSelf ();
+ cancel_button.RemoveSelf ();
+ ok_button.RemoveSelf ();
+ size_entry.RemoveSelf ();
+ basic_view.RemoveSelf ();
+
+ if (comm_port >= B_OK)
+ delete_port (comm_port);
+ }
+
+ EmacsFontSelectionDialog (bool monospace_only,
+ int initial_family_idx,
+ int initial_style_idx,
+ int initial_size,
+ bool initial_antialias)
+ : BWindow (BRect (0, 0, 500, 500),
+ "Select font from list",
+ B_TITLED_WINDOW_LOOK,
+ B_MODAL_APP_WINDOW_FEEL, 0),
+ basic_view (NULL, 0),
+ antialias_checkbox ("Disable antialiasing", "Disable antialiasing",
+ new BMessage (SET_DISABLE_ANTIALIASING)),
+ preview_checkbox ("Show preview", "Show preview",
+ new BMessage (SET_PREVIEW_DIALOG)),
+ font_family_pane (BRect (0, 0, 0, 0), NULL,
+ B_SINGLE_SELECTION_LIST,
+ B_FOLLOW_ALL_SIDES),
+ font_style_pane (BRect (0, 0, 0, 0), NULL,
+ B_SINGLE_SELECTION_LIST,
+ B_FOLLOW_ALL_SIDES),
+ font_family_scroller (NULL, &font_family_pane,
+ B_FOLLOW_LEFT | B_FOLLOW_TOP,
+ 0, false, true),
+ font_style_scroller (NULL, &font_style_pane,
+ B_FOLLOW_ALL_SIDES,
+ B_SUPPORTS_LAYOUT, false, true),
+ style_view (&font_style_scroller, &antialias_checkbox,
+ &preview_checkbox),
+ all_families (20, true),
+ all_styles (20, true),
+ cancel_button ("Cancel", "Cancel",
+ new BMessage (B_CANCEL)),
+ ok_button ("OK", "OK", new BMessage (B_OK)),
+ size_entry (NULL, "Size:", NULL,
+ new BMessage (UPDATE_PREVIEW_DIALOG)),
+ allow_monospace_only (monospace_only),
+ pending_selection_idx (initial_style_idx),
+ preview (NULL)
+ {
+ BStringItem *family_item;
+ int i, n_families;
+ font_family name;
+ uint32 flags, c;
+ BMessage *selection;
+ BTextView *size_text;
+ char format_buffer[4];
+
+ AddChild (&basic_view);
+
+ basic_view.AddChild (&split_view);
+ basic_view.AddChild (&cancel_button);
+ basic_view.AddChild (&ok_button);
+ basic_view.AddChild (&size_entry);
+ split_view.AddChild (&font_family_scroller, 0.7);
+ split_view.AddChild (&style_view, 0.3);
+ style_view.AddChild (&font_style_scroller);
+ style_view.AddChild (&antialias_checkbox);
+ style_view.AddChild (&preview_checkbox);
+
+ basic_view.SetViewUIColor (B_PANEL_BACKGROUND_COLOR);
+ style_view.SetViewUIColor (B_PANEL_BACKGROUND_COLOR);
+
+ FrameResized (801, 801);
+ UpdateForSelectedStyle ();
+
+ selection = new BMessage (FONT_FAMILY_SELECTED);
+ font_family_pane.SetSelectionMessage (selection);
+ selection = new BMessage (FONT_STYLE_SELECTED);
+ font_style_pane.SetSelectionMessage (selection);
+ selection = new BMessage (B_OK);
+ font_style_pane.SetInvocationMessage (selection);
+ selection = new BMessage (UPDATE_PREVIEW_DIALOG);
+ size_entry.SetModificationMessage (selection);
+
+ comm_port = create_port (1, "font dialog port");
+
+ n_families = count_font_families ();
+
+ for (i = 0; i < n_families; ++i)
+ {
+ if (get_font_family (i, &name, &flags) == B_OK)
+ {
+ family_item = new BStringItem (name);
+
+ all_families.AddItem (family_item);
+ font_family_pane.AddItem (family_item);
+
+ family_item->SetEnabled (!allow_monospace_only
+ || flags & B_IS_FIXED);
+ }
+ else
+ {
+ family_item = new BStringItem ("<error>");
+
+ all_families.AddItem (family_item);
+ font_family_pane.AddItem (family_item);
+ }
+ }
+
+ if (initial_family_idx >= 0)
+ {
+ font_family_pane.Select (initial_family_idx);
+ font_family_pane.ScrollToSelection ();
+ }
+
+ size_text = size_entry.TextView ();
+
+ for (c = 0; c <= 47; ++c)
+ size_text->DisallowChar (c);
+
+ for (c = 58; c <= 127; ++c)
+ size_text->DisallowChar (c);
+
+ if (initial_size > 0 && initial_size < 1000)
+ {
+ sprintf (format_buffer, "%d", initial_size);
+ size_entry.SetText (format_buffer);
+ }
+
+ if (!initial_antialias)
+ antialias_checkbox.SetValue (B_CONTROL_ON);
+ }
+
+ void
+ FrameResized (float new_width, float new_height)
+ {
+ BRect frame;
+ float ok_height, ok_width;
+ float cancel_height, cancel_width;
+ float size_width, size_height;
+ float bone;
+ int max_height;
+
+ ok_button.GetPreferredSize (&ok_width, &ok_height);
+ cancel_button.GetPreferredSize (&cancel_width,
+ &cancel_height);
+ size_entry.GetPreferredSize (&size_width, &size_height);
+
+ max_height = std::max (std::max (ok_height, cancel_height),
+ size_height);
+
+ SetSizeLimits (cancel_width + ok_width + size_width + 6,
+ 65535, max_height + 64, 65535);
+ frame = Frame ();
+
+ basic_view.ResizeTo (BE_RECT_WIDTH (frame), BE_RECT_HEIGHT (frame));
+ split_view.ResizeTo (BE_RECT_WIDTH (frame) - 1,
+ BE_RECT_HEIGHT (frame) - 4 - max_height);
+
+ bone = BE_RECT_HEIGHT (frame) - 2 - max_height / 2;
+
+ ok_button.MoveTo ((BE_RECT_WIDTH (frame)
+ - 4 - cancel_width - ok_width),
+ bone - ok_height / 2);
+ cancel_button.MoveTo (BE_RECT_WIDTH (frame) - 2 - cancel_width,
+ bone - cancel_height / 2);
+ size_entry.MoveTo (2, bone - size_height / 2);
+
+ ok_button.ResizeTo (ok_width, ok_height);
+ cancel_button.ResizeTo (cancel_width, cancel_height);
+ size_entry.ResizeTo (std::max (size_width,
+ BE_RECT_WIDTH (frame) / 4),
+ size_height);
+ }
+
+ void
+ WaitForChoice (struct font_selection_dialog_message *msg,
+ void (*process_pending_signals_function) (void),
+ bool (*should_quit_function) (void))
+ {
+ int32 reply_type;
+ struct object_wait_info infos[2];
+ ssize_t status;
+
+ infos[0].object = port_application_to_emacs;
+ infos[0].type = B_OBJECT_TYPE_PORT;
+ infos[0].events = B_EVENT_READ;
+
+ infos[1].object = comm_port;
+ infos[1].type = B_OBJECT_TYPE_PORT;
+ infos[1].events = B_EVENT_READ;
+
+ while (true)
+ {
+ status = wait_for_objects (infos, 2);
+
+ if (status < B_OK)
+ continue;
+
+ if (infos[1].events & B_EVENT_READ)
+ {
+ if (read_port (comm_port, &reply_type,
+ msg, sizeof *msg) >= B_OK)
+ return;
+
+ goto cancel;
+ }
+
+ if (infos[0].events & B_EVENT_READ)
+ process_pending_signals_function ();
+
+ if (should_quit_function ())
+ goto cancel;
+
+ infos[0].events = B_EVENT_READ;
+ infos[1].events = B_EVENT_READ;
+ }
+
+ cancel:
+ msg->cancel = true;
+ return;
+ }
+
+ status_t
+ InitCheck (void)
+ {
+ return comm_port >= B_OK ? B_OK : comm_port;
+ }
+};
+
+class EmacsFilePanelCallbackLooper : public BLooper
+{
+ port_id comm_port;
+
+ void
+ MessageReceived (BMessage *msg)
+ {
+ const char *str_path, *name;
+ char *file_name, *str_buf;
+ BEntry entry;
+ BPath path;
+ entry_ref ref;
+ int32 old_what;
+
+ if (msg->what == FILE_PANEL_SELECTION
+ || ((msg->FindInt32 ("old_what", &old_what) == B_OK
+ && old_what == FILE_PANEL_SELECTION)))
+ {
+ file_name = NULL;
+
+ if (msg->FindRef ("refs", &ref) == B_OK
+ && entry.SetTo (&ref, 0) == B_OK
+ && entry.GetPath (&path) == B_OK)
+ {
+ str_path = path.Path ();
+
+ if (str_path)
+ file_name = strdup (str_path);
+ }
+ else if (msg->FindRef ("directory", &ref) == B_OK
+ && entry.SetTo (&ref, 0) == B_OK
+ && entry.GetPath (&path) == B_OK)
+ {
+ name = msg->GetString ("name");
+ str_path = path.Path ();
+
+ if (name)
+ {
+ str_buf = (char *) alloca (std::strlen (str_path)
+ + std::strlen (name) + 2);
+ snprintf (str_buf, std::strlen (str_path)
+ + std::strlen (name) + 2, "%s/%s",
+ str_path, name);
+ file_name = strdup (str_buf);
+ }
+ }
+
+ write_port (comm_port, 0, &file_name, sizeof file_name);
+ }
+
+ BLooper::MessageReceived (msg);
+ }
+
+public:
+ EmacsFilePanelCallbackLooper (void) : BLooper ()
+ {
+ comm_port = create_port (1, "file panel port");
+ }
+
+ ~EmacsFilePanelCallbackLooper (void)
+ {
+ delete_port (comm_port);
+ }
+
+ char *
+ ReadFileName (void (*process_pending_signals_function) (void))
+ {
+ object_wait_info infos[2];
+ ssize_t status;
+ int32 reply_type;
+ char *file_name;
+
+ file_name = NULL;
+
+ infos[0].object = port_application_to_emacs;
+ infos[0].type = B_OBJECT_TYPE_PORT;
+ infos[0].events = B_EVENT_READ;
+
+ infos[1].object = comm_port;
+ infos[1].type = B_OBJECT_TYPE_PORT;
+ infos[1].events = B_EVENT_READ;
+
+ while (true)
+ {
+ status = wait_for_objects (infos, 2);
+
+ if (status == B_INTERRUPTED || status == B_WOULD_BLOCK)
+ continue;
+
+ if (infos[0].events & B_EVENT_READ)
+ process_pending_signals_function ();
+
+ if (infos[1].events & B_EVENT_READ)
+ {
+ status = read_port (comm_port,
+ &reply_type, &file_name,
+ sizeof file_name);
+
+ if (status < B_OK)
+ file_name = NULL;
+
+ goto out;
+ }
+
+ infos[0].events = B_EVENT_READ;
+ infos[1].events = B_EVENT_READ;
+ }
+
+ out:
+ return file_name;
+ }
+
+ status_t
+ InitCheck (void)
+ {
+ return comm_port >= B_OK ? B_OK : comm_port;
+ }
+};
+
+/* A view that is added as a child of a tooltip's text view, and
+ prevents motion events from reaching it (thereby moving the
+ tooltip). */
+class EmacsMotionSuppressionView : public BView
+{
+ void
+ AttachedToWindow (void)
+ {
+ BView *text_view, *tooltip_view;
+
+ /* We know that this view is a child of the text view, whose
+ parent is the tooltip view, and that the tooltip view has
+ already set its mouse event mask. */
+
+ text_view = Parent ();
+
+ if (!text_view)
+ return;
+
+ tooltip_view = text_view->Parent ();
+
+ if (!tooltip_view)
+ return;
+
+ tooltip_view->SetEventMask (B_KEYBOARD_EVENTS, 0);
+ }
+
+public:
+ EmacsMotionSuppressionView (void) : BView (BRect (-1, -1, 1, 1),
+ NULL, 0, 0)
+ {
+ return;
+ }
+};
+
+static int32
+start_running_application (void *data)
+{
+ Emacs *app = (Emacs *) data;
+
+ haiku_io_init_in_app_thread ();
+
+ if (!app->Lock ())
+ gui_abort ("Failed to lock application");
+
+ app->Run ();
+ app->Unlock ();
+ return 0;
+}
+
+/* Take BITMAP, a reference to a BBitmap, and return a pointer to its
+ data. */
+void *
+BBitmap_data (void *bitmap)
+{
+ return ((BBitmap *) bitmap)->Bits ();
+}
+
+/* Convert bitmap if required, placing the new bitmap in NEW_BITMAP,
+ and return non-null if bitmap was successfully converted. Bitmaps
+ should be freed with `BBitmap_free'. */
+int
+BBitmap_convert (void *_bitmap, void **new_bitmap)
+{
+ BBitmap *bitmap = (BBitmap *) _bitmap;
+ if (bitmap->ColorSpace () == B_RGBA32)
+ return -1;
+ BRect bounds = bitmap->Bounds ();
+ BBitmap *bmp = new (std::nothrow) BBitmap (bounds, B_RGBA32);
+ if (!bmp || bmp->InitCheck () != B_OK)
+ {
+ if (bmp)
+ delete bmp;
+ return 0;
+ }
+ if (bmp->ImportBits (bitmap) != B_OK)
+ {
+ delete bmp;
+ return 0;
+ }
+ *(BBitmap **) new_bitmap = bmp;
+ return 1;
+}
+
+void
+BBitmap_free (void *bitmap)
+{
+ delete (BBitmap *) bitmap;
+}
+
+/* Create new bitmap in RGB32 format, or in GRAY1 if MONO_P is
+ non-zero. */
+void *
+BBitmap_new (int width, int height, int mono_p)
+{
+ BBitmap *bn = new (std::nothrow) BBitmap (BRect (0, 0, width - 1, height - 1),
+ mono_p ? B_GRAY1 : B_RGB32);
+
+ return bn->InitCheck () == B_OK ? (void *) bn : (void *) (delete bn, NULL);
+}
+
+void
+BBitmap_dimensions (void *bitmap, int *left, int *top,
+ int *right, int *bottom,
+ int32_t *bytes_per_row, int *mono_p)
+{
+ BRect rect = ((BBitmap *) bitmap)->Bounds ();
+ *left = rect.left;
+ *top = rect.top;
+ *right = rect.right;
+ *bottom = rect.bottom;
+
+ *bytes_per_row = ((BBitmap *) bitmap)->BytesPerRow ();
+ *mono_p = (((BBitmap *) bitmap)->ColorSpace () == B_GRAY1);
+}
+
+static void
+wait_for_exit_of_app_thread (void)
+{
+ status_t ret;
+
+ be_app->PostMessage (QUIT_APPLICATION);
+ wait_for_thread (app_thread, &ret);
+}
+
+/* Set up an application and return it. If starting the application
+ thread fails, abort Emacs. */
+void *
+BApplication_setup (void)
+{
+ thread_id id;
+ Emacs *app;
+
+ if (be_app)
+ return be_app;
+
+ app = new Emacs;
+ app->Unlock ();
+
+ if ((id = spawn_thread (start_running_application, "Emacs app thread",
+ B_DEFAULT_MEDIA_PRIORITY, app)) < 0)
+ gui_abort ("spawn_thread failed");
+
+ resume_thread (id);
+ app_thread = id;
+
+ atexit (wait_for_exit_of_app_thread);
+ return app;
+}
+
+/* Set up and return a window with its view put in VIEW. */
+void *
+BWindow_new (void **view)
+{
+ BWindow *window;
+ BView *vw;
+
+ window = new (std::nothrow) EmacsWindow;
+ if (!window)
+ {
+ *view = NULL;
+ return window;
+ }
+
+ vw = new (std::nothrow) EmacsView;
+ if (!vw)
+ {
+ *view = NULL;
+ window->LockLooper ();
+ window->Quit ();
+ return NULL;
+ }
+
+ /* Windows are created locked by the current thread, but calling
+ Show for the first time causes them to be unlocked. To avoid a
+ deadlock when a frame is created invisible in one thread, and
+ another thread later tries to lock it, the window is unlocked
+ here, and EmacsShow will lock it manually if it's being shown for
+ the first time. */
+ window->UnlockLooper ();
+ window->AddChild (vw);
+ *view = vw;
+ return window;
+}
+
+void
+BWindow_quit (void *window)
+{
+ BWindow *w = (BWindow *) window;
+
+ w->LockLooper ();
+ w->Quit ();
+}
+
+/* Set WINDOW's offset to X, Y. */
+void
+BWindow_set_offset (void *window, int x, int y)
+{
+ BWindow *wn = (BWindow *) window;
+ EmacsWindow *w = dynamic_cast<EmacsWindow *> (wn);
+ if (w)
+ {
+ if (!w->LockLooper ())
+ gui_abort ("Failed to lock window looper setting offset");
+ w->EmacsMoveTo (x, y);
+ w->UnlockLooper ();
+ }
+ else
+ wn->MoveTo (x, y);
+}
+
+void
+BWindow_dimensions (void *window, int *width, int *height)
+{
+ BWindow *w = (BWindow *) window;
+ BRect frame = w->Frame ();
+
+ *width = BE_RECT_WIDTH (frame);
+ *height = BE_RECT_HEIGHT (frame);
+}
+
+/* Iconify WINDOW. */
+void
+BWindow_iconify (void *window)
+{
+ if (((BWindow *) window)->IsHidden ())
+ BWindow_set_visible (window, true);
+ ((BWindow *) window)->Minimize (true);
+}
+
+/* Show or hide WINDOW. */
+void
+BWindow_set_visible (void *window, int visible_p)
+{
+ EmacsWindow *win = (EmacsWindow *) window;
+ if (visible_p)
+ {
+ if (win->IsMinimized ())
+ win->Minimize (false);
+ win->EmacsShow ();
+ }
+ else if (!win->IsHidden ())
+ {
+ if (win->IsMinimized ())
+ win->Minimize (false);
+ win->EmacsHide ();
+ }
+}
+
+/* Change the title of WINDOW to the multibyte string TITLE. */
+void
+BWindow_retitle (void *window, const char *title)
+{
+ ((BWindow *) window)->SetTitle (title);
+}
+
+/* Resize WINDOW to WIDTH by HEIGHT. */
+void
+BWindow_resize (void *window, int width, int height)
+{
+ ((BWindow *) window)->ResizeTo (width - 1, height - 1);
+}
+
+/* Activate WINDOW, making it the subject of keyboard focus and
+ bringing it to the front of the screen. */
+void
+BWindow_activate (void *window)
+{
+ ((BWindow *) window)->Activate ();
+}
+
+/* Return the pixel dimensions of the main screen in WIDTH and
+ HEIGHT. */
+void
+be_get_screen_dimensions (int *width, int *height)
+{
+ BScreen screen;
+ BRect frame;
+
+ if (!screen.IsValid ())
+ gui_abort ("Invalid screen");
+
+ frame = screen.Frame ();
+
+ *width = BE_RECT_WIDTH (frame);
+ *height = BE_RECT_HEIGHT (frame);
+}
+
+/* Resize VIEW to WIDTH, HEIGHT. */
+void
+BView_resize_to (void *view, int width, int height)
+{
+ EmacsView *vw = (EmacsView *) view;
+ if (!vw->LockLooper ())
+ gui_abort ("Failed to lock view for resize");
+ vw->ResizeTo (width, height);
+ vw->AfterResize ();
+ vw->UnlockLooper ();
+}
+
+void
+be_delete_cursor (void *cursor)
+{
+ if (cursor)
+ delete (BCursor *) cursor;
+}
+
+void *
+be_create_cursor_from_id (int id)
+{
+ return new BCursor ((enum BCursorID) id);
+}
+
+void
+BView_set_view_cursor (void *view, void *cursor)
+{
+ BView *v = (BView *) view;
+
+ if (!v->LockLooper ())
+ gui_abort ("Failed to lock view setting cursor");
+ v->SetViewCursor ((BCursor *) cursor);
+ v->UnlockLooper ();
+}
+
+void
+BWindow_Flush (void *window)
+{
+ ((BWindow *) window)->Flush ();
+}
+
+/* Make a scrollbar, attach it to VIEW's window, and return it. */
+void *
+be_make_scroll_bar_for_view (void *view, int horizontal_p,
+ int x, int y, int x1, int y1)
+{
+ EmacsScrollBar *scroll_bar;
+ BView *vw = (BView *) view;
+
+ if (!vw->LockLooper ())
+ gui_abort ("Failed to lock scrollbar owner");
+
+ scroll_bar = new EmacsScrollBar (x, y, x1, y1, horizontal_p,
+ (EmacsView *) vw);
+
+ vw->AddChild (scroll_bar);
+ vw->UnlockLooper ();
+
+ return scroll_bar;
+}
+
+void
+BScrollBar_delete (void *sb)
+{
+ BView *view = (BView *) sb;
+ BView *pr = view->Parent ();
+
+ if (!pr->LockLooper ())
+ gui_abort ("Failed to lock scrollbar parent");
+ pr->RemoveChild (view);
+ pr->UnlockLooper ();
+
+ delete (EmacsScrollBar *) sb;
+}
+
+void
+BView_move_frame (void *view, int x, int y, int x1, int y1)
+{
+ BView *vw = (BView *) view;
+
+ if (!vw->LockLooper ())
+ gui_abort ("Failed to lock view moving frame");
+ vw->MoveTo (x, y);
+ vw->ResizeTo (x1 - x, y1 - y);
+ vw->UnlockLooper ();
+}
+
+/* DRAGGING can either be 0 (which means to update everything), 1
+ (which means to update nothing), or -1 (which means to update only
+ the thumb size and range). */
+
+void
+BView_scroll_bar_update (void *sb, int portion, int whole, int position,
+ int dragging, bool can_overscroll)
+{
+ BScrollBar *bar = (BScrollBar *) sb;
+ BMessage msg = BMessage (SCROLL_BAR_UPDATE);
+ BMessenger mr = BMessenger (bar);
+ msg.AddInt32 ("emacs:range", whole);
+ msg.AddInt32 ("emacs:units", position);
+ msg.AddInt32 ("emacs:portion", portion);
+ msg.AddInt32 ("emacs:dragging", dragging);
+ msg.AddBool ("emacs:overscroll", can_overscroll);
+
+ mr.SendMessage (&msg);
+}
+
+/* Return the default scrollbar size. */
+int
+BScrollBar_default_size (int horizontal_p)
+{
+ return be_control_look->GetScrollBarWidth (horizontal_p
+ ? B_HORIZONTAL
+ : B_VERTICAL);
+}
+
+/* Invalidate VIEW, causing it to be drawn again. */
+void
+BView_invalidate (void *view)
+{
+ BView *vw = (BView *) view;
+ if (!vw->LockLooper ())
+ gui_abort ("Couldn't lock view while invalidating it");
+ vw->Invalidate ();
+ vw->UnlockLooper ();
+}
+
+/* Lock VIEW in preparation for drawing operations. This should be
+ called before any attempt to draw onto VIEW or to lock it for Cairo
+ drawing. `BView_draw_unlock' should be called afterwards.
+
+ If any drawing is going to take place, INVALID_REGION should be
+ true, and X, Y, WIDTH, HEIGHT should specify a rectangle in which
+ the drawing will take place. */
+void
+BView_draw_lock (void *view, bool invalidate_region,
+ int x, int y, int width, int height)
+{
+ EmacsView *vw = (EmacsView *) view;
+ if (vw->looper_locked_count)
+ {
+ vw->looper_locked_count++;
+
+ if (invalidate_region && vw->offscreen_draw_view)
+ vw->invalid_region.Include (BRect (x, y, x + width - 1,
+ y + height - 1));
+ return;
+ }
+ BView *v = (BView *) find_appropriate_view_for_draw (vw);
+ if (v != vw)
+ {
+ if (!vw->offscreen_draw_bitmap_1->Lock ())
+ gui_abort ("Failed to lock offscreen bitmap while acquiring draw lock");
+ }
+ else if (!v->LockLooper ())
+ gui_abort ("Failed to lock draw view while acquiring draw lock");
+
+ if (v != vw && !vw->LockLooper ())
+ gui_abort ("Failed to lock view while acquiring draw lock");
+
+ if (invalidate_region && vw->offscreen_draw_view)
+ vw->invalid_region.Include (BRect (x, y, x + width - 1,
+ y + height - 1));
+ vw->looper_locked_count++;
+}
+
+void
+BView_invalidate_region (void *view, int x, int y, int width, int height)
+{
+ EmacsView *vw = (EmacsView *) view;
+
+ if (vw->offscreen_draw_view)
+ vw->invalid_region.Include (BRect (x, y, x + width - 1,
+ y + height - 1));
+}
+
+void
+BView_draw_unlock (void *view)
+{
+ EmacsView *vw = (EmacsView *) view;
+ if (--vw->looper_locked_count)
+ return;
+
+ BView *v = (BView *) find_appropriate_view_for_draw (view);
+ if (v == vw)
+ vw->UnlockLooper ();
+ else
+ {
+ vw->offscreen_draw_bitmap_1->Unlock ();
+ vw->UnlockLooper ();
+ }
+}
+
+void
+BWindow_center_on_screen (void *window)
+{
+ BWindow *w = (BWindow *) window;
+ w->CenterOnScreen ();
+}
+
+/* Import fringe bitmap (short array, low bit rightmost) BITS into
+ BITMAP using the B_GRAY1 colorspace. */
+void
+BBitmap_import_fringe_bitmap (void *bitmap, unsigned short *bits, int wd, int h)
+{
+ BBitmap *bmp = (BBitmap *) bitmap;
+ unsigned char *data = (unsigned char *) bmp->Bits ();
+ int i;
+
+ for (i = 0; i < h; i++)
+ {
+ if (wd <= 8)
+ data[0] = bits[i] & 0xff;
+ else
+ {
+ data[1] = bits[i] & 0xff;
+ data[0] = bits[i] >> 8;
+ }
+
+ data += bmp->BytesPerRow ();
+ }
+}
+
+/* Make a scrollbar at X, Y known to the view VIEW. */
+void
+BView_publish_scroll_bar (void *view, int x, int y, int width, int height)
+{
+ EmacsView *vw = (EmacsView *) view;
+ if (vw->LockLooper ())
+ {
+ vw->sb_region.Include (BRect (x, y, x - 1 + width,
+ y - 1 + height));
+ vw->UnlockLooper ();
+ }
+}
+
+void
+BView_forget_scroll_bar (void *view, int x, int y, int width, int height)
+{
+ EmacsView *vw = (EmacsView *) view;
+ if (vw->LockLooper ())
+ {
+ vw->sb_region.Exclude (BRect (x, y, x - 1 + width,
+ y - 1 + height));
+ vw->UnlockLooper ();
+ }
+}
+
+bool
+BView_inside_scroll_bar (void *view, int x, int y)
+{
+ EmacsView *vw = (EmacsView *) view;
+ bool val;
+
+ if (vw->LockLooper ())
+ {
+ val = vw->sb_region.Contains (BPoint (x, y));
+ vw->UnlockLooper ();
+ }
+ else
+ val = false;
+
+ return val;
+}
+
+void
+BView_get_mouse (void *view, int *x, int *y)
+{
+ BPoint l;
+ BView *vw = (BView *) view;
+ if (!vw->LockLooper ())
+ gui_abort ("Failed to lock view in BView_get_mouse");
+ vw->GetMouse (&l, NULL, 1);
+ vw->UnlockLooper ();
+
+ *x = std::lrint (l.x);
+ *y = std::lrint (l.y);
+}
+
+/* Perform an in-place conversion of X and Y from VIEW's coordinate
+ system to its screen's coordinate system. */
+void
+BView_convert_to_screen (void *view, int *x, int *y)
+{
+ BPoint l = BPoint (*x, *y);
+ BView *vw = (BView *) view;
+ if (!vw->LockLooper ())
+ gui_abort ("Failed to lock view in convert_to_screen");
+ vw->ConvertToScreen (&l);
+ vw->UnlockLooper ();
+
+ *x = std::lrint (l.x);
+ *y = std::lrint (l.y);
+}
+
+void
+BView_convert_from_screen (void *view, int *x, int *y)
+{
+ BPoint l = BPoint (*x, *y);
+ BView *vw = (BView *) view;
+ if (!vw->LockLooper ())
+ gui_abort ("Failed to lock view in convert_from_screen");
+ vw->ConvertFromScreen (&l);
+ vw->UnlockLooper ();
+
+ *x = std::lrint (l.x);
+ *y = std::lrint (l.y);
+}
+
+/* Decorate or undecorate WINDOW depending on DECORATE_P. */
+void
+BWindow_change_decoration (void *window, int decorate_p)
+{
+ EmacsWindow *w = (EmacsWindow *) window;
+ if (!w->LockLooper ())
+ gui_abort ("Failed to lock window while changing its decorations");
+
+ if (!w->override_redirect_p)
+ {
+ if (decorate_p)
+ w->SetLook (B_TITLED_WINDOW_LOOK);
+ else
+ w->SetLook (B_NO_BORDER_WINDOW_LOOK);
+ }
+ else
+ {
+ if (decorate_p)
+ w->pre_override_redirect_look = B_TITLED_WINDOW_LOOK;
+ else
+ w->pre_override_redirect_look = B_NO_BORDER_WINDOW_LOOK;
+ }
+ w->UnlockLooper ();
+}
+
+/* Decorate WINDOW appropriately for use as a tooltip. */
+void
+BWindow_set_tooltip_decoration (void *window)
+{
+ EmacsWindow *w = (EmacsWindow *) window;
+ if (!w->LockLooper ())
+ gui_abort ("Failed to lock window while setting ttip decoration");
+ w->tooltip_p = true;
+ w->RecomputeFeel ();
+ w->SetLook (B_BORDERED_WINDOW_LOOK);
+ w->SetFlags (B_NOT_ZOOMABLE
+ | B_NOT_MINIMIZABLE
+ | B_AVOID_FRONT
+ | B_AVOID_FOCUS);
+ w->UnlockLooper ();
+}
+
+/* Set B_AVOID_FOCUS on WINDOW if AVOID_FOCUS_P is non-nil, or clear
+ it otherwise. */
+void
+BWindow_set_avoid_focus (void *window, int avoid_focus_p)
+{
+ BWindow *w = (BWindow *) window;
+ if (!w->LockLooper ())
+ gui_abort ("Failed to lock window while setting avoid focus");
+
+ if (!avoid_focus_p)
+ w->SetFlags (w->Flags () & ~B_AVOID_FOCUS);
+ else
+ w->SetFlags (w->Flags () | B_AVOID_FOCUS);
+ w->UnlockLooper ();
+}
+
+void
+BView_emacs_delete (void *view)
+{
+ EmacsView *vw = (EmacsView *) view;
+ if (!vw->LockLooper ())
+ gui_abort ("Failed to lock view while deleting it");
+ vw->RemoveSelf ();
+ delete vw;
+}
+
+/* Create a popup menu. */
+void *
+BPopUpMenu_new (const char *name)
+{
+ BPopUpMenu *menu = new BPopUpMenu (name);
+
+ menu->SetRadioMode (0);
+ return menu;
+}
+
+/* Add a title item to MENU. These items cannot be highlighted or
+ triggered, and their labels will display as bold text. */
+void
+BMenu_add_title (void *menu, const char *text)
+{
+ BMenu *be_menu = (BMenu *) menu;
+ EmacsTitleMenuItem *it;
+
+ it = new EmacsTitleMenuItem (text);
+ be_menu->AddItem (it);
+}
+
+/* Add an item to the menu MENU. */
+void
+BMenu_add_item (void *menu, const char *label, void *ptr, bool enabled_p,
+ bool marked_p, bool mbar_p, void *mbw_ptr, const char *key,
+ const char *help)
+{
+ BMenu *m = (BMenu *) menu;
+ BMessage *msg;
+ if (ptr)
+ msg = new BMessage ();
+ EmacsMenuItem *it = new EmacsMenuItem (key, label, help, ptr ? msg : NULL);
+ it->SetTarget (m->Window ());
+ it->SetEnabled (enabled_p);
+ it->SetMarked (marked_p);
+ if (mbar_p)
+ {
+ it->menu_bar_id = (intptr_t) ptr;
+ it->wind_ptr = mbw_ptr;
+ }
+ it->menu_ptr = ptr;
+ if (ptr)
+ msg->AddPointer ("menuptr", ptr);
+ m->AddItem (it);
+}
+
+/* Add a separator to the menu MENU. */
+void
+BMenu_add_separator (void *menu)
+{
+ BMenu *m = (BMenu *) menu;
+
+ m->AddSeparatorItem ();
+}
+
+/* Create a submenu and attach it to MENU. */
+void *
+BMenu_new_submenu (void *menu, const char *label, bool enabled_p)
+{
+ BMenu *m = (BMenu *) menu;
+ BMenu *mn = new BMenu (label, B_ITEMS_IN_COLUMN);
+ mn->SetRadioMode (0);
+ BMenuItem *i = new BMenuItem (mn);
+ i->SetEnabled (enabled_p);
+ m->AddItem (i);
+ return mn;
+}
+
+/* Create a submenu that notifies Emacs upon opening. */
+void *
+BMenu_new_menu_bar_submenu (void *menu, const char *label)
+{
+ BMenu *m = (BMenu *) menu;
+ BMenu *mn = new BMenu (label, B_ITEMS_IN_COLUMN);
+ mn->SetRadioMode (0);
+ BMenuItem *i = new BMenuItem (mn);
+ i->SetEnabled (1);
+ m->AddItem (i);
+ return mn;
+}
+
+/* Run MENU, waiting for it to close, and return a pointer to the
+ data of the selected item (if one exists), or NULL. X, Y should
+ be in the screen coordinate system. */
+void *
+BMenu_run (void *menu, int x, int y,
+ void (*run_help_callback) (void *, void *),
+ void (*block_input_function) (void),
+ void (*unblock_input_function) (void),
+ struct timespec (*process_pending_signals_function) (void),
+ void *run_help_callback_data)
+{
+ BPopUpMenu *mn = (BPopUpMenu *) menu;
+ enum haiku_event_type type;
+ void *buf;
+ void *ptr = NULL;
+ struct be_popup_menu_data data;
+ struct object_wait_info infos[3];
+ struct haiku_menu_bar_help_event *event;
+ BMessage *msg;
+ ssize_t stat;
+ struct timespec next_time;
+ bigtime_t timeout;
+
+ block_input_function ();
+ port_popup_menu_to_emacs = create_port (1800, "popup menu port");
+ data.x = x;
+ data.y = y;
+ data.menu = mn;
+ unblock_input_function ();
+
+ if (port_popup_menu_to_emacs < B_OK)
+ return NULL;
+
+ block_input_function ();
+ mn->SetRadioMode (0);
+ buf = alloca (200);
+
+ infos[0].object = port_popup_menu_to_emacs;
+ infos[0].type = B_OBJECT_TYPE_PORT;
+ infos[0].events = B_EVENT_READ;
+
+ infos[1].object = spawn_thread (be_popup_menu_thread_entry,
+ "Menu tracker", B_DEFAULT_MEDIA_PRIORITY,
+ (void *) &data);
+ infos[1].type = B_OBJECT_TYPE_THREAD;
+ infos[1].events = B_EVENT_INVALID;
+
+ infos[2].object = port_application_to_emacs;
+ infos[2].type = B_OBJECT_TYPE_PORT;
+ infos[2].events = B_EVENT_READ;
+ unblock_input_function ();
+
+ if (infos[1].object < B_OK)
+ {
+ block_input_function ();
+ delete_port (port_popup_menu_to_emacs);
+ unblock_input_function ();
+ return NULL;
+ }
+
+ block_input_function ();
+ resume_thread (infos[1].object);
+ unblock_input_function ();
+
+ while (true)
+ {
+ next_time = process_pending_signals_function ();
+
+ if (next_time.tv_nsec < 0)
+ timeout = 10000000000;
+ else
+ timeout = (next_time.tv_sec * 1000000
+ + next_time.tv_nsec / 1000);
+
+ if ((stat = wait_for_objects_etc ((object_wait_info *) &infos, 3,
+ B_RELATIVE_TIMEOUT, timeout)) < B_OK)
+ {
+ if (stat == B_INTERRUPTED || stat == B_TIMED_OUT
+ || stat == B_WOULD_BLOCK)
+ continue;
+ else
+ gui_abort ("Failed to wait for popup");
+ }
+
+ if (infos[0].events & B_EVENT_READ)
+ {
+ while (!haiku_read_with_timeout (&type, buf, 200, 0, true))
+ {
+ switch (type)
+ {
+ case MENU_BAR_HELP_EVENT:
+ event = (struct haiku_menu_bar_help_event *) buf;
+ run_help_callback (event->highlight_p
+ ? event->data
+ : NULL, run_help_callback_data);
+ break;
+ default:
+ gui_abort ("Unknown popup menu event");
+ }
+ }
+ }
+
+ if (infos[1].events & B_EVENT_INVALID)
+ {
+ block_input_function ();
+ msg = (BMessage *) popup_track_message;
+ if (popup_track_message)
+ ptr = (void *) msg->GetPointer ("menuptr");
+
+ delete_port (port_popup_menu_to_emacs);
+ unblock_input_function ();
+ return ptr;
+ }
+
+ infos[0].events = B_EVENT_READ;
+ infos[1].events = B_EVENT_INVALID;
+ infos[2].events = B_EVENT_READ;
+ }
+}
+
+/* Delete the entire menu hierarchy of MENU, and then delete MENU
+ itself. */
+void
+BPopUpMenu_delete (void *menu)
+{
+ delete (BPopUpMenu *) menu;
+}
+
+/* Create a menubar, attach it to VIEW, and return it. */
+void *
+BMenuBar_new (void *view)
+{
+ BView *vw = (BView *) view;
+ EmacsMenuBar *bar = new EmacsMenuBar ();
+
+ if (!vw->LockLooper ())
+ gui_abort ("Failed to lock menu bar parent");
+ vw->AddChild ((BView *) bar);
+ vw->UnlockLooper ();
+
+ return bar;
+}
+
+/* Delete MENUBAR along with all subitems. */
+void
+BMenuBar_delete (void *menubar)
+{
+ BView *vw = (BView *) menubar;
+ BView *p = vw->Parent ();
+ EmacsWindow *window = (EmacsWindow *) p->Window ();
+
+ if (!p->LockLooper ())
+ gui_abort ("Failed to lock menu bar parent while removing menubar");
+ window->SetKeyMenuBar (NULL);
+ /* MenusEnded isn't called if the menu bar is destroyed
+ before it closes. */
+ window->menu_bar_active_p = false;
+ vw->RemoveSelf ();
+ p->UnlockLooper ();
+ delete vw;
+}
+
+/* Delete all items from MENU. */
+void
+BMenu_delete_all (void *menu)
+{
+ BMenu *mn = (BMenu *) menu;
+ mn->RemoveItems (0, mn->CountItems (), true);
+}
+
+/* Delete COUNT items from MENU starting from START. */
+void
+BMenu_delete_from (void *menu, int start, int count)
+{
+ BMenu *mn = (BMenu *) menu;
+ mn->RemoveItems (start, count, true);
+}
+
+/* Count items in menu MENU. */
+int
+BMenu_count_items (void *menu)
+{
+ return ((BMenu *) menu)->CountItems ();
+}
+
+/* Find the item in MENU at IDX. */
+void *
+BMenu_item_at (void *menu, int idx)
+{
+ return ((BMenu *) menu)->ItemAt (idx);
+}
+
+/* Set ITEM's label to LABEL. */
+void
+BMenu_item_set_label (void *item, const char *label)
+{
+ ((BMenuItem *) item)->SetLabel (label);
+}
+
+/* Get ITEM's menu. */
+void *
+BMenu_item_get_menu (void *item)
+{
+ return ((BMenuItem *) item)->Submenu ();
+}
+
+/* Emit a beep noise. */
+void
+haiku_ring_bell (void)
+{
+ beep ();
+}
+
+/* Create a BAlert with TEXT. */
+void *
+BAlert_new (const char *text, enum haiku_alert_type type)
+{
+ return new BAlert (NULL, text, NULL, NULL, NULL, B_WIDTH_AS_USUAL,
+ (enum alert_type) type);
+}
+
+/* Add a button to ALERT and return the button. */
+void *
+BAlert_add_button (void *alert, const char *text)
+{
+ BAlert *al = (BAlert *) alert;
+ al->AddButton (text);
+ return al->ButtonAt (al->CountButtons () - 1);
+}
+
+/* Make sure the leftmost button is grouped to the left hand side of
+ the alert. */
+void
+BAlert_set_offset_spacing (void *alert)
+{
+ BAlert *al = (BAlert *) alert;
+
+ al->SetButtonSpacing (B_OFFSET_SPACING);
+}
+
+static int32
+be_alert_thread_entry (void *thread_data)
+{
+ BAlert *alert = (BAlert *) thread_data;
+ int32 value;
+
+ if (alert->LockLooper ())
+ value = alert->Go ();
+ else
+ value = -1;
+
+ alert_popup_value = value;
+ return 0;
+}
+
+/* Run ALERT, returning the number of the button that was selected,
+ or -1 if no button was selected before the alert was closed. */
+int32
+BAlert_go (void *alert,
+ void (*block_input_function) (void),
+ void (*unblock_input_function) (void),
+ void (*process_pending_signals_function) (void))
+{
+ struct object_wait_info infos[2];
+ ssize_t stat;
+ BAlert *alert_object = (BAlert *) alert;
+
+ infos[0].object = port_application_to_emacs;
+ infos[0].type = B_OBJECT_TYPE_PORT;
+ infos[0].events = B_EVENT_READ;
+
+ block_input_function ();
+ /* Alerts are created locked, just like other windows. */
+ alert_object->UnlockLooper ();
+ infos[1].object = spawn_thread (be_alert_thread_entry,
+ "Popup tracker",
+ B_DEFAULT_MEDIA_PRIORITY,
+ alert);
+ infos[1].type = B_OBJECT_TYPE_THREAD;
+ infos[1].events = B_EVENT_INVALID;
+ unblock_input_function ();
+
+ if (infos[1].object < B_OK)
+ return -1;
+
+ block_input_function ();
+ resume_thread (infos[1].object);
+ unblock_input_function ();
+
+ while (true)
+ {
+ stat = wait_for_objects ((object_wait_info *) &infos, 2);
+
+ if (stat == B_INTERRUPTED)
+ continue;
+ else if (stat < B_OK)
+ gui_abort ("Failed to wait for popup dialog");
+
+ if (infos[1].events & B_EVENT_INVALID)
+ return alert_popup_value;
+
+ if (infos[0].events & B_EVENT_READ)
+ process_pending_signals_function ();
+
+ infos[0].events = B_EVENT_READ;
+ infos[1].events = B_EVENT_INVALID;
+ }
+}
+
+/* Enable or disable BUTTON depending on ENABLED_P. */
+void
+BButton_set_enabled (void *button, int enabled_p)
+{
+ ((BButton *) button)->SetEnabled (enabled_p);
+}
+
+/* Set VIEW's tooltip to TOOLTIP. */
+void
+BView_set_tooltip (void *view, const char *tooltip)
+{
+ ((BView *) view)->SetToolTip (tooltip);
+}
+
+/* Set VIEW's tooltip to a sticky tooltip at X by Y. */
+void
+be_show_sticky_tooltip (void *view, const char *tooltip_text,
+ int x, int y)
+{
+ BToolTip *tooltip;
+ BView *vw, *tooltip_view;
+ BPoint point;
+
+ vw = (BView *) view;
+
+ if (!vw->LockLooper ())
+ gui_abort ("Failed to lock view while showing sticky tooltip");
+
+ vw->SetToolTip ((const char *) NULL);
+
+ /* If the tooltip text is empty, then a tooltip object won't be
+ created by SetToolTip. */
+ if (tooltip_text[0] == '\0')
+ tooltip_text = " ";
+
+ vw->SetToolTip (tooltip_text);
+
+ tooltip = vw->ToolTip ();
+
+ vw->GetMouse (&point, NULL, 1);
+ point.x -= x;
+ point.y -= y;
+
+ point.x = -point.x;
+ point.y = -point.y;
+
+ /* We don't have to make the tooltip sticky since not receiving
+ mouse movement is enough to prevent it from being hidden. */
+ tooltip->SetMouseRelativeLocation (point);
+
+ /* Prevent the tooltip from moving in response to mouse
+ movement. */
+ tooltip_view = tooltip->View ();
+
+ if (tooltip_view)
+ tooltip_view->AddChild (new EmacsMotionSuppressionView);
+
+ vw->ShowToolTip (tooltip);
+ vw->UnlockLooper ();
+}
+
+/* Delete ALERT. */
+void
+BAlert_delete (void *alert)
+{
+ delete (BAlert *) alert;
+}
+
+/* Place the resolution of the monitor in DPI in X_OUT and Y_OUT. */
+void
+be_get_display_resolution (double *x_out, double *y_out)
+{
+ BScreen s (B_MAIN_SCREEN_ID);
+ monitor_info i;
+ double x_inches, y_inches;
+ BRect frame;
+
+ if (!s.IsValid ())
+ gui_abort ("Invalid screen for resolution checks");
+
+ if (s.GetMonitorInfo (&i) == B_OK)
+ {
+ frame = s.Frame ();
+
+ x_inches = (double) i.width * 25.4;
+ y_inches = (double) i.height * 25.4;
+
+ *x_out = (double) BE_RECT_WIDTH (frame) / x_inches;
+ *y_out = (double) BE_RECT_HEIGHT (frame) / y_inches;
+ return;
+ }
+
+ *x_out = 72.0;
+ *y_out = 72.0;
+}
+
+/* Add WINDOW to OTHER_WINDOW's subset and parent it to
+ OTHER_WINDOW. */
+void
+EmacsWindow_parent_to (void *window, void *other_window)
+{
+ EmacsWindow *w = (EmacsWindow *) window;
+ if (!w->LockLooper ())
+ gui_abort ("Failed to lock window while parenting");
+ w->ParentTo ((EmacsWindow *) other_window);
+ w->UnlockLooper ();
+}
+
+void
+EmacsWindow_unparent (void *window)
+{
+ EmacsWindow *w = (EmacsWindow *) window;
+ if (!w->LockLooper ())
+ gui_abort ("Failed to lock window while unparenting");
+ w->UnparentAndUnlink ();
+ w->UnlockLooper ();
+}
+
+/* Place text describing the current version of Haiku in VERSION,
+ which should be a buffer LEN bytes wide. */
+void
+be_get_version_string (char *version, int len)
+{
+ std::strncpy (version, "Unknown Haiku release", len - 1);
+ version[len - 1] = '\0';
+
+ BPath path;
+ if (find_directory (B_BEOS_LIB_DIRECTORY, &path) == B_OK)
+ {
+ path.Append ("libbe.so");
+
+ BAppFileInfo appFileInfo;
+ version_info versionInfo;
+ BFile file;
+ if (file.SetTo (path.Path (), B_READ_ONLY) == B_OK
+ && appFileInfo.SetTo (&file) == B_OK
+ && appFileInfo.GetVersionInfo (&versionInfo,
+ B_APP_VERSION_KIND) == B_OK
+ && versionInfo.short_info[0] != '\0')
+ {
+ std::strncpy (version, versionInfo.short_info, len - 1);
+ version[len - 1] = '\0';
+ }
+ }
+}
+
+/* Return the amount of color planes in the current display. */
+int
+be_get_display_planes (void)
+{
+ color_space space = dpy_color_space;
+ BScreen screen;
+
+ if (space == B_NO_COLOR_SPACE)
+ {
+ if (!screen.IsValid ())
+ gui_abort ("Invalid screen");
+
+ space = dpy_color_space = screen.ColorSpace ();
+ }
+
+ switch (space)
+ {
+ case B_RGB32:
+ case B_RGB24:
+ return 24;
+ case B_RGB16:
+ return 16;
+ case B_RGB15:
+ return 15;
+ case B_CMAP8:
+ case B_GRAY8:
+ return 8;
+ case B_GRAY1:
+ return 1;
+
+ default:
+ gui_abort ("Bad colorspace for screen");
+ }
+
+ /* https://www.haiku-os.org/docs/api/classBScreen.html
+ says a valid screen can't be anything else. */
+ return -1;
+}
+
+/* Return the amount of colors the display can handle. */
+int
+be_get_display_color_cells (void)
+{
+ BScreen screen;
+ color_space space = dpy_color_space;
+
+ if (space == B_NO_COLOR_SPACE)
+ {
+ if (!screen.IsValid ())
+ gui_abort ("Invalid screen");
+
+ space = dpy_color_space = screen.ColorSpace ();
+ }
+
+ switch (space)
+ {
+ case B_RGB32:
+ case B_RGB24:
+ return 16777216;
+ case B_RGB16:
+ return 65536;
+ case B_RGB15:
+ return 32768;
+ case B_CMAP8:
+ case B_GRAY8:
+ return 256;
+ case B_GRAY1:
+ return 2;
+
+ default:
+ gui_abort ("Bad colorspace for screen");
+ }
+
+ return -1;
+}
+
+/* Return whether or not the current display is only capable of
+ producing grayscale colors. */
+bool
+be_is_display_grayscale (void)
+{
+ BScreen screen;
+ color_space space = dpy_color_space;
+
+ if (space == B_NO_COLOR_SPACE)
+ {
+ if (!screen.IsValid ())
+ gui_abort ("Invalid screen");
+
+ space = dpy_color_space = screen.ColorSpace ();
+ }
+
+ return space == B_GRAY8 || space == B_GRAY1;
+}
+
+/* Warp the pointer to X by Y. */
+void
+be_warp_pointer (int x, int y)
+{
+ /* We're not supposed to use the following function without a
+ BWindowScreen object, but in Haiku nothing actually prevents us
+ from doing so. */
+
+ set_mouse_position (x, y);
+}
+
+/* Update the position of CHILD in WINDOW without actually moving
+ it. */
+void
+EmacsWindow_move_weak_child (void *window, void *child, int xoff, int yoff)
+{
+ EmacsWindow *w = (EmacsWindow *) window;
+ EmacsWindow *c = (EmacsWindow *) child;
+
+ if (!w->LockLooper ())
+ gui_abort ("Couldn't lock window for weak move");
+ w->MoveChild (c, xoff, yoff, 1);
+ w->UnlockLooper ();
+}
+
+/* Find an appropriate view to draw onto. If VW is double-buffered,
+ this will be the view used for double buffering instead of VW
+ itself. */
+void *
+find_appropriate_view_for_draw (void *vw)
+{
+ BView *v = (BView *) vw;
+ EmacsView *ev = dynamic_cast<EmacsView *>(v);
+ if (!ev)
+ return v;
+
+ return ev->offscreen_draw_view ? ev->offscreen_draw_view : vw;
+}
+
+/* Set up double buffering for VW. */
+void
+EmacsView_set_up_double_buffering (void *vw)
+{
+ EmacsView *view = (EmacsView *) vw;
+ if (!view->LockLooper ())
+ gui_abort ("Couldn't lock view while setting up double buffering");
+ if (view->offscreen_draw_view)
+ {
+ view->UnlockLooper ();
+ return;
+ }
+ view->SetUpDoubleBuffering ();
+ view->UnlockLooper ();
+}
+
+/* Flip and invalidate the view VW. */
+void
+EmacsView_flip_and_blit (void *vw)
+{
+ EmacsView *view = (EmacsView *) vw;
+ if (!view->offscreen_draw_view)
+ return;
+ if (!view->LockLooper ())
+ gui_abort ("Couldn't lock view in flip_and_blit");
+ view->FlipBuffers ();
+ view->UnlockLooper ();
+}
+
+/* Disable double buffering for VW. */
+void
+EmacsView_disable_double_buffering (void *vw)
+{
+ EmacsView *view = (EmacsView *) vw;
+ if (!view->LockLooper ())
+ gui_abort ("Couldn't lock view tearing down double buffering");
+ view->TearDownDoubleBuffering ();
+ view->UnlockLooper ();
+}
+
+/* Return non-0 if VW is double-buffered. */
+int
+EmacsView_double_buffered_p (void *vw)
+{
+ EmacsView *view = (EmacsView *) vw;
+ if (!view->LockLooper ())
+ gui_abort ("Couldn't lock view testing double buffering status");
+ int db_p = !!view->offscreen_draw_view;
+ view->UnlockLooper ();
+ return db_p;
+}
+
+/* Popup a file dialog. */
+char *
+be_popup_file_dialog (int open_p, const char *default_dir, int must_match_p,
+ int dir_only_p, void *window, const char *save_text,
+ const char *prompt,
+ void (*process_pending_signals_function) (void))
+{
+ BWindow *panel_window;
+ BEntry path;
+ BMessage msg (FILE_PANEL_SELECTION);
+ BFilePanel panel (open_p ? B_OPEN_PANEL : B_SAVE_PANEL,
+ NULL, NULL, (dir_only_p
+ ? B_DIRECTORY_NODE
+ : B_FILE_NODE | B_DIRECTORY_NODE));
+ char *file_name;
+ EmacsFilePanelCallbackLooper *looper;
+
+ looper = new EmacsFilePanelCallbackLooper;
+
+ if (looper->InitCheck () < B_OK)
+ {
+ delete looper;
+ return NULL;
+ }
+
+ if (default_dir)
+ {
+ if (path.SetTo (default_dir, 0) != B_OK)
+ default_dir = NULL;
+ }
+
+ panel_window = panel.Window ();
+
+ if (default_dir)
+ panel.SetPanelDirectory (&path);
+
+ if (save_text)
+ panel.SetSaveText (save_text);
+
+ panel_window->SetTitle (prompt);
+ panel_window->SetFeel (B_MODAL_APP_WINDOW_FEEL);
+
+ panel.SetHideWhenDone (false);
+ panel.SetTarget (BMessenger (looper));
+ panel.SetMessage (&msg);
+ panel.Show ();
+
+ looper->Run ();
+ file_name = looper->ReadFileName (process_pending_signals_function);
+
+ if (looper->Lock ())
+ looper->Quit ();
+
+ return file_name;
+}
+
+/* Move the pointer into MBAR and start tracking. Return whether the
+ menu bar was opened correctly. */
+bool
+BMenuBar_start_tracking (void *mbar)
+{
+ EmacsMenuBar *mb = (EmacsMenuBar *) mbar;
+ BMessenger messenger (mb);
+ BMessage reply;
+
+ messenger.SendMessage (SHOW_MENU_BAR, &reply);
+
+ return reply.what == BE_MENU_BAR_OPEN;
+}
+
+#ifdef HAVE_NATIVE_IMAGE_API
+int
+be_can_translate_type_to_bitmap_p (const char *mime)
+{
+ BTranslatorRoster *r = BTranslatorRoster::Default ();
+ translator_id *ids;
+ int32 id_len;
+
+ if (r->GetAllTranslators (&ids, &id_len) != B_OK)
+ return 0;
+
+ int found_in = 0;
+ int found_out = 0;
+
+ for (int i = 0; i < id_len; ++i)
+ {
+ found_in = 0;
+ found_out = 0;
+ const translation_format *i_fmts;
+ const translation_format *o_fmts;
+
+ int32 i_count, o_count;
+
+ if (r->GetInputFormats (ids[i], &i_fmts, &i_count) != B_OK)
+ continue;
+
+ if (r->GetOutputFormats (ids[i], &o_fmts, &o_count) != B_OK)
+ continue;
+
+ for (int x = 0; x < i_count; ++x)
+ {
+ if (!strcmp (i_fmts[x].MIME, mime))
+ {
+ found_in = 1;
+ break;
+ }
+ }
+
+ for (int x = 0; x < i_count; ++x)
+ {
+ if (!strcmp (o_fmts[x].MIME, "image/x-be-bitmap") ||
+ !strcmp (o_fmts[x].MIME, "image/x-vnd.Be-bitmap"))
+ {
+ found_out = 1;
+ break;
+ }
+ }
+
+ if (found_in && found_out)
+ break;
+ }
+
+ delete [] ids;
+
+ return found_in && found_out;
+}
+
+void *
+be_translate_bitmap_from_file_name (const char *filename)
+{
+ BBitmap *bm = BTranslationUtils::GetBitmap (filename);
+ return bm;
+}
+
+void *
+be_translate_bitmap_from_memory (const void *buf, size_t bytes)
+{
+ BMemoryIO io (buf, bytes);
+ BBitmap *bm = BTranslationUtils::GetBitmap (&io);
+ return bm;
+}
+#endif
+
+/* Return the size of BITMAP's data, in bytes. */
+size_t
+BBitmap_bytes_length (void *bitmap)
+{
+ BBitmap *bm = (BBitmap *) bitmap;
+ return bm->BitsLength ();
+}
+
+/* Show VIEW's tooltip. */
+void
+BView_show_tooltip (void *view)
+{
+ BView *vw = (BView *) view;
+ if (vw->LockLooper ())
+ {
+ vw->ShowToolTip (vw->ToolTip ());
+ vw->UnlockLooper ();
+ }
+}
+
+
+#ifdef USE_BE_CAIRO
+/* Return VIEW's cairo context. */
+cairo_t *
+EmacsView_cairo_context (void *view)
+{
+ EmacsView *vw = (EmacsView *) view;
+ return vw->cr_context;
+}
+
+/* Transfer each clip rectangle in VIEW to the cairo context
+ CTX. */
+void
+BView_cr_dump_clipping (void *view, cairo_t *ctx)
+{
+ BView *vw = (BView *) find_appropriate_view_for_draw (view);
+ BRegion cr;
+ vw->GetClippingRegion (&cr);
+
+ for (int i = 0; i < cr.CountRects (); ++i)
+ {
+ BRect r = cr.RectAt (i);
+ cairo_rectangle (ctx, r.left, r.top,
+ BE_RECT_WIDTH (r),
+ BE_RECT_HEIGHT (r));
+ }
+
+ cairo_clip (ctx);
+}
+
+/* Lock WINDOW in preparation for drawing using Cairo. */
+void
+EmacsWindow_begin_cr_critical_section (void *window)
+{
+ BWindow *w = (BWindow *) window;
+ BView *vw = (BView *) w->FindView ("Emacs");
+ EmacsView *ev = dynamic_cast <EmacsView *> (vw);
+ if (ev && !ev->cr_surface_lock.Lock ())
+ gui_abort ("Couldn't lock view cairo surface");
+}
+
+/* Unlock WINDOW in preparation for drawing using Cairo. */
+void
+EmacsWindow_end_cr_critical_section (void *window)
+{
+ BWindow *w = (BWindow *) window;
+ BView *vw = (BView *) w->FindView ("Emacs");
+ EmacsView *ev = dynamic_cast <EmacsView *> (vw);
+ if (ev)
+ ev->cr_surface_lock.Unlock ();
+}
+#endif
+
+/* Get the width of STR in the plain font. */
+int
+be_string_width_with_plain_font (const char *str)
+{
+ return be_plain_font->StringWidth (str);
+}
+
+/* Get the ascent + descent of the plain font. */
+int
+be_plain_font_height (void)
+{
+ struct font_height fheight;
+ be_plain_font->GetHeight (&fheight);
+
+ return fheight.ascent + fheight.descent;
+}
+
+/* Return the number of physical displays connected. */
+int
+be_get_display_screens (void)
+{
+ int count = 1;
+ BScreen scr;
+
+ if (!scr.IsValid ())
+ gui_abort ("Main screen vanished!");
+ while (scr.SetToNext () == B_OK && scr.IsValid ())
+ ++count;
+
+ return count;
+}
+
+/* Set the minimum width the user can resize WINDOW to. */
+/* Synchronize WINDOW's connection to the App Server. */
+void
+BWindow_sync (void *window)
+{
+ BWindow *w = (BWindow *) window;
+
+ if (!w->LockLooper ())
+ gui_abort ("Failed to lock window looper for sync");
+ w->Sync ();
+ w->UnlockLooper ();
+}
+
+/* Set the alignment of WINDOW's dimensions. */
+void
+BWindow_set_size_alignment (void *window, int align_width, int align_height)
+{
+ BWindow *w = (BWindow *) window;
+
+ if (!w->LockLooper ())
+ gui_abort ("Failed to lock window looper setting alignment");
+#if 0 /* Haiku does not currently implement SetWindowAlignment. */
+ if (w->SetWindowAlignment (B_PIXEL_ALIGNMENT, -1, -1, align_width,
+ align_width, -1, -1, align_height,
+ align_height) != B_NO_ERROR)
+ gui_abort ("Invalid pixel alignment");
+#endif
+ w->UnlockLooper ();
+}
+
+void
+BWindow_send_behind (void *window, void *other_window)
+{
+ BWindow *w = (BWindow *) window;
+ BWindow *other = (BWindow *) other_window;
+
+ if (!w->LockLooper ())
+ gui_abort ("Failed to lock window in order to send it behind another");
+ w->SendBehind (other);
+ w->UnlockLooper ();
+}
+
+bool
+BWindow_is_active (void *window)
+{
+ BWindow *w = (BWindow *) window;
+ return w->IsActive ();
+}
+
+bool
+be_use_subpixel_antialiasing (void)
+{
+ bool current_subpixel_antialiasing;
+
+ if (get_subpixel_antialiasing (&current_subpixel_antialiasing) != B_OK)
+ return false;
+
+ return current_subpixel_antialiasing;
+}
+
+void
+BWindow_set_override_redirect (void *window, bool override_redirect_p)
+{
+ EmacsWindow *w = (EmacsWindow *) window;
+
+ if (w->LockLooper ())
+ {
+ if (override_redirect_p && !w->override_redirect_p)
+ {
+ w->override_redirect_p = true;
+ w->pre_override_redirect_look = w->Look ();
+ w->RecomputeFeel ();
+ w->SetLook (B_NO_BORDER_WINDOW_LOOK);
+ w->pre_override_redirect_workspaces = w->Workspaces ();
+ w->SetWorkspaces (B_ALL_WORKSPACES);
+ }
+ else if (w->override_redirect_p)
+ {
+ w->override_redirect_p = false;
+ w->SetLook (w->pre_override_redirect_look);
+ w->RecomputeFeel ();
+ w->SetWorkspaces (w->pre_override_redirect_workspaces);
+ }
+
+ w->UnlockLooper ();
+ }
+}
+
+/* Find a resource by the name NAME inside the settings file. The
+ string returned is in UTF-8 encoding, and will stay allocated as
+ long as the BApplication (a.k.a display) is alive. */
+const char *
+be_find_setting (const char *name)
+{
+ Emacs *app = (Emacs *) be_app;
+ const char *value;
+
+ /* Note that this is thread-safe since the constructor of `Emacs'
+ runs in the main thread. */
+ if (!app->settings_valid_p)
+ return NULL;
+
+ if (app->settings.FindString (name, 0, &value) != B_OK)
+ return NULL;
+
+ return value;
+}
+
+void
+BMessage_delete (void *message)
+{
+ delete (BMessage *) message;
+}
+
+static int32
+be_drag_message_thread_entry (void *thread_data)
+{
+ BMessenger *messenger;
+ BMessage reply;
+
+ messenger = (BMessenger *) thread_data;
+ messenger->SendMessage (WAIT_FOR_RELEASE, &reply);
+
+ return 0;
+}
+
+bool
+be_drag_message (void *view, void *message, bool allow_same_view,
+ void (*block_input_function) (void),
+ void (*unblock_input_function) (void),
+ void (*process_pending_signals_function) (void),
+ bool (*should_quit_function) (void))
+{
+ EmacsView *vw = (EmacsView *) view;
+ EmacsWindow *window = (EmacsWindow *) vw->Window ();
+ BMessage *msg = (BMessage *) message;
+ BMessage wait_for_release;
+ BMessenger messenger (vw);
+ BMessage cancel_message (CANCEL_DROP);
+ struct object_wait_info infos[2];
+ ssize_t stat;
+ thread_id window_thread;
+
+ block_input_function ();
+
+ if (!allow_same_view)
+ window_thread = window->Looper ()->Thread ();
+
+ if (!allow_same_view
+ && (msg->ReplaceInt64 ("emacs:thread_id", window_thread)
+ == B_NAME_NOT_FOUND))
+ msg->AddInt64 ("emacs:thread_id", window_thread);
+
+ if (!vw->LockLooper ())
+ gui_abort ("Failed to lock view looper for drag");
+
+ vw->DragMessage (msg, BRect (0, 0, 0, 0));
+ vw->UnlockLooper ();
+
+ infos[0].object = port_application_to_emacs;
+ infos[0].type = B_OBJECT_TYPE_PORT;
+ infos[0].events = B_EVENT_READ;
+
+ infos[1].object = spawn_thread (be_drag_message_thread_entry,
+ "Drag waiter thread",
+ B_DEFAULT_MEDIA_PRIORITY,
+ (void *) &messenger);
+ infos[1].type = B_OBJECT_TYPE_THREAD;
+ infos[1].events = B_EVENT_INVALID;
+ unblock_input_function ();
+
+ if (infos[1].object < B_OK)
+ return false;
+
+ block_input_function ();
+ resume_thread (infos[1].object);
+ unblock_input_function ();
+
+ drag_and_drop_in_progress = true;
+
+ while (true)
+ {
+ block_input_function ();
+ stat = wait_for_objects ((struct object_wait_info *) &infos, 2);
+ unblock_input_function ();
+
+ if (stat == B_INTERRUPTED || stat == B_TIMED_OUT
+ || stat == B_WOULD_BLOCK)
+ continue;
+
+ if (stat < B_OK)
+ gui_abort ("Failed to wait for drag");
+
+ if (infos[0].events & B_EVENT_READ)
+ process_pending_signals_function ();
+
+ if (should_quit_function ())
+ {
+ /* Do the best we can to prevent something from being
+ dropped, since Haiku doesn't provide a way to actually
+ cancel drag-and-drop. */
+ if (vw->LockLooper ())
+ {
+ vw->DragMessage (&cancel_message, BRect (0, 0, 0, 0));
+ vw->UnlockLooper ();
+ }
+
+ messenger.SendMessage (CANCEL_DROP);
+ drag_and_drop_in_progress = false;
+ return true;
+ }
+
+ if (infos[1].events & B_EVENT_INVALID)
+ {
+ drag_and_drop_in_progress = false;
+ return false;
+ }
+
+ infos[0].events = B_EVENT_READ;
+ infos[1].events = B_EVENT_INVALID;
+ }
+}
+
+bool
+be_drag_and_drop_in_progress (void)
+{
+ return drag_and_drop_in_progress;
+}
+
+/* Replay the menu bar click event EVENT. Return whether or not the
+ menu bar actually opened. */
+bool
+be_replay_menu_bar_event (void *menu_bar,
+ struct haiku_menu_bar_click_event *event)
+{
+ BMenuBar *m = (BMenuBar *) menu_bar;
+ BMessenger messenger (m);
+ BMessage reply, msg (REPLAY_MENU_BAR);
+
+ msg.AddPoint ("emacs:point", BPoint (event->x, event->y));
+ messenger.SendMessage (&msg, &reply);
+ return reply.what == BE_MENU_BAR_OPEN;
+}
+
+void
+BWindow_set_z_group (void *window, enum haiku_z_group z_group)
+{
+ EmacsWindow *w = (EmacsWindow *) window;
+
+ if (w->LockLooper ())
+ {
+ if (w->z_group != z_group)
+ {
+ w->z_group = z_group;
+ w->RecomputeFeel ();
+
+ if (w->z_group == Z_GROUP_BELOW)
+ w->SetFlags (w->Flags () | B_AVOID_FRONT);
+ else
+ w->SetFlags (w->Flags () & ~B_AVOID_FRONT);
+ }
+
+ w->UnlockLooper ();
+ }
+}
+
+int
+be_get_ui_color (const char *name, uint32_t *color)
+{
+ color_which which;
+ rgb_color rgb;
+
+ which = which_ui_color (name);
+
+ if (which == B_NO_COLOR)
+ return 1;
+
+ rgb = ui_color (which);
+ *color = (rgb.blue | rgb.green << 8
+ | rgb.red << 16 | 255 << 24);
+
+ return 0;
+}
+
+bool
+be_select_font (void (*process_pending_signals_function) (void),
+ bool (*should_quit_function) (void),
+ haiku_font_family_or_style *family,
+ haiku_font_family_or_style *style,
+ int *size, bool allow_monospace_only,
+ int initial_family, int initial_style,
+ int initial_size, bool initial_antialias,
+ bool *disable_antialias)
+{
+ EmacsFontSelectionDialog *dialog;
+ struct font_selection_dialog_message msg;
+ uint32 flags;
+ font_family family_buffer;
+ font_style style_buffer;
+
+ dialog = new EmacsFontSelectionDialog (allow_monospace_only,
+ initial_family, initial_style,
+ initial_size, initial_antialias);
+ dialog->CenterOnScreen ();
+
+ if (dialog->InitCheck () < B_OK)
+ {
+ dialog->Quit ();
+ return false;
+ }
+
+ dialog->Show ();
+ dialog->WaitForChoice (&msg, process_pending_signals_function,
+ should_quit_function);
+
+ if (!dialog->LockLooper ())
+ gui_abort ("Failed to lock font selection dialog looper");
+ dialog->Quit ();
+
+ if (msg.cancel)
+ return false;
+
+ if (get_font_family (msg.family_idx,
+ &family_buffer, &flags) != B_OK
+ || get_font_style (family_buffer, msg.style_idx,
+ &style_buffer, &flags) != B_OK)
+ return false;
+
+ memcpy (family, family_buffer, sizeof *family);
+ memcpy (style, style_buffer, sizeof *style);
+ *size = msg.size_specified ? msg.size : -1;
+ *disable_antialias = msg.disable_antialias;
+
+ return true;
+}
+
+void
+BWindow_set_sticky (void *window, bool sticky)
+{
+ BWindow *w = (BWindow *) window;
+
+ if (w->LockLooper ())
+ {
+ w->SetFlags (sticky ? (w->Flags ()
+ | B_SAME_POSITION_IN_ALL_WORKSPACES)
+ : w->Flags () & ~B_SAME_POSITION_IN_ALL_WORKSPACES);
+
+ w->UnlockLooper ();
+ }
+}
+
+status_t
+be_roster_launch (const char *type, const char *file, char **cargs,
+ ptrdiff_t nargs, void *message, team_id *team_id)
+{
+ BEntry entry;
+ entry_ref ref;
+
+ if (type)
+ {
+ if (message)
+ return be_roster->Launch (type, (BMessage *) message,
+ team_id);
+
+ return be_roster->Launch (type, (nargs > INT_MAX
+ ? INT_MAX : nargs),
+ cargs, team_id);
+ }
+
+ if (entry.SetTo (file) != B_OK)
+ return B_ERROR;
+
+ if (entry.GetRef (&ref) != B_OK)
+ return B_ERROR;
+
+ if (message)
+ return be_roster->Launch (&ref, (BMessage *) message,
+ team_id);
+
+ return be_roster->Launch (&ref, (nargs > INT_MAX
+ ? INT_MAX : nargs),
+ cargs, team_id);
+}
+
+void *
+be_create_pixmap_cursor (void *bitmap, int x, int y)
+{
+ BBitmap *bm;
+ BCursor *cursor;
+
+ bm = (BBitmap *) bitmap;
+ cursor = new BCursor (bm, BPoint (x, y));
+
+ if (cursor->InitCheck () != B_OK)
+ {
+ delete cursor;
+ return NULL;
+ }
+
+ return cursor;
+}
+
+void
+be_get_window_decorator_dimensions (void *window, int *left, int *top,
+ int *right, int *bottom)
+{
+ BWindow *wnd;
+ BRect frame, window_frame;
+
+ wnd = (BWindow *) window;
+
+ if (!wnd->LockLooper ())
+ gui_abort ("Failed to lock window looper frame");
+
+ frame = wnd->DecoratorFrame ();
+ window_frame = wnd->Frame ();
+
+ if (left)
+ *left = window_frame.left - frame.left;
+
+ if (top)
+ *top = window_frame.top - frame.top;
+
+ if (right)
+ *right = frame.right - window_frame.right;
+
+ if (bottom)
+ *bottom = frame.bottom - window_frame.bottom;
+
+ wnd->UnlockLooper ();
+}
+
+void
+be_get_window_decorator_frame (void *window, int *left, int *top,
+ int *width, int *height)
+{
+ BWindow *wnd;
+ BRect frame;
+
+ wnd = (BWindow *) window;
+
+ if (!wnd->LockLooper ())
+ gui_abort ("Failed to lock window looper frame");
+
+ frame = wnd->DecoratorFrame ();
+
+ *left = frame.left;
+ *top = frame.top;
+ *width = BE_RECT_WIDTH (frame);
+ *height = BE_RECT_HEIGHT (frame);
+
+ wnd->UnlockLooper ();
+}
+
+/* Request that a MOVE_EVENT be sent for WINDOW. This is so that
+ frame offsets can be updated after a frame parameter affecting
+ decorators changes. Sending an event instead of updating the
+ offsets directly avoids race conditions where events with older
+ information are received after the update happens. */
+void
+be_send_move_frame_event (void *window)
+{
+ BWindow *wnd = (BWindow *) window;
+ BMessenger msg (wnd);
+
+ msg.SendMessage (SEND_MOVE_FRAME_EVENT);
+}
+
+void
+be_lock_window (void *window)
+{
+ BWindow *wnd = (BWindow *) window;
+
+ if (!wnd->LockLooper ())
+ gui_abort ("Failed to lock window looper");
+}
+
+void
+be_unlock_window (void *window)
+{
+ BWindow *wnd = (BWindow *) window;
+
+ wnd->UnlockLooper ();
+}
+
+void
+be_set_window_fullscreen_mode (void *window, enum haiku_fullscreen_mode mode)
+{
+ EmacsWindow *w = (EmacsWindow *) window;
+
+ if (!w->LockLooper ())
+ gui_abort ("Failed to lock window to set fullscreen mode");
+
+ w->SetFullscreen (mode);
+ w->UnlockLooper ();
+}
+
+bool
+be_get_explicit_workarea (int *x, int *y, int *width, int *height)
+{
+ BDeskbar deskbar;
+ BRect zoom;
+ deskbar_location location;
+
+ location = deskbar.Location ();
+
+ if (location != B_DESKBAR_TOP
+ && location != B_DESKBAR_BOTTOM)
+ return false;
+
+ zoom = get_zoom_rect (NULL);
+
+ *x = zoom.left;
+ *y = zoom.top;
+ *width = BE_RECT_WIDTH (zoom);
+ *height = BE_RECT_HEIGHT (zoom);
+
+ return true;
+}
+
+/* Clear the grab view. This has to be called manually from some
+ places, since we don't get B_MOUSE_UP messages after a popup menu
+ is run. */
+
+void
+be_clear_grab_view (void)
+{
+ if (grab_view_locker.Lock ())
+ {
+ grab_view = NULL;
+ grab_view_locker.Unlock ();
+ }
+}
+
+void
+be_set_use_frame_synchronization (void *view, bool sync)
+{
+ EmacsView *vw;
+
+ vw = (EmacsView *) view;
+ vw->SetFrameSynchronization (sync);
+}
+
+status_t
+be_write_node_message (const char *path, const char *name, void *message)
+{
+ BNode node (path);
+ status_t rc;
+ ssize_t flat, result;
+ char *buffer;
+ BMessage *msg;
+
+ rc = node.InitCheck ();
+ msg = (BMessage *) message;
+
+ if (rc < B_OK)
+ return rc;
+
+ flat = msg->FlattenedSize ();
+ if (flat < B_OK)
+ return flat;
+
+ buffer = new (std::nothrow) char[flat];
+ if (!buffer)
+ return B_NO_MEMORY;
+
+ rc = msg->Flatten (buffer, flat);
+ if (rc < B_OK)
+ {
+ delete[] buffer;
+ return rc;
+ }
+
+ result = node.WriteAttr (name, B_MIME_TYPE, 0,
+ buffer, flat);
+ delete[] buffer;
+
+ if (result < B_OK)
+ return result;
+
+ if (result != flat)
+ return B_ERROR;
+
+ return B_OK;
+}
+
+void
+be_send_message (const char *app_id, void *message)
+{
+ BMessenger messenger (app_id);
+
+ messenger.SendMessage ((BMessage *) message);
+}