xref: /haiku/src/servers/app/font/GlobalFontManager.cpp (revision 2a2e7ad562841be14b2d1f8ad870780f32be2b1f)
1 /*
2  * Copyright 2001-2016, Haiku.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		DarkWyrm <bpmagic@columbus.rr.com>
7  *		Axel Dörfler, axeld@pinc-software.de
8  */
9 
10 
11 /*!	Manages font families and styles */
12 
13 
14 #include "GlobalFontManager.h"
15 
16 #include <new>
17 
18 #include <Autolock.h>
19 #include <Debug.h>
20 #include <Directory.h>
21 #include <Entry.h>
22 #include <File.h>
23 #include <FindDirectory.h>
24 #include <Message.h>
25 #include <NodeMonitor.h>
26 #include <Path.h>
27 #include <String.h>
28 
29 #include <ft2build.h>
30 #include FT_FREETYPE_H
31 
32 #include "FontFamily.h"
33 #include "ServerConfig.h"
34 #include "ServerFont.h"
35 
36 
37 //#define TRACE_GLOBAL_FONT_MANAGER
38 #ifdef TRACE_GLOBAL_FONT_MANAGER
39 #	define FTRACE(x) debug_printf x
40 #else
41 #	define FTRACE(x) ;
42 #endif
43 
44 
45 // TODO: needs some more work for multi-user support
46 
47 GlobalFontManager* gFontManager = NULL;
48 extern FT_Library gFreeTypeLibrary;
49 
50 
51 struct GlobalFontManager::font_directory {
52 	node_ref	directory;
53 	uid_t		user;
54 	gid_t		group;
55 	uint32		revision;
56 	BObjectList<FontStyle> styles;
57 
58 	bool AlreadyScanned() const { return revision != 0; }
59 	FontStyle* FindStyle(const node_ref& nodeRef) const;
60 };
61 
62 
63 struct GlobalFontManager::font_mapping {
64 	BString		family;
65 	BString		style;
66 	entry_ref	ref;
67 };
68 
69 
70 FontStyle*
71 GlobalFontManager::font_directory::FindStyle(const node_ref& nodeRef) const
72 {
73 	for (int32 i = styles.CountItems(); i-- > 0;) {
74 		FontStyle* style = styles.ItemAt(i);
75 
76 		if (nodeRef == style->NodeRef())
77 			return style;
78 	}
79 
80 	return NULL;
81 }
82 
83 
84 static status_t
85 set_entry(node_ref& nodeRef, const char* name, BEntry& entry)
86 {
87 	entry_ref ref;
88 	ref.device = nodeRef.device;
89 	ref.directory = nodeRef.node;
90 
91 	status_t status = ref.set_name(name);
92 	if (status != B_OK)
93 		return status;
94 
95 	return entry.SetTo(&ref);
96 }
97 
98 
99 //	#pragma mark -
100 
101 
102 //! Does basic set up so that directories can be scanned
103 GlobalFontManager::GlobalFontManager()
104 	: BLooper("GlobalFontManager"),
105 	fDirectories(10, true),
106 	fMappings(10, true),
107 
108 	fDefaultPlainFont(NULL),
109 	fDefaultBoldFont(NULL),
110 	fDefaultFixedFont(NULL),
111 
112 	fScanned(false)
113 {
114 	fInitStatus = FT_Init_FreeType(&gFreeTypeLibrary) == 0 ? B_OK : B_ERROR;
115 	if (fInitStatus == B_OK) {
116 		_AddSystemPaths();
117 		_LoadRecentFontMappings();
118 
119 		fInitStatus = _SetDefaultFonts();
120 
121 		if (fInitStatus == B_OK) {
122 			// Precache the plain and bold fonts
123 			_PrecacheFontFile(fDefaultPlainFont.Get());
124 			_PrecacheFontFile(fDefaultBoldFont.Get());
125 
126 			// Post a message so we scan the initial paths.
127 			PostMessage(B_PULSE);
128 		}
129 	}
130 }
131 
132 
133 //! Frees items allocated in the constructor and shuts down FreeType
134 GlobalFontManager::~GlobalFontManager()
135 {
136 	fDefaultPlainFont.Unset();
137 	fDefaultBoldFont.Unset();
138 	fDefaultFixedFont.Unset();
139 
140 	_RemoveAllFonts();
141 
142 	FT_Done_FreeType(gFreeTypeLibrary);
143 }
144 
145 
146 void
147 GlobalFontManager::MessageReceived(BMessage* message)
148 {
149 	switch (message->what) {
150 		case B_NODE_MONITOR:
151 		{
152 			int32 opcode;
153 			if (message->FindInt32("opcode", &opcode) != B_OK)
154 				return;
155 
156 			switch (opcode) {
157 				case B_ENTRY_CREATED:
158 				{
159 					const char* name;
160 					node_ref nodeRef;
161 					if (message->FindInt32("device", &nodeRef.device) != B_OK
162 						|| message->FindInt64("directory", &nodeRef.node) != B_OK
163 						|| message->FindString("name", &name) != B_OK)
164 						break;
165 
166 					// TODO: make this better (possible under Haiku)
167 					snooze(100000);
168 						// let the font be written completely before trying to open it
169 
170 					BEntry entry;
171 					if (set_entry(nodeRef, name, entry) != B_OK)
172 						break;
173 
174 					if (entry.IsDirectory()) {
175 						// a new directory to watch for us
176 						_AddPath(entry);
177 					} else {
178 						// a new font
179 						font_directory* directory = _FindDirectory(nodeRef);
180 						if (directory == NULL) {
181 							// unknown directory? how come?
182 							break;
183 						}
184 
185 						_AddFont(*directory, entry);
186 					}
187 					break;
188 				}
189 
190 				case B_ENTRY_MOVED:
191 				{
192 					// has the entry been moved into a monitored directory or has
193 					// it been removed from one?
194 					const char* name;
195 					node_ref nodeRef;
196 					uint64 fromNode;
197 					uint64 node;
198 					if (message->FindInt32("device", &nodeRef.device) != B_OK
199 						|| message->FindInt64("to directory", &nodeRef.node) != B_OK
200 						|| message->FindInt64("from directory", (int64 *)&fromNode) != B_OK
201 						|| message->FindInt64("node", (int64 *)&node) != B_OK
202 						|| message->FindString("name", &name) != B_OK)
203 						break;
204 
205 					font_directory* directory = _FindDirectory(nodeRef);
206 
207 					BEntry entry;
208 					if (set_entry(nodeRef, name, entry) != B_OK)
209 						break;
210 
211 					if (directory != NULL) {
212 						// something has been added to our watched font directories
213 
214 						// test, if the source directory is one of ours as well
215 						nodeRef.node = fromNode;
216 						font_directory* fromDirectory = _FindDirectory(nodeRef);
217 
218 						if (entry.IsDirectory()) {
219 							if (fromDirectory == NULL) {
220 								// there is a new directory to watch for us
221 								_AddPath(entry);
222 								FTRACE(("new directory moved in"));
223 							} else {
224 								// A directory from our watched directories has
225 								// been renamed or moved within the watched
226 								// directories - we only need to update the
227 								// path names of the styles in that directory
228 								nodeRef.node = node;
229 								directory = _FindDirectory(nodeRef);
230 								if (directory != NULL) {
231 									for (int32 i = 0; i < directory->styles.CountItems(); i++) {
232 										FontStyle* style = directory->styles.ItemAt(i);
233 										style->UpdatePath(directory->directory);
234 									}
235 								}
236 								FTRACE(("directory renamed"));
237 							}
238 						} else {
239 							if (fromDirectory != NULL) {
240 								// find style in source and move it to the target
241 								nodeRef.node = node;
242 								FontStyle* style = fromDirectory->FindStyle(nodeRef);
243 								if (style != NULL) {
244 									fromDirectory->styles.RemoveItem(style, false);
245 									directory->styles.AddItem(style);
246 									style->UpdatePath(directory->directory);
247 								}
248 								FTRACE(("font moved"));
249 							} else {
250 								FTRACE(("font added: %s\n", name));
251 								_AddFont(*directory, entry);
252 							}
253 						}
254 					} else {
255 						// and entry has been removed from our font directories
256 						if (entry.IsDirectory()) {
257 							if (entry.GetNodeRef(&nodeRef) == B_OK
258 								&& (directory = _FindDirectory(nodeRef)) != NULL)
259 								_RemoveDirectory(directory);
260 						} else {
261 							// remove font style from directory
262 							_RemoveStyle(nodeRef.device, fromNode, node);
263 						}
264 					}
265 					break;
266 				}
267 
268 				case B_ENTRY_REMOVED:
269 				{
270 					node_ref nodeRef;
271 					uint64 directoryNode;
272 					if (message->FindInt32("device", &nodeRef.device) != B_OK
273 						|| message->FindInt64("directory", (int64 *)&directoryNode) != B_OK
274 						|| message->FindInt64("node", &nodeRef.node) != B_OK)
275 						break;
276 
277 					font_directory* directory = _FindDirectory(nodeRef);
278 					if (directory != NULL) {
279 						// the directory has been removed, so we remove it as well
280 						_RemoveDirectory(directory);
281 					} else {
282 						// remove font style from directory
283 						_RemoveStyle(nodeRef.device, directoryNode, nodeRef.node);
284 					}
285 					break;
286 				}
287 			}
288 			break;
289 		}
290 
291 		default:
292 			BLooper::MessageReceived(message);
293 			break;
294 	}
295 
296 	// Scan fonts here if we need to, preventing other threads from having to do so.
297 	_ScanFontsIfNecessary();
298 }
299 
300 
301 int32
302 GlobalFontManager::CheckRevision(uid_t user)
303 {
304 	BAutolock locker(this);
305 	int32 revision = 0;
306 
307 	_ScanFontsIfNecessary();
308 
309 	for (int32 i = 0; i < fDirectories.CountItems(); i++) {
310 		font_directory* directory = fDirectories.ItemAt(i);
311 
312 		// TODO: for now, add all directories
313 		revision += directory->revision;
314 	}
315 
316 	return revision;
317 }
318 
319 
320 void
321 GlobalFontManager::SaveRecentFontMappings()
322 {
323 }
324 
325 
326 void
327 GlobalFontManager::_AddDefaultMapping(const char* family, const char* style,
328 	const char* path)
329 {
330 	font_mapping* mapping = new (std::nothrow) font_mapping;
331 	if (mapping == NULL)
332 		return;
333 
334 	mapping->family = family;
335 	mapping->style = style;
336 	BEntry entry(path);
337 
338 	if (entry.GetRef(&mapping->ref) != B_OK
339 		|| !entry.Exists()
340 		|| !fMappings.AddItem(mapping))
341 		delete mapping;
342 }
343 
344 
345 bool
346 GlobalFontManager::_LoadRecentFontMappings()
347 {
348 	// default known mappings
349 	// TODO: load them for real, and use these as a fallback
350 
351 	BPath ttfontsPath;
352 	if (find_directory(B_BEOS_FONTS_DIRECTORY, &ttfontsPath) == B_OK) {
353 		ttfontsPath.Append("ttfonts");
354 
355 		BPath veraFontPath = ttfontsPath;
356 		veraFontPath.Append("NotoSans-Regular.ttf");
357 		_AddDefaultMapping("Noto Sans", "Book", veraFontPath.Path());
358 
359 		veraFontPath.SetTo(ttfontsPath.Path());
360 		veraFontPath.Append("NotoSans-Bold.ttf");
361 		_AddDefaultMapping("Noto Sans", "Bold", veraFontPath.Path());
362 
363 		veraFontPath.SetTo(ttfontsPath.Path());
364 		veraFontPath.Append("NotoSansMono-Regular.ttf");
365 		_AddDefaultMapping("Noto Sans Mono", "Regular", veraFontPath.Path());
366 
367 		return true;
368 	}
369 
370 	return false;
371 }
372 
373 
374 status_t
375 GlobalFontManager::_AddMappedFont(const char* familyName, const char* styleName)
376 {
377 	FTRACE(("_AddMappedFont(family = \"%s\", style = \"%s\")\n",
378 		familyName, styleName));
379 
380 	for (int32 i = 0; i < fMappings.CountItems(); i++) {
381 		font_mapping* mapping = fMappings.ItemAt(i);
382 
383 		if (mapping->family == familyName) {
384 			if (styleName != NULL && mapping->style != styleName)
385 				continue;
386 
387 			BEntry entry(&mapping->ref);
388 			if (entry.InitCheck() != B_OK)
389 				continue;
390 
391 			// find parent directory
392 
393 			node_ref nodeRef;
394 			nodeRef.device = mapping->ref.device;
395 			nodeRef.node = mapping->ref.directory;
396 			font_directory* directory = _FindDirectory(nodeRef);
397 			if (directory == NULL) {
398 				// unknown directory, maybe this is a user font - try
399 				// to create the missing directory
400 				BPath path(&entry);
401 				if (path.GetParent(&path) != B_OK
402 					|| _CreateDirectories(path.Path()) != B_OK
403 					|| (directory = _FindDirectory(nodeRef)) == NULL)
404 					continue;
405 			}
406 
407 			return _AddFont(*directory, entry);
408 		}
409 	}
410 
411 	return B_ENTRY_NOT_FOUND;
412 }
413 
414 
415 FontStyle*
416 GlobalFontManager::_GetDefaultStyle(const char* familyName, const char* styleName,
417 	const char* fallbackFamily, const char* fallbackStyle,
418 	uint16 fallbackFace)
419 {
420 	// try to find a matching font
421 	FontStyle* style = GetStyle(familyName, styleName);
422 	if (style == NULL) {
423 		style = GetStyle(fallbackFamily, fallbackStyle);
424 		if (style == NULL) {
425 			style = FindStyleMatchingFace(fallbackFace);
426 			if (style == NULL && FamilyAt(0) != NULL)
427 				style = FamilyAt(0)->StyleAt(0);
428 		}
429 	}
430 
431 	return style;
432 }
433 
434 
435 /*!	\brief Sets the fonts that will be used when you create an empty
436 		ServerFont without specifying a style, as well as the default
437 		Desktop fonts if there are no settings available.
438 */
439 status_t
440 GlobalFontManager::_SetDefaultFonts()
441 {
442 	FontStyle* style = NULL;
443 
444 	// plain font
445 	style = _GetDefaultStyle(DEFAULT_PLAIN_FONT_FAMILY, DEFAULT_PLAIN_FONT_STYLE,
446 		FALLBACK_PLAIN_FONT_FAMILY, FALLBACK_PLAIN_FONT_STYLE, B_REGULAR_FACE);
447 	if (style == NULL)
448 		return B_ERROR;
449 
450 	fDefaultPlainFont.SetTo(new (std::nothrow) ServerFont(*style,
451 		DEFAULT_FONT_SIZE));
452 	if (!fDefaultPlainFont.IsSet())
453 		return B_NO_MEMORY;
454 
455 	// bold font
456 	style = _GetDefaultStyle(DEFAULT_BOLD_FONT_FAMILY, DEFAULT_BOLD_FONT_STYLE,
457 		FALLBACK_BOLD_FONT_FAMILY, FALLBACK_BOLD_FONT_STYLE, B_BOLD_FACE);
458 
459 	fDefaultBoldFont.SetTo(new (std::nothrow) ServerFont(*style,
460 		DEFAULT_FONT_SIZE));
461 	if (!fDefaultBoldFont.IsSet())
462 		return B_NO_MEMORY;
463 
464 	// fixed font
465 	style = _GetDefaultStyle(DEFAULT_FIXED_FONT_FAMILY, DEFAULT_FIXED_FONT_STYLE,
466 		FALLBACK_FIXED_FONT_FAMILY, FALLBACK_FIXED_FONT_STYLE, B_REGULAR_FACE);
467 
468 	fDefaultFixedFont.SetTo(new (std::nothrow) ServerFont(*style,
469 		DEFAULT_FONT_SIZE));
470 	if (!fDefaultFixedFont.IsSet())
471 		return B_NO_MEMORY;
472 
473 	fDefaultFixedFont->SetSpacing(B_FIXED_SPACING);
474 
475 	return B_OK;
476 }
477 
478 
479 /*!	\brief Removes the style from the font directory.
480 
481 	It doesn't necessary delete the font style, if it's still
482 	in use, though.
483 */
484 void
485 GlobalFontManager::_RemoveStyle(font_directory& directory, FontStyle* style)
486 {
487 	FTRACE(("font removed: %s\n", style->Name()));
488 
489 	directory.styles.RemoveItem(style);
490 	directory.revision++;
491 
492 	_RemoveFont(style->Family()->ID(), style->ID());
493 }
494 
495 
496 void
497 GlobalFontManager::_RemoveStyle(dev_t device, uint64 directoryNode, uint64 node)
498 {
499 	// remove font style from directory
500 	node_ref nodeRef;
501 	nodeRef.device = device;
502 	nodeRef.node = directoryNode;
503 
504 	font_directory* directory = _FindDirectory(nodeRef);
505 	if (directory != NULL) {
506 		// find style in directory and remove it
507 		nodeRef.node = node;
508 		FontStyle* style = directory->FindStyle(nodeRef);
509 		if (style != NULL)
510 			_RemoveStyle(*directory, style);
511 	}
512 }
513 
514 
515 /*!	\brief Counts the number of font families available
516 	\return The number of unique font families currently available
517 */
518 int32
519 GlobalFontManager::CountFamilies()
520 {
521 	_ScanFontsIfNecessary();
522 
523 	return FontManager::CountFamilies();
524 }
525 
526 
527 /*!	\brief Counts the number of styles available in a font family
528 	\param family Name of the font family to scan
529 	\return The number of font styles currently available for the font family
530 */
531 int32
532 GlobalFontManager::CountStyles(const char* familyName)
533 {
534 	_ScanFontsIfNecessary();
535 
536 	FontFamily* family = GetFamily(familyName);
537 	if (family)
538 		return family->CountStyles();
539 
540 	return 0;
541 }
542 
543 
544 /*!	\brief Counts the number of styles available in a font family
545 	\param family Name of the font family to scan
546 	\return The number of font styles currently available for the font family
547 */
548 int32
549 GlobalFontManager::CountStyles(uint16 familyID)
550 {
551 	_ScanFontsIfNecessary();
552 
553 	FontFamily* family = GetFamily(familyID);
554 	if (family)
555 		return family->CountStyles();
556 
557 	return 0;
558 }
559 
560 
561 FontStyle*
562 GlobalFontManager::GetStyle(uint16 familyID, uint16 styleID) const
563 {
564 	return FontManager::GetStyle(familyID, styleID);
565 }
566 
567 
568 /*!	\brief Retrieves the FontStyle object that comes closest to the one
569 		specified.
570 
571 	\param family The font's family or NULL in which case \a familyID is used
572 	\param style The font's style or NULL in which case \a styleID is used
573 	\param familyID will only be used if \a family is NULL (or empty)
574 	\param styleID will only be used if \a style is NULL (or empty)
575 	\param face is used to specify the style if both \a style is NULL or empty
576 		and styleID is 0xffff.
577 
578 	\return The FontStyle having those attributes or NULL if not available
579 */
580 FontStyle*
581 GlobalFontManager::GetStyle(const char* familyName, const char* styleName,
582 	uint16 familyID, uint16 styleID, uint16 face)
583 {
584 	ASSERT(IsLocked());
585 
586 	FontFamily* family;
587 
588 	// find family
589 
590 	if (familyName != NULL && familyName[0])
591 		family = GetFamily(familyName);
592 	else
593 		family = GetFamily(familyID);
594 
595 	if (family == NULL)
596 		return NULL;
597 
598 	// find style
599 
600 	if (styleName != NULL && styleName[0]) {
601 		FontStyle* fontStyle = family->GetStyle(styleName);
602 		if (fontStyle != NULL)
603 			return fontStyle;
604 
605 		// before we fail, we try the mappings for a match
606 		if (_AddMappedFont(family->Name(), styleName) == B_OK) {
607 			fontStyle = family->GetStyle(styleName);
608 			if (fontStyle != NULL)
609 				return fontStyle;
610 		}
611 
612 		_ScanFonts();
613 		return family->GetStyle(styleName);
614 	}
615 
616 	if (styleID != 0xffff)
617 		return family->GetStyleByID(styleID);
618 
619 	// try to get from face
620 	return family->GetStyleMatchingFace(face);
621 }
622 
623 
624 void
625 GlobalFontManager::_PrecacheFontFile(const ServerFont* font)
626 {
627 	if (font == NULL)
628 		return;
629 
630 	size_t bufferSize = 32768;
631 	uint8* buffer = new (std::nothrow) uint8[bufferSize];
632 	if (buffer == NULL) {
633 		// We don't care. Pre-caching doesn't make sense anyways when there
634 		// is not enough RAM...
635 		return;
636 	}
637 
638 	BFile file(font->Path(), B_READ_ONLY);
639 	if (file.InitCheck() != B_OK) {
640 		delete[] buffer;
641 		return;
642 	}
643 
644 	while (true) {
645 		// We just want the file in the kernel file cache...
646 		ssize_t read = file.Read(buffer, bufferSize);
647 		if (read < (ssize_t)bufferSize)
648 			break;
649 	}
650 
651 	delete[] buffer;
652 }
653 
654 
655 void
656 GlobalFontManager::_AddSystemPaths()
657 {
658 	BPath path;
659 	if (find_directory(B_SYSTEM_FONTS_DIRECTORY, &path, true) == B_OK)
660 		_AddPath(path.Path());
661 
662 	// We don't scan these in test mode to help shave off some startup time
663 #if !TEST_MODE
664 	if (find_directory(B_SYSTEM_NONPACKAGED_FONTS_DIRECTORY, &path, true) == B_OK)
665 		_AddPath(path.Path());
666 #endif
667 }
668 
669 
670 void
671 GlobalFontManager::_ScanFontsIfNecessary()
672 {
673 	if (!fScanned)
674 		_ScanFonts();
675 }
676 
677 
678 //! Scans all currently known font directories
679 void
680 GlobalFontManager::_ScanFonts()
681 {
682 	if (fScanned)
683 		return;
684 
685 	for (int32 i = fDirectories.CountItems(); i-- > 0;) {
686 		font_directory* directory = fDirectories.ItemAt(i);
687 
688 		if (directory->AlreadyScanned())
689 			continue;
690 
691 		_ScanFontDirectory(*directory);
692 	}
693 
694 	fScanned = true;
695 }
696 
697 
698 /*!	\brief Adds the FontFamily/FontStyle that is represented by this path.
699 */
700 status_t
701 GlobalFontManager::_AddFont(font_directory& directory, BEntry& entry)
702 {
703 	node_ref nodeRef;
704 	status_t status = entry.GetNodeRef(&nodeRef);
705 	if (status < B_OK)
706 		return status;
707 
708 	BPath path;
709 	status = entry.GetPath(&path);
710 	if (status < B_OK)
711 		return status;
712 
713 	FT_Face face;
714 	FT_Error error = FT_New_Face(gFreeTypeLibrary, path.Path(), 0, &face);
715 	if (error != 0)
716 		return B_ERROR;
717 
718 	uint16 familyID, styleID;
719 	status = FontManager::_AddFont(face, nodeRef, path.Path(), familyID, styleID);
720 	if (status == B_NAME_IN_USE)
721 		return B_OK;
722 	if (status < B_OK)
723 		return status;
724 
725 	directory.styles.AddItem(GetStyle(familyID, styleID));
726 
727 	if (directory.AlreadyScanned())
728 		directory.revision++;
729 
730 	return B_OK;
731 }
732 
733 
734 GlobalFontManager::font_directory*
735 GlobalFontManager::_FindDirectory(node_ref& nodeRef)
736 {
737 	for (int32 i = fDirectories.CountItems(); i-- > 0;) {
738 		font_directory* directory = fDirectories.ItemAt(i);
739 
740 		if (directory->directory == nodeRef)
741 			return directory;
742 	}
743 
744 	return NULL;
745 }
746 
747 
748 void
749 GlobalFontManager::_RemoveDirectory(font_directory* directory)
750 {
751 	FTRACE(("FontManager: Remove directory (%" B_PRIdINO ")!\n",
752 		directory->directory.node));
753 
754 	fDirectories.RemoveItem(directory, false);
755 
756 	// TODO: remove styles from this directory!
757 
758 	watch_node(&directory->directory, B_STOP_WATCHING, this);
759 	delete directory;
760 }
761 
762 
763 status_t
764 GlobalFontManager::_AddPath(const char* path)
765 {
766 	BEntry entry;
767 	status_t status = entry.SetTo(path);
768 	if (status != B_OK)
769 		return status;
770 
771 	return _AddPath(entry);
772 }
773 
774 
775 status_t
776 GlobalFontManager::_AddPath(BEntry& entry, font_directory** _newDirectory)
777 {
778 	node_ref nodeRef;
779 	status_t status = entry.GetNodeRef(&nodeRef);
780 	if (status != B_OK)
781 		return status;
782 
783 	// check if we are already know this directory
784 
785 	font_directory* directory = _FindDirectory(nodeRef);
786 	if (directory != NULL) {
787 		if (_newDirectory)
788 			*_newDirectory = directory;
789 		return B_OK;
790 	}
791 
792 	// it's a new one, so let's add it
793 
794 	directory = new (std::nothrow) font_directory;
795 	if (directory == NULL)
796 		return B_NO_MEMORY;
797 
798 	struct stat stat;
799 	status = entry.GetStat(&stat);
800 	if (status != B_OK) {
801 		delete directory;
802 		return status;
803 	}
804 
805 	directory->directory = nodeRef;
806 	directory->user = stat.st_uid;
807 	directory->group = stat.st_gid;
808 	directory->revision = 0;
809 
810 	status = watch_node(&nodeRef, B_WATCH_DIRECTORY, this);
811 	if (status != B_OK) {
812 		// we cannot watch this directory - while this is unfortunate,
813 		// it's not a critical error
814 		printf("could not watch directory %" B_PRIdDEV ":%" B_PRIdINO "\n",
815 			nodeRef.device, nodeRef.node);
816 			// TODO: should go into syslog()
817 	} else {
818 		BPath path(&entry);
819 		FTRACE(("FontManager: now watching: %s\n", path.Path()));
820 	}
821 
822 	fDirectories.AddItem(directory);
823 
824 	if (_newDirectory)
825 		*_newDirectory = directory;
826 
827 	fScanned = false;
828 	return B_OK;
829 }
830 
831 
832 /*!	\brief Creates all unknown font_directories of the specified path - but
833 		only if one of its parent directories is already known.
834 
835 	This method is used to create the font_directories for font_mappings.
836 	It recursively walks upwards in the directory hierarchy until it finds
837 	a known font_directory (or hits the root directory, in which case it
838 	bails out).
839 */
840 status_t
841 GlobalFontManager::_CreateDirectories(const char* path)
842 {
843 	FTRACE(("_CreateDirectories(path = %s)\n", path));
844 
845 	if (!strcmp(path, "/")) {
846 		// we walked our way up to the root
847 		return B_ENTRY_NOT_FOUND;
848 	}
849 
850 	BEntry entry;
851 	status_t status = entry.SetTo(path);
852 	if (status != B_OK)
853 		return status;
854 
855 	node_ref nodeRef;
856 	status = entry.GetNodeRef(&nodeRef);
857 	if (status != B_OK)
858 		return status;
859 
860 	// check if we are already know this directory
861 
862 	font_directory* directory = _FindDirectory(nodeRef);
863 	if (directory != NULL)
864 		return B_OK;
865 
866 	// We don't know this one yet - keep walking the path upwards
867 	// and try to find a match.
868 
869 	BPath parent(path);
870 	status = parent.GetParent(&parent);
871 	if (status != B_OK)
872 		return status;
873 
874 	status = _CreateDirectories(parent.Path());
875 	if (status != B_OK)
876 		return status;
877 
878 	// We have our match, create sub directory
879 
880 	return _AddPath(path);
881 }
882 
883 
884 /*!	\brief Scan a folder for all valid fonts
885 	\param directoryPath Path of the folder to scan.
886 */
887 status_t
888 GlobalFontManager::_ScanFontDirectory(font_directory& fontDirectory)
889 {
890 	// This bad boy does all the real work. It loads each entry in the
891 	// directory. If a valid font file, it adds both the family and the style.
892 
893 	BDirectory directory;
894 	status_t status = directory.SetTo(&fontDirectory.directory);
895 	if (status != B_OK)
896 		return status;
897 
898 	BEntry entry;
899 	while (directory.GetNextEntry(&entry) == B_OK) {
900 		if (entry.IsDirectory()) {
901 			// scan this directory recursively
902 			font_directory* newDirectory;
903 			if (_AddPath(entry, &newDirectory) == B_OK && newDirectory != NULL)
904 				_ScanFontDirectory(*newDirectory);
905 
906 			continue;
907 		}
908 
909 // TODO: Commenting this out makes my "Unicode glyph lookup"
910 // work with our default fonts. The real fix is to select the
911 // Unicode char map (if supported), and/or adjust the
912 // utf8 -> glyph-index mapping everywhere to handle other
913 // char maps. We could also ignore fonts that don't support
914 // the Unicode lookup as a temporary "solution".
915 #if 0
916 		FT_CharMap charmap = _GetSupportedCharmap(face);
917 		if (!charmap) {
918 		    FT_Done_Face(face);
919 		    continue;
920     	}
921 
922 		face->charmap = charmap;
923 #endif
924 
925 		_AddFont(fontDirectory, entry);
926 			// takes over ownership of the FT_Face object
927 	}
928 
929 	fontDirectory.revision = 1;
930 	return B_OK;
931 }
932 
933 
934 /*!	\brief Locates a FontFamily object by name
935 	\param name The family to find
936 	\return Pointer to the specified family or NULL if not found.
937 */
938 FontFamily*
939 GlobalFontManager::GetFamily(const char* name)
940 {
941 	if (name == NULL)
942 		return NULL;
943 
944 	FontFamily* family = _FindFamily(name);
945 	if (family != NULL)
946 		return family;
947 
948 	if (fScanned)
949 		return NULL;
950 
951 	// try font mappings before failing
952 	if (_AddMappedFont(name) == B_OK)
953 		return _FindFamily(name);
954 
955 	_ScanFonts();
956 	return _FindFamily(name);
957 }
958 
959 
960 FontFamily*
961 GlobalFontManager::GetFamily(uint16 familyID) const
962 {
963 	return FontManager::GetFamily(familyID);
964 }
965 
966 
967 const ServerFont*
968 GlobalFontManager::DefaultPlainFont() const
969 {
970 	return fDefaultPlainFont.Get();
971 }
972 
973 
974 const ServerFont*
975 GlobalFontManager::DefaultBoldFont() const
976 {
977 	return fDefaultBoldFont.Get();
978 }
979 
980 
981 const ServerFont*
982 GlobalFontManager::DefaultFixedFont() const
983 {
984 	return fDefaultFixedFont.Get();
985 }
986 
987 
988 void
989 GlobalFontManager::AttachUser(uid_t userID)
990 {
991 	BAutolock locker(this);
992 
993 #if !TEST_MODE
994 	// TODO: actually, find_directory() cannot know which user ID we want here
995 	// TODO: avoids user fonts in safe mode
996 	BPath path;
997 	if (find_directory(B_USER_FONTS_DIRECTORY, &path, true) == B_OK)
998 		_AddPath(path.Path());
999 	if (find_directory(B_USER_NONPACKAGED_FONTS_DIRECTORY, &path, true)
1000 			== B_OK) {
1001 		_AddPath(path.Path());
1002 	}
1003 #endif
1004 }
1005 
1006 
1007 void
1008 GlobalFontManager::DetachUser(uid_t userID)
1009 {
1010 	BAutolock locker(this);
1011 
1012 	// TODO!
1013 }
1014