/* Haiku window system support. Hey, Emacs, this is -*- C++ -*- Copyright (C) 2021 Free Software Foundation, Inc. This file is part of GNU Emacs. GNU Emacs is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. GNU Emacs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Emacs. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "haiku_support.h" #define SCROLL_BAR_UPDATE 3000 static color_space dpy_color_space = B_NO_COLOR_SPACE; static key_map *key_map = NULL; static char *key_chars = NULL; extern "C" { extern void emacs_abort (void); /* Also defined in haikuterm.h. */ extern void be_app_quit (void); } 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) 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); } class Emacs : public BApplication { public: Emacs () : BApplication ("application/x-vnd.GNU-emacs") { } bool QuitRequested (void) { BApplication::QuitRequested (); return 0; } }; class EmacsWindow : public BDirectWindow { public: struct child_frame { struct child_frame *next; int xoff, yoff; EmacsWindow *window; } *subset_windows = NULL; EmacsWindow *parent = NULL; BRect pre_fullscreen_rect; BRect pre_zoom_rect; int x_before_zoom = INT_MIN; int y_before_zoom = INT_MIN; int fullscreen_p = 0; int zoomed_p = 0; EmacsWindow () : BDirectWindow (BRect (0, 0, 0, 0), "", B_TITLED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, B_NO_SERVER_SIDE_WINDOW_MODIFIERS) { } ~EmacsWindow () { struct child_frame *next; for (struct child_frame *f = subset_windows; f; f = next) { f->window->Unparent (); next = f->next; delete f; } if (this->parent) UnparentAndUnlink (); } void UpwardsSubset (EmacsWindow *w) { for (; w; w = w->parent) AddToSubset (w); } void UpwardsSubsetChildren (EmacsWindow *w) { UpwardsSubset (w); for (struct child_frame *f = subset_windows; f; f = f->next) f->window->UpwardsSubsetChildren (w); } void UpwardsUnSubset (EmacsWindow *w) { for (; w; w = w->parent) RemoveFromSubset (w); } void UpwardsUnSubsetChildren (EmacsWindow *w) { UpwardsUnSubset (w); for (struct child_frame *f = subset_windows; f; f = f->next) f->window->UpwardsUnSubsetChildren (w); } void Unparent (void) { this->SetFeel (B_NORMAL_WINDOW_FEEL); UpwardsUnSubsetChildren (parent); this->RemoveFromSubset (this); this->parent = NULL; if (fullscreen_p) { fullscreen_p = 0; MakeFullscreen (1); } } void UnparentAndUnlink (void) { this->parent->UnlinkChild (this); this->Unparent (); } void UnlinkChild (EmacsWindow *window) { struct child_frame *last = NULL; struct child_frame *tem = subset_windows; for (; tem; last = tem, tem = tem->next) { if (tem->window == window) { if (last) last->next = tem->next; if (tem == subset_windows) subset_windows = NULL; delete tem; return; } } emacs_abort (); } void ParentTo (EmacsWindow *window) { if (this->parent) UnparentAndUnlink (); this->parent = window; this->SetFeel (B_FLOATING_SUBSET_WINDOW_FEEL); this->AddToSubset (this); if (!IsHidden () && this->parent) UpwardsSubsetChildren (parent); if (fullscreen_p) { fullscreen_p = 0; MakeFullscreen (1); } this->Sync (); window->LinkChild (this); } void LinkChild (EmacsWindow *window) { struct child_frame *f = new struct child_frame; for (struct child_frame *f = subset_windows; f; f = f->next) { if (window == f->window) emacs_abort (); } f->window = window; f->next = subset_windows; f->xoff = -1; f->yoff = -1; subset_windows = f; } void DoMove (struct child_frame *f) { BRect frame = this->Frame (); f->window->MoveTo (frame.left + f->xoff, frame.top + f->yoff); this->Sync (); } void DoUpdateWorkspace (struct child_frame *f) { f->window->SetWorkspaces (this->Workspaces ()); } void MoveChild (EmacsWindow *window, int xoff, int yoff, int weak_p) { for (struct child_frame *f = subset_windows; f; f = f->next) { if (window == f->window) { f->xoff = xoff; f->yoff = yoff; if (!weak_p) DoMove (f); return; } } emacs_abort (); } void WindowActivated (bool activated) { struct haiku_activation_event rq; rq.window = this; rq.activated_p = activated; haiku_write (ACTIVATION, &rq); } void MessageReceived (BMessage *msg) { int32 old_what = 0; if (msg->GetPointer ("menuptr")) { struct haiku_menu_bar_select_event rq; rq.window = this; rq.ptr = (void *) msg->GetPointer ("menuptr"); haiku_write (MENU_BAR_SELECT_EVENT, &rq); } else if (msg->what == 'FPSE' || ((msg->FindInt32 ("old_what", &old_what) == B_OK && old_what == 'FPSE'))) { struct haiku_file_panel_event rq; BEntry entry; BPath path; entry_ref ref; rq.ptr = NULL; if (msg->FindRef ("refs", &ref) == B_OK && entry.SetTo (&ref, 0) == B_OK && entry.GetPath (&path) == B_OK) { const char *str_path = path.Path (); if (str_path) rq.ptr = strdup (str_path); } if (msg->FindRef ("directory", &ref), entry.SetTo (&ref, 0) == B_OK && entry.GetPath (&path) == B_OK) { const char *name = msg->GetString ("name"); const char *str_path = path.Path (); if (name) { char str_buf[std::strlen (str_path) + std::strlen (name) + 2]; snprintf ((char *) &str_buf, std::strlen (str_path) + std::strlen (name) + 2, "%s/%s", str_path, name); rq.ptr = strdup (str_buf); } } haiku_write (FILE_PANEL_EVENT, &rq); } BDirectWindow::MessageReceived (msg); } void DispatchMessage (BMessage *msg, BHandler *handler) { if (msg->what == B_KEY_DOWN || msg->what == B_KEY_UP) { struct haiku_key_event rq; rq.window = this; int32_t code = msg->GetInt32 ("raw_char", 0); rq.modifiers = 0; uint32_t mods = modifiers (); if (mods & B_SHIFT_KEY) rq.modifiers |= HAIKU_MODIFIER_SHIFT; if (mods & B_CONTROL_KEY) rq.modifiers |= HAIKU_MODIFIER_CTRL; if (mods & B_COMMAND_KEY) rq.modifiers |= HAIKU_MODIFIER_ALT; rq.mb_char = code; rq.kc = msg->GetInt32 ("key", -1); rq.unraw_mb_char = BUnicodeChar::FromUTF8 (msg->GetString ("bytes")); if ((mods & B_SHIFT_KEY) && rq.kc >= 0) map_shift (rq.kc, &rq.unraw_mb_char); 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; 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 BDirectWindow::DispatchMessage (msg, handler); } void MenusBeginning () { struct haiku_menu_bar_state_event rq; rq.window = this; haiku_write (MENU_BAR_OPEN, &rq); } void MenusEnded () { struct haiku_menu_bar_state_event rq; rq.window = this; haiku_write (MENU_BAR_CLOSE, &rq); } void FrameResized (float newWidth, float newHeight) { struct haiku_resize_event rq; rq.window = this; rq.px_heightf = newHeight; rq.px_widthf = newWidth; haiku_write (FRAME_RESIZED, &rq); BDirectWindow::FrameResized (newWidth, newHeight); } void FrameMoved (BPoint newPosition) { struct haiku_move_event rq; rq.window = this; rq.x = std::lrint (newPosition.x); rq.y = std::lrint (newPosition.y); haiku_write (MOVE_EVENT, &rq); for (struct child_frame *f = subset_windows; f; f = f->next) DoMove (f); BDirectWindow::FrameMoved (newPosition); } void WorkspacesChanged (uint32_t old, uint32_t n) { for (struct child_frame *f = subset_windows; f; f = f->next) DoUpdateWorkspace (f); } void EmacsMoveTo (int x, int y) { if (!this->parent) this->MoveTo (x, y); else this->parent->MoveChild (this, x, y, 0); } bool QuitRequested () { struct haiku_quit_requested_event rq; rq.window = this; haiku_write (QUIT_REQUESTED, &rq); return false; } void Minimize (bool minimized_p) { BDirectWindow::Minimize (minimized_p); struct haiku_iconification_event rq; rq.window = this; rq.iconified_p = !parent && minimized_p; haiku_write (ICONIFICATION, &rq); } void EmacsHide (void) { if (this->IsHidden ()) return; Hide (); if (this->parent) UpwardsUnSubsetChildren (this->parent); } void EmacsShow (void) { if (!this->IsHidden ()) return; Show (); if (this->parent) UpwardsSubsetChildren (this->parent); } void Zoom (BPoint o, float w, float h) { struct haiku_zoom_event rq; rq.window = this; rq.x = o.x; rq.y = o.y; rq.width = w; rq.height = h; if (fullscreen_p) MakeFullscreen (0); if (o.x != x_before_zoom || o.y != y_before_zoom) { x_before_zoom = Frame ().left; y_before_zoom = Frame ().top; pre_zoom_rect = Frame (); zoomed_p = 1; haiku_write (ZOOM_EVENT, &rq); } else { zoomed_p = 0; x_before_zoom = y_before_zoom = INT_MIN; } BDirectWindow::Zoom (o, w, h); } void UnZoom (void) { if (!zoomed_p) return; zoomed_p = 0; EmacsMoveTo (pre_zoom_rect.left, pre_zoom_rect.top); ResizeTo (pre_zoom_rect.Width (), pre_zoom_rect.Height ()); } void GetParentWidthHeight (int *width, int *height) { if (parent) { *width = parent->Frame ().Width (); *height = parent->Frame ().Height (); } else { BScreen s (this); *width = s.Frame ().Width (); *height = s.Frame ().Height (); } } void OffsetChildRect (BRect *r, EmacsWindow *c) { for (struct child_frame *f; f; f = f->next) if (f->window == c) { r->top -= f->yoff; r->bottom -= f->yoff; r->left -= f->xoff; r->right -= f->xoff; return; } emacs_abort (); } void MakeFullscreen (int make_fullscreen_p) { BScreen screen (this); if (make_fullscreen_p == fullscreen_p) return; fullscreen_p = make_fullscreen_p; uint32 flags = Flags (); if (fullscreen_p) { if (zoomed_p) UnZoom (); flags |= B_NOT_MOVABLE | B_NOT_ZOOMABLE; pre_fullscreen_rect = Frame (); if (parent) parent->OffsetChildRect (&pre_fullscreen_rect, this); int w, h; EmacsMoveTo (0, 0); GetParentWidthHeight (&w, &h); ResizeTo (w, h); } else { flags &= ~(B_NOT_MOVABLE | B_NOT_ZOOMABLE); EmacsMoveTo (pre_fullscreen_rect.left, pre_fullscreen_rect.top); ResizeTo (pre_fullscreen_rect.Width (), pre_fullscreen_rect.Height ()); } SetFlags (flags); } }; class EmacsMenuBar : public BMenuBar { public: EmacsMenuBar () : BMenuBar (BRect (0, 0, 0, 0), NULL) { } void FrameResized (float newWidth, float newHeight) { struct haiku_menu_bar_resize_event rq; rq.window = this->Window (); rq.height = std::lrint (newHeight); rq.width = std::lrint (newWidth); haiku_write (MENU_BAR_RESIZE, &rq); BMenuBar::FrameResized (newWidth, newHeight); } }; class EmacsView : public BView { public: uint32_t visible_bell_color = 0; uint32_t previous_buttons = 0; int looper_locked_count = 0; BRegion sb_region; BView *offscreen_draw_view = NULL; BBitmap *offscreen_draw_bitmap_1 = NULL; BBitmap *offscreen_blit_bitmap_1 = NULL; BScreen *screen = NULL; color_space cspace; EmacsView () : BView (BRect (0, 0, 0, 0), NULL, B_FOLLOW_NONE, B_WILL_DRAW) { } ~EmacsView () { TearDownDoubleBuffering (); delete screen; } void AttachedToWindow (void) { screen = new BScreen (Window ()); cspace = screen->ColorSpace (); } void TearDownDoubleBuffering (void) { if (offscreen_draw_view) { if (!looper_locked_count) if (!offscreen_draw_view->LockLooper ()) emacs_abort (); offscreen_draw_view->RemoveSelf (); delete offscreen_draw_view; offscreen_draw_view = NULL; delete offscreen_draw_bitmap_1; offscreen_draw_bitmap_1 = NULL; delete offscreen_blit_bitmap_1; offscreen_blit_bitmap_1 = NULL; } } void AfterResize (float newWidth, float newHeight) { if (offscreen_draw_view) { if (!offscreen_draw_view->LockLooper ()) emacs_abort (); offscreen_draw_view->RemoveSelf (); delete offscreen_blit_bitmap_1; offscreen_blit_bitmap_1 = new (std::nothrow) BBitmap (Frame (), cspace, 1); if (!offscreen_blit_bitmap_1) emacs_abort (); delete offscreen_draw_bitmap_1; offscreen_draw_bitmap_1 = new (std::nothrow) BBitmap (Frame (), cspace, 1); if (!offscreen_draw_bitmap_1) emacs_abort (); offscreen_draw_view->MoveTo (Frame ().left, Frame ().top); offscreen_draw_view->ResizeTo (Frame ().Width (), Frame ().Height ()); offscreen_draw_bitmap_1->AddChild (offscreen_draw_view); if (looper_locked_count) { offscreen_blit_bitmap_1->Lock (); offscreen_draw_bitmap_1->Lock (); } } } void Draw (BRect expose_bounds) { struct haiku_expose_event rq; if (visible_bell_color > 0) { PushState (); BView_SetHighColorForVisibleBell (this, visible_bell_color); visible_bell_color = 0; 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); } else { DrawBitmap (offscreen_blit_bitmap_1, expose_bounds, expose_bounds); } } void DoVisibleBell (uint32_t color) { if (LockLooper ()) emacs_abort (); visible_bell_color = color | (255 << 24); PushState (); BView_SetHighColorForVisibleBell (this, visible_bell_color); FillRect (Frame ()); PopState (); Invalidate (); UnlockLooper (); } void FlipBuffers (void) { if (!LockLooper ()) emacs_abort (); if (!offscreen_draw_view) emacs_abort (); offscreen_draw_view->RemoveSelf (); BBitmap *b = offscreen_blit_bitmap_1; offscreen_blit_bitmap_1 = offscreen_draw_bitmap_1; offscreen_draw_bitmap_1 = b; b->ImportBits (offscreen_blit_bitmap_1); offscreen_draw_bitmap_1->AddChild (offscreen_draw_view); Invalidate (); UnlockLooper (); return; } void SetUpDoubleBuffering (void) { if (!LockLooper ()) emacs_abort (); if (offscreen_draw_view) emacs_abort (); offscreen_draw_view = new BView (Frame (), NULL, B_FOLLOW_NONE, B_WILL_DRAW); offscreen_blit_bitmap_1 = new BBitmap (Frame (), cspace, 1); offscreen_draw_bitmap_1 = new BBitmap (Frame (), cspace, 1); offscreen_draw_bitmap_1->AddChild (offscreen_draw_view); if (looper_locked_count) { offscreen_blit_bitmap_1->Lock (); offscreen_draw_bitmap_1->Lock (); } UnlockLooper (); Invalidate (); } void MouseMoved (BPoint point, uint32 transit, const BMessage *msg) { struct haiku_mouse_motion_event rq; rq.just_exited_p = transit == B_EXITED_VIEW; rq.x = point.x; rq.y = point.y; rq.be_code = transit; rq.window = this->Window (); haiku_write (MOUSE_MOTION, &rq); } void MouseDown (BPoint point) { struct haiku_button_event rq; uint32 buttons; this->GetMouse (&point, &buttons, false); rq.window = this->Window (); rq.btn_no = 0; if (!(previous_buttons & B_PRIMARY_MOUSE_BUTTON) && (buttons & B_PRIMARY_MOUSE_BUTTON)) rq.btn_no = 0; else if (!(previous_buttons & B_SECONDARY_MOUSE_BUTTON) && (buttons & B_SECONDARY_MOUSE_BUTTON)) rq.btn_no = 2; else if (!(previous_buttons & B_TERTIARY_MOUSE_BUTTON) && (buttons & B_TERTIARY_MOUSE_BUTTON)) rq.btn_no = 1; previous_buttons = buttons; rq.x = point.x; rq.y = point.y; uint32_t mods = modifiers (); rq.modifiers = 0; if (mods & B_SHIFT_KEY) rq.modifiers |= HAIKU_MODIFIER_SHIFT; if (mods & B_CONTROL_KEY) rq.modifiers |= HAIKU_MODIFIER_CTRL; if (mods & B_COMMAND_KEY) rq.modifiers |= HAIKU_MODIFIER_ALT; haiku_write (BUTTON_DOWN, &rq); } void MouseUp (BPoint point) { struct haiku_button_event rq; uint32 buttons; this->GetMouse (&point, &buttons, false); rq.window = this->Window (); rq.btn_no = 0; if ((previous_buttons & B_PRIMARY_MOUSE_BUTTON) && !(buttons & B_PRIMARY_MOUSE_BUTTON)) rq.btn_no = 0; else if ((previous_buttons & B_SECONDARY_MOUSE_BUTTON) && !(buttons & B_SECONDARY_MOUSE_BUTTON)) rq.btn_no = 2; else if ((previous_buttons & B_TERTIARY_MOUSE_BUTTON) && !(buttons & B_TERTIARY_MOUSE_BUTTON)) rq.btn_no = 1; previous_buttons = buttons; rq.x = point.x; rq.y = point.y; uint32_t mods = modifiers (); rq.modifiers = 0; if (mods & B_SHIFT_KEY) rq.modifiers |= HAIKU_MODIFIER_SHIFT; if (mods & B_CONTROL_KEY) rq.modifiers |= HAIKU_MODIFIER_CTRL; if (mods & B_COMMAND_KEY) rq.modifiers |= HAIKU_MODIFIER_ALT; haiku_write (BUTTON_UP, &rq); } }; class EmacsScrollBar : public BScrollBar { public: void *scroll_bar; EmacsScrollBar (int x, int y, int x1, int y1, bool horizontal_p) : BScrollBar (BRect (x, y, x1, y1), NULL, NULL, 0, 0, horizontal_p ? B_HORIZONTAL : B_VERTICAL) { BView *vw = (BView *) this; vw->SetResizingMode (B_FOLLOW_NONE); } void MessageReceived (BMessage *msg) { if (msg->what == SCROLL_BAR_UPDATE) { this->SetRange (0, msg->GetInt32 ("emacs:range", 0)); this->SetValue (msg->GetInt32 ("emacs:units", 0)); } BScrollBar::MessageReceived (msg); } void ValueChanged (float new_value) { struct haiku_scroll_bar_value_event rq; rq.scroll_bar = scroll_bar; rq.position = new_value; haiku_write (SCROLL_BAR_VALUE_EVENT, &rq); } void MouseDown (BPoint pt) { struct haiku_scroll_bar_drag_event rq; rq.dragging_p = 1; rq.scroll_bar = scroll_bar; haiku_write (SCROLL_BAR_DRAG_EVENT, &rq); BScrollBar::MouseDown (pt); } void MouseUp (BPoint pt) { struct haiku_scroll_bar_drag_event rq; rq.dragging_p = 0; rq.scroll_bar = scroll_bar; haiku_write (SCROLL_BAR_DRAG_EVENT, &rq); BScrollBar::MouseUp (pt); } }; class EmacsMenuItem : public BMenuItem { public: int menu_bar_id = -1; void *wind_ptr = NULL; char *key = NULL; EmacsMenuItem (const char *ky, const char *str, BMessage *message = NULL) : BMenuItem (str, message) { if (ky) { key = strdup (ky); if (!key) { perror ("strdup"); emacs_abort (); } } } ~EmacsMenuItem () { if (key) free (key); } void DrawContent (void) { BMenu *menu = Menu (); BMenuItem::DrawContent (); if (key) { BRect r = Frame (); int w = menu->StringWidth (key); menu->MovePenTo (r.Width () - w - 4, menu->PenLocation ().y); menu->DrawString (key); } } void GetContentSize (float *w, float *h) { BMenuItem::GetContentSize (w, h); if (Menu () && key) *w += 4 + Menu ()->StringWidth (key); } void Highlight (bool highlight_p) { struct haiku_menu_bar_help_event rq; if (menu_bar_id >= 0) { rq.window = wind_ptr; rq.mb_idx = highlight_p ? menu_bar_id : -1; haiku_write (MENU_BAR_HELP_EVENT, &rq); } BMenuItem::Highlight (highlight_p); } }; static int32 start_running_application (void *data) { haiku_io_init_in_app_thread (); ((Emacs *) data)->Lock (); ((Emacs *) data)->Run (); ((Emacs *) data)->Unlock (); return 0; } static uint8_t reverse_fringe_byte (uint8_t b) { b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; b = (b & 0xCC) >> 2 | (b & 0x33) << 2; b = (b & 0xAA) >> 1 | (b & 0x55) << 1; return b; } void * BBitmap_data (void *bitmap) { return ((BBitmap *) bitmap)->Bits (); } int BBitmap_convert (void *_bitmap, void **new_bitmap) { BBitmap *bitmap = (BBitmap *) _bitmap; if (bitmap->ColorSpace () == B_RGBA32) return -1; BRect bounds = bitmap->Bounds (); BBitmap *bmp = new (std::nothrow) BBitmap (bounds, B_RGBA32); if (!bmp) return 0; if (bmp->ImportBits (bitmap) != B_OK) { delete bmp; return 0; } *(BBitmap **) new_bitmap = bmp; return 1; } void BBitmap_free (void *bitmap) { delete (BBitmap *) bitmap; } void * BBitmap_new (int width, int height, int mono_p) { BBitmap *bn = new (std::nothrow) BBitmap (BRect (0, 0, width - 1, height - 1), mono_p ? B_GRAY1 : B_RGB32); return bn; } void BBitmap_dimensions (void *bitmap, int *left, int *top, int *right, int *bottom, int32_t *bytes_per_row, int *mono_p) { BRect rect = ((BBitmap *) bitmap)->Bounds (); *left = rect.left; *top = rect.top; *right = rect.right; *bottom = rect.bottom; *bytes_per_row = ((BBitmap *) bitmap)->BytesPerRow (); *mono_p = (((BBitmap *) bitmap)->ColorSpace () == B_GRAY1); } void * BApplication_setup (void) { if (be_app) return be_app; Emacs *app; app = new Emacs; if (spawn_thread (start_running_application, "Emacs app thread", B_DEFAULT_MEDIA_PRIORITY, app) < 0) { fprintf (stderr, "spawn_thread failed\n"); emacs_abort (); } return app; } void * BWindow_new (void *_view) { BWindow *window = new (std::nothrow) EmacsWindow; BView **v = (BView **) _view; if (!window) { *v = NULL; return window; } BView *vw = new (std::nothrow) EmacsView; if (!vw) { *v = NULL; window->Lock (); window->Quit (); return NULL; } window->AddChild (vw); *v = vw; return window; } void BWindow_quit (void *window) { ((BWindow *) window)->Lock (); ((BWindow *) window)->Quit (); } void BWindow_set_offset (void *window, int x, int y) { BWindow *wn = (BWindow *) window; EmacsWindow *w = dynamic_cast (wn); if (w) { if (!w->LockLooper ()) emacs_abort (); w->EmacsMoveTo (x, y); w->UnlockLooper (); } else wn->MoveTo (x, y); } void BWindow_iconify (void *window) { if (((BWindow *) window)->IsHidden ()) BWindow_set_visible (window, true); ((BWindow *) window)->Minimize (true); } void BWindow_set_visible (void *window, int visible_p) { EmacsWindow *win = (EmacsWindow *) window; if (visible_p) { if (win->IsMinimized ()) win->Minimize (false); win->EmacsShow (); } else if (!win->IsHidden ()) { if (win->IsMinimized ()) win->Minimize (false); win->EmacsHide (); } win->Sync (); } void BWindow_retitle (void *window, const char *title) { ((BWindow *) window)->SetTitle (title); } void BWindow_resize (void *window, int width, int height) { ((BWindow *) window)->ResizeTo (width, height); } void BWindow_activate (void *window) { ((BWindow *) window)->Activate (); } void BScreen_px_dim (int *width, int *height) { BScreen screen; BRect frame = screen.Frame (); *width = frame.right - frame.left; *height = frame.bottom - frame.top; } void BView_resize_to (void *view, int width, int height) { EmacsView *vw = (EmacsView *) view; if (!vw->LockLooper ()) emacs_abort (); vw->ResizeTo (width, height); vw->AfterResize (width, height); vw->UnlockLooper (); } void * BCursor_create_default (void) { return new BCursor (B_CURSOR_ID_SYSTEM_DEFAULT); } void * BCursor_from_id (enum haiku_cursor cursor) { return new BCursor ((enum BCursorID) cursor); } void * BCursor_create_i_beam (void) { return new BCursor (B_CURSOR_ID_I_BEAM); } void * BCursor_create_progress_cursor (void) { return new BCursor (B_CURSOR_ID_PROGRESS); } void * BCursor_create_grab (void) { return new BCursor (B_CURSOR_ID_GRAB); } void BCursor_delete (void *cursor) { delete (BCursor *) cursor; } void BView_set_view_cursor (void *view, void *cursor) { if (!((BView *) view)->LockLooper ()) emacs_abort (); ((BView *) view)->SetViewCursor ((BCursor *) cursor); ((BView *) view)->UnlockLooper (); } void BWindow_Flush (void *window) { ((BWindow *) window)->Flush (); } void BMapKey (uint32_t kc, int *non_ascii_p, unsigned *code) { *code = 0; switch (kc) { default: *non_ascii_p = 0; if (kc < 0xe && kc > 0x1) { *code = XK_F1 + kc - 2; *non_ascii_p = 1; } return; case 0x1e: *code = XK_BackSpace; break; case 0x32: case 0x5b: case 0x47: *code = XK_Return; break; case 0x61: *code = XK_Left; break; case 0x63: *code = XK_Right; break; case 0x57: *code = XK_Up; break; case 0x62: *code = XK_Down; break; case 0x64: *code = XK_Insert; break; case 0x65: *code = XK_Delete; break; case 0x37: *code = XK_Home; break; case 0x58: *code = XK_End; break; case 0x39: *code = XK_Page_Up; break; case 0x5a: *code = XK_Page_Down; break; case 0x1: *code = XK_Escape; break; case 0x68: *code = XK_Menu; break; } *non_ascii_p = 1; } void * BScrollBar_make_for_view (void *view, int horizontal_p, int x, int y, int x1, int y1, void *scroll_bar_ptr) { EmacsScrollBar *sb = new EmacsScrollBar (x, y, x1, y1, horizontal_p); sb->scroll_bar = scroll_bar_ptr; BView *vw = (BView *) view; BView *sv = (BView *) sb; if (!vw->LockLooper ()) emacs_abort (); vw->AddChild ((BView *) sb); sv->WindowActivated (vw->Window ()->IsActive ()); vw->UnlockLooper (); return sb; } void BScrollBar_delete (void *sb) { BView *view = (BView *) sb; BView *pr = view->Parent (); if (!pr->LockLooper ()) emacs_abort (); pr->RemoveChild (view); pr->UnlockLooper (); delete (EmacsScrollBar *) sb; } void BView_move_frame (void *view, int x, int y, int x1, int y1) { BView *vw = (BView *) view; if (!vw->LockLooper ()) emacs_abort (); vw->MoveTo (x, y); vw->ResizeTo (x1 - x, y1 - y); vw->UnlockLooper (); } void BView_scroll_bar_update (void *sb, int portion, int whole, int position) { BScrollBar *bar = (BScrollBar *) sb; BMessage msg = BMessage (SCROLL_BAR_UPDATE); BMessenger mr = BMessenger (bar); msg.AddInt32 ("emacs:range", whole); msg.AddInt32 ("emacs:units", position); mr.SendMessage (&msg); } int BScrollBar_default_size (int horizontal_p) { return horizontal_p ? B_H_SCROLL_BAR_HEIGHT : B_V_SCROLL_BAR_WIDTH; } void BView_hide (void *view) { BView *vw = (BView *) view; if (!vw->LockLooper ()) emacs_abort (); vw->Hide (); vw->UnlockLooper (); } void BView_show (void *view) { BView *vw = (BView *) view; if (!vw->LockLooper ()) emacs_abort (); vw->Show (); vw->UnlockLooper (); } void BView_invalidate (void *view) { BView *vw = (BView *) view; if (!vw->LockLooper ()) emacs_abort (); vw->Invalidate (); vw->UnlockLooper (); } void BView_draw_lock (void *view) { EmacsView *vw = (EmacsView *) view; if (vw->looper_locked_count) { vw->looper_locked_count++; return; } BView *v = (BView *) find_appropriate_view_for_draw (vw); if (v != vw) { vw->offscreen_blit_bitmap_1->Lock (); vw->offscreen_draw_bitmap_1->Lock (); } else v->LockLooper (); if (v != vw && !vw->LockLooper ()) emacs_abort (); vw->looper_locked_count++; } void BView_draw_unlock (void *view) { EmacsView *vw = (EmacsView *) view; if (--vw->looper_locked_count) return; BView *v = (BView *) find_appropriate_view_for_draw (view); if (v == vw) vw->UnlockLooper (); else { vw->offscreen_blit_bitmap_1->Unlock (); vw->offscreen_draw_bitmap_1->Unlock (); vw->UnlockLooper (); } } void BWindow_center_on_screen (void *window) { BWindow *w = (BWindow *) window; w->CenterOnScreen (); } void BView_mouse_down (void *view, int x, int y) { BView *vw = (BView *) view; if (vw->LockLooper ()) { vw->MouseDown (BPoint (x, y)); vw->UnlockLooper (); } } void BView_mouse_up (void *view, int x, int y) { BView *vw = (BView *) view; if (vw->LockLooper ()) { vw->MouseUp (BPoint (x, y)); vw->UnlockLooper (); } } void BView_mouse_moved (void *view, int x, int y, uint32_t transit) { BView *vw = (BView *) view; if (vw->LockLooper ()) { vw->MouseMoved (BPoint (x, y), transit, NULL); vw->UnlockLooper (); } } void BBitmap_import_mono_bits (void *bitmap, void *bits, int wd, int h) { BBitmap *bmp = (BBitmap *) bitmap; unsigned char *data = (unsigned char *) bmp->Bits (); unsigned short *bts = (unsigned short *) bits; for (int i = 0; i < (h * (wd / 8)); i++) { *((unsigned short *) data) = reverse_fringe_byte (~bts[i]); data += bmp->BytesPerRow (); } } void BView_publish_scroll_bar (void *view, int x, int y, int width, int height) { EmacsView *vw = (EmacsView *) view; if (vw->LockLooper ()) { vw->sb_region.Include (BRect (x, y, x - 1 + width, y - 1 + height)); vw->UnlockLooper (); } } void BView_forget_scroll_bar (void *view, int x, int y, int width, int height) { EmacsView *vw = (EmacsView *) view; if (vw->LockLooper ()) { vw->sb_region.Exclude (BRect (x, y, x - 1 + width, y - 1 + height)); vw->UnlockLooper (); } } int BFont_family_count (void) { return count_font_families (); } void BView_get_mouse (void *view, int *x, int *y) { BPoint l; BView *vw = (BView *) view; if (!vw->LockLooper ()) emacs_abort (); vw->GetMouse (&l, NULL, 1); vw->UnlockLooper (); *x = std::lrint (l.x); *y = std::lrint (l.y); } void BView_convert_to_screen (void *view, int *x, int *y) { BPoint l = BPoint (*x, *y); BView *vw = (BView *) view; if (!vw->LockLooper ()) emacs_abort (); vw->ConvertToScreen (&l); vw->UnlockLooper (); *x = std::lrint (l.x); *y = std::lrint (l.y); } void BView_convert_from_screen (void *view, int *x, int *y) { BPoint l = BPoint (*x, *y); BView *vw = (BView *) view; if (!vw->LockLooper ()) emacs_abort (); vw->ConvertFromScreen (&l); vw->UnlockLooper (); *x = std::lrint (l.x); *y = std::lrint (l.y); } void BWindow_change_decoration (void *window, int decorate_p) { BWindow *w = (BWindow *) window; if (!w->LockLooper ()) emacs_abort (); if (decorate_p) w->SetLook (B_TITLED_WINDOW_LOOK); else w->SetLook (B_NO_BORDER_WINDOW_LOOK); w->UnlockLooper (); } void BWindow_set_tooltip_decoration (void *window) { BWindow *w = (BWindow *) window; if (!w->LockLooper ()) emacs_abort (); w->SetLook (B_BORDERED_WINDOW_LOOK); w->SetFeel (B_FLOATING_APP_WINDOW_FEEL); w->UnlockLooper (); } void BWindow_set_avoid_focus (void *window, int avoid_focus_p) { BWindow *w = (BWindow *) window; if (!w->LockLooper ()) emacs_abort (); if (!avoid_focus_p) w->SetFlags (w->Flags () & ~B_AVOID_FOCUS); else w->SetFlags (w->Flags () | B_AVOID_FOCUS); w->Sync (); w->UnlockLooper (); } void BView_emacs_delete (void *view) { EmacsView *vw = (EmacsView *) view; if (!vw->LockLooper ()) emacs_abort (); vw->RemoveSelf (); delete vw; } uint32_t haiku_current_workspace (void) { return current_workspace (); } uint32_t BWindow_workspaces (void *window) { return ((BWindow *) window)->Workspaces (); } void * BPopUpMenu_new (const char *name) { BPopUpMenu *menu = new BPopUpMenu (name, false); return menu; } void BMenu_add_item (void *menu, const char *label, void *ptr, bool enabled_p, bool marked_p, bool mbar_p, void *mbw_ptr, const char *key) { BMenu *m = (BMenu *) menu; BMessage *msg; if (ptr) msg = new BMessage (); EmacsMenuItem *it = new EmacsMenuItem (key, label, ptr ? msg : NULL); it->SetTarget (m->Window ()); it->SetEnabled (enabled_p); it->SetMarked (marked_p); if (mbar_p) { it->menu_bar_id = (intptr_t) ptr; it->wind_ptr = mbw_ptr; } if (ptr) msg->AddPointer ("menuptr", ptr); m->AddItem (it); } void BMenu_add_separator (void *menu) { BMenu *m = (BMenu *) menu; m->AddSeparatorItem (); } void * BMenu_new_submenu (void *menu, const char *label, bool enabled_p) { BMenu *m = (BMenu *) menu; BMenu *mn = new BMenu (label, B_ITEMS_IN_COLUMN); mn->SetRadioMode (0); BMenuItem *i = new BMenuItem (mn); i->SetEnabled (enabled_p); m->AddItem (i); return mn; } void * BMenu_new_menu_bar_submenu (void *menu, const char *label) { BMenu *m = (BMenu *) menu; BMenu *mn = new BMenu (label, B_ITEMS_IN_COLUMN); mn->SetRadioMode (0); BMenuItem *i = new BMenuItem (mn); i->SetEnabled (1); m->AddItem (i); return mn; } void * BMenu_run (void *menu, int x, int y) { BPopUpMenu *mn = (BPopUpMenu *) menu; BMenuItem *it = mn->Go (BPoint (x, y)); if (it) { BMessage *mg = it->Message (); if (mg) return (void *) mg->GetPointer ("menuptr"); else return NULL; } return NULL; } void BPopUpMenu_delete (void *menu) { delete (BPopUpMenu *) menu; } void * BMenuBar_new (void *view) { BView *vw = (BView *) view; EmacsMenuBar *bar = new EmacsMenuBar (); if (!vw->LockLooper ()) emacs_abort (); vw->AddChild ((BView *) bar); vw->UnlockLooper (); return bar; } void BMenuBar_delete (void *menubar) { BView *vw = (BView *) menubar; BView *p = vw->Parent (); if (!p->LockLooper ()) emacs_abort (); vw->RemoveSelf (); p->UnlockLooper (); delete vw; } void BMenu_delete_all (void *menu) { BMenu *mn = (BMenu *) menu; mn->RemoveItems (0, mn->CountItems (), true); } void BMenu_delete_from (void *menu, int start, int count) { BMenu *mn = (BMenu *) menu; mn->RemoveItems (start, count, true); } int BMenu_count_items (void *menu) { return ((BMenu *) menu)->CountItems (); } void * BMenu_item_at (void *menu, int idx) { return ((BMenu *) menu)->ItemAt (idx); } void BMenu_item_set_label (void *item, const char *label) { ((BMenuItem *) item)->SetLabel (label); } void * BMenu_item_get_menu (void *item) { return ((BMenuItem *) item)->Submenu (); } void haiku_ring_bell (void) { beep (); } void * BAlert_new (const char *text, enum haiku_alert_type type) { return new BAlert (NULL, text, NULL, NULL, NULL, B_WIDTH_AS_USUAL, (enum alert_type) type); } void * BAlert_add_button (void *alert, const char *text) { BAlert *al = (BAlert *) alert; al->AddButton (text); return al->ButtonAt (al->CountButtons () - 1); } int32_t BAlert_go (void *alert) { return ((BAlert *) alert)->Go (); } void BButton_set_enabled (void *button, int enabled_p) { ((BButton *) button)->SetEnabled (enabled_p); } void BView_set_tooltip (void *view, const char *tooltip) { ((BView *) view)->SetToolTip (tooltip); } void BAlert_delete (void *alert) { delete (BAlert *) alert; } void BScreen_res (double *rrsx, double *rrsy) { BScreen s (B_MAIN_SCREEN_ID); if (!s.IsValid ()) emacs_abort (); monitor_info i; if (s.GetMonitorInfo (&i) == B_OK) { *rrsx = (double) i.width / (double) 2.54; *rrsy = (double) i.height / (double) 2.54; } else { *rrsx = 72.27; *rrsy = 72.27; } } void EmacsWindow_parent_to (void *window, void *other_window) { EmacsWindow *w = (EmacsWindow *) window; if (!w->LockLooper ()) emacs_abort (); w->ParentTo ((EmacsWindow *) other_window); w->UnlockLooper (); } void EmacsWindow_unparent (void *window) { EmacsWindow *w = (EmacsWindow *) window; if (!w->LockLooper ()) emacs_abort (); w->UnparentAndUnlink (); w->UnlockLooper (); } void be_get_version_string (char *version, int len) { std::strncpy (version, "Unknown Haiku release", len - 1); BPath path; if (find_directory (B_BEOS_LIB_DIRECTORY, &path) == B_OK) { path.Append ("libbe.so"); BAppFileInfo appFileInfo; version_info versionInfo; BFile file; if (file.SetTo (path.Path (), B_READ_ONLY) == B_OK && appFileInfo.SetTo (&file) == B_OK && appFileInfo.GetVersionInfo (&versionInfo, B_APP_VERSION_KIND) == B_OK && versionInfo.short_info[0] != '\0') std::strncpy (version, versionInfo.short_info, len - 1); } } int be_get_display_planes (void) { color_space space = dpy_color_space; if (space == B_NO_COLOR_SPACE) { BScreen screen; /* This is actually a very slow operation. */ space = dpy_color_space = screen.ColorSpace (); } if (space == B_RGB32 || space == B_RGB24) return 24; if (space == B_RGB16) return 16; if (space == B_RGB15) return 15; if (space == B_CMAP8) return 8; emacs_abort (); /* https://www.haiku-os.org/docs/api/classBScreen.html says a valid screen can't be anything else. */ return -1; } int be_get_display_color_cells (void) { color_space space = dpy_color_space; if (space == B_NO_COLOR_SPACE) { BScreen screen; space = dpy_color_space = screen.ColorSpace (); } if (space == B_RGB32 || space == B_RGB24) return 1677216; if (space == B_RGB16) return 65536; if (space == B_RGB15) return 32768; if (space == B_CMAP8) return 256; emacs_abort (); return -1; } void be_warp_pointer (int x, int y) { /* We're not supposed to use the following function without a BWindowScreen object, but in Haiku nothing actually prevents us from doing so. */ set_mouse_position (x, y); } void EmacsWindow_move_weak_child (void *window, void *child, int xoff, int yoff) { EmacsWindow *w = (EmacsWindow *) window; EmacsWindow *c = (EmacsWindow *) child; if (!w->LockLooper ()) emacs_abort (); w->MoveChild (c, xoff, yoff, 1); w->UnlockLooper (); } void * find_appropriate_view_for_draw (void *vw) { BView *v = (BView *) vw; EmacsView *ev = dynamic_cast(v); if (!ev) return v; return ev->offscreen_draw_view ? ev->offscreen_draw_view : vw; } void EmacsView_set_up_double_buffering (void *vw) { EmacsView *view = (EmacsView *) vw; if (!view->LockLooper ()) emacs_abort (); if (view->offscreen_draw_view) { view->UnlockLooper (); return; } view->SetUpDoubleBuffering (); view->UnlockLooper (); } void EmacsView_flip_and_blit (void *vw) { EmacsView *view = (EmacsView *) vw; if (!view->offscreen_draw_view) return; if (!view->LockLooper ()) emacs_abort (); view->FlipBuffers (); view->UnlockLooper (); } void EmacsView_disable_double_buffering (void *vw) { EmacsView *view = (EmacsView *) vw; if (!view->LockLooper ()) emacs_abort (); view->TearDownDoubleBuffering (); view->UnlockLooper (); } int EmacsView_double_buffered_p (void *vw) { EmacsView *view = (EmacsView *) vw; if (!view->LockLooper ()) emacs_abort (); int db_p = !!view->offscreen_draw_view; view->UnlockLooper (); return db_p; } struct popup_file_dialog_data { BMessage *msg; BFilePanel *panel; BEntry *entry; }; static void unwind_popup_file_dialog (void *ptr) { struct popup_file_dialog_data *data = (struct popup_file_dialog_data *) ptr; BFilePanel *panel = data->panel; delete panel; delete data->entry; delete data->msg; } static void be_popup_file_dialog_safe_set_target (BFilePanel *dialog, BWindow *window) { dialog->SetTarget (BMessenger (window)); } char * be_popup_file_dialog (int open_p, const char *default_dir, int must_match_p, int dir_only_p, void *window, const char *save_text, const char *prompt, void (*block_input_function) (void), void (*unblock_input_function) (void)) { ptrdiff_t idx = c_specpdl_idx_from_cxx (); /* setjmp/longjmp is UB with automatic objects. */ block_input_function (); BWindow *w = (BWindow *) window; uint32_t mode = dir_only_p ? B_DIRECTORY_NODE : B_FILE_NODE | B_DIRECTORY_NODE; BEntry *path = new BEntry; BMessage *msg = new BMessage ('FPSE'); BFilePanel *panel = new BFilePanel (open_p ? B_OPEN_PANEL : B_SAVE_PANEL, NULL, NULL, mode); unblock_input_function (); struct popup_file_dialog_data dat; dat.entry = path; dat.msg = msg; dat.panel = panel; record_c_unwind_protect_from_cxx (unwind_popup_file_dialog, &dat); if (default_dir) { if (path->SetTo (default_dir, 0) != B_OK) default_dir = NULL; } panel->SetMessage (msg); if (default_dir) panel->SetPanelDirectory (path); if (save_text) panel->SetSaveText (save_text); panel->SetHideWhenDone (0); panel->Window ()->SetTitle (prompt); be_popup_file_dialog_safe_set_target (panel, w); panel->Show (); void *buf = alloca (200); while (1) { enum haiku_event_type type; char *ptr = NULL; if (!haiku_read_with_timeout (&type, buf, 200, 100000)) { if (type != FILE_PANEL_EVENT) haiku_write (type, buf); else if (type != ACTIVATION) { if (!ptr) ptr = (char *) ((struct haiku_file_panel_event *) buf)->ptr; } } ssize_t b_s; haiku_read_size (&b_s); if (!b_s || b_s == -1 || ptr) { c_unbind_to_nil_from_cxx (idx); return ptr; } } emacs_abort (); return NULL; } void be_app_quit (void) { if (be_app) be_app->PostMessage (B_QUIT_REQUESTED); } void EmacsView_do_visible_bell (void *view, uint32_t color) { EmacsView *vw = (EmacsView *) view; vw->DoVisibleBell (color); } void BWindow_zoom (void *window) { BWindow *w = (BWindow *) window; w->Zoom (); } void EmacsWindow_make_fullscreen (void *window, int fullscreen_p) { EmacsWindow *w = (EmacsWindow *) window; w->MakeFullscreen (fullscreen_p); } void EmacsWindow_unzoom (void *window) { EmacsWindow *w = (EmacsWindow *) window; w->UnZoom (); } void BMenuBar_start_tracking (void *mbar) { EmacsMenuBar *mb = (EmacsMenuBar *) mbar; if (!mb->LockLooper ()) emacs_abort (); BRect frame = mb->Frame (); BPoint pt = frame.LeftTop (); BPoint l = pt; mb->Parent ()->ConvertToScreen (&pt); set_mouse_position (pt.x, pt.y); mb->MouseDown (l); mb->UnlockLooper (); } #ifdef HAVE_NATIVE_IMAGE_API int be_can_translate_type_to_bitmap_p (const char *mime) { BTranslatorRoster *r = BTranslatorRoster::Default (); translator_id *ids; int32 id_len; if (r->GetAllTranslators (&ids, &id_len) != B_OK) return 0; int found_in = 0; int found_out = 0; for (int i = 0; i < id_len; ++i) { found_in = 0; found_out = 0; const translation_format *i_fmts; const translation_format *o_fmts; int32 i_count, o_count; if (r->GetInputFormats (ids[i], &i_fmts, &i_count) != B_OK) continue; if (r->GetOutputFormats (ids[i], &o_fmts, &o_count) != B_OK) continue; for (int x = 0; x < i_count; ++x) { if (!strcmp (i_fmts[x].MIME, mime)) { found_in = 1; break; } } for (int x = 0; x < i_count; ++x) { if (!strcmp (o_fmts[x].MIME, "image/x-be-bitmap") || !strcmp (o_fmts[x].MIME, "image/x-vnd.Be-bitmap")) { found_out = 1; break; } } if (found_in && found_out) break; } delete [] ids; return found_in && found_out; } void * be_translate_bitmap_from_file_name (const char *filename) { BBitmap *bm = BTranslationUtils::GetBitmap (filename); return bm; } void * be_translate_bitmap_from_memory (const void *buf, size_t bytes) { BMemoryIO io (buf, bytes); BBitmap *bm = BTranslationUtils::GetBitmap (&io); return bm; } #endif size_t BBitmap_bytes_length (void *bitmap) { BBitmap *bm = (BBitmap *) bitmap; return bm->BitsLength (); } void BView_show_tooltip (void *view) { BView *vw = (BView *) view; vw->ShowToolTip (); }