/* Haiku window system support. Hey, Emacs, this is -*- C++ -*- Copyright (C) 2021 Free Software Foundation, Inc. This file is part of GNU Emacs. GNU Emacs is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. GNU Emacs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Emacs. If not, see . */ #include #include #include #include #include #include #include "haiku_support.h" /* Haiku doesn't expose font language data in BFont objects. Thus, we select a few representative characters for each supported `:lang' (currently Chinese, Korean and Japanese,) and test for those instead. */ static uint32_t language_code_points[MAX_LANGUAGE][4] = {{20154, 20754, 22996, 0}, /* Chinese. */ {51312, 49440, 44544, 0}, /* Korean. */ {26085, 26412, 12371, 0}, /* Japanese. */}; static void estimate_font_ascii (BFont *font, int *max_width, int *min_width, int *avg_width) { char ch[2]; bool tems[1]; int total = 0; int count = 0; int min = 0; int max = 0; std::memset (ch, 0, sizeof ch); for (ch[0] = 1; ch[0] < 127; ++ch[0]) { tems[0] = false; font->GetHasGlyphs (ch, 1, tems); if (tems[0]) { int w = font->StringWidth (ch); ++count; total += w; if (!min || min > w) min = w; if (max < w) max = w; } } *min_width = min; *max_width = max; *avg_width = total / count; } void * BFont_open_default (double size, int plain_or_bold_or_fixed) { BFont *ft; if (!plain_or_bold_or_fixed) ft = new BFont (be_fixed_font); else if (plain_or_bold_or_fixed > 0) ft = new BFont (be_plain_font); else ft = new BFont (be_bold_font); ft->SetSize (size); ft->SetEncoding (B_UNICODE_UTF8); return ft; } void BFont_close (void *font) { if (font != (void *) be_fixed_font && font != (void *) be_plain_font && font != (void *) be_bold_font) delete (BFont *) font; } void BFont_dat (void *font, int *px_size, int *min_width, int *max_width, int *avg_width, int *height, int *space_width, int *ascent, int *descent, int *underline_position, int *underline_thickness) { BFont *ft = (BFont *) font; struct font_height fheight; bool have_space_p; char atem[1]; bool otem[1]; ft->GetHeight (&fheight); atem[0] = ' '; otem[0] = false; ft->GetHasGlyphs (atem, 1, otem); have_space_p = otem[0]; estimate_font_ascii (ft, max_width, min_width, avg_width); *ascent = std::lrint (fheight.ascent); *descent = std::lrint (fheight.descent); *height = *ascent + *descent; *space_width = have_space_p ? ft->StringWidth (" ") : 0; *px_size = std::lrint (ft->Size ()); *underline_position = 0; *underline_thickness = 0; } int BFont_have_char_p (void *font, int32_t chr) { BFont *ft = (BFont *) font; return ft->IncludesBlock (chr, chr); } void BFont_char_bounds (void *font, const char *mb_str, int *advance, int *lb, int *rb) { BFont *ft = (BFont *) font; edge_info edge_info; font_height height; float size, escapement; size = ft->Size (); ft->GetEdges (mb_str, 1, &edge_info); ft->GetEscapements (mb_str, 1, &escapement); ft->GetHeight (&height); *advance = std::lrint (escapement * size); *lb = std::lrint (edge_info.left * size); *rb = *advance + std::lrint (edge_info.right * size); } void BFont_nchar_bounds (void *font, const char *mb_str, int *advance, int *lb, int *rb, int32_t n) { BFont *ft = (BFont *) font; edge_info edge_info[n]; float size; float escapement[n]; size = ft->Size (); ft->GetEdges (mb_str, n, edge_info); ft->GetEscapements (mb_str, n, (float *) escapement); for (int32_t i = 0; i < n; ++i) { advance[i] = std::lrint (escapement[i] * size); lb[i] = advance[i] - std::lrint (edge_info[i].left * size); rb[i] = advance[i] + std::lrint (edge_info[i].right * size); } } void * BFont_new (void) { return new BFont (); } int BFont_family_has_style_p (haiku_font_family_or_style family, haiku_font_family_or_style style) { int sc = count_font_styles (family); for (int idx = 0; idx < sc; ++idx) { font_style s; if (get_font_style (family, idx, &s) == B_OK) { if (!strcmp (s, style)) return 1; } } return 0; } int BFont_family (int idx, char *f, int *fixed_p) { uint32 flags; if (get_font_family (idx, (char (*)[64]) f, &flags) != B_OK) return 1; *fixed_p = flags & B_IS_FIXED; return 0; } int BFont_set_family_and_style (void *font, haiku_font_family_or_style family, haiku_font_family_or_style style) { BFont *ft = (BFont *) font; return ft->SetFamilyAndStyle (family, style) != B_OK; } int BFont_set_family_face (void *font, haiku_font_family_or_style family, int italic_p, int bold_p) { BFont *ft = (BFont *) font; return ft->SetFamilyAndFace (family, (italic_p ? B_ITALIC_FACE : 0) | (bold_p == HAIKU_BOLD ? B_BOLD_FACE : 0)); } static void font_style_to_flags (char *st, struct haiku_font_pattern *pattern) { char *style = strdup (st); char *token; pattern->weight = -1; pattern->width = NO_WIDTH; pattern->slant = NO_SLANT; int tok = 0; while ((token = std::strtok (!tok ? style : NULL, " ")) && tok < 3) { if (token && !strcmp (token, "Thin")) pattern->weight = HAIKU_THIN; else if (token && !strcmp (token, "UltraLight")) pattern->weight = HAIKU_ULTRALIGHT; else if (token && !strcmp (token, "ExtraLight")) pattern->weight = HAIKU_EXTRALIGHT; else if (token && !strcmp (token, "Light")) pattern->weight = HAIKU_LIGHT; else if (token && !strcmp (token, "SemiLight")) pattern->weight = HAIKU_SEMI_LIGHT; else if (token && !strcmp (token, "Regular")) { if (pattern->slant == NO_SLANT) pattern->slant = SLANT_REGULAR; if (pattern->width == NO_WIDTH) pattern->width = NORMAL_WIDTH; if (pattern->weight == -1) pattern->weight = HAIKU_REGULAR; } else if (token && !strcmp (token, "SemiBold")) pattern->weight = HAIKU_SEMI_BOLD; else if (token && !strcmp (token, "Bold")) pattern->weight = HAIKU_BOLD; else if (token && (!strcmp (token, "ExtraBold") || /* This has actually been seen in the wild. */ !strcmp (token, "Extrabold"))) pattern->weight = HAIKU_EXTRA_BOLD; else if (token && !strcmp (token, "UltraBold")) pattern->weight = HAIKU_ULTRA_BOLD; else if (token && !strcmp (token, "Oblique")) pattern->slant = SLANT_OBLIQUE; else if (token && !strcmp (token, "Italic")) pattern->slant = SLANT_ITALIC; else if (token && !strcmp (token, "UltraCondensed")) pattern->width = ULTRA_CONDENSED; else if (token && !strcmp (token, "ExtraCondensed")) pattern->width = EXTRA_CONDENSED; else if (token && !strcmp (token, "Condensed")) pattern->width = CONDENSED; else if (token && !strcmp (token, "SemiCondensed")) pattern->width = SEMI_CONDENSED; else if (token && !strcmp (token, "SemiExpanded")) pattern->width = SEMI_EXPANDED; else if (token && !strcmp (token, "Expanded")) pattern->width = EXPANDED; else if (token && !strcmp (token, "ExtraExpanded")) pattern->width = EXTRA_EXPANDED; else if (token && !strcmp (token, "UltraExpanded")) pattern->width = ULTRA_EXPANDED; else { tok = 1000; break; } tok++; } if (pattern->weight != -1) pattern->specified |= FSPEC_WEIGHT; if (pattern->slant != NO_SLANT) pattern->specified |= FSPEC_SLANT; if (pattern->width != NO_WIDTH) pattern->specified |= FSPEC_WIDTH; if (tok > 3) { pattern->specified &= ~FSPEC_SLANT; pattern->specified &= ~FSPEC_WEIGHT; pattern->specified &= ~FSPEC_WIDTH; pattern->specified |= FSPEC_STYLE; std::strncpy ((char *) &pattern->style, st, sizeof pattern->style - 1); } free (style); } static bool font_check_wanted_chars (struct haiku_font_pattern *pattern, font_family family, char *style) { BFont ft; if (ft.SetFamilyAndStyle (family, style) != B_OK) return false; for (int i = 0; i < pattern->want_chars_len; ++i) if (!ft.IncludesBlock (pattern->wanted_chars[i], pattern->wanted_chars[i])) return false; return true; } static bool font_check_one_of (struct haiku_font_pattern *pattern, font_family family, char *style) { BFont ft; if (ft.SetFamilyAndStyle (family, style) != B_OK) return false; for (int i = 0; i < pattern->need_one_of_len; ++i) if (ft.IncludesBlock (pattern->need_one_of[i], pattern->need_one_of[i])) return true; return false; } static bool font_check_language (struct haiku_font_pattern *pattern, font_family family, char *style) { BFont ft; if (ft.SetFamilyAndStyle (family, style) != B_OK) return false; if (pattern->language == MAX_LANGUAGE) return false; for (uint32_t *ch = (uint32_t *) &language_code_points[pattern->language]; *ch; ch++) if (!ft.IncludesBlock (*ch, *ch)) return false; return true; } static bool font_family_style_matches_p (font_family family, char *style, uint32_t flags, struct haiku_font_pattern *pattern, int ignore_flags_p = 0) { struct haiku_font_pattern m; m.specified = 0; if (style) font_style_to_flags (style, &m); if ((pattern->specified & FSPEC_FAMILY) && strcmp ((char *) &pattern->family, family)) return false; if (!ignore_flags_p && (pattern->specified & FSPEC_SPACING) && !(pattern->mono_spacing_p) != !(flags & B_IS_FIXED)) return false; if (pattern->specified & FSPEC_STYLE) return style && !strcmp (style, pattern->style); if ((pattern->specified & FSPEC_WEIGHT) && pattern->weight != ((m.specified & FSPEC_WEIGHT) ? m.weight : HAIKU_REGULAR)) return false; if ((pattern->specified & FSPEC_SLANT) && pattern->slant != ((m.specified & FSPEC_SLANT) ? m.slant : SLANT_REGULAR)) return false; if ((pattern->specified & FSPEC_WANTED) && !font_check_wanted_chars (pattern, family, style)) return false; if ((pattern->specified & FSPEC_WIDTH) && pattern->width != ((m.specified & FSPEC_WIDTH) ? m.width : NORMAL_WIDTH)) return false; if ((pattern->specified & FSPEC_NEED_ONE_OF) && !font_check_one_of (pattern, family, style)) return false; if ((pattern->specified & FSPEC_LANGUAGE) && !font_check_language (pattern, family, style)) return false; return true; } static void haiku_font_fill_pattern (struct haiku_font_pattern *pattern, font_family family, char *style, uint32_t flags) { if (style) font_style_to_flags (style, pattern); pattern->specified |= FSPEC_FAMILY; std::strncpy (pattern->family, family, sizeof pattern->family - 1); pattern->specified |= FSPEC_SPACING; pattern->mono_spacing_p = flags & B_IS_FIXED; } void haiku_font_pattern_free (struct haiku_font_pattern *pt) { struct haiku_font_pattern *tem = pt; while (tem) { struct haiku_font_pattern *t = tem; tem = t->next; delete t; } } struct haiku_font_pattern * BFont_find (struct haiku_font_pattern *pt) { struct haiku_font_pattern *r = NULL; font_family name; font_style sname; uint32 flags; int sty_count; int fam_count = count_font_families (); for (int fi = 0; fi < fam_count; ++fi) { if (get_font_family (fi, &name, &flags) == B_OK) { sty_count = count_font_styles (name); if (!sty_count && font_family_style_matches_p (name, NULL, flags, pt)) { struct haiku_font_pattern *p = new struct haiku_font_pattern; p->specified = 0; p->oblique_seen_p = 1; haiku_font_fill_pattern (p, name, NULL, flags); p->next = r; if (p->next) p->next->last = p; p->last = NULL; p->next_family = r; r = p; } else if (sty_count) { for (int si = 0; si < sty_count; ++si) { int oblique_seen_p = 0; struct haiku_font_pattern *head = r; struct haiku_font_pattern *p = NULL; if (get_font_style (name, si, &sname, &flags) == B_OK) { if (font_family_style_matches_p (name, (char *) &sname, flags, pt)) { p = new struct haiku_font_pattern; p->specified = 0; haiku_font_fill_pattern (p, name, (char *) &sname, flags); if (p->specified & FSPEC_SLANT && ((p->slant == SLANT_OBLIQUE) || (p->slant == SLANT_ITALIC))) oblique_seen_p = 1; p->next = r; if (p->next) p->next->last = p; r = p; p->next_family = head; } } if (p) p->last = NULL; for (; head; head = head->last) { head->oblique_seen_p = oblique_seen_p; } } } } } /* There's a very good chance that this result will get cached if no slant is specified. Thus, we look through each font that hasn't seen an oblique style, and add one. */ if (!(pt->specified & FSPEC_SLANT)) { /* r->last is invalid from here onwards. */ for (struct haiku_font_pattern *p = r; p;) { if (!p->oblique_seen_p) { struct haiku_font_pattern *n = new haiku_font_pattern; *n = *p; n->slant = SLANT_OBLIQUE; p->next = n; p = p->next_family; } else p = p->next_family; } } return r; } int BFont_open_pattern (struct haiku_font_pattern *pat, void **font, float size) { int sty_count; font_family name; font_style sname; uint32 flags = 0; if (!(pat->specified & FSPEC_FAMILY)) return 1; strncpy (name, pat->family, sizeof name - 1); sty_count = count_font_styles (name); if (!sty_count && font_family_style_matches_p (name, NULL, flags, pat, 1)) { BFont *ft = new BFont; if (ft->SetFamilyAndStyle (name, NULL) != B_OK) { delete ft; return 1; } ft->SetSize (size); ft->SetEncoding (B_UNICODE_UTF8); ft->SetSpacing (B_BITMAP_SPACING); *font = (void *) ft; return 0; } else if (sty_count) { for (int si = 0; si < sty_count; ++si) { if (get_font_style (name, si, &sname, &flags) == B_OK && font_family_style_matches_p (name, (char *) &sname, flags, pat)) { BFont *ft = new BFont; if (ft->SetFamilyAndStyle (name, sname) != B_OK) { delete ft; return 1; } ft->SetSize (size); ft->SetEncoding (B_UNICODE_UTF8); ft->SetSpacing (B_BITMAP_SPACING); *font = (void *) ft; return 0; } } } if (pat->specified & FSPEC_SLANT && pat->slant == SLANT_OBLIQUE) { struct haiku_font_pattern copy = *pat; copy.slant = SLANT_REGULAR; int code = BFont_open_pattern (©, font, size); if (code) return code; BFont *ft = (BFont *) *font; /* XXX Font measurements don't respect shear. Haiku bug? This apparently worked in BeOS. ft->SetShear (100.0); */ ft->SetFace (B_ITALIC_FACE); return 0; } return 1; } void BFont_populate_fixed_family (struct haiku_font_pattern *ptn) { font_family f; font_style s; be_fixed_font->GetFamilyAndStyle (&f, &s); ptn->specified |= FSPEC_FAMILY; strncpy (ptn->family, f, sizeof ptn->family - 1); } void BFont_populate_plain_family (struct haiku_font_pattern *ptn) { font_family f; font_style s; be_plain_font->GetFamilyAndStyle (&f, &s); ptn->specified |= FSPEC_FAMILY; strncpy (ptn->family, f, sizeof ptn->family - 1); } int BFont_string_width (void *font, const char *utf8) { return ((BFont *) font)->StringWidth (utf8); }