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 (&copy, 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