/* Haiku window system support
Copyright (C) 2021 Free Software Foundation, Inc.
This file is part of GNU Emacs.
GNU Emacs is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at
your option) any later version.
GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Emacs. If not, see . */
#include
#include
#include "lisp.h"
#include "frame.h"
#include "blockinput.h"
#include "termchar.h"
#include "font.h"
#include "keyboard.h"
#include "buffer.h"
#include "dispextern.h"
#include "haikugui.h"
#include "haikuterm.h"
#include "haiku_support.h"
#include "termhooks.h"
#define RGB_TO_ULONG(r, g, b) \
(((r) << 16) | ((g) << 8) | (b));
#define RED_FROM_ULONG(color) (((color) >> 16) & 0xff)
#define GREEN_FROM_ULONG(color) (((color) >> 8) & 0xff)
#define BLUE_FROM_ULONG(color) ((color) & 0xff)
/* The frame of the currently visible tooltip. */
static Lisp_Object tip_frame;
/* The window-system window corresponding to the frame of the
currently visible tooltip. */
static Window tip_window;
/* A timer that hides or deletes the currently visible tooltip when it
fires. */
static Lisp_Object tip_timer;
/* STRING argument of last `x-show-tip' call. */
static Lisp_Object tip_last_string;
/* Normalized FRAME argument of last `x-show-tip' call. */
static Lisp_Object tip_last_frame;
/* PARMS argument of last `x-show-tip' call. */
static Lisp_Object tip_last_parms;
static void
haiku_explicitly_set_name (struct frame *f, Lisp_Object arg, Lisp_Object oldval);
static void
haiku_set_title (struct frame *f, Lisp_Object name, Lisp_Object old_name);
static ptrdiff_t image_cache_refcount;
static Lisp_Object
get_geometry_from_preferences (struct haiku_display_info *dpyinfo,
Lisp_Object parms)
{
struct {
const char *val;
const char *cls;
Lisp_Object tem;
} r[] = {
{ "width", "Width", Qwidth },
{ "height", "Height", Qheight },
{ "left", "Left", Qleft },
{ "top", "Top", Qtop },
};
int i;
for (i = 0; i < ARRAYELTS (r); ++i)
{
if (NILP (Fassq (r[i].tem, parms)))
{
Lisp_Object value
= gui_display_get_arg (dpyinfo, parms, r[i].tem, r[i].val, r[i].cls,
RES_TYPE_NUMBER);
if (! EQ (value, Qunbound))
parms = Fcons (Fcons (r[i].tem, value), parms);
}
}
return parms;
}
void
haiku_change_tool_bar_height (struct frame *f, int height)
{
int unit = FRAME_LINE_HEIGHT (f);
int old_height = FRAME_TOOL_BAR_HEIGHT (f);
int lines = (height + unit - 1) / unit;
Lisp_Object fullscreen = get_frame_param (f, Qfullscreen);
/* Make sure we redisplay all windows in this frame. */
fset_redisplay (f);
FRAME_TOOL_BAR_HEIGHT (f) = height;
FRAME_TOOL_BAR_LINES (f) = lines;
store_frame_param (f, Qtool_bar_lines, make_fixnum (lines));
if (FRAME_HAIKU_WINDOW (f) && FRAME_TOOL_BAR_HEIGHT (f) == 0)
{
clear_frame (f);
clear_current_matrices (f);
}
if ((height < old_height) && WINDOWP (f->tool_bar_window))
clear_glyph_matrix (XWINDOW (f->tool_bar_window)->current_matrix);
if (!f->tool_bar_resized)
{
/* As long as tool_bar_resized is false, effectively try to change
F's native height. */
if (NILP (fullscreen) || EQ (fullscreen, Qfullwidth))
adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
1, false, Qtool_bar_lines);
else
adjust_frame_size (f, -1, -1, 4, false, Qtool_bar_lines);
f->tool_bar_resized = f->tool_bar_redisplayed;
}
else
/* Any other change may leave the native size of F alone. */
adjust_frame_size (f, -1, -1, 3, false, Qtool_bar_lines);
/* adjust_frame_size might not have done anything, garbage frame
here. */
adjust_frame_glyphs (f);
SET_FRAME_GARBAGED (f);
if (FRAME_HAIKU_WINDOW (f))
haiku_clear_under_internal_border (f);
}
void
haiku_change_tab_bar_height (struct frame *f, int height)
{
int unit = FRAME_LINE_HEIGHT (f);
int old_height = FRAME_TAB_BAR_HEIGHT (f);
int lines = (height + unit - 1) / unit;
Lisp_Object fullscreen = get_frame_param (f, Qfullscreen);
/* Make sure we redisplay all windows in this frame. */
fset_redisplay (f);
/* Recalculate tab bar and frame text sizes. */
FRAME_TAB_BAR_HEIGHT (f) = height;
FRAME_TAB_BAR_LINES (f) = lines;
store_frame_param (f, Qtab_bar_lines, make_fixnum (lines));
if (FRAME_HAIKU_WINDOW (f) && FRAME_TAB_BAR_HEIGHT (f) == 0)
{
clear_frame (f);
clear_current_matrices (f);
}
if ((height < old_height) && WINDOWP (f->tab_bar_window))
clear_glyph_matrix (XWINDOW (f->tab_bar_window)->current_matrix);
if (!f->tab_bar_resized)
{
/* As long as tab_bar_resized is false, effectively try to change
F's native height. */
if (NILP (fullscreen) || EQ (fullscreen, Qfullwidth))
adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
1, false, Qtab_bar_lines);
else
adjust_frame_size (f, -1, -1, 4, false, Qtab_bar_lines);
f->tab_bar_resized = f->tab_bar_redisplayed;
}
else
/* Any other change may leave the native size of F alone. */
adjust_frame_size (f, -1, -1, 3, false, Qtab_bar_lines);
/* adjust_frame_size might not have done anything, garbage frame
here. */
adjust_frame_glyphs (f);
SET_FRAME_GARBAGED (f);
if (FRAME_HAIKU_WINDOW (f))
haiku_clear_under_internal_border (f);
}
static void
haiku_set_tool_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
{
int nlines;
/* Treat tool bars like menu bars. */
if (FRAME_MINIBUF_ONLY_P (f))
return;
/* Use VALUE only if an int >= 0. */
if (RANGED_FIXNUMP (0, value, INT_MAX))
nlines = XFIXNAT (value);
else
nlines = 0;
haiku_change_tool_bar_height (f, nlines * FRAME_LINE_HEIGHT (f));
}
static void
haiku_set_tab_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
{
int olines = FRAME_TAB_BAR_LINES (f);
int nlines;
/* Treat tab bars like menu bars. */
if (FRAME_MINIBUF_ONLY_P (f))
return;
/* Use VALUE only if an int >= 0. */
if (RANGED_FIXNUMP (0, value, INT_MAX))
nlines = XFIXNAT (value);
else
nlines = 0;
if (nlines != olines && (olines == 0 || nlines == 0))
haiku_change_tab_bar_height (f, nlines * FRAME_LINE_HEIGHT (f));
}
int
haiku_get_color (const char *name, Emacs_Color *color)
{
unsigned short r16, g16, b16;
Lisp_Object tem;
if (parse_color_spec (name, &r16, &g16, &b16))
{
color->pixel = RGB_TO_ULONG (r16 / 256, g16 / 256, b16 / 256);
color->red = r16;
color->green = g16;
color->blue = b16;
return 0;
}
else
{
block_input ();
eassert (x_display_list && !NILP (x_display_list->color_map));
tem = x_display_list->color_map;
for (; CONSP (tem); tem = XCDR (tem))
{
Lisp_Object col = XCAR (tem);
if (CONSP (col) && !strcasecmp (SSDATA (XCAR (col)), name))
{
int32_t clr = XFIXNUM (XCDR (col));
color->pixel = clr;
color->red = RED_FROM_ULONG (clr) * 257;
color->green = GREEN_FROM_ULONG (clr) * 257;
color->blue = BLUE_FROM_ULONG (clr) * 257;
unblock_input ();
return 0;
}
}
unblock_input ();
}
return 1;
}
static struct haiku_display_info *
haiku_display_info_for_name (Lisp_Object name)
{
CHECK_STRING (name);
if (!NILP (Fstring_equal (name, build_string ("be"))))
{
if (!x_display_list)
return x_display_list;
error ("Be windowing not initialized");
}
error ("Be displays can only be named \"be\"");
}
static struct haiku_display_info *
check_haiku_display_info (Lisp_Object object)
{
struct haiku_display_info *dpyinfo = NULL;
if (NILP (object))
{
struct frame *sf = XFRAME (selected_frame);
if (FRAME_HAIKU_P (sf) && FRAME_LIVE_P (sf))
dpyinfo = FRAME_DISPLAY_INFO (sf);
else if (x_display_list)
dpyinfo = x_display_list;
else
error ("Be windowing not present");
}
else if (TERMINALP (object))
{
struct terminal *t = decode_live_terminal (object);
if (t->type != output_haiku)
error ("Terminal %d is not a Be display", t->id);
dpyinfo = t->display_info.haiku;
}
else if (STRINGP (object))
dpyinfo = haiku_display_info_for_name (object);
else
{
struct frame *f = decode_window_system_frame (object);
dpyinfo = FRAME_DISPLAY_INFO (f);
}
return dpyinfo;
}
static void
haiku_set_title_bar_text (struct frame *f, Lisp_Object text)
{
if (FRAME_HAIKU_WINDOW (f))
{
block_input ();
BWindow_retitle (FRAME_HAIKU_WINDOW (f), SSDATA (ENCODE_UTF_8 (text)));
unblock_input ();
}
}
static void
haiku_set_title (struct frame *f, Lisp_Object name, Lisp_Object old_name)
{
/* Don't change the title if it's already NAME. */
if (EQ (name, f->title))
return;
update_mode_lines = 26;
fset_title (f, name);
if (NILP (name))
name = f->name;
haiku_set_title_bar_text (f, name);
}
static void
haiku_explicitly_set_name (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
{
haiku_set_name (f, arg, 1);
}
static void
haiku_set_no_accept_focus (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
{
block_input ();
if (!EQ (new_value, old_value))
FRAME_NO_ACCEPT_FOCUS (f) = !NILP (new_value);
if (FRAME_HAIKU_WINDOW (f))
{
BWindow_set_avoid_focus (FRAME_HAIKU_WINDOW (f),
FRAME_NO_ACCEPT_FOCUS (f));
}
unblock_input ();
}
static void
unwind_create_frame (Lisp_Object frame)
{
struct frame *f = XFRAME (frame);
/* If frame is already dead, nothing to do. This can happen if the
display is disconnected after the frame has become official, but
before x_create_frame removes the unwind protect. */
if (!FRAME_LIVE_P (f))
return;
/* If frame is ``official'', nothing to do. */
if (NILP (Fmemq (frame, Vframe_list)))
{
#if defined GLYPH_DEBUG && defined ENABLE_CHECKING
struct haiku_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
#endif
/* If the frame's image cache refcount is still the same as our
private shadow variable, it means we are unwinding a frame
for which we didn't yet call init_frame_faces, where the
refcount is incremented. Therefore, we increment it here, so
that free_frame_faces, called in free_frame_resources later,
will not mistakenly decrement the counter that was not
incremented yet to account for this new frame. */
if (FRAME_IMAGE_CACHE (f) != NULL
&& FRAME_IMAGE_CACHE (f)->refcount == image_cache_refcount)
FRAME_IMAGE_CACHE (f)->refcount++;
haiku_free_frame_resources (f);
free_glyphs (f);
#if defined GLYPH_DEBUG && defined ENABLE_CHECKING
/* Check that reference counts are indeed correct. */
if (dpyinfo->terminal->image_cache)
eassert (dpyinfo->terminal->image_cache->refcount == image_cache_refcount);
#endif
}
}
static void
unwind_create_tip_frame (Lisp_Object frame)
{
unwind_create_frame (frame);
tip_window = NULL;
tip_frame = Qnil;
}
static void
haiku_set_foreground_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
{
struct haiku_output *output = FRAME_OUTPUT_DATA (f);
unsigned long old_fg;
Emacs_Color color;
if (haiku_get_color (SSDATA (arg), &color))
{
store_frame_param (f, Qforeground_color, oldval);
unblock_input ();
error ("Bad color");
}
old_fg = FRAME_FOREGROUND_PIXEL (f);
FRAME_FOREGROUND_PIXEL (f) = color.pixel;
if (FRAME_HAIKU_WINDOW (f))
{
block_input ();
if (output->cursor_color.pixel == old_fg)
{
output->cursor_color.pixel = old_fg;
output->cursor_color.red = RED_FROM_ULONG (old_fg);
output->cursor_color.green = GREEN_FROM_ULONG (old_fg);
output->cursor_color.blue = BLUE_FROM_ULONG (old_fg);
}
unblock_input ();
update_face_from_frame_parameter (f, Qforeground_color, arg);
if (FRAME_VISIBLE_P (f))
redraw_frame (f);
}
}
static Lisp_Object
haiku_create_frame (Lisp_Object parms, int ttip_p)
{
struct frame *f;
Lisp_Object frame, tem;
Lisp_Object name;
bool minibuffer_only = false;
long window_prompting = 0;
ptrdiff_t count = SPECPDL_INDEX ();
Lisp_Object display;
struct haiku_display_info *dpyinfo = NULL;
Lisp_Object parent_frame;
Lisp_Object font;
struct kboard *kb;
parms = Fcopy_alist (parms);
Vx_resource_name = Vinvocation_name;
display = gui_display_get_arg (dpyinfo, parms, Qterminal, 0, 0,
RES_TYPE_STRING);
if (EQ (display, Qunbound))
display = Qnil;
dpyinfo = check_haiku_display_info (display);
kb = dpyinfo->terminal->kboard;
if (!dpyinfo->terminal->name)
error ("Terminal is not live, can't create new frames on it");
name = gui_display_get_arg (dpyinfo, parms, Qname, 0, 0,
RES_TYPE_STRING);
if (!STRINGP (name)
&& ! EQ (name, Qunbound)
&& ! NILP (name))
error ("Invalid frame name--not a string or nil");
if (STRINGP (name))
Vx_resource_name = name;
block_input ();
/* make_frame_without_minibuffer can run Lisp code and garbage collect. */
/* No need to protect DISPLAY because that's not used after passing
it to make_frame_without_minibuffer. */
frame = Qnil;
tem = gui_display_get_arg (dpyinfo, parms, Qminibuffer,
"minibuffer", "Minibuffer",
RES_TYPE_SYMBOL);
if (EQ (tem, Qnone) || NILP (tem))
f = make_frame_without_minibuffer (Qnil, kb, display);
else if (EQ (tem, Qonly))
{
f = make_minibuffer_frame ();
minibuffer_only = 1;
}
else if (WINDOWP (tem))
f = make_frame_without_minibuffer (tem, kb, display);
else
f = make_frame (1);
XSETFRAME (frame, f);
f->terminal = dpyinfo->terminal;
f->output_method = output_haiku;
f->output_data.haiku = xzalloc (sizeof *f->output_data.haiku);
fset_icon_name (f, gui_display_get_arg (dpyinfo, parms, Qicon_name,
"iconName", "Title",
RES_TYPE_STRING));
if (! STRINGP (f->icon_name))
fset_icon_name (f, Qnil);
FRAME_DISPLAY_INFO (f) = dpyinfo;
/* With FRAME_DISPLAY_INFO set up, this unwind-protect is safe. */
if (!ttip_p)
record_unwind_protect (unwind_create_frame, frame);
else
record_unwind_protect (unwind_create_tip_frame, frame);
FRAME_OUTPUT_DATA (f)->parent_desc = NULL;
FRAME_OUTPUT_DATA (f)->explicit_parent = 0;
/* Set the name; the functions to which we pass f expect the name to
be set. */
if (EQ (name, Qunbound) || NILP (name) || ! STRINGP (name))
{
fset_name (f, Vinvocation_name);
f->explicit_name = 0;
}
else
{
fset_name (f, name);
f->explicit_name = 1;
specbind (Qx_resource_name, name);
}
register_font_driver (&haikufont_driver, f);
image_cache_refcount =
FRAME_IMAGE_CACHE (f) ? FRAME_IMAGE_CACHE (f)->refcount : 0;
gui_default_parameter (f, parms, Qfont_backend, Qnil,
"fontBackend", "FontBackend", RES_TYPE_STRING);
Lisp_Object font_param = gui_display_get_arg (dpyinfo,
parms, Qfont, NULL, NULL,
RES_TYPE_STRING);
if (EQ (font_param, Qunbound))
font_param = Qnil;
font = !NILP (font_param) ? font_param
: gui_display_get_arg (dpyinfo, parms, Qfont, "font", "Font",
RES_TYPE_STRING);
font = font_open_by_name (f, STRINGP (font) ? font : build_string
(ttip_p ? "Sans Serif" : "monospace"));
if (!NILP (font_param))
gui_set_frame_parameters (f, Fcons (Fcons (Qfont_parameter, font_param),
Qnil));
gui_default_parameter (f, parms, Qfont, font, "font", "Font", RES_TYPE_STRING);
unblock_input ();
gui_default_parameter (f, parms, Qborder_width, make_fixnum (0),
"borderwidth", "BorderWidth", RES_TYPE_NUMBER);
gui_default_parameter (f, parms, Qinternal_border_width, make_fixnum (ttip_p ? 1 : 2),
"internalBorderWidth", "InternalBorderWidth",
RES_TYPE_NUMBER);
gui_default_parameter (f, parms, Qchild_frame_border_width, Qnil,
"childFrameBorderWidth", "childFrameBorderWidth",
RES_TYPE_NUMBER);
gui_default_parameter (f, parms, Qright_divider_width, make_fixnum (0),
NULL, NULL, RES_TYPE_NUMBER);
gui_default_parameter (f, parms, Qbottom_divider_width, make_fixnum (0),
NULL, NULL, RES_TYPE_NUMBER);
gui_default_parameter (f, parms, Qvertical_scroll_bars, !ttip_p ? Qt : Qnil,
"verticalScrollBars", "VerticalScrollBars",
RES_TYPE_SYMBOL);
gui_default_parameter (f, parms, Qhorizontal_scroll_bars, Qnil,
"horizontalScrollBars", "HorizontalScrollBars",
RES_TYPE_SYMBOL);
gui_default_parameter (f, parms, Qforeground_color, build_string ("black"),
"foreground", "Foreground", RES_TYPE_STRING);
gui_default_parameter (f, parms, Qbackground_color, build_string ("white"),
"background", "Background", RES_TYPE_STRING);
gui_default_parameter (f, parms, Qline_spacing, Qnil,
"lineSpacing", "LineSpacing", RES_TYPE_NUMBER);
gui_default_parameter (f, parms, Qleft_fringe, Qnil,
"leftFringe", "LeftFringe", RES_TYPE_NUMBER);
gui_default_parameter (f, parms, Qright_fringe, Qnil,
"rightFringe", "RightFringe", RES_TYPE_NUMBER);
gui_default_parameter (f, parms, Qno_special_glyphs, ttip_p ? Qnil : Qt,
NULL, NULL, RES_TYPE_BOOLEAN);
init_frame_faces (f);
/* Read comment about this code in corresponding place in xfns.c. */
tem = gui_display_get_arg (dpyinfo, parms, Qmin_width, NULL, NULL,
RES_TYPE_NUMBER);
if (FIXNUMP (tem))
store_frame_param (f, Qmin_width, tem);
tem = gui_display_get_arg (dpyinfo, parms, Qmin_height, NULL, NULL,
RES_TYPE_NUMBER);
if (FIXNUMP (tem))
store_frame_param (f, Qmin_height, tem);
adjust_frame_size (f, FRAME_COLS (f) * FRAME_COLUMN_WIDTH (f),
FRAME_LINES (f) * FRAME_LINE_HEIGHT (f), 5, 1,
Qx_create_frame_1);
tem = gui_display_get_arg (dpyinfo, parms, Qundecorated, NULL, NULL,
RES_TYPE_BOOLEAN);
FRAME_UNDECORATED (f) = !NILP (tem) && !EQ (tem, Qunbound);
store_frame_param (f, Qundecorated, FRAME_UNDECORATED (f) ? Qt : Qnil);
parent_frame = gui_display_get_arg (dpyinfo, parms, Qparent_frame, NULL, NULL,
RES_TYPE_SYMBOL);
if (!NILP (parent_frame)
|| EQ (parent_frame, Qunbound)
|| NILP (parent_frame)
|| !FRAMEP (parent_frame)
|| !FRAME_LIVE_P (XFRAME (parent_frame)))
parent_frame = Qnil;
fset_parent_frame (f, parent_frame);
store_frame_param (f, Qparent_frame, parent_frame);
gui_default_parameter (f, parms, Qz_group, Qnil, NULL, NULL, RES_TYPE_SYMBOL);
gui_default_parameter (f, parms, Qno_focus_on_map, Qnil,
NULL, NULL, RES_TYPE_BOOLEAN);
if (!ttip_p)
gui_default_parameter (f, parms, Qno_accept_focus, Qnil,
NULL, NULL, RES_TYPE_BOOLEAN);
/* The resources controlling the menu-bar and tool-bar are
processed specially at startup, and reflected in the mode
variables; ignore them here. */
gui_default_parameter (f, parms, Qmenu_bar_lines,
NILP (Vmenu_bar_mode) || ttip_p
? make_fixnum (0) : make_fixnum (1),
NULL, NULL, RES_TYPE_NUMBER);
gui_default_parameter (f, parms, Qtool_bar_lines,
NILP (Vtool_bar_mode) || ttip_p
? make_fixnum (0) : make_fixnum (1),
NULL, NULL, RES_TYPE_NUMBER);
gui_default_parameter (f, parms, Qbuffer_predicate, Qnil, "bufferPredicate",
"BufferPredicate", RES_TYPE_SYMBOL);
gui_default_parameter (f, parms, Qtitle, Qnil, "title", "Title",
RES_TYPE_STRING);
parms = get_geometry_from_preferences (dpyinfo, parms);
window_prompting = gui_figure_window_size (f, parms, false, true);
if (ttip_p)
{
/* No fringes on tip frame. */
f->fringe_cols = 0;
f->left_fringe_width = 0;
f->right_fringe_width = 0;
/* No dividers on tip frame. */
f->right_divider_width = 0;
f->bottom_divider_width = 0;
f->tooltip = 1;
}
tem = gui_display_get_arg (dpyinfo, parms, Qunsplittable, 0, 0,
RES_TYPE_BOOLEAN);
f->no_split = minibuffer_only || (!EQ (tem, Qunbound) && !NILP (tem));
/* Add `tooltip' frame parameter's default value. */
if (NILP (Fframe_parameter (frame, Qtooltip)) && ttip_p)
Fmodify_frame_parameters (frame, Fcons (Fcons (Qtooltip, Qt), Qnil));
#define ASSIGN_CURSOR(cursor, be_cursor) \
(FRAME_OUTPUT_DATA (f)->cursor = be_cursor)
ASSIGN_CURSOR (text_cursor, BCursor_create_i_beam ());
ASSIGN_CURSOR (nontext_cursor, BCursor_create_default ());
ASSIGN_CURSOR (modeline_cursor, BCursor_create_default ());
ASSIGN_CURSOR (hand_cursor, BCursor_create_grab ());
ASSIGN_CURSOR (hourglass_cursor, BCursor_create_progress_cursor ());
ASSIGN_CURSOR (horizontal_drag_cursor,
BCursor_from_id (CURSOR_ID_RESIZE_EAST_WEST));
ASSIGN_CURSOR (vertical_drag_cursor,
BCursor_from_id (CURSOR_ID_RESIZE_NORTH_SOUTH));
ASSIGN_CURSOR (left_edge_cursor,
BCursor_from_id (CURSOR_ID_RESIZE_WEST));
ASSIGN_CURSOR (top_left_corner_cursor,
BCursor_from_id (CURSOR_ID_RESIZE_NORTH_WEST));
ASSIGN_CURSOR (top_edge_cursor,
BCursor_from_id (CURSOR_ID_RESIZE_NORTH));
ASSIGN_CURSOR (top_right_corner_cursor,
BCursor_from_id (CURSOR_ID_RESIZE_NORTH_EAST));
ASSIGN_CURSOR (right_edge_cursor,
BCursor_from_id (CURSOR_ID_RESIZE_EAST));
ASSIGN_CURSOR (bottom_right_corner_cursor,
BCursor_from_id (CURSOR_ID_RESIZE_SOUTH_EAST));
ASSIGN_CURSOR (bottom_edge_cursor,
BCursor_from_id (CURSOR_ID_RESIZE_SOUTH));
ASSIGN_CURSOR (bottom_left_corner_cursor,
BCursor_from_id (CURSOR_ID_RESIZE_SOUTH_WEST));
ASSIGN_CURSOR (no_cursor,
BCursor_from_id (CURSOR_ID_NO_CURSOR));
ASSIGN_CURSOR (current_cursor, FRAME_OUTPUT_DATA (f)->text_cursor);
#undef ASSIGN_CURSOR
if (ttip_p)
f->no_split = true;
f->terminal->reference_count++;
FRAME_OUTPUT_DATA (f)->window = BWindow_new (&FRAME_OUTPUT_DATA (f)->view);
if (!FRAME_OUTPUT_DATA (f)->window)
xsignal1 (Qerror, build_unibyte_string ("Could not create window"));
if (!minibuffer_only && !ttip_p && FRAME_EXTERNAL_MENU_BAR (f))
initialize_frame_menubar (f);
FRAME_OUTPUT_DATA (f)->window_desc = FRAME_OUTPUT_DATA (f)->window;
Vframe_list = Fcons (frame, Vframe_list);
gui_default_parameter (f, parms, Qicon_type, Qnil,
"bitmapIcon", "BitmapIcon", RES_TYPE_SYMBOL);
if (ttip_p)
{
gui_default_parameter (f, parms, Qundecorated, Qt, NULL, NULL, RES_TYPE_BOOLEAN);
gui_default_parameter (f, parms, Qno_accept_focus, Qt, NULL, NULL,
RES_TYPE_BOOLEAN);
}
gui_default_parameter (f, parms, Qauto_raise, Qnil,
"autoRaise", "AutoRaiseLower", RES_TYPE_BOOLEAN);
gui_default_parameter (f, parms, Qauto_lower, Qnil,
"autoLower", "AutoLower", RES_TYPE_BOOLEAN);
gui_default_parameter (f, parms, Qcursor_type, Qbox,
"cursorType", "CursorType", RES_TYPE_SYMBOL);
gui_default_parameter (f, parms, Qscroll_bar_width, Qnil,
"scrollBarWidth", "ScrollBarWidth",
RES_TYPE_NUMBER);
gui_default_parameter (f, parms, Qscroll_bar_height, Qnil,
"scrollBarHeight", "ScrollBarHeight",
RES_TYPE_NUMBER);
gui_default_parameter (f, parms, Qalpha, Qnil,
"alpha", "Alpha", RES_TYPE_NUMBER);
gui_default_parameter (f, parms, Qfullscreen, Qnil,
"fullscreen", "Fullscreen", RES_TYPE_SYMBOL);
f->can_set_window_size = true;
adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
0, true, ttip_p ? Qtip_frame : Qx_create_frame_2);
if (!FRAME_OUTPUT_DATA (f)->explicit_parent && !ttip_p)
{
Lisp_Object visibility;
visibility = gui_display_get_arg (dpyinfo, parms, Qvisibility, 0, 0,
RES_TYPE_SYMBOL);
if (EQ (visibility, Qunbound))
visibility = Qt;
if (EQ (visibility, Qicon))
haiku_iconify_frame (f);
else if (!NILP (visibility))
haiku_visualize_frame (f);
else /* Qnil */
{
f->was_invisible = true;
}
}
if (FRAME_HAS_MINIBUF_P (f)
&& (!FRAMEP (KVAR (kb, Vdefault_minibuffer_frame))
|| !FRAME_LIVE_P (XFRAME (KVAR (kb, Vdefault_minibuffer_frame)))))
kset_default_minibuffer_frame (kb, frame);
for (tem = parms; CONSP (tem); tem = XCDR (tem))
if (CONSP (XCAR (tem)) && !NILP (XCAR (XCAR (tem))))
fset_param_alist (f, Fcons (XCAR (tem), f->param_alist));
if (window_prompting & USPosition)
haiku_set_offset (f, f->left_pos, f->top_pos, 1);
/* Make sure windows on this frame appear in calls to next-window
and similar functions. */
Vwindow_list = Qnil;
return unbind_to (count, frame);
}
static void
compute_tip_xy (struct frame *f,
Lisp_Object parms, Lisp_Object dx, Lisp_Object dy,
int width, int height, int *root_x, int *root_y)
{
Lisp_Object left, top, right, bottom;
int min_x = 0, min_y = 0, max_x = 0, max_y = 0;
/* User-specified position? */
left = Fcdr (Fassq (Qleft, parms));
top = Fcdr (Fassq (Qtop, parms));
right = Fcdr (Fassq (Qright, parms));
bottom = Fcdr (Fassq (Qbottom, parms));
/* Move the tooltip window where the mouse pointer is. Resize and
show it. */
if ((!FIXNUMP (left) && !FIXNUMP (right))
|| (!FIXNUMP (top) && !FIXNUMP (bottom)))
{
int x, y;
/* Default min and max values. */
min_x = 0;
min_y = 0;
BScreen_px_dim (&max_x, &max_y);
block_input ();
BView_get_mouse (FRAME_HAIKU_VIEW (f), &x, &y);
BView_convert_to_screen (FRAME_HAIKU_VIEW (f), &x, &y);
*root_x = x;
*root_y = y;
unblock_input ();
}
if (FIXNUMP (top))
*root_y = XFIXNUM (top);
else if (FIXNUMP (bottom))
*root_y = XFIXNUM (bottom) - height;
else if (*root_y + XFIXNUM (dy) <= min_y)
*root_y = min_y; /* Can happen for negative dy */
else if (*root_y + XFIXNUM (dy) + height <= max_y)
/* It fits below the pointer */
*root_y += XFIXNUM (dy);
else if (height + XFIXNUM (dy) + min_y <= *root_y)
/* It fits above the pointer. */
*root_y -= height + XFIXNUM (dy);
else
/* Put it on the top. */
*root_y = min_y;
if (FIXNUMP (left))
*root_x = XFIXNUM (left);
else if (FIXNUMP (right))
*root_x = XFIXNUM (right) - width;
else if (*root_x + XFIXNUM (dx) <= min_x)
*root_x = 0; /* Can happen for negative dx */
else if (*root_x + XFIXNUM (dx) + width <= max_x)
/* It fits to the right of the pointer. */
*root_x += XFIXNUM (dx);
else if (width + XFIXNUM (dx) + min_x <= *root_x)
/* It fits to the left of the pointer. */
*root_x -= width + XFIXNUM (dx);
else
/* Put it left justified on the screen -- it ought to fit that way. */
*root_x = min_x;
}
static Lisp_Object
haiku_hide_tip (bool delete)
{
if (!NILP (tip_timer))
{
call1 (Qcancel_timer, tip_timer);
tip_timer = Qnil;
}
if (NILP (tip_frame)
|| (!delete && FRAMEP (tip_frame)
&& !FRAME_VISIBLE_P (XFRAME (tip_frame))))
return Qnil;
else
{
ptrdiff_t count;
Lisp_Object was_open = Qnil;
count = SPECPDL_INDEX ();
specbind (Qinhibit_redisplay, Qt);
specbind (Qinhibit_quit, Qt);
if (FRAMEP (tip_frame))
{
if (FRAME_LIVE_P (XFRAME (tip_frame)))
{
if (delete)
{
delete_frame (tip_frame, Qnil);
tip_frame = Qnil;
}
else
haiku_unvisualize_frame (XFRAME (tip_frame));
was_open = Qt;
}
else
tip_frame = Qnil;
}
else
tip_frame = Qnil;
return unbind_to (count, was_open);
}
}
static void
haiku_set_undecorated (struct frame *f, Lisp_Object new_value,
Lisp_Object old_value)
{
if (EQ (new_value, old_value))
return;
block_input ();
FRAME_UNDECORATED (f) = !NILP (new_value);
BWindow_change_decoration (FRAME_HAIKU_WINDOW (f), NILP (new_value));
unblock_input ();
}
static void
haiku_set_menu_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
{
int nlines;
if (TYPE_RANGED_FIXNUMP (int, value))
nlines = XFIXNUM (value);
else
nlines = 0;
fset_redisplay (f);
FRAME_MENU_BAR_LINES (f) = 0;
FRAME_MENU_BAR_HEIGHT (f) = 0;
if (nlines)
{
FRAME_EXTERNAL_MENU_BAR (f) = 1;
if (FRAME_HAIKU_P (f) && !FRAME_HAIKU_MENU_BAR (f))
XWINDOW (FRAME_SELECTED_WINDOW (f))->update_mode_line = 1;
}
else
{
if (FRAME_EXTERNAL_MENU_BAR (f))
free_frame_menubar (f);
FRAME_EXTERNAL_MENU_BAR (f) = 0;
if (FRAME_HAIKU_P (f))
FRAME_HAIKU_MENU_BAR (f) = 0;
}
adjust_frame_glyphs (f);
}
void
haiku_set_background_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
{
CHECK_STRING (arg);
block_input ();
Emacs_Color color;
if (haiku_get_color (SSDATA (arg), &color))
{
store_frame_param (f, Qbackground_color, oldval);
unblock_input ();
error ("Bad color");
}
FRAME_OUTPUT_DATA (f)->cursor_fg = color.pixel;
FRAME_BACKGROUND_PIXEL (f) = color.pixel;
if (FRAME_HAIKU_VIEW (f))
{
struct face *defface;
BView_draw_lock (FRAME_HAIKU_VIEW (f));
BView_SetViewColor (FRAME_HAIKU_VIEW (f), color.pixel);
BView_draw_unlock (FRAME_HAIKU_VIEW (f));
defface = FACE_FROM_ID_OR_NULL (f, DEFAULT_FACE_ID);
if (defface)
{
defface->background = color.pixel;
update_face_from_frame_parameter (f, Qbackground_color, arg);
clear_frame (f);
}
}
if (FRAME_VISIBLE_P (f))
SET_FRAME_GARBAGED (f);
unblock_input ();
}
void
haiku_set_cursor_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
{
CHECK_STRING (arg);
block_input ();
Emacs_Color color;
if (haiku_get_color (SSDATA (arg), &color))
{
store_frame_param (f, Qcursor_color, oldval);
unblock_input ();
error ("Bad color");
}
FRAME_CURSOR_COLOR (f) = color;
if (FRAME_VISIBLE_P (f))
{
gui_update_cursor (f, 0);
gui_update_cursor (f, 1);
}
update_face_from_frame_parameter (f, Qcursor_color, arg);
unblock_input ();
}
void
haiku_set_cursor_type (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
{
set_frame_cursor_types (f, arg);
}
unsigned long
XGetPixel (haiku bitmap, int x, int y)
{
unsigned char *data;
int32_t bytes_per_row;
int mono_p;
int left;
int right;
int top;
int bottom;
data = BBitmap_data (bitmap);
BBitmap_dimensions (bitmap, &left, &top, &right, &bottom,
&bytes_per_row, &mono_p);
return mono_p ? data[(y * bytes_per_row + x / 8)] & (0x80 >> x % 8) :
((uint32_t *) (data + (bytes_per_row * y)))[x];
}
void
XPutPixel (haiku bitmap, int x, int y, unsigned long pixel)
{
unsigned char *data;
int32_t bytes_per_row;
int mono_p;
int left;
int right;
int top;
int bottom;
data = BBitmap_data (bitmap);
BBitmap_dimensions (bitmap, &left, &top, &right, &bottom,
&bytes_per_row, &mono_p);
if (mono_p)
{
ptrdiff_t off = y * bytes_per_row;
ptrdiff_t bit = x % 8;
ptrdiff_t xoff = x / 8;
unsigned char *byte = ((unsigned char *) data) + off + xoff;
if (pixel)
*byte &= ~(1 << bit);
else
*byte |= 1 << bit;
}
else
((uint32_t *) (data + (bytes_per_row * y)))[x] = pixel;
}
void
haiku_free_frame_resources (struct frame *f)
{
haiku window, drawable, mbar;
Mouse_HLInfo *hlinfo;
struct haiku_display_info *dpyinfo;
Lisp_Object bar;
struct scroll_bar *b;
block_input ();
check_window_system (f);
hlinfo = MOUSE_HL_INFO (f);
window = FRAME_HAIKU_WINDOW (f);
drawable = FRAME_HAIKU_VIEW (f);
mbar = FRAME_HAIKU_MENU_BAR (f);
dpyinfo = FRAME_DISPLAY_INFO (f);
free_frame_faces (f);
/* Free scroll bars */
for (bar = FRAME_SCROLL_BARS (f); !NILP (bar); bar = b->next)
{
b = XSCROLL_BAR (bar);
haiku_scroll_bar_remove (b);
}
if (f == dpyinfo->highlight_frame)
dpyinfo->highlight_frame = 0;
if (f == dpyinfo->focused_frame)
dpyinfo->focused_frame = 0;
if (f == dpyinfo->last_mouse_motion_frame)
dpyinfo->last_mouse_motion_frame = NULL;
if (f == dpyinfo->last_mouse_frame)
dpyinfo->last_mouse_frame = NULL;
if (f == dpyinfo->focus_event_frame)
dpyinfo->focus_event_frame = NULL;
if (f == hlinfo->mouse_face_mouse_frame)
reset_mouse_highlight (hlinfo);
if (mbar)
BMenuBar_delete (mbar);
if (drawable)
BView_emacs_delete (drawable);
if (window)
BWindow_quit (window);
/* Free cursors */
BCursor_delete (f->output_data.haiku->text_cursor);
BCursor_delete (f->output_data.haiku->nontext_cursor);
BCursor_delete (f->output_data.haiku->modeline_cursor);
BCursor_delete (f->output_data.haiku->hand_cursor);
BCursor_delete (f->output_data.haiku->hourglass_cursor);
BCursor_delete (f->output_data.haiku->horizontal_drag_cursor);
BCursor_delete (f->output_data.haiku->vertical_drag_cursor);
BCursor_delete (f->output_data.haiku->left_edge_cursor);
BCursor_delete (f->output_data.haiku->top_left_corner_cursor);
BCursor_delete (f->output_data.haiku->top_edge_cursor);
BCursor_delete (f->output_data.haiku->top_right_corner_cursor);
BCursor_delete (f->output_data.haiku->right_edge_cursor);
BCursor_delete (f->output_data.haiku->bottom_right_corner_cursor);
BCursor_delete (f->output_data.haiku->bottom_edge_cursor);
BCursor_delete (f->output_data.haiku->bottom_left_corner_cursor);
BCursor_delete (f->output_data.haiku->no_cursor);
xfree (FRAME_OUTPUT_DATA (f));
FRAME_OUTPUT_DATA (f) = NULL;
unblock_input ();
}
void
haiku_iconify_frame (struct frame *frame)
{
if (FRAME_ICONIFIED_P (frame))
return;
block_input ();
SET_FRAME_VISIBLE (frame, false);
SET_FRAME_ICONIFIED (frame, true);
BWindow_iconify (FRAME_HAIKU_WINDOW (frame));
unblock_input ();
}
void
haiku_set_offset (struct frame *frame, int x, int y,
int change_gravity)
{
if (change_gravity > 0)
{
frame->top_pos = y;
frame->left_pos = x;
frame->size_hint_flags &= ~ (XNegative | YNegative);
if (x < 0)
frame->size_hint_flags |= XNegative;
if (y < 0)
frame->size_hint_flags |= YNegative;
frame->win_gravity = NorthWestGravity;
}
block_input ();
if (change_gravity)
BWindow_set_offset (FRAME_HAIKU_WINDOW (frame), x, y);
unblock_input ();
}
void
haiku_visualize_frame (struct frame *f)
{
block_input ();
if (!FRAME_VISIBLE_P (f))
{
BWindow_set_visible (FRAME_HAIKU_WINDOW (f), 1);
haiku_set_offset (f, f->left_pos, f->top_pos, 1);
SET_FRAME_VISIBLE (f, 1);
SET_FRAME_ICONIFIED (f, 0);
}
unblock_input ();
}
void
haiku_unvisualize_frame (struct frame *f)
{
block_input ();
BWindow_set_visible (FRAME_HAIKU_WINDOW (f), 0);
SET_FRAME_VISIBLE (f, 0);
SET_FRAME_ICONIFIED (f, 0);
unblock_input ();
}
void
haiku_set_internal_border_width (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
{
int old_width = FRAME_INTERNAL_BORDER_WIDTH (f);
int new_width = check_int_nonnegative (arg);
if (new_width == old_width)
return;
f->internal_border_width = new_width;
if (FRAME_HAIKU_WINDOW (f))
adjust_frame_size (f, -1, -1, 3, 0, Qinternal_border_width);
SET_FRAME_GARBAGED (f);
}
void
haiku_set_frame_visible_invisible (struct frame *f, bool visible_p)
{
if (visible_p)
haiku_visualize_frame (f);
else
haiku_unvisualize_frame (f);
}
void
frame_set_mouse_pixel_position (struct frame *f, int pix_x, int pix_y)
{
}
void
haiku_query_color (uint32_t col, Emacs_Color *color_def)
{
color_def->red = RED_FROM_ULONG (col) * 257;
color_def->green = GREEN_FROM_ULONG (col) * 257;
color_def->blue = BLUE_FROM_ULONG (col) * 257;
color_def->pixel = col;
}
Display_Info *
check_x_display_info (Lisp_Object object)
{
return check_haiku_display_info (object);
}
/* Rename frame F to NAME. If NAME is nil, set F's name to "GNU
Emacs". If EXPLICIT_P is non-zero, that indicates Lisp code is
setting the name, not redisplay; in that case, set F's name to NAME
and set F->explicit_name; if NAME is nil, clear F->explicit_name.
If EXPLICIT_P is zero, it means redisplay is setting the name; the
name provided will be ignored if explicit_name is set. */
void
haiku_set_name (struct frame *f, Lisp_Object name, bool explicit_p)
{
if (explicit_p)
{
if (f->explicit_name && NILP (name))
update_mode_lines = 24;
f->explicit_name = !NILP (name);
}
else if (f->explicit_name)
return;
if (NILP (name))
name = build_unibyte_string ("GNU Emacs");
if (!NILP (Fstring_equal (name, f->name)))
return;
fset_name (f, name);
if (!NILP (f->title))
name = f->title;
haiku_set_title_bar_text (f, name);
}
DEFUN ("xw-display-color-p", Fxw_display_color_p, Sxw_display_color_p, 0, 1, 0,
doc: /* SKIP: real doc in xfns.c. */)
(Lisp_Object terminal)
{
return Qt;
}
DEFUN ("xw-color-defined-p", Fxw_color_defined_p, Sxw_color_defined_p, 1, 2, 0,
doc: /* SKIP: real doc in xfns.c. */)
(Lisp_Object color, Lisp_Object frame)
{
Emacs_Color col;
CHECK_STRING (color);
decode_window_system_frame (frame);
return haiku_get_color (SSDATA (color), &col) ? Qnil : Qt;
}
DEFUN ("xw-color-values", Fxw_color_values, Sxw_color_values, 1, 2, 0,
doc: /* SKIP: real doc in xfns.c. */)
(Lisp_Object color, Lisp_Object frame)
{
Emacs_Color col;
CHECK_STRING (color);
decode_window_system_frame (frame);
block_input ();
if (haiku_get_color (SSDATA (color), &col))
{
unblock_input ();
return Qnil;
}
unblock_input ();
return list3i (lrint (col.red), lrint (col.green), lrint (col.blue));
}
DEFUN ("x-display-grayscale-p", Fx_display_grayscale_p, Sx_display_grayscale_p,
0, 1, 0,
doc: /* SKIP: real doc in xfns.c. */)
(Lisp_Object terminal)
{
return Qnil;
}
DEFUN ("x-open-connection", Fx_open_connection, Sx_open_connection,
1, 3, 0,
doc: /* SKIP: real doc in xfns.c. */)
(Lisp_Object display, Lisp_Object resource_string, Lisp_Object must_succeed)
{
struct haiku_display_info *dpy_info;
CHECK_STRING (display);
if (NILP (Fstring_equal (display, build_string ("be"))))
!NILP (must_succeed) ? fatal ("Bad display") : error ("Bad display");
dpy_info = haiku_term_init ();
if (!dpy_info)
!NILP (must_succeed) ? fatal ("Display not responding") :
error ("Display not responding");
return Qnil;
}
DEFUN ("x-display-pixel-width", Fx_display_pixel_width, Sx_display_pixel_width,
0, 1, 0,
doc: /* SKIP: real doc in xfns.c. */)
(Lisp_Object terminal)
{
check_haiku_display_info (terminal);
int width, height;
BScreen_px_dim (&width, &height);
return make_fixnum (width);
}
DEFUN ("x-display-pixel-height", Fx_display_pixel_height, Sx_display_pixel_height,
0, 1, 0,
doc: /* SKIP: real doc in xfns.c. */)
(Lisp_Object terminal)
{
check_haiku_display_info (terminal);
int width, height;
BScreen_px_dim (&width, &height);
return make_fixnum (width);
}
DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame,
1, 1, 0,
doc: /* SKIP: real doc in xfns.c. */)
(Lisp_Object parms)
{
return haiku_create_frame (parms, 0);
}
DEFUN ("x-display-visual-class", Fx_display_visual_class,
Sx_display_visual_class, 0, 1, 0,
doc: /* SKIP: real doc in xfns.c. */)
(Lisp_Object terminal)
{
check_haiku_display_info (terminal);
return intern ("direct-color");
}
DEFUN ("x-show-tip", Fx_show_tip, Sx_show_tip, 1, 6, 0,
doc: /* SKIP: real doc in xfns.c. */)
(Lisp_Object string, Lisp_Object frame, Lisp_Object parms,
Lisp_Object timeout, Lisp_Object dx, Lisp_Object dy)
{
struct frame *tip_f;
struct window *w;
int root_x, root_y;
struct buffer *old_buffer;
struct text_pos pos;
int width, height;
int old_windows_or_buffers_changed = windows_or_buffers_changed;
ptrdiff_t count = SPECPDL_INDEX ();
ptrdiff_t count_1;
Lisp_Object window, size, tip_buf;
AUTO_STRING (tip, " *tip*");
specbind (Qinhibit_redisplay, Qt);
CHECK_STRING (string);
if (NILP (frame))
frame = selected_frame;
decode_window_system_frame (frame);
if (NILP (timeout))
timeout = make_fixnum (5);
else
CHECK_FIXNAT (timeout);
if (NILP (dx))
dx = make_fixnum (5);
else
CHECK_FIXNUM (dx);
if (NILP (dy))
dy = make_fixnum (-10);
else
CHECK_FIXNUM (dy);
if (FRAMEP (tip_frame) && FRAME_LIVE_P (XFRAME (tip_frame)))
{
if (FRAME_VISIBLE_P (XFRAME (tip_frame))
&& EQ (frame, tip_last_frame)
&& !NILP (Fequal_including_properties (string, tip_last_string))
&& !NILP (Fequal (parms, tip_last_parms)))
{
/* Only DX and DY have changed. */
tip_f = XFRAME (tip_frame);
if (!NILP (tip_timer))
{
Lisp_Object timer = tip_timer;
tip_timer = Qnil;
call1 (Qcancel_timer, timer);
}
block_input ();
compute_tip_xy (tip_f, parms, dx, dy, FRAME_PIXEL_WIDTH (tip_f),
FRAME_PIXEL_HEIGHT (tip_f), &root_x, &root_y);
haiku_set_offset (tip_f, root_x, root_y, 1);
haiku_visualize_frame (tip_f);
unblock_input ();
goto start_timer;
}
else if (tooltip_reuse_hidden_frame && EQ (frame, tip_last_frame))
{
bool delete = false;
Lisp_Object tail, elt, parm, last;
/* Check if every parameter in PARMS has the same value in
tip_last_parms. This may destruct tip_last_parms
which, however, will be recreated below. */
for (tail = parms; CONSP (tail); tail = XCDR (tail))
{
elt = XCAR (tail);
parm = Fcar (elt);
/* The left, top, right and bottom parameters are handled
by compute_tip_xy so they can be ignored here. */
if (!EQ (parm, Qleft) && !EQ (parm, Qtop)
&& !EQ (parm, Qright) && !EQ (parm, Qbottom))
{
last = Fassq (parm, tip_last_parms);
if (NILP (Fequal (Fcdr (elt), Fcdr (last))))
{
/* We lost, delete the old tooltip. */
delete = true;
break;
}
else
tip_last_parms =
call2 (Qassq_delete_all, parm, tip_last_parms);
}
else
tip_last_parms =
call2 (Qassq_delete_all, parm, tip_last_parms);
}
/* Now check if there's a parameter left in tip_last_parms with a
non-nil value. */
for (tail = tip_last_parms; CONSP (tail); tail = XCDR (tail))
{
elt = XCAR (tail);
parm = Fcar (elt);
if (!EQ (parm, Qleft) && !EQ (parm, Qtop) && !EQ (parm, Qright)
&& !EQ (parm, Qbottom) && !NILP (Fcdr (elt)))
{
/* We lost, delete the old tooltip. */
delete = true;
break;
}
}
haiku_hide_tip (delete);
}
else
haiku_hide_tip (true);
}
else
haiku_hide_tip (true);
tip_last_frame = frame;
tip_last_string = string;
tip_last_parms = parms;
/* Block input until the tip has been fully drawn, to avoid crashes
when drawing tips in menus. */
block_input ();
if (!FRAMEP (tip_frame) || !FRAME_LIVE_P (XFRAME (tip_frame)))
{
/* Add default values to frame parameters. */
if (NILP (Fassq (Qname, parms)))
parms = Fcons (Fcons (Qname, build_string ("tooltip")), parms);
if (NILP (Fassq (Qinternal_border_width, parms)))
parms = Fcons (Fcons (Qinternal_border_width, make_fixnum (3)), parms);
if (NILP (Fassq (Qborder_width, parms)))
parms = Fcons (Fcons (Qborder_width, make_fixnum (1)), parms);
if (NILP (Fassq (Qborder_color, parms)))
parms = Fcons (Fcons (Qborder_color, build_string ("lightyellow")),
parms);
if (NILP (Fassq (Qbackground_color, parms)))
parms = Fcons (Fcons (Qbackground_color, build_string ("lightyellow")),
parms);
/* Create a frame for the tooltip and record it in the global
variable tip_frame. */
if (NILP (tip_frame = haiku_create_frame (parms, 1)))
{
/* Creating the tip frame failed. */
unblock_input ();
return unbind_to (count, Qnil);
}
}
tip_f = XFRAME (tip_frame);
window = FRAME_ROOT_WINDOW (tip_f);
tip_buf = Fget_buffer_create (tip, Qnil);
/* We will mark the tip window a "pseudo-window" below, and such
windows cannot have display margins. */
bset_left_margin_cols (XBUFFER (tip_buf), make_fixnum (0));
bset_right_margin_cols (XBUFFER (tip_buf), make_fixnum (0));
set_window_buffer (window, tip_buf, false, false);
w = XWINDOW (window);
w->pseudo_window_p = true;
/* Try to avoid that `other-window' select us (Bug#47207). */
Fset_window_parameter (window, Qno_other_window, Qt);
/* Set up the frame's root window. Note: The following code does not
try to size the window or its frame correctly. Its only purpose is
to make the subsequent text size calculations work. The right
sizes should get installed when the toolkit gets back to us. */
w->left_col = 0;
w->top_line = 0;
w->pixel_left = 0;
w->pixel_top = 0;
if (CONSP (Vx_max_tooltip_size)
&& RANGED_FIXNUMP (1, XCAR (Vx_max_tooltip_size), INT_MAX)
&& RANGED_FIXNUMP (1, XCDR (Vx_max_tooltip_size), INT_MAX))
{
w->total_cols = XFIXNAT (XCAR (Vx_max_tooltip_size));
w->total_lines = XFIXNAT (XCDR (Vx_max_tooltip_size));
}
else
{
w->total_cols = 80;
w->total_lines = 40;
}
w->pixel_width = w->total_cols * FRAME_COLUMN_WIDTH (tip_f);
w->pixel_height = w->total_lines * FRAME_LINE_HEIGHT (tip_f);
FRAME_TOTAL_COLS (tip_f) = WINDOW_TOTAL_COLS (w);
adjust_frame_glyphs (tip_f);
/* Insert STRING into the root window's buffer and fit the frame to
the buffer. */
count_1 = SPECPDL_INDEX ();
old_buffer = current_buffer;
set_buffer_internal_1 (XBUFFER (w->contents));
bset_truncate_lines (current_buffer, Qnil);
specbind (Qinhibit_read_only, Qt);
specbind (Qinhibit_modification_hooks, Qt);
specbind (Qinhibit_point_motion_hooks, Qt);
Ferase_buffer ();
Finsert (1, &string);
clear_glyph_matrix (w->desired_matrix);
clear_glyph_matrix (w->current_matrix);
SET_TEXT_POS (pos, BEGV, BEGV_BYTE);
try_window (window, pos, TRY_WINDOW_IGNORE_FONTS_CHANGE);
/* Calculate size of tooltip window. */
size = Fwindow_text_pixel_size (window, Qnil, Qnil, Qnil,
make_fixnum (w->pixel_height), Qnil);
/* Add the frame's internal border to calculated size. */
width = XFIXNUM (Fcar (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f);
height = XFIXNUM (Fcdr (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f);
/* Calculate position of tooltip frame. */
compute_tip_xy (tip_f, parms, dx, dy, width, height, &root_x, &root_y);
BWindow_resize (FRAME_HAIKU_WINDOW (tip_f), width, height);
haiku_set_offset (tip_f, root_x, root_y, 1);
BWindow_set_tooltip_decoration (FRAME_HAIKU_WINDOW (tip_f));
SET_FRAME_VISIBLE (tip_f, 1);
BWindow_set_visible (FRAME_HAIKU_WINDOW (tip_f), 1);
w->must_be_updated_p = true;
update_single_window (w);
set_buffer_internal_1 (old_buffer);
unbind_to (count_1, Qnil);
unblock_input ();
windows_or_buffers_changed = old_windows_or_buffers_changed;
start_timer:
/* Let the tip disappear after timeout seconds. */
tip_timer = call3 (intern ("run-at-time"), timeout, Qnil,
intern ("x-hide-tip"));
return unbind_to (count, Qnil);
}
DEFUN ("x-hide-tip", Fx_hide_tip, Sx_hide_tip, 0, 0, 0,
doc: /* SKIP: real doc in xfns.c. */)
(void)
{
return haiku_hide_tip (!tooltip_reuse_hidden_frame);
}
frame_parm_handler haiku_frame_parm_handlers[] =
{
gui_set_autoraise,
gui_set_autolower,
haiku_set_background_color,
NULL, /* x_set_border_color */
gui_set_border_width,
haiku_set_cursor_color,
haiku_set_cursor_type,
gui_set_font,
haiku_set_foreground_color,
NULL, /* set icon name */
NULL, /* set icon type */
NULL, /* set child frame border width */
haiku_set_internal_border_width,
gui_set_right_divider_width,
gui_set_bottom_divider_width,
haiku_set_menu_bar_lines,
NULL, /* set mouse color */
haiku_explicitly_set_name,
gui_set_scroll_bar_width,
gui_set_scroll_bar_height,
haiku_set_title,
gui_set_unsplittable,
gui_set_vertical_scroll_bars,
gui_set_horizontal_scroll_bars,
gui_set_visibility,
haiku_set_tab_bar_lines,
haiku_set_tool_bar_lines,
NULL, /* set scroll bar fg */
NULL, /* set scroll bar bkg */
gui_set_screen_gamma,
gui_set_line_spacing,
gui_set_left_fringe,
gui_set_right_fringe,
NULL, /* x wait for wm */
gui_set_fullscreen,
gui_set_font_backend,
gui_set_alpha,
NULL, /* set sticky */
NULL, /* set tool bar pos */
NULL, /* set inhibit double buffering */
haiku_set_undecorated,
NULL, /* set parent frame */
NULL, /* set skip taskbar */
NULL, /* set no focus on map */
haiku_set_no_accept_focus,
NULL, /* set z group */
NULL, /* set override redir */
gui_set_no_special_glyphs
};
void
syms_of_haikufns (void)
{
DEFSYM (Qfont_parameter, "font-parameter");
DEFSYM (Qcancel_timer, "cancel-timer");
DEFSYM (Qassq_delete_all, "assq-delete-all");
defsubr (&Sx_hide_tip);
defsubr (&Sxw_display_color_p);
defsubr (&Sx_display_grayscale_p);
defsubr (&Sx_open_connection);
defsubr (&Sx_create_frame);
defsubr (&Sx_display_pixel_width);
defsubr (&Sx_display_pixel_height);
defsubr (&Sxw_color_values);
defsubr (&Sxw_color_defined_p);
defsubr (&Sx_display_visual_class);
defsubr (&Sx_show_tip);
tip_timer = Qnil;
staticpro (&tip_timer);
tip_frame = Qnil;
staticpro (&tip_frame);
tip_last_frame = Qnil;
staticpro (&tip_last_frame);
tip_last_string = Qnil;
staticpro (&tip_last_string);
tip_last_parms = Qnil;
staticpro (&tip_last_parms);
DEFVAR_LISP ("x-max-tooltip-size", Vx_max_tooltip_size,
doc: /* SKIP: real doc in xfns.c. */);
Vx_max_tooltip_size = Fcons (make_fixnum (80), make_fixnum (40));
return;
}