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
47GlobalFontManager* gFontManager = NULL;
48extern FT_Library gFreeTypeLibrary;
49
50
51struct 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
63struct GlobalFontManager::font_mapping {
64	BString		family;
65	BString		style;
66	entry_ref	ref;
67};
68
69
70FontStyle*
71GlobalFontManager::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
84static status_t
85set_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
103GlobalFontManager::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
134GlobalFontManager::~GlobalFontManager()
135{
136	fDefaultPlainFont.Unset();
137	fDefaultBoldFont.Unset();
138	fDefaultFixedFont.Unset();
139
140	_RemoveAllFonts();
141
142	FT_Done_FreeType(gFreeTypeLibrary);
143}
144
145
146void
147GlobalFontManager::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;
243								while ((style = fromDirectory->FindStyle(nodeRef)) != 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
301int32
302GlobalFontManager::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
320void
321GlobalFontManager::SaveRecentFontMappings()
322{
323}
324
325
326void
327GlobalFontManager::_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
345bool
346GlobalFontManager::_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
374status_t
375GlobalFontManager::_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
415FontStyle*
416GlobalFontManager::_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*/
439status_t
440GlobalFontManager::_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*/
484void
485GlobalFontManager::_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
496void
497GlobalFontManager::_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;
509		while ((style = directory->FindStyle(nodeRef)) != 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*/
518int32
519GlobalFontManager::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*/
531int32
532GlobalFontManager::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*/
548int32
549GlobalFontManager::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
561FontStyle*
562GlobalFontManager::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*/
580FontStyle*
581GlobalFontManager::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
624void
625GlobalFontManager::_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
655void
656GlobalFontManager::_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
670void
671GlobalFontManager::_ScanFontsIfNecessary()
672{
673	if (!fScanned)
674		_ScanFonts();
675}
676
677
678//! Scans all currently known font directories
679void
680GlobalFontManager::_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*/
700status_t
701GlobalFontManager::_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(), -1, &face);
715	if (error != 0)
716		return B_ERROR;
717	FT_Long count = face->num_faces;
718	FT_Done_Face(face);
719
720	for (FT_Long i = 0; i < count; i++) {
721		FT_Error error = FT_New_Face(gFreeTypeLibrary, path.Path(), -(i + 1), &face);
722		if (error != 0)
723			return B_ERROR;
724		uint32 variableCount = (face->style_flags & 0x7fff0000) >> 16;
725		FT_Done_Face(face);
726
727		uint32 j = variableCount == 0 ? 0 : 1;
728		do {
729			FT_Long faceIndex = i | (j << 16);
730			error = FT_New_Face(gFreeTypeLibrary, path.Path(), faceIndex, &face);
731			if (error != 0)
732				return B_ERROR;
733
734			uint16 familyID, styleID;
735			status = FontManager::_AddFont(face, nodeRef, path.Path(), familyID, styleID);
736			if (status == B_NAME_IN_USE) {
737				status = B_OK;
738				j++;
739				continue;
740			}
741			if (status < B_OK)
742				return status;
743			directory.styles.AddItem(GetStyle(familyID, styleID));
744			j++;
745		} while (j <= variableCount);
746	}
747
748	if (directory.AlreadyScanned())
749		directory.revision++;
750
751	return B_OK;
752}
753
754
755GlobalFontManager::font_directory*
756GlobalFontManager::_FindDirectory(node_ref& nodeRef)
757{
758	for (int32 i = fDirectories.CountItems(); i-- > 0;) {
759		font_directory* directory = fDirectories.ItemAt(i);
760
761		if (directory->directory == nodeRef)
762			return directory;
763	}
764
765	return NULL;
766}
767
768
769void
770GlobalFontManager::_RemoveDirectory(font_directory* directory)
771{
772	FTRACE(("FontManager: Remove directory (%" B_PRIdINO ")!\n",
773		directory->directory.node));
774
775	fDirectories.RemoveItem(directory, false);
776
777	// TODO: remove styles from this directory!
778
779	watch_node(&directory->directory, B_STOP_WATCHING, this);
780	delete directory;
781}
782
783
784status_t
785GlobalFontManager::_AddPath(const char* path)
786{
787	BEntry entry;
788	status_t status = entry.SetTo(path);
789	if (status != B_OK)
790		return status;
791
792	return _AddPath(entry);
793}
794
795
796status_t
797GlobalFontManager::_AddPath(BEntry& entry, font_directory** _newDirectory)
798{
799	node_ref nodeRef;
800	status_t status = entry.GetNodeRef(&nodeRef);
801	if (status != B_OK)
802		return status;
803
804	// check if we are already know this directory
805
806	font_directory* directory = _FindDirectory(nodeRef);
807	if (directory != NULL) {
808		if (_newDirectory)
809			*_newDirectory = directory;
810		return B_OK;
811	}
812
813	// it's a new one, so let's add it
814
815	directory = new (std::nothrow) font_directory;
816	if (directory == NULL)
817		return B_NO_MEMORY;
818
819	struct stat stat;
820	status = entry.GetStat(&stat);
821	if (status != B_OK) {
822		delete directory;
823		return status;
824	}
825
826	directory->directory = nodeRef;
827	directory->user = stat.st_uid;
828	directory->group = stat.st_gid;
829	directory->revision = 0;
830
831	status = watch_node(&nodeRef, B_WATCH_DIRECTORY, this);
832	if (status != B_OK) {
833		// we cannot watch this directory - while this is unfortunate,
834		// it's not a critical error
835		printf("could not watch directory %" B_PRIdDEV ":%" B_PRIdINO "\n",
836			nodeRef.device, nodeRef.node);
837			// TODO: should go into syslog()
838	} else {
839		BPath path(&entry);
840		FTRACE(("FontManager: now watching: %s\n", path.Path()));
841	}
842
843	fDirectories.AddItem(directory);
844
845	if (_newDirectory)
846		*_newDirectory = directory;
847
848	fScanned = false;
849	return B_OK;
850}
851
852
853/*!	\brief Creates all unknown font_directories of the specified path - but
854		only if one of its parent directories is already known.
855
856	This method is used to create the font_directories for font_mappings.
857	It recursively walks upwards in the directory hierarchy until it finds
858	a known font_directory (or hits the root directory, in which case it
859	bails out).
860*/
861status_t
862GlobalFontManager::_CreateDirectories(const char* path)
863{
864	FTRACE(("_CreateDirectories(path = %s)\n", path));
865
866	if (!strcmp(path, "/")) {
867		// we walked our way up to the root
868		return B_ENTRY_NOT_FOUND;
869	}
870
871	BEntry entry;
872	status_t status = entry.SetTo(path);
873	if (status != B_OK)
874		return status;
875
876	node_ref nodeRef;
877	status = entry.GetNodeRef(&nodeRef);
878	if (status != B_OK)
879		return status;
880
881	// check if we are already know this directory
882
883	font_directory* directory = _FindDirectory(nodeRef);
884	if (directory != NULL)
885		return B_OK;
886
887	// We don't know this one yet - keep walking the path upwards
888	// and try to find a match.
889
890	BPath parent(path);
891	status = parent.GetParent(&parent);
892	if (status != B_OK)
893		return status;
894
895	status = _CreateDirectories(parent.Path());
896	if (status != B_OK)
897		return status;
898
899	// We have our match, create sub directory
900
901	return _AddPath(path);
902}
903
904
905/*!	\brief Scan a folder for all valid fonts
906	\param directoryPath Path of the folder to scan.
907*/
908status_t
909GlobalFontManager::_ScanFontDirectory(font_directory& fontDirectory)
910{
911	// This bad boy does all the real work. It loads each entry in the
912	// directory. If a valid font file, it adds both the family and the style.
913
914	BDirectory directory;
915	status_t status = directory.SetTo(&fontDirectory.directory);
916	if (status != B_OK)
917		return status;
918
919	BEntry entry;
920	while (directory.GetNextEntry(&entry) == B_OK) {
921		if (entry.IsDirectory()) {
922			// scan this directory recursively
923			font_directory* newDirectory;
924			if (_AddPath(entry, &newDirectory) == B_OK && newDirectory != NULL)
925				_ScanFontDirectory(*newDirectory);
926
927			continue;
928		}
929
930// TODO: Commenting this out makes my "Unicode glyph lookup"
931// work with our default fonts. The real fix is to select the
932// Unicode char map (if supported), and/or adjust the
933// utf8 -> glyph-index mapping everywhere to handle other
934// char maps. We could also ignore fonts that don't support
935// the Unicode lookup as a temporary "solution".
936#if 0
937		FT_CharMap charmap = _GetSupportedCharmap(face);
938		if (!charmap) {
939		    FT_Done_Face(face);
940		    continue;
941    	}
942
943		face->charmap = charmap;
944#endif
945
946		_AddFont(fontDirectory, entry);
947			// takes over ownership of the FT_Face object
948	}
949
950	fontDirectory.revision = 1;
951	return B_OK;
952}
953
954
955/*!	\brief Locates a FontFamily object by name
956	\param name The family to find
957	\return Pointer to the specified family or NULL if not found.
958*/
959FontFamily*
960GlobalFontManager::GetFamily(const char* name)
961{
962	if (name == NULL)
963		return NULL;
964
965	FontFamily* family = _FindFamily(name);
966	if (family != NULL)
967		return family;
968
969	if (fScanned)
970		return NULL;
971
972	// try font mappings before failing
973	if (_AddMappedFont(name) == B_OK)
974		return _FindFamily(name);
975
976	_ScanFonts();
977	return _FindFamily(name);
978}
979
980
981FontFamily*
982GlobalFontManager::GetFamily(uint16 familyID) const
983{
984	return FontManager::GetFamily(familyID);
985}
986
987
988const ServerFont*
989GlobalFontManager::DefaultPlainFont() const
990{
991	return fDefaultPlainFont.Get();
992}
993
994
995const ServerFont*
996GlobalFontManager::DefaultBoldFont() const
997{
998	return fDefaultBoldFont.Get();
999}
1000
1001
1002const ServerFont*
1003GlobalFontManager::DefaultFixedFont() const
1004{
1005	return fDefaultFixedFont.Get();
1006}
1007
1008
1009void
1010GlobalFontManager::AttachUser(uid_t userID)
1011{
1012	BAutolock locker(this);
1013
1014#if !TEST_MODE
1015	// TODO: actually, find_directory() cannot know which user ID we want here
1016	// TODO: avoids user fonts in safe mode
1017	BPath path;
1018	if (find_directory(B_USER_FONTS_DIRECTORY, &path, true) == B_OK)
1019		_AddPath(path.Path());
1020	if (find_directory(B_USER_NONPACKAGED_FONTS_DIRECTORY, &path, true)
1021			== B_OK) {
1022		_AddPath(path.Path());
1023	}
1024#endif
1025}
1026
1027
1028void
1029GlobalFontManager::DetachUser(uid_t userID)
1030{
1031	BAutolock locker(this);
1032
1033	// TODO!
1034}
1035