xterm
[VSoRC/.git] / node_modules / xterm / src / browser / renderer / atlas / CharAtlasCache.ts
1 /**
2  * Copyright (c) 2017 The xterm.js authors. All rights reserved.
3  * @license MIT
4  */
5
6 import { generateConfig, configEquals } from 'browser/renderer/atlas/CharAtlasUtils';
7 import { BaseCharAtlas } from 'browser/renderer/atlas/BaseCharAtlas';
8 import { DynamicCharAtlas } from 'browser/renderer/atlas/DynamicCharAtlas';
9 import { ICharAtlasConfig } from 'browser/renderer/atlas/Types';
10 import { IColorSet } from 'browser/Types';
11 import { ITerminalOptions } from 'common/services/Services';
12
13 interface ICharAtlasCacheEntry {
14   atlas: BaseCharAtlas;
15   config: ICharAtlasConfig;
16   // N.B. This implementation potentially holds onto copies of the terminal forever, so
17   // this may cause memory leaks.
18   ownedBy: number[];
19 }
20
21 const charAtlasCache: ICharAtlasCacheEntry[] = [];
22
23 /**
24  * Acquires a char atlas, either generating a new one or returning an existing
25  * one that is in use by another terminal.
26  */
27 export function acquireCharAtlas(
28   options: ITerminalOptions,
29   rendererId: number,
30   colors: IColorSet,
31   scaledCharWidth: number,
32   scaledCharHeight: number
33 ): BaseCharAtlas {
34   const newConfig = generateConfig(scaledCharWidth, scaledCharHeight, options, colors);
35
36   // Check to see if the renderer already owns this config
37   for (let i = 0; i < charAtlasCache.length; i++) {
38     const entry = charAtlasCache[i];
39     const ownedByIndex = entry.ownedBy.indexOf(rendererId);
40     if (ownedByIndex >= 0) {
41       if (configEquals(entry.config, newConfig)) {
42         return entry.atlas;
43       }
44       // The configs differ, release the renderer from the entry
45       if (entry.ownedBy.length === 1) {
46         entry.atlas.dispose();
47         charAtlasCache.splice(i, 1);
48       } else {
49         entry.ownedBy.splice(ownedByIndex, 1);
50       }
51       break;
52     }
53   }
54
55   // Try match a char atlas from the cache
56   for (let i = 0; i < charAtlasCache.length; i++) {
57     const entry = charAtlasCache[i];
58     if (configEquals(entry.config, newConfig)) {
59       // Add the renderer to the cache entry and return
60       entry.ownedBy.push(rendererId);
61       return entry.atlas;
62     }
63   }
64
65   const newEntry: ICharAtlasCacheEntry = {
66     atlas: new DynamicCharAtlas(
67       document,
68       newConfig
69     ),
70     config: newConfig,
71     ownedBy: [rendererId]
72   };
73   charAtlasCache.push(newEntry);
74   return newEntry.atlas;
75 }
76
77 /**
78  * Removes a terminal reference from the cache, allowing its memory to be freed.
79  */
80 export function removeTerminalFromCache(rendererId: number): void {
81   for (let i = 0; i < charAtlasCache.length; i++) {
82     const index = charAtlasCache[i].ownedBy.indexOf(rendererId);
83     if (index !== -1) {
84       if (charAtlasCache[i].ownedBy.length === 1) {
85         // Remove the cache entry if it's the only renderer
86         charAtlasCache[i].atlas.dispose();
87         charAtlasCache.splice(i, 1);
88       } else {
89         // Remove the reference from the cache entry
90         charAtlasCache[i].ownedBy.splice(index, 1);
91       }
92       break;
93     }
94   }
95 }