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