/* 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
#include
#include
#include
#include
#ifdef USE_BE_CAIRO
#include
#endif
#include "haiku_support.h"
#define SCROLL_BAR_UPDATE 3000
static color_space dpy_color_space = B_NO_COLOR_SPACE;
static key_map *key_map = NULL;
static char *key_chars = NULL;
static BLocker key_map_lock;
extern "C"
{
extern _Noreturn void emacs_abort (void);
/* Also defined in haikuterm.h. */
extern void be_app_quit (void);
}
static thread_id app_thread;
_Noreturn void
gui_abort (const char *msg)
{
fprintf (stderr, "Abort in GUI code: %s\n", msg);
fprintf (stderr, "Under Haiku, Emacs cannot recover from errors in GUI code\n");
fprintf (stderr, "App Server disconnects usually manifest as bitmap "
"initialization failures or lock failures.");
emacs_abort ();
}
#ifdef USE_BE_CAIRO
static cairo_format_t
cairo_format_from_color_space (color_space space)
{
switch (space)
{
case B_RGBA32:
return CAIRO_FORMAT_ARGB32;
case B_RGB32:
return CAIRO_FORMAT_RGB24;
case B_RGB16:
return CAIRO_FORMAT_RGB16_565;
case B_GRAY8:
return CAIRO_FORMAT_A8;
case B_GRAY1:
return CAIRO_FORMAT_A1;
default:
gui_abort ("Unsupported color space");
}
}
#endif
static void
map_key (char *chars, int32 offset, uint32_t *c)
{
int size = chars[offset++];
switch (size)
{
case 0:
break;
case 1:
*c = chars[offset];
break;
default:
{
char str[5];
int i = (size <= 4) ? size : 4;
strncpy (str, &(chars[offset]), i);
str[i] = '0';
*c = BUnicodeChar::FromUTF8 ((char *) &str);
break;
}
}
}
static void
map_shift (uint32_t kc, uint32_t *ch)
{
if (!key_map_lock.Lock ())
gui_abort ("Failed to lock keymap");
if (!key_map)
get_key_map (&key_map, &key_chars);
if (!key_map)
return;
if (kc >= 128)
return;
int32_t m = key_map->shift_map[kc];
map_key (key_chars, m, ch);
key_map_lock.Unlock ();
}
static void
map_normal (uint32_t kc, uint32_t *ch)
{
if (!key_map_lock.Lock ())
gui_abort ("Failed to lock keymap");
if (!key_map)
get_key_map (&key_map, &key_chars);
if (!key_map)
return;
if (kc >= 128)
return;
int32_t m = key_map->normal_map[kc];
map_key (key_chars, m, ch);
key_map_lock.Unlock ();
}
class Emacs : public BApplication
{
public:
Emacs () : BApplication ("application/x-vnd.GNU-emacs")
{
}
void
AboutRequested (void)
{
BAlert *about = new BAlert (PACKAGE_NAME,
PACKAGE_STRING
"\nThe extensible, self-documenting, real-time display editor.",
"Close");
about->Go ();
}
bool
QuitRequested (void)
{
struct haiku_app_quit_requested_event rq;
haiku_write (APP_QUIT_REQUESTED_EVENT, &rq);
return 0;
}
void
RefsReceived (BMessage *msg)
{
struct haiku_refs_event rq;
entry_ref ref;
BEntry entry;
BPath path;
int32 cookie = 0;
while (msg->FindRef ("refs", cookie++, &ref) == B_OK)
{
if (entry.SetTo (&ref, 0) == B_OK
&& entry.GetPath (&path) == B_OK)
{
rq.ref = strdup (path.Path ());
haiku_write (REFS_EVENT, &rq);
}
}
}
};
class EmacsWindow : public BDirectWindow
{
public:
struct child_frame
{
struct child_frame *next;
int xoff, yoff;
EmacsWindow *window;
} *subset_windows = NULL;
EmacsWindow *parent = NULL;
BRect pre_fullscreen_rect;
BRect pre_zoom_rect;
int x_before_zoom = INT_MIN;
int y_before_zoom = INT_MIN;
int fullscreen_p = 0;
int zoomed_p = 0;
int shown_flag = 0;
#ifdef USE_BE_CAIRO
BLocker surface_lock;
cairo_surface_t *cr_surface = NULL;
#endif
EmacsWindow () : BDirectWindow (BRect (0, 0, 0, 0), "", B_TITLED_WINDOW_LOOK,
B_NORMAL_WINDOW_FEEL, B_NO_SERVER_SIDE_WINDOW_MODIFIERS)
{
}
~EmacsWindow ()
{
struct child_frame *next;
for (struct child_frame *f = subset_windows; f; f = next)
{
f->window->Unparent ();
next = f->next;
delete f;
}
if (this->parent)
UnparentAndUnlink ();
#ifdef USE_BE_CAIRO
if (!surface_lock.Lock ())
gui_abort ("Failed to lock cairo surface");
if (cr_surface)
{
cairo_surface_destroy (cr_surface);
cr_surface = NULL;
}
surface_lock.Unlock ();
#endif
}
void
UpwardsSubset (EmacsWindow *w)
{
for (; w; w = w->parent)
AddToSubset (w);
}
void
UpwardsSubsetChildren (EmacsWindow *w)
{
UpwardsSubset (w);
for (struct child_frame *f = subset_windows; f;
f = f->next)
f->window->UpwardsSubsetChildren (w);
}
void
UpwardsUnSubset (EmacsWindow *w)
{
for (; w; w = w->parent)
RemoveFromSubset (w);
}
void
UpwardsUnSubsetChildren (EmacsWindow *w)
{
UpwardsUnSubset (w);
for (struct child_frame *f = subset_windows; f;
f = f->next)
f->window->UpwardsUnSubsetChildren (w);
}
void
Unparent (void)
{
this->SetFeel (B_NORMAL_WINDOW_FEEL);
UpwardsUnSubsetChildren (parent);
this->RemoveFromSubset (this);
this->parent = NULL;
if (fullscreen_p)
{
fullscreen_p = 0;
MakeFullscreen (1);
}
}
void
UnparentAndUnlink (void)
{
this->parent->UnlinkChild (this);
this->Unparent ();
}
void
UnlinkChild (EmacsWindow *window)
{
struct child_frame *last = NULL;
struct child_frame *tem = subset_windows;
for (; tem; last = tem, tem = tem->next)
{
if (tem->window == window)
{
if (last)
last->next = tem->next;
if (tem == subset_windows)
subset_windows = NULL;
delete tem;
return;
}
}
gui_abort ("Failed to unlink child frame");
}
void
ParentTo (EmacsWindow *window)
{
if (this->parent)
UnparentAndUnlink ();
this->parent = window;
this->SetFeel (B_FLOATING_SUBSET_WINDOW_FEEL);
this->AddToSubset (this);
if (!IsHidden () && this->parent)
UpwardsSubsetChildren (parent);
if (fullscreen_p)
{
fullscreen_p = 0;
MakeFullscreen (1);
}
this->Sync ();
window->LinkChild (this);
}
void
LinkChild (EmacsWindow *window)
{
struct child_frame *f = new struct child_frame;
for (struct child_frame *f = subset_windows; f;
f = f->next)
{
if (window == f->window)
gui_abort ("Trying to link a child frame that is already present");
}
f->window = window;
f->next = subset_windows;
f->xoff = -1;
f->yoff = -1;
subset_windows = f;
}
void
DoMove (struct child_frame *f)
{
BRect frame = this->Frame ();
f->window->MoveTo (frame.left + f->xoff,
frame.top + f->yoff);
this->Sync ();
}
void
DoUpdateWorkspace (struct child_frame *f)
{
f->window->SetWorkspaces (this->Workspaces ());
}
void
MoveChild (EmacsWindow *window, int xoff, int yoff,
int weak_p)
{
for (struct child_frame *f = subset_windows; f;
f = f->next)
{
if (window == f->window)
{
f->xoff = xoff;
f->yoff = yoff;
if (!weak_p)
DoMove (f);
return;
}
}
gui_abort ("Trying to move a child frame that doesn't exist");
}
void
WindowActivated (bool activated)
{
struct haiku_activation_event rq;
rq.window = this;
rq.activated_p = activated;
haiku_write (ACTIVATION, &rq);
}
void
DirectConnected (direct_buffer_info *info)
{
#ifdef USE_BE_CAIRO
if (!surface_lock.Lock ())
gui_abort ("Failed to lock window direct cr surface");
if (cr_surface)
{
cairo_surface_destroy (cr_surface);
cr_surface = NULL;
}
if (info->buffer_state != B_DIRECT_STOP)
{
int left, top, right, bottom;
left = info->clip_bounds.left;
top = info->clip_bounds.top;
right = info->clip_bounds.right;
bottom = info->clip_bounds.bottom;
unsigned char *bits = (unsigned char *) info->bits;
if ((info->bits_per_pixel % 8) == 0)
{
bits += info->bytes_per_row * top;
bits += (left * info->bits_per_pixel / 8);
cr_surface = cairo_image_surface_create_for_data
(bits,
cairo_format_from_color_space (info->pixel_format),
right - left + 1,
bottom - top + 1,
info->bytes_per_row);
}
}
surface_lock.Unlock ();
#endif
}
void
MessageReceived (BMessage *msg)
{
int32 old_what = 0;
if (msg->WasDropped ())
{
entry_ref ref;
if (msg->FindRef ("refs", &ref) == B_OK)
{
msg->what = B_REFS_RECEIVED;
be_app->PostMessage (msg);
msg->SendReply (B_OK);
}
}
else if (msg->GetPointer ("menuptr"))
{
struct haiku_menu_bar_select_event rq;
rq.window = this;
rq.ptr = (void *) msg->GetPointer ("menuptr");
haiku_write (MENU_BAR_SELECT_EVENT, &rq);
}
else if (msg->what == 'FPSE' ||
((msg->FindInt32 ("old_what", &old_what) == B_OK &&
old_what == 'FPSE')))
{
struct haiku_file_panel_event rq;
BEntry entry;
BPath path;
entry_ref ref;
rq.ptr = NULL;
if (msg->FindRef ("refs", &ref) == B_OK &&
entry.SetTo (&ref, 0) == B_OK &&
entry.GetPath (&path) == B_OK)
{
const char *str_path = path.Path ();
if (str_path)
rq.ptr = strdup (str_path);
}
if (msg->FindRef ("directory", &ref),
entry.SetTo (&ref, 0) == B_OK &&
entry.GetPath (&path) == B_OK)
{
const char *name = msg->GetString ("name");
const char *str_path = path.Path ();
if (name)
{
char str_buf[std::strlen (str_path) + std::strlen (name) + 2];
snprintf ((char *) &str_buf,
std::strlen (str_path) + std::strlen (name) + 2, "%s/%s",
str_path, name);
rq.ptr = strdup (str_buf);
}
}
haiku_write (FILE_PANEL_EVENT, &rq);
}
else
BDirectWindow::MessageReceived (msg);
}
void
DispatchMessage (BMessage *msg, BHandler *handler)
{
if (msg->what == B_KEY_DOWN || msg->what == B_KEY_UP)
{
struct haiku_key_event rq;
rq.window = this;
int32_t code = msg->GetInt32 ("raw_char", 0);
rq.modifiers = 0;
uint32_t mods = modifiers ();
if (mods & B_SHIFT_KEY)
rq.modifiers |= HAIKU_MODIFIER_SHIFT;
if (mods & B_CONTROL_KEY)
rq.modifiers |= HAIKU_MODIFIER_CTRL;
if (mods & B_COMMAND_KEY)
rq.modifiers |= HAIKU_MODIFIER_ALT;
if (mods & B_OPTION_KEY)
rq.modifiers |= HAIKU_MODIFIER_SUPER;
rq.mb_char = code;
rq.kc = msg->GetInt32 ("key", -1);
rq.unraw_mb_char =
BUnicodeChar::FromUTF8 (msg->GetString ("bytes"));
if ((mods & B_SHIFT_KEY) && rq.kc >= 0)
map_shift (rq.kc, &rq.unraw_mb_char);
else if (rq.kc >= 0)
map_normal (rq.kc, &rq.unraw_mb_char);
haiku_write (msg->what == B_KEY_DOWN ? KEY_DOWN : KEY_UP, &rq);
}
else if (msg->what == B_MOUSE_WHEEL_CHANGED)
{
struct haiku_wheel_move_event rq;
rq.window = this;
rq.modifiers = 0;
uint32_t mods = modifiers ();
if (mods & B_SHIFT_KEY)
rq.modifiers |= HAIKU_MODIFIER_SHIFT;
if (mods & B_CONTROL_KEY)
rq.modifiers |= HAIKU_MODIFIER_CTRL;
if (mods & B_COMMAND_KEY)
rq.modifiers |= HAIKU_MODIFIER_ALT;
if (mods & B_OPTION_KEY)
rq.modifiers |= HAIKU_MODIFIER_SUPER;
float dx, dy;
if (msg->FindFloat ("be:wheel_delta_x", &dx) == B_OK &&
msg->FindFloat ("be:wheel_delta_y", &dy) == B_OK)
{
rq.delta_x = dx * 10;
rq.delta_y = dy * 10;
haiku_write (WHEEL_MOVE_EVENT, &rq);
};
}
else
BDirectWindow::DispatchMessage (msg, handler);
}
void
MenusBeginning ()
{
struct haiku_menu_bar_state_event rq;
rq.window = this;
haiku_write (MENU_BAR_OPEN, &rq);
}
void
MenusEnded ()
{
struct haiku_menu_bar_state_event rq;
rq.window = this;
haiku_write (MENU_BAR_CLOSE, &rq);
}
void
FrameResized (float newWidth, float newHeight)
{
struct haiku_resize_event rq;
rq.window = this;
rq.px_heightf = newHeight;
rq.px_widthf = newWidth;
haiku_write (FRAME_RESIZED, &rq);
BDirectWindow::FrameResized (newWidth, newHeight);
}
void
FrameMoved (BPoint newPosition)
{
struct haiku_move_event rq;
rq.window = this;
rq.x = std::lrint (newPosition.x);
rq.y = std::lrint (newPosition.y);
haiku_write (MOVE_EVENT, &rq);
for (struct child_frame *f = subset_windows;
f; f = f->next)
DoMove (f);
BDirectWindow::FrameMoved (newPosition);
}
void
WorkspacesChanged (uint32_t old, uint32_t n)
{
for (struct child_frame *f = subset_windows;
f; f = f->next)
DoUpdateWorkspace (f);
}
void
EmacsMoveTo (int x, int y)
{
if (!this->parent)
this->MoveTo (x, y);
else
this->parent->MoveChild (this, x, y, 0);
}
bool
QuitRequested ()
{
struct haiku_quit_requested_event rq;
rq.window = this;
haiku_write (QUIT_REQUESTED, &rq);
return false;
}
void
Minimize (bool minimized_p)
{
BDirectWindow::Minimize (minimized_p);
struct haiku_iconification_event rq;
rq.window = this;
rq.iconified_p = !parent && minimized_p;
haiku_write (ICONIFICATION, &rq);
}
void
EmacsHide (void)
{
if (this->IsHidden ())
return;
Hide ();
if (this->parent)
UpwardsUnSubsetChildren (this->parent);
}
void
EmacsShow (void)
{
if (!this->IsHidden ())
return;
if (this->parent)
shown_flag = 1;
Show ();
if (this->parent)
UpwardsSubsetChildren (this->parent);
}
void
Zoom (BPoint o, float w, float h)
{
struct haiku_zoom_event rq;
rq.window = this;
rq.x = o.x;
rq.y = o.y;
rq.width = w;
rq.height = h;
if (fullscreen_p)
MakeFullscreen (0);
if (o.x != x_before_zoom ||
o.y != y_before_zoom)
{
x_before_zoom = Frame ().left;
y_before_zoom = Frame ().top;
pre_zoom_rect = Frame ();
zoomed_p = 1;
haiku_write (ZOOM_EVENT, &rq);
}
else
{
zoomed_p = 0;
x_before_zoom = y_before_zoom = INT_MIN;
}
BDirectWindow::Zoom (o, w, h);
}
void
UnZoom (void)
{
if (!zoomed_p)
return;
zoomed_p = 0;
EmacsMoveTo (pre_zoom_rect.left, pre_zoom_rect.top);
ResizeTo (pre_zoom_rect.Width (),
pre_zoom_rect.Height ());
}
void
GetParentWidthHeight (int *width, int *height)
{
if (parent)
{
*width = parent->Frame ().Width ();
*height = parent->Frame ().Height ();
}
else
{
BScreen s (this);
*width = s.Frame ().Width ();
*height = s.Frame ().Height ();
}
}
void
OffsetChildRect (BRect *r, EmacsWindow *c)
{
for (struct child_frame *f; f; f = f->next)
if (f->window == c)
{
r->top -= f->yoff;
r->bottom -= f->yoff;
r->left -= f->xoff;
r->right -= f->xoff;
return;
}
gui_abort ("Trying to calculate offsets for a child frame that doesn't exist");
}
void
MakeFullscreen (int make_fullscreen_p)
{
BScreen screen (this);
if (!screen.IsValid ())
gui_abort ("Trying to make a window fullscreen without a screen");
if (make_fullscreen_p == fullscreen_p)
return;
fullscreen_p = make_fullscreen_p;
uint32 flags = Flags ();
if (fullscreen_p)
{
if (zoomed_p)
UnZoom ();
flags |= B_NOT_MOVABLE | B_NOT_ZOOMABLE;
pre_fullscreen_rect = Frame ();
if (parent)
parent->OffsetChildRect (&pre_fullscreen_rect, this);
int w, h;
EmacsMoveTo (0, 0);
GetParentWidthHeight (&w, &h);
ResizeTo (w, h);
}
else
{
flags &= ~(B_NOT_MOVABLE | B_NOT_ZOOMABLE);
EmacsMoveTo (pre_fullscreen_rect.left,
pre_fullscreen_rect.top);
ResizeTo (pre_fullscreen_rect.Width (),
pre_fullscreen_rect.Height ());
}
SetFlags (flags);
}
};
class EmacsMenuBar : public BMenuBar
{
public:
EmacsMenuBar () : BMenuBar (BRect (0, 0, 0, 0), NULL)
{
}
void
FrameResized (float newWidth, float newHeight)
{
struct haiku_menu_bar_resize_event rq;
rq.window = this->Window ();
rq.height = std::lrint (newHeight);
rq.width = std::lrint (newWidth);
haiku_write (MENU_BAR_RESIZE, &rq);
BMenuBar::FrameResized (newWidth, newHeight);
}
};
class EmacsView : public BView
{
public:
uint32_t visible_bell_color = 0;
uint32_t previous_buttons = 0;
int looper_locked_count = 0;
BRegion sb_region;
BView *offscreen_draw_view = NULL;
BBitmap *offscreen_draw_bitmap_1 = NULL;
BBitmap *copy_bitmap = NULL;
#ifdef USE_BE_CAIRO
cairo_surface_t *cr_surface = NULL;
BLocker cr_surface_lock;
#endif
BPoint tt_absl_pos;
color_space cspace;
EmacsView () : BView (BRect (0, 0, 0, 0), "Emacs", B_FOLLOW_NONE, B_WILL_DRAW)
{
}
~EmacsView ()
{
TearDownDoubleBuffering ();
}
void
AttachedToWindow (void)
{
cspace = B_RGBA32;
}
#ifdef USE_BE_CAIRO
void
DetachCairoSurface (void)
{
if (!cr_surface_lock.Lock ())
gui_abort ("Could not lock cr surface during detachment");
if (!cr_surface)
gui_abort ("Trying to detach window cr surface when none exists");
cairo_surface_destroy (cr_surface);
cr_surface = NULL;
cr_surface_lock.Unlock ();
}
void
AttachCairoSurface (void)
{
if (!cr_surface_lock.Lock ())
gui_abort ("Could not lock cr surface during attachment");
if (cr_surface)
gui_abort ("Trying to attach cr surface when one already exists");
cr_surface = cairo_image_surface_create_for_data
((unsigned char *) offscreen_draw_bitmap_1->Bits (),
CAIRO_FORMAT_ARGB32, offscreen_draw_bitmap_1->Bounds ().Width (),
offscreen_draw_bitmap_1->Bounds ().Height (),
offscreen_draw_bitmap_1->BytesPerRow ());
if (!cr_surface)
gui_abort ("Cr surface allocation failed for double-buffered view");
cr_surface_lock.Unlock ();
}
#endif
void
TearDownDoubleBuffering (void)
{
if (offscreen_draw_view)
{
if (Window ())
ClearViewBitmap ();
if (copy_bitmap)
{
delete copy_bitmap;
copy_bitmap = NULL;
}
if (!looper_locked_count)
if (!offscreen_draw_view->LockLooper ())
gui_abort ("Failed to lock offscreen draw view");
#ifdef USE_BE_CAIRO
if (cr_surface)
DetachCairoSurface ();
#endif
offscreen_draw_view->RemoveSelf ();
delete offscreen_draw_view;
offscreen_draw_view = NULL;
delete offscreen_draw_bitmap_1;
offscreen_draw_bitmap_1 = NULL;
}
}
void
AfterResize (float newWidth, float newHeight)
{
if (offscreen_draw_view)
{
if (!LockLooper ())
gui_abort ("Failed to lock looper after resize");
if (!offscreen_draw_view->LockLooper ())
gui_abort ("Failed to lock offscreen draw view after resize");
#ifdef USE_BE_CAIRO
DetachCairoSurface ();
#endif
offscreen_draw_view->RemoveSelf ();
delete offscreen_draw_bitmap_1;
offscreen_draw_bitmap_1 = new BBitmap (Frame (), cspace, 1);
if (offscreen_draw_bitmap_1->InitCheck () != B_OK)
gui_abort ("Offscreen draw bitmap initialization failed");
offscreen_draw_view->MoveTo (Frame ().left, Frame ().top);
offscreen_draw_view->ResizeTo (Frame ().Width (), Frame ().Height ());
offscreen_draw_bitmap_1->AddChild (offscreen_draw_view);
#ifdef USE_BE_CAIRO
AttachCairoSurface ();
#endif
if (looper_locked_count)
{
offscreen_draw_bitmap_1->Lock ();
}
UnlockLooper ();
}
}
void
Pulse (void)
{
visible_bell_color = 0;
SetFlags (Flags () & ~B_PULSE_NEEDED);
Window ()->SetPulseRate (0);
Invalidate ();
}
void
Draw (BRect expose_bounds)
{
struct haiku_expose_event rq;
EmacsWindow *w = (EmacsWindow *) Window ();
if (visible_bell_color > 0)
{
PushState ();
BView_SetHighColorForVisibleBell (this, visible_bell_color);
FillRect (Frame ());
PopState ();
return;
}
if (w->shown_flag)
{
PushState ();
SetDrawingMode (B_OP_ERASE);
FillRect (Frame ());
PopState ();
return;
}
if (!offscreen_draw_view)
{
if (sb_region.Contains (std::lrint (expose_bounds.left),
std::lrint (expose_bounds.top)) &&
sb_region.Contains (std::lrint (expose_bounds.right),
std::lrint (expose_bounds.top)) &&
sb_region.Contains (std::lrint (expose_bounds.left),
std::lrint (expose_bounds.bottom)) &&
sb_region.Contains (std::lrint (expose_bounds.right),
std::lrint (expose_bounds.bottom)))
return;
rq.x = std::floor (expose_bounds.left);
rq.y = std::floor (expose_bounds.top);
rq.width = std::ceil (expose_bounds.right - expose_bounds.left + 1);
rq.height = std::ceil (expose_bounds.bottom - expose_bounds.top + 1);
if (!rq.width)
rq.width = 1;
if (!rq.height)
rq.height = 1;
rq.window = this->Window ();
haiku_write (FRAME_EXPOSED, &rq);
}
}
void
DoVisibleBell (uint32_t color)
{
if (!LockLooper ())
gui_abort ("Failed to lock looper during visible bell");
visible_bell_color = color | (255 << 24);
SetFlags (Flags () | B_PULSE_NEEDED);
Window ()->SetPulseRate (100 * 1000);
Invalidate ();
UnlockLooper ();
}
void
FlipBuffers (void)
{
if (!LockLooper ())
gui_abort ("Failed to lock looper during buffer flip");
if (!offscreen_draw_view)
gui_abort ("Failed to lock offscreen view during buffer flip");
offscreen_draw_view->Flush ();
offscreen_draw_view->Sync ();
EmacsWindow *w = (EmacsWindow *) Window ();
w->shown_flag = 0;
if (copy_bitmap &&
copy_bitmap->Bounds () != offscreen_draw_bitmap_1->Bounds ())
{
delete copy_bitmap;
copy_bitmap = NULL;
}
if (!copy_bitmap)
copy_bitmap = new BBitmap (offscreen_draw_bitmap_1);
else
copy_bitmap->ImportBits (offscreen_draw_bitmap_1);
if (copy_bitmap->InitCheck () != B_OK)
gui_abort ("Failed to init copy bitmap during buffer flip");
SetViewBitmap (copy_bitmap,
Frame (), Frame (), B_FOLLOW_NONE, 0);
Invalidate ();
UnlockLooper ();
return;
}
void
SetUpDoubleBuffering (void)
{
if (!LockLooper ())
gui_abort ("Failed to lock self setting up double buffering");
if (offscreen_draw_view)
gui_abort ("Failed to lock offscreen view setting up double buffering");
offscreen_draw_bitmap_1 = new BBitmap (Frame (), cspace, 1);
if (offscreen_draw_bitmap_1->InitCheck () != B_OK)
gui_abort ("Failed to init offscreen bitmap");
#ifdef USE_BE_CAIRO
AttachCairoSurface ();
#endif
offscreen_draw_view = new BView (Frame (), NULL, B_FOLLOW_NONE, B_WILL_DRAW);
offscreen_draw_bitmap_1->AddChild (offscreen_draw_view);
if (looper_locked_count)
{
if (!offscreen_draw_bitmap_1->Lock ())
gui_abort ("Failed to lock bitmap after double buffering was set up.");
}
UnlockLooper ();
Invalidate ();
}
void
MouseMoved (BPoint point, uint32 transit, const BMessage *msg)
{
struct haiku_mouse_motion_event rq;
rq.just_exited_p = transit == B_EXITED_VIEW;
rq.x = point.x;
rq.y = point.y;
rq.be_code = transit;
rq.window = this->Window ();
if (ToolTip ())
ToolTip ()->SetMouseRelativeLocation (BPoint (-(point.x - tt_absl_pos.x),
-(point.y - tt_absl_pos.y)));
haiku_write (MOUSE_MOTION, &rq);
}
void
MouseDown (BPoint point)
{
struct haiku_button_event rq;
uint32 buttons;
this->GetMouse (&point, &buttons, false);
rq.window = this->Window ();
rq.btn_no = 0;
if (!(previous_buttons & B_PRIMARY_MOUSE_BUTTON) &&
(buttons & B_PRIMARY_MOUSE_BUTTON))
rq.btn_no = 0;
else if (!(previous_buttons & B_SECONDARY_MOUSE_BUTTON) &&
(buttons & B_SECONDARY_MOUSE_BUTTON))
rq.btn_no = 2;
else if (!(previous_buttons & B_TERTIARY_MOUSE_BUTTON) &&
(buttons & B_TERTIARY_MOUSE_BUTTON))
rq.btn_no = 1;
previous_buttons = buttons;
rq.x = point.x;
rq.y = point.y;
uint32_t mods = modifiers ();
rq.modifiers = 0;
if (mods & B_SHIFT_KEY)
rq.modifiers |= HAIKU_MODIFIER_SHIFT;
if (mods & B_CONTROL_KEY)
rq.modifiers |= HAIKU_MODIFIER_CTRL;
if (mods & B_COMMAND_KEY)
rq.modifiers |= HAIKU_MODIFIER_ALT;
if (mods & B_OPTION_KEY)
rq.modifiers |= HAIKU_MODIFIER_SUPER;
SetMouseEventMask (B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
haiku_write (BUTTON_DOWN, &rq);
}
void
MouseUp (BPoint point)
{
struct haiku_button_event rq;
uint32 buttons;
this->GetMouse (&point, &buttons, false);
rq.window = this->Window ();
rq.btn_no = 0;
if ((previous_buttons & B_PRIMARY_MOUSE_BUTTON) &&
!(buttons & B_PRIMARY_MOUSE_BUTTON))
rq.btn_no = 0;
else if ((previous_buttons & B_SECONDARY_MOUSE_BUTTON) &&
!(buttons & B_SECONDARY_MOUSE_BUTTON))
rq.btn_no = 2;
else if ((previous_buttons & B_TERTIARY_MOUSE_BUTTON) &&
!(buttons & B_TERTIARY_MOUSE_BUTTON))
rq.btn_no = 1;
previous_buttons = buttons;
rq.x = point.x;
rq.y = point.y;
uint32_t mods = modifiers ();
rq.modifiers = 0;
if (mods & B_SHIFT_KEY)
rq.modifiers |= HAIKU_MODIFIER_SHIFT;
if (mods & B_CONTROL_KEY)
rq.modifiers |= HAIKU_MODIFIER_CTRL;
if (mods & B_COMMAND_KEY)
rq.modifiers |= HAIKU_MODIFIER_ALT;
if (mods & B_OPTION_KEY)
rq.modifiers |= HAIKU_MODIFIER_SUPER;
if (!buttons)
SetMouseEventMask (0, 0);
haiku_write (BUTTON_UP, &rq);
}
};
class EmacsScrollBar : public BScrollBar
{
public:
void *scroll_bar;
EmacsScrollBar (int x, int y, int x1, int y1, bool horizontal_p) :
BScrollBar (BRect (x, y, x1, y1), NULL, NULL, 0, 0, horizontal_p ?
B_HORIZONTAL : B_VERTICAL)
{
BView *vw = (BView *) this;
vw->SetResizingMode (B_FOLLOW_NONE);
}
void
MessageReceived (BMessage *msg)
{
if (msg->what == SCROLL_BAR_UPDATE)
{
this->SetRange (0, msg->GetInt32 ("emacs:range", 0));
this->SetValue (msg->GetInt32 ("emacs:units", 0));
}
BScrollBar::MessageReceived (msg);
}
void
ValueChanged (float new_value)
{
struct haiku_scroll_bar_value_event rq;
rq.scroll_bar = scroll_bar;
rq.position = new_value;
haiku_write (SCROLL_BAR_VALUE_EVENT, &rq);
}
void
MouseDown (BPoint pt)
{
struct haiku_scroll_bar_drag_event rq;
rq.dragging_p = 1;
rq.scroll_bar = scroll_bar;
haiku_write (SCROLL_BAR_DRAG_EVENT, &rq);
BScrollBar::MouseDown (pt);
}
void
MouseUp (BPoint pt)
{
struct haiku_scroll_bar_drag_event rq;
rq.dragging_p = 0;
rq.scroll_bar = scroll_bar;
haiku_write (SCROLL_BAR_DRAG_EVENT, &rq);
BScrollBar::MouseUp (pt);
}
};
class EmacsTitleMenuItem : public BMenuItem
{
public:
EmacsTitleMenuItem (const char *str) : BMenuItem (str, NULL)
{
SetEnabled (0);
}
void
DrawContent (void)
{
BMenu *menu = Menu ();
menu->PushState ();
menu->SetFont (be_bold_font);
BView_SetHighColorForVisibleBell (menu, 0);
BMenuItem::DrawContent ();
menu->PopState ();
}
};
class EmacsMenuItem : public BMenuItem
{
public:
int menu_bar_id = -1;
void *wind_ptr = NULL;
char *key = NULL;
char *help = NULL;
EmacsMenuItem (const char *ky,
const char *str,
const char *help,
BMessage *message = NULL) : BMenuItem (str, message)
{
if (ky)
{
key = strdup (ky);
if (!key)
gui_abort ("strdup failed");
}
if (help)
{
this->help = strdup (help);
if (!this->help)
gui_abort ("strdup failed");
}
}
~EmacsMenuItem ()
{
if (key)
free (key);
if (help)
free (help);
}
void
DrawContent (void)
{
BMenu *menu = Menu ();
BMenuItem::DrawContent ();
if (key)
{
BRect r = menu->Frame ();
int w = menu->StringWidth (key);
menu->MovePenTo (BPoint (r.Width () - w - 4,
menu->PenLocation ().y));
menu->DrawString (key);
}
}
void
GetContentSize (float *w, float *h)
{
BMenuItem::GetContentSize (w, h);
if (Menu () && key)
*w += 4 + Menu ()->StringWidth (key);
}
void
Highlight (bool highlight_p)
{
struct haiku_menu_bar_help_event rq;
if (menu_bar_id >= 0)
{
rq.window = wind_ptr;
rq.mb_idx = highlight_p ? menu_bar_id : -1;
haiku_write (MENU_BAR_HELP_EVENT, &rq);
}
else if (help)
{
Menu ()->SetToolTip (highlight_p ? help : NULL);
}
BMenuItem::Highlight (highlight_p);
}
};
class EmacsPopUpMenu : public BPopUpMenu
{
public:
EmacsPopUpMenu (const char *name) : BPopUpMenu (name, 0)
{
}
void
FrameResized (float w, float h)
{
Invalidate ();
BPopUpMenu::FrameResized (w, h);
}
};
static int32
start_running_application (void *data)
{
haiku_io_init_in_app_thread ();
if (!((Emacs *) data)->Lock ())
gui_abort ("Failed to lock application");
((Emacs *) data)->Run ();
((Emacs *) data)->Unlock ();
return 0;
}
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 || 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;
}
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);
}
void *
BApplication_setup (void)
{
if (be_app)
return be_app;
thread_id id;
Emacs *app;
app = new Emacs;
app->Unlock ();
if ((id = spawn_thread (start_running_application, "Emacs app thread",
B_DEFAULT_MEDIA_PRIORITY, app)) < 0)
gui_abort ("spawn_thread failed");
resume_thread (id);
app_thread = id;
return app;
}
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 ())
gui_abort ("Failed to lock window looper setting offset");
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;
if (!screen.IsValid ())
gui_abort ("Invalid 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 ())
gui_abort ("Failed to lock view for resize");
vw->ResizeTo (width, height);
vw->AfterResize (width, height);
vw->UnlockLooper ();
}
void *
BCursor_create_default (void)
{
return new BCursor (B_CURSOR_ID_SYSTEM_DEFAULT);
}
void *
BCursor_create_modeline (void)
{
return new BCursor (B_CURSOR_ID_CONTEXT_MENU);
}
void *
BCursor_from_id (enum haiku_cursor cursor)
{
return new BCursor ((enum BCursorID) cursor);
}
void *
BCursor_create_i_beam (void)
{
return new BCursor (B_CURSOR_ID_I_BEAM);
}
void *
BCursor_create_progress_cursor (void)
{
return new BCursor (B_CURSOR_ID_PROGRESS);
}
void *
BCursor_create_grab (void)
{
return new BCursor (B_CURSOR_ID_GRAB);
}
void
BCursor_delete (void *cursor)
{
delete (BCursor *) cursor;
}
void
BView_set_view_cursor (void *view, void *cursor)
{
if (!((BView *) view)->LockLooper ())
gui_abort ("Failed to lock view setting cursor");
((BView *) view)->SetViewCursor ((BCursor *) cursor);
((BView *) view)->UnlockLooper ();
}
void
BWindow_Flush (void *window)
{
((BWindow *) window)->Flush ();
}
void
BMapKey (uint32_t kc, int *non_ascii_p, unsigned *code)
{
if (*code == 10 && kc != 0x42)
{
*code = XK_Return;
*non_ascii_p = 1;
return;
}
switch (kc)
{
default:
*non_ascii_p = 0;
if (kc < 0xe && kc > 0x1)
{
*code = XK_F1 + kc - 2;
*non_ascii_p = 1;
}
return;
case 0x1e:
*code = XK_BackSpace;
break;
case 0x61:
*code = XK_Left;
break;
case 0x63:
*code = XK_Right;
break;
case 0x57:
*code = XK_Up;
break;
case 0x62:
*code = XK_Down;
break;
case 0x64:
*code = XK_Insert;
break;
case 0x65:
*code = XK_Delete;
break;
case 0x37:
*code = XK_Home;
break;
case 0x58:
*code = XK_End;
break;
case 0x39:
*code = XK_Page_Up;
break;
case 0x5a:
*code = XK_Page_Down;
break;
case 0x1:
*code = XK_Escape;
break;
case 0x68:
*code = XK_Menu;
break;
}
*non_ascii_p = 1;
}
void *
BScrollBar_make_for_view (void *view, int horizontal_p,
int x, int y, int x1, int y1,
void *scroll_bar_ptr)
{
EmacsScrollBar *sb = new EmacsScrollBar (x, y, x1, y1, horizontal_p);
sb->scroll_bar = scroll_bar_ptr;
BView *vw = (BView *) view;
BView *sv = (BView *) sb;
if (!vw->LockLooper ())
gui_abort ("Failed to lock scrollbar owner");
vw->AddChild ((BView *) sb);
sv->WindowActivated (vw->Window ()->IsActive ());
vw->UnlockLooper ();
return sb;
}
void
BScrollBar_delete (void *sb)
{
BView *view = (BView *) sb;
BView *pr = view->Parent ();
if (!pr->LockLooper ())
gui_abort ("Failed to lock scrollbar parent");
pr->RemoveChild (view);
pr->UnlockLooper ();
delete (EmacsScrollBar *) sb;
}
void
BView_move_frame (void *view, int x, int y, int x1, int y1)
{
BView *vw = (BView *) view;
if (!vw->LockLooper ())
gui_abort ("Failed to lock view moving frame");
vw->MoveTo (x, y);
vw->ResizeTo (x1 - x, y1 - y);
vw->Flush ();
vw->Sync ();
vw->UnlockLooper ();
}
void
BView_scroll_bar_update (void *sb, int portion, int whole, int position)
{
BScrollBar *bar = (BScrollBar *) sb;
BMessage msg = BMessage (SCROLL_BAR_UPDATE);
BMessenger mr = BMessenger (bar);
msg.AddInt32 ("emacs:range", whole);
msg.AddInt32 ("emacs:units", position);
mr.SendMessage (&msg);
}
int
BScrollBar_default_size (int horizontal_p)
{
return horizontal_p ? B_H_SCROLL_BAR_HEIGHT : B_V_SCROLL_BAR_WIDTH;
}
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 ();
}
void
BView_draw_lock (void *view)
{
EmacsView *vw = (EmacsView *) view;
if (vw->looper_locked_count)
{
vw->looper_locked_count++;
return;
}
BView *v = (BView *) find_appropriate_view_for_draw (vw);
if (v != vw)
{
if (!vw->offscreen_draw_bitmap_1->Lock ())
gui_abort ("Failed to lock offscreen bitmap while acquiring draw lock");
}
else if (!v->LockLooper ())
gui_abort ("Failed to lock draw view while acquiring draw lock");
if (v != vw && !vw->LockLooper ())
gui_abort ("Failed to lock view while acquiring draw lock");
vw->looper_locked_count++;
}
void
BView_draw_unlock (void *view)
{
EmacsView *vw = (EmacsView *) view;
if (--vw->looper_locked_count)
return;
BView *v = (BView *) find_appropriate_view_for_draw (view);
if (v == vw)
vw->UnlockLooper ();
else
{
vw->offscreen_draw_bitmap_1->Unlock ();
vw->UnlockLooper ();
}
}
void
BWindow_center_on_screen (void *window)
{
BWindow *w = (BWindow *) window;
w->CenterOnScreen ();
}
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) = 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 ())
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);
}
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);
}
void
BWindow_change_decoration (void *window, int decorate_p)
{
BWindow *w = (BWindow *) window;
if (!w->LockLooper ())
gui_abort ("Failed to lock window while changing its decorations");
if (decorate_p)
w->SetLook (B_TITLED_WINDOW_LOOK);
else
w->SetLook (B_NO_BORDER_WINDOW_LOOK);
w->UnlockLooper ();
}
void
BWindow_set_tooltip_decoration (void *window)
{
BWindow *w = (BWindow *) window;
if (!w->LockLooper ())
gui_abort ("Failed to lock window while setting ttip decoration");
w->SetLook (B_BORDERED_WINDOW_LOOK);
w->SetFeel (B_FLOATING_APP_WINDOW_FEEL);
w->UnlockLooper ();
}
void
BWindow_set_avoid_focus (void *window, int avoid_focus_p)
{
BWindow *w = (BWindow *) window;
if (!w->LockLooper ())
gui_abort ("Failed to lock window while setting avoid focus");
if (!avoid_focus_p)
w->SetFlags (w->Flags () & ~B_AVOID_FOCUS);
else
w->SetFlags (w->Flags () | B_AVOID_FOCUS);
w->Sync ();
w->UnlockLooper ();
}
void
BView_emacs_delete (void *view)
{
EmacsView *vw = (EmacsView *) view;
if (!vw->LockLooper ())
gui_abort ("Failed to lock view while deleting it");
vw->RemoveSelf ();
delete vw;
}
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 EmacsPopUpMenu (name);
menu->SetRadioMode (0);
return menu;
}
void
BMenu_add_title (void *menu, const char *text)
{
EmacsTitleMenuItem *it = new EmacsTitleMenuItem (text);
BMenu *mn = (BMenu *) menu;
mn->AddItem (it);
}
void
BMenu_add_item (void *menu, const char *label, void *ptr, bool enabled_p,
bool marked_p, bool mbar_p, void *mbw_ptr, const char *key,
const char *help)
{
BMenu *m = (BMenu *) menu;
BMessage *msg;
if (ptr)
msg = new BMessage ();
EmacsMenuItem *it = new EmacsMenuItem (key, label, help, ptr ? msg : NULL);
it->SetTarget (m->Window ());
it->SetEnabled (enabled_p);
it->SetMarked (marked_p);
if (mbar_p)
{
it->menu_bar_id = (intptr_t) ptr;
it->wind_ptr = mbw_ptr;
}
if (ptr)
msg->AddPointer ("menuptr", ptr);
m->AddItem (it);
}
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;
mn->SetRadioMode (0);
BMenuItem *it = mn->Go (BPoint (x, y));
if (it)
{
BMessage *mg = it->Message ();
if (mg)
return (void *) mg->GetPointer ("menuptr");
else
return NULL;
}
return NULL;
}
void
BPopUpMenu_delete (void *menu)
{
delete (BPopUpMenu *) menu;
}
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;
}
void
BMenuBar_delete (void *menubar)
{
BView *vw = (BView *) menubar;
BView *p = vw->Parent ();
if (!p->LockLooper ())
gui_abort ("Failed to lock menu bar parent while removing menubar");
vw->RemoveSelf ();
p->UnlockLooper ();
delete vw;
}
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
BView_set_and_show_sticky_tooltip (void *view, const char *tooltip,
int x, int y)
{
BToolTip *tip;
BView *vw = (BView *) view;
if (!vw->LockLooper ())
gui_abort ("Failed to lock view while showing sticky tooltip");
vw->SetToolTip (tooltip);
tip = vw->ToolTip ();
BPoint pt;
EmacsView *ev = dynamic_cast (vw);
if (ev)
ev->tt_absl_pos = BPoint (x, y);
vw->GetMouse (&pt, NULL, 1);
pt.x -= x;
pt.y -= y;
pt.x = -pt.x;
pt.y = -pt.y;
tip->SetMouseRelativeLocation (pt);
tip->SetSticky (1);
vw->ShowToolTip (tip);
vw->UnlockLooper ();
}
void
BAlert_delete (void *alert)
{
delete (BAlert *) alert;
}
void
BScreen_res (double *rrsx, double *rrsy)
{
BScreen s (B_MAIN_SCREEN_ID);
if (!s.IsValid ())
gui_abort ("Invalid screen for resolution checks");
monitor_info i;
if (s.GetMonitorInfo (&i) == B_OK)
{
*rrsx = (double) i.width / (double) 2.54;
*rrsy = (double) i.height / (double) 2.54;
}
else
{
*rrsx = 72.27;
*rrsy = 72.27;
}
}
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 ();
}
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. */
if (!screen.IsValid ())
gui_abort ("Invalid screen");
space = dpy_color_space = screen.ColorSpace ();
}
if (space == B_RGB32 || space == B_RGB24)
return 24;
if (space == B_RGB16)
return 16;
if (space == B_RGB15)
return 15;
if (space == B_CMAP8)
return 8;
gui_abort ("Bad colorspace for screen");
/* https://www.haiku-os.org/docs/api/classBScreen.html
says a valid screen can't be anything else. */
return -1;
}
int
be_get_display_color_cells (void)
{
color_space space = dpy_color_space;
if (space == B_NO_COLOR_SPACE)
{
BScreen screen;
if (!screen.IsValid ())
gui_abort ("Invalid screen");
space = dpy_color_space = screen.ColorSpace ();
}
if (space == B_RGB32 || space == B_RGB24)
return 1677216;
if (space == B_RGB16)
return 65536;
if (space == B_RGB15)
return 32768;
if (space == B_CMAP8)
return 256;
gui_abort ("Bad colorspace for screen");
return -1;
}
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 ())
gui_abort ("Couldn't lock window for weak move");
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 ())
gui_abort ("Couldn't lock view while setting up double buffering");
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 ())
gui_abort ("Couldn't lock view in flip_and_blit");
view->FlipBuffers ();
view->UnlockLooper ();
}
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 ();
}
int
EmacsView_double_buffered_p (void *vw)
{
EmacsView *view = (EmacsView *) vw;
if (!view->LockLooper ())
gui_abort ("Couldn't lock view testing double buffering status");
int db_p = !!view->offscreen_draw_view;
view->UnlockLooper ();
return db_p;
}
struct popup_file_dialog_data
{
BMessage *msg;
BFilePanel *panel;
BEntry *entry;
};
static void
unwind_popup_file_dialog (void *ptr)
{
struct popup_file_dialog_data *data =
(struct popup_file_dialog_data *) ptr;
BFilePanel *panel = data->panel;
delete panel;
delete data->entry;
delete data->msg;
}
static void
be_popup_file_dialog_safe_set_target (BFilePanel *dialog, BWindow *window)
{
dialog->SetTarget (BMessenger (window));
}
char *
be_popup_file_dialog (int open_p, const char *default_dir, int must_match_p, int dir_only_p,
void *window, const char *save_text, const char *prompt,
void (*block_input_function) (void),
void (*unblock_input_function) (void))
{
ptrdiff_t idx = c_specpdl_idx_from_cxx ();
/* setjmp/longjmp is UB with automatic objects. */
block_input_function ();
BWindow *w = (BWindow *) window;
uint32_t mode = dir_only_p ? B_DIRECTORY_NODE : B_FILE_NODE | B_DIRECTORY_NODE;
BEntry *path = new BEntry;
BMessage *msg = new BMessage ('FPSE');
BFilePanel *panel = new BFilePanel (open_p ? B_OPEN_PANEL : B_SAVE_PANEL,
NULL, NULL, mode);
unblock_input_function ();
struct popup_file_dialog_data dat;
dat.entry = path;
dat.msg = msg;
dat.panel = panel;
record_c_unwind_protect_from_cxx (unwind_popup_file_dialog, &dat);
if (default_dir)
{
if (path->SetTo (default_dir, 0) != B_OK)
default_dir = NULL;
}
panel->SetMessage (msg);
if (default_dir)
panel->SetPanelDirectory (path);
if (save_text)
panel->SetSaveText (save_text);
panel->SetHideWhenDone (0);
panel->Window ()->SetTitle (prompt);
be_popup_file_dialog_safe_set_target (panel, w);
panel->Show ();
panel->Window ()->Show ();
void *buf = alloca (200);
while (1)
{
enum haiku_event_type type;
char *ptr = NULL;
if (!haiku_read_with_timeout (&type, buf, 200, 100000))
{
if (type != FILE_PANEL_EVENT)
haiku_write (type, buf);
else if (!ptr)
ptr = (char *) ((struct haiku_file_panel_event *) buf)->ptr;
}
ssize_t b_s;
haiku_read_size (&b_s);
if (!b_s || b_s == -1 || ptr || panel->Window ()->IsHidden ())
{
c_unbind_to_nil_from_cxx (idx);
return ptr;
}
}
}
void
be_app_quit (void)
{
if (be_app)
{
status_t e;
while (!be_app->Lock ());
be_app->Quit ();
wait_for_thread (app_thread, &e);
}
}
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 ())
gui_abort ("Couldn't lock menubar");
BRect frame = mb->Frame ();
BPoint pt = frame.LeftTop ();
BPoint l = pt;
mb->Parent ()->ConvertToScreen (&pt);
set_mouse_position (pt.x, pt.y);
mb->MouseDown (l);
mb->UnlockLooper ();
}
#ifdef HAVE_NATIVE_IMAGE_API
int
be_can_translate_type_to_bitmap_p (const char *mime)
{
BTranslatorRoster *r = BTranslatorRoster::Default ();
translator_id *ids;
int32 id_len;
if (r->GetAllTranslators (&ids, &id_len) != B_OK)
return 0;
int found_in = 0;
int found_out = 0;
for (int i = 0; i < id_len; ++i)
{
found_in = 0;
found_out = 0;
const translation_format *i_fmts;
const translation_format *o_fmts;
int32 i_count, o_count;
if (r->GetInputFormats (ids[i], &i_fmts, &i_count) != B_OK)
continue;
if (r->GetOutputFormats (ids[i], &o_fmts, &o_count) != B_OK)
continue;
for (int x = 0; x < i_count; ++x)
{
if (!strcmp (i_fmts[x].MIME, mime))
{
found_in = 1;
break;
}
}
for (int x = 0; x < i_count; ++x)
{
if (!strcmp (o_fmts[x].MIME, "image/x-be-bitmap") ||
!strcmp (o_fmts[x].MIME, "image/x-vnd.Be-bitmap"))
{
found_out = 1;
break;
}
}
if (found_in && found_out)
break;
}
delete [] ids;
return found_in && found_out;
}
void *
be_translate_bitmap_from_file_name (const char *filename)
{
BBitmap *bm = BTranslationUtils::GetBitmap (filename);
return bm;
}
void *
be_translate_bitmap_from_memory (const void *buf, size_t bytes)
{
BMemoryIO io (buf, bytes);
BBitmap *bm = BTranslationUtils::GetBitmap (&io);
return bm;
}
#endif
size_t
BBitmap_bytes_length (void *bitmap)
{
BBitmap *bm = (BBitmap *) bitmap;
return bm->BitsLength ();
}
void
BView_show_tooltip (void *view)
{
BView *vw = (BView *) view;
if (vw->LockLooper ())
{
vw->ShowToolTip (vw->ToolTip ());
vw->UnlockLooper ();
}
}
#ifdef USE_BE_CAIRO
cairo_surface_t *
EmacsView_cairo_surface (void *view)
{
EmacsView *vw = (EmacsView *) view;
EmacsWindow *wn = (EmacsWindow *) vw->Window ();
return vw->cr_surface ? vw->cr_surface : wn->cr_surface;
}
void
BView_cr_dump_clipping (void *view, cairo_t *ctx)
{
BView *vw = (BView *) find_appropriate_view_for_draw (view);
BRegion cr;
vw->GetClippingRegion (&cr);
for (int i = 0; i < cr.CountRects (); ++i)
{
BRect r = cr.RectAt (i);
cairo_rectangle (ctx, r.left, r.top, r.Width () + 1,
r.Height () + 1);
}
cairo_clip (ctx);
}
void
EmacsWindow_begin_cr_critical_section (void *window)
{
EmacsWindow *w = (EmacsWindow *) window;
if (!w->surface_lock.Lock ())
gui_abort ("Couldn't lock cairo surface");
BView *vw = (BView *) w->FindView ("Emacs");
EmacsView *ev = dynamic_cast (vw);
if (ev && !ev->cr_surface_lock.Lock ())
gui_abort ("Couldn't lock view cairo surface");
}
void
EmacsWindow_end_cr_critical_section (void *window)
{
EmacsWindow *w = (EmacsWindow *) window;
w->surface_lock.Unlock ();
BView *vw = (BView *) w->FindView ("Emacs");
EmacsView *ev = dynamic_cast (vw);
if (ev)
ev->cr_surface_lock.Unlock ();
}
#endif
int
be_string_width_with_plain_font (const char *str)
{
return be_plain_font->StringWidth (str);
}
int
be_plain_font_height (void)
{
struct font_height fheight;
be_plain_font->GetHeight (&fheight);
return fheight.ascent + fheight.descent;
}
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;
}
void
BWindow_set_min_size (void *window, int width, int height)
{
BWindow *w = (BWindow *) window;
if (!w->LockLooper ())
gui_abort ("Failed to lock window looper setting min size");
w->SetSizeLimits (width, -1, height, -1);
w->UnlockLooper ();
}
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 ();
}