1 /* Haiku window system support. Hey, Emacs, this is -*- C++ -*-
2 Copyright (C) 2021 Free Software Foundation, Inc.
3
4 This file is part of GNU Emacs.
5
6 GNU Emacs is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or (at
9 your option) any later version.
10
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
18
19 #include <config.h>
20
21 #include <Font.h>
22 #include <Rect.h>
23 #include <AffineTransform.h>
24
25 #include <cstring>
26 #include <cmath>
27
28 #include "haiku_support.h"
29
30 /* Haiku doesn't expose font language data in BFont objects. Thus, we
31 select a few representative characters for each supported `:lang'
32 (currently Chinese, Korean and Japanese,) and test for those
33 instead. */
34
35 static uint32_t language_code_points[MAX_LANGUAGE][4] =
36 {{20154, 20754, 22996, 0}, /* Chinese. */
37 {51312, 49440, 44544, 0}, /* Korean. */
38 {26085, 26412, 12371, 0}, /* Japanese. */};
39
40 static void
estimate_font_ascii(BFont * font,int * max_width,int * min_width,int * avg_width)41 estimate_font_ascii (BFont *font, int *max_width,
42 int *min_width, int *avg_width)
43 {
44 char ch[2];
45 bool tems[1];
46 int total = 0;
47 int count = 0;
48 int min = 0;
49 int max = 0;
50
51 std::memset (ch, 0, sizeof ch);
52 for (ch[0] = 32; ch[0] < 127; ++ch[0])
53 {
54 tems[0] = false;
55 font->GetHasGlyphs (ch, 1, tems);
56 if (tems[0])
57 {
58 int w = font->StringWidth (ch);
59 ++count;
60 total += w;
61
62 if (!min || min > w)
63 min = w;
64 if (max < w)
65 max = w;
66 }
67 }
68
69 *min_width = min;
70 *max_width = max;
71 *avg_width = total / count;
72 }
73
74 void
BFont_close(void * font)75 BFont_close (void *font)
76 {
77 if (font != (void *) be_fixed_font &&
78 font != (void *) be_plain_font &&
79 font != (void *) be_bold_font)
80 delete (BFont *) font;
81 }
82
83 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)84 BFont_dat (void *font, int *px_size, int *min_width, int *max_width,
85 int *avg_width, int *height, int *space_width, int *ascent,
86 int *descent, int *underline_position, int *underline_thickness)
87 {
88 BFont *ft = (BFont *) font;
89 struct font_height fheight;
90 bool have_space_p;
91
92 char atem[1];
93 bool otem[1];
94
95 ft->GetHeight (&fheight);
96 atem[0] = ' ';
97 otem[0] = false;
98 ft->GetHasGlyphs (atem, 1, otem);
99 have_space_p = otem[0];
100
101 estimate_font_ascii (ft, max_width, min_width, avg_width);
102 *ascent = std::lrint (fheight.ascent);
103 *descent = std::lrint (fheight.descent);
104 *height = *ascent + *descent;
105
106 *space_width = have_space_p ? ft->StringWidth (" ") : 0;
107
108 *px_size = std::lrint (ft->Size ());
109 *underline_position = 0;
110 *underline_thickness = 0;
111 }
112
113 /* Return non-null if FONT contains CHR, a Unicode code-point. */
114 int
BFont_have_char_p(void * font,int32_t chr)115 BFont_have_char_p (void *font, int32_t chr)
116 {
117 BFont *ft = (BFont *) font;
118 return ft->IncludesBlock (chr, chr);
119 }
120
121 /* Return non-null if font contains a block from BEG to END. */
122 int
BFont_have_char_block(void * font,int32_t beg,int32_t end)123 BFont_have_char_block (void *font, int32_t beg, int32_t end)
124 {
125 BFont *ft = (BFont *) font;
126 return ft->IncludesBlock (beg, end);
127 }
128
129 /* Compute bounds for MB_STR, a character in multibyte encoding,
130 used with font. The width (in pixels) is returned in ADVANCE,
131 the left bearing in LB, and the right bearing in RB. */
132 void
BFont_char_bounds(void * font,const char * mb_str,int * advance,int * lb,int * rb)133 BFont_char_bounds (void *font, const char *mb_str, int *advance,
134 int *lb, int *rb)
135 {
136 BFont *ft = (BFont *) font;
137 edge_info edge_info;
138 float size, escapement;
139 size = ft->Size ();
140
141 ft->GetEdges (mb_str, 1, &edge_info);
142 ft->GetEscapements (mb_str, 1, &escapement);
143 *advance = std::lrint (escapement * size);
144 *lb = std::lrint (edge_info.left * size);
145 *rb = *advance + std::lrint (edge_info.right * size);
146 }
147
148 /* The same, but for a variable amount of chars. */
149 void
BFont_nchar_bounds(void * font,const char * mb_str,int * advance,int * lb,int * rb,int32_t n)150 BFont_nchar_bounds (void *font, const char *mb_str, int *advance,
151 int *lb, int *rb, int32_t n)
152 {
153 BFont *ft = (BFont *) font;
154 edge_info edge_info[n];
155 float size;
156 float escapement[n];
157
158 size = ft->Size ();
159
160 ft->GetEdges (mb_str, n, edge_info);
161 ft->GetEscapements (mb_str, n, (float *) escapement);
162
163 for (int32_t i = 0; i < n; ++i)
164 {
165 advance[i] = std::lrint (escapement[i] * size);
166 lb[i] = advance[i] - std::lrint (edge_info[i].left * size);
167 rb[i] = advance[i] + std::lrint (edge_info[i].right * size);
168 }
169 }
170
171 static void
font_style_to_flags(char * st,struct haiku_font_pattern * pattern)172 font_style_to_flags (char *st, struct haiku_font_pattern *pattern)
173 {
174 char *style = strdup (st);
175 char *token;
176 pattern->weight = -1;
177 pattern->width = NO_WIDTH;
178 pattern->slant = NO_SLANT;
179 int tok = 0;
180
181 while ((token = std::strtok (!tok ? style : NULL, " ")) && tok < 3)
182 {
183 if (token && !strcmp (token, "Thin"))
184 pattern->weight = HAIKU_THIN;
185 else if (token && !strcmp (token, "UltraLight"))
186 pattern->weight = HAIKU_ULTRALIGHT;
187 else if (token && !strcmp (token, "ExtraLight"))
188 pattern->weight = HAIKU_EXTRALIGHT;
189 else if (token && !strcmp (token, "Light"))
190 pattern->weight = HAIKU_LIGHT;
191 else if (token && !strcmp (token, "SemiLight"))
192 pattern->weight = HAIKU_SEMI_LIGHT;
193 else if (token && !strcmp (token, "Regular"))
194 {
195 if (pattern->slant == NO_SLANT)
196 pattern->slant = SLANT_REGULAR;
197
198 if (pattern->width == NO_WIDTH)
199 pattern->width = NORMAL_WIDTH;
200
201 if (pattern->weight == -1)
202 pattern->weight = HAIKU_REGULAR;
203 }
204 else if (token && !strcmp (token, "SemiBold"))
205 pattern->weight = HAIKU_SEMI_BOLD;
206 else if (token && !strcmp (token, "Bold"))
207 pattern->weight = HAIKU_BOLD;
208 else if (token && (!strcmp (token, "ExtraBold") ||
209 /* This has actually been seen in the wild. */
210 !strcmp (token, "Extrabold")))
211 pattern->weight = HAIKU_EXTRA_BOLD;
212 else if (token && !strcmp (token, "UltraBold"))
213 pattern->weight = HAIKU_ULTRA_BOLD;
214 else if (token && !strcmp (token, "Book"))
215 pattern->weight = HAIKU_BOOK;
216 else if (token && !strcmp (token, "Heavy"))
217 pattern->weight = HAIKU_HEAVY;
218 else if (token && !strcmp (token, "UltraHeavy"))
219 pattern->weight = HAIKU_ULTRA_HEAVY;
220 else if (token && !strcmp (token, "Black"))
221 pattern->weight = HAIKU_BLACK;
222 else if (token && !strcmp (token, "Medium"))
223 pattern->weight = HAIKU_MEDIUM;
224 else if (token && !strcmp (token, "Oblique"))
225 pattern->slant = SLANT_OBLIQUE;
226 else if (token && !strcmp (token, "Italic"))
227 pattern->slant = SLANT_ITALIC;
228 else if (token && !strcmp (token, "UltraCondensed"))
229 pattern->width = ULTRA_CONDENSED;
230 else if (token && !strcmp (token, "ExtraCondensed"))
231 pattern->width = EXTRA_CONDENSED;
232 else if (token && !strcmp (token, "Condensed"))
233 pattern->width = CONDENSED;
234 else if (token && !strcmp (token, "SemiCondensed"))
235 pattern->width = SEMI_CONDENSED;
236 else if (token && !strcmp (token, "SemiExpanded"))
237 pattern->width = SEMI_EXPANDED;
238 else if (token && !strcmp (token, "Expanded"))
239 pattern->width = EXPANDED;
240 else if (token && !strcmp (token, "ExtraExpanded"))
241 pattern->width = EXTRA_EXPANDED;
242 else if (token && !strcmp (token, "UltraExpanded"))
243 pattern->width = ULTRA_EXPANDED;
244 else
245 {
246 tok = 1000;
247 break;
248 }
249 tok++;
250 }
251
252 if (pattern->weight != -1)
253 pattern->specified |= FSPEC_WEIGHT;
254 if (pattern->slant != NO_SLANT)
255 pattern->specified |= FSPEC_SLANT;
256 if (pattern->width != NO_WIDTH)
257 pattern->specified |= FSPEC_WIDTH;
258
259 if (tok > 3)
260 {
261 pattern->specified &= ~FSPEC_SLANT;
262 pattern->specified &= ~FSPEC_WEIGHT;
263 pattern->specified &= ~FSPEC_WIDTH;
264 pattern->specified |= FSPEC_STYLE;
265 std::strncpy ((char *) &pattern->style, st,
266 sizeof pattern->style - 1);
267 }
268
269 free (style);
270 }
271
272 static bool
font_check_wanted_chars(struct haiku_font_pattern * pattern,font_family family,char * style)273 font_check_wanted_chars (struct haiku_font_pattern *pattern, font_family family,
274 char *style)
275 {
276 BFont ft;
277
278 if (ft.SetFamilyAndStyle (family, style) != B_OK)
279 return false;
280
281 for (int i = 0; i < pattern->want_chars_len; ++i)
282 if (!ft.IncludesBlock (pattern->wanted_chars[i],
283 pattern->wanted_chars[i]))
284 return false;
285
286 return true;
287 }
288
289 static bool
font_check_one_of(struct haiku_font_pattern * pattern,font_family family,char * style)290 font_check_one_of (struct haiku_font_pattern *pattern, font_family family,
291 char *style)
292 {
293 BFont ft;
294
295 if (ft.SetFamilyAndStyle (family, style) != B_OK)
296 return false;
297
298 for (int i = 0; i < pattern->need_one_of_len; ++i)
299 if (ft.IncludesBlock (pattern->need_one_of[i],
300 pattern->need_one_of[i]))
301 return true;
302
303 return false;
304 }
305
306 static bool
font_check_language(struct haiku_font_pattern * pattern,font_family family,char * style)307 font_check_language (struct haiku_font_pattern *pattern, font_family family,
308 char *style)
309 {
310 BFont ft;
311
312 if (ft.SetFamilyAndStyle (family, style) != B_OK)
313 return false;
314
315 if (pattern->language == MAX_LANGUAGE)
316 return false;
317
318 for (uint32_t *ch = (uint32_t *)
319 &language_code_points[pattern->language]; *ch; ch++)
320 if (!ft.IncludesBlock (*ch, *ch))
321 return false;
322
323 return true;
324 }
325
326 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)327 font_family_style_matches_p (font_family family, char *style, uint32_t flags,
328 struct haiku_font_pattern *pattern,
329 int ignore_flags_p = 0)
330 {
331 struct haiku_font_pattern m;
332 m.specified = 0;
333
334 if (style)
335 font_style_to_flags (style, &m);
336
337 if ((pattern->specified & FSPEC_FAMILY) &&
338 strcmp ((char *) &pattern->family, family))
339 return false;
340
341 if (!ignore_flags_p && (pattern->specified & FSPEC_SPACING) &&
342 !(pattern->mono_spacing_p) != !(flags & B_IS_FIXED))
343 return false;
344
345 if (pattern->specified & FSPEC_STYLE)
346 return style && !strcmp (style, pattern->style);
347
348 if ((pattern->specified & FSPEC_WEIGHT)
349 && (pattern->weight
350 != ((m.specified & FSPEC_WEIGHT) ? m.weight : HAIKU_REGULAR)))
351 return false;
352
353 if ((pattern->specified & FSPEC_SLANT)
354 && (pattern->slant
355 != ((m.specified & FSPEC_SLANT) ? m.slant : SLANT_REGULAR)))
356 return false;
357
358 if ((pattern->specified & FSPEC_WANTED)
359 && !font_check_wanted_chars (pattern, family, style))
360 return false;
361
362 if ((pattern->specified & FSPEC_WIDTH)
363 && (pattern->width !=
364 ((m.specified & FSPEC_WIDTH) ? m.width : NORMAL_WIDTH)))
365 return false;
366
367 if ((pattern->specified & FSPEC_NEED_ONE_OF)
368 && !font_check_one_of (pattern, family, style))
369 return false;
370
371 if ((pattern->specified & FSPEC_LANGUAGE)
372 && !font_check_language (pattern, family, style))
373 return false;
374
375 return true;
376 }
377
378 static void
haiku_font_fill_pattern(struct haiku_font_pattern * pattern,font_family family,char * style,uint32_t flags)379 haiku_font_fill_pattern (struct haiku_font_pattern *pattern,
380 font_family family, char *style,
381 uint32_t flags)
382 {
383 if (style)
384 font_style_to_flags (style, pattern);
385
386 pattern->specified |= FSPEC_FAMILY;
387 std::strncpy (pattern->family, family,
388 sizeof pattern->family - 1);
389 pattern->specified |= FSPEC_SPACING;
390 pattern->mono_spacing_p = flags & B_IS_FIXED;
391 }
392
393 /* Delete every element of the font pattern PT. */
394 void
haiku_font_pattern_free(struct haiku_font_pattern * pt)395 haiku_font_pattern_free (struct haiku_font_pattern *pt)
396 {
397 struct haiku_font_pattern *tem = pt;
398 while (tem)
399 {
400 struct haiku_font_pattern *t = tem;
401 tem = t->next;
402 delete t;
403 }
404 }
405
406 /* Find all fonts matching the font pattern PT. */
407 struct haiku_font_pattern *
BFont_find(struct haiku_font_pattern * pt)408 BFont_find (struct haiku_font_pattern *pt)
409 {
410 struct haiku_font_pattern *r = NULL;
411 font_family name;
412 font_style sname;
413 uint32 flags;
414 int sty_count;
415 int fam_count = count_font_families ();
416
417 for (int fi = 0; fi < fam_count; ++fi)
418 {
419 if (get_font_family (fi, &name, &flags) == B_OK)
420 {
421 sty_count = count_font_styles (name);
422 if (!sty_count &&
423 font_family_style_matches_p (name, NULL, flags, pt))
424 {
425 struct haiku_font_pattern *p = new struct haiku_font_pattern;
426 p->specified = 0;
427 p->oblique_seen_p = 1;
428 haiku_font_fill_pattern (p, name, NULL, flags);
429 p->next = r;
430 if (p->next)
431 p->next->last = p;
432 p->last = NULL;
433 p->next_family = r;
434 r = p;
435 }
436 else if (sty_count)
437 {
438 for (int si = 0; si < sty_count; ++si)
439 {
440 int oblique_seen_p = 0;
441 struct haiku_font_pattern *head = r;
442 struct haiku_font_pattern *p = NULL;
443
444 if (get_font_style (name, si, &sname, &flags) == B_OK)
445 {
446 if (font_family_style_matches_p (name, (char *) &sname, flags, pt))
447 {
448 p = new struct haiku_font_pattern;
449 p->specified = 0;
450 haiku_font_fill_pattern (p, name, (char *) &sname, flags);
451 if (p->specified & FSPEC_SLANT &&
452 ((p->slant == SLANT_OBLIQUE) || (p->slant == SLANT_ITALIC)))
453 oblique_seen_p = 1;
454
455 p->next = r;
456 if (p->next)
457 p->next->last = p;
458 r = p;
459 p->next_family = head;
460 }
461 }
462
463 if (p)
464 p->last = NULL;
465
466 for (; head; head = head->last)
467 {
468 head->oblique_seen_p = oblique_seen_p;
469 }
470 }
471 }
472 }
473 }
474
475 /* There's a very good chance that this result will get cached if no
476 slant is specified. Thus, we look through each font that hasn't
477 seen an oblique style, and add one. */
478
479 if (!(pt->specified & FSPEC_SLANT))
480 {
481 /* r->last is invalid from here onwards. */
482 for (struct haiku_font_pattern *p = r; p;)
483 {
484 if (!p->oblique_seen_p)
485 {
486 struct haiku_font_pattern *n = new haiku_font_pattern;
487 *n = *p;
488 n->slant = SLANT_OBLIQUE;
489 p->next = n;
490 p = p->next_family;
491 }
492 else
493 p = p->next_family;
494 }
495 }
496
497 return r;
498 }
499
500 /* Find and open a font matching the pattern PAT, which must have its
501 family set. */
502 int
BFont_open_pattern(struct haiku_font_pattern * pat,void ** font,float size)503 BFont_open_pattern (struct haiku_font_pattern *pat, void **font, float size)
504 {
505 int sty_count;
506 font_family name;
507 font_style sname;
508 uint32 flags = 0;
509 if (!(pat->specified & FSPEC_FAMILY))
510 return 1;
511 strncpy (name, pat->family, sizeof name - 1);
512 sty_count = count_font_styles (name);
513
514 if (!sty_count &&
515 font_family_style_matches_p (name, NULL, flags, pat, 1))
516 {
517 BFont *ft = new BFont;
518 if (ft->SetFamilyAndStyle (name, NULL) != B_OK)
519 {
520 delete ft;
521 return 1;
522 }
523 ft->SetSize (size);
524 ft->SetEncoding (B_UNICODE_UTF8);
525 ft->SetSpacing (B_BITMAP_SPACING);
526 *font = (void *) ft;
527 return 0;
528 }
529 else if (sty_count)
530 {
531 for (int si = 0; si < sty_count; ++si)
532 {
533 if (get_font_style (name, si, &sname, &flags) == B_OK &&
534 font_family_style_matches_p (name, (char *) &sname, flags, pat))
535 {
536 BFont *ft = new BFont;
537 if (ft->SetFamilyAndStyle (name, sname) != B_OK)
538 {
539 delete ft;
540 return 1;
541 }
542 ft->SetSize (size);
543 ft->SetEncoding (B_UNICODE_UTF8);
544 ft->SetSpacing (B_BITMAP_SPACING);
545 *font = (void *) ft;
546 return 0;
547 }
548 }
549 }
550
551 if (pat->specified & FSPEC_SLANT && pat->slant == SLANT_OBLIQUE)
552 {
553 struct haiku_font_pattern copy = *pat;
554 copy.slant = SLANT_REGULAR;
555 int code = BFont_open_pattern (©, font, size);
556 if (code)
557 return code;
558 BFont *ft = (BFont *) *font;
559 /* XXX Font measurements don't respect shear. Haiku bug?
560 This apparently worked in BeOS.
561 ft->SetShear (100.0); */
562 ft->SetFace (B_ITALIC_FACE);
563 return 0;
564 }
565
566 return 1;
567 }
568
569 /* Query the family of the default fixed font. */
570 void
BFont_populate_fixed_family(struct haiku_font_pattern * ptn)571 BFont_populate_fixed_family (struct haiku_font_pattern *ptn)
572 {
573 font_family f;
574 font_style s;
575 be_fixed_font->GetFamilyAndStyle (&f, &s);
576
577 ptn->specified |= FSPEC_FAMILY;
578 strncpy (ptn->family, f, sizeof ptn->family - 1);
579 }
580
581 void
BFont_populate_plain_family(struct haiku_font_pattern * ptn)582 BFont_populate_plain_family (struct haiku_font_pattern *ptn)
583 {
584 font_family f;
585 font_style s;
586 be_plain_font->GetFamilyAndStyle (&f, &s);
587
588 ptn->specified |= FSPEC_FAMILY;
589 strncpy (ptn->family, f, sizeof ptn->family - 1);
590 }
591
592 int
BFont_string_width(void * font,const char * utf8)593 BFont_string_width (void *font, const char *utf8)
594 {
595 return ((BFont *) font)->StringWidth (utf8);
596 }
597