1 /* vifm
2  * Copyright (C) 2013 xaizek.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 
19 #include "color_manager.h"
20 
21 #include <assert.h> /* assert() */
22 #include <stddef.h> /* NULL */
23 
24 #include "../utils/macros.h"
25 #include "colors.h"
26 
27 /* Number of color pairs preallocated by curses library. */
28 #define PREALLOCATED_COUNT 1
29 
30 static int find_pair(int fg, int bg);
31 static int color_pair_matches(int pair, int fg, int bg);
32 static int allocate_pair(int fg, int bg);
33 static int compress_pair_space(void);
34 
35 /* Number of color pairs available. */
36 static int avail_pairs;
37 
38 /* Number of used color pairs. */
39 static int used_pairs;
40 
41 /* Configuration data passed in during initialization. */
42 static colmgr_conf_t conf;
43 
44 /* Flag, which is set after the unit is initialized. */
45 static int initialized;
46 
47 /* Default foreground and background colors. */
48 static short def_fg, def_bg;
49 
50 void
colmgr_init(const colmgr_conf_t * conf_init)51 colmgr_init(const colmgr_conf_t *conf_init)
52 {
53 	assert(conf_init != NULL && "conf_init structure is required.");
54 	assert(conf_init->init_pair != NULL && "init_pair must be set.");
55 	assert(conf_init->pair_content != NULL && "pair_content must be set.");
56 	assert(conf_init->pair_in_use != NULL && "pair_in_use must be set.");
57 	assert(conf_init->move_pair != NULL && "move_pair must be set.");
58 
59 	conf = *conf_init;
60 	initialized = 1;
61 
62 	colmgr_reset();
63 
64 	/* Query default colors as implementation might not return -1 via
65 	 * pair_content(), which will confuse this unit. */
66 	if(conf.pair_content(0, &def_fg, &def_bg) != 0)
67 	{
68 		def_fg = -1;
69 		def_bg = -1;
70 	}
71 }
72 
73 void
colmgr_reset(void)74 colmgr_reset(void)
75 {
76 	assert(initialized && "colmgr_init() must be called before this function!");
77 
78 	used_pairs = PREALLOCATED_COUNT;
79 	avail_pairs = conf.max_color_pairs - used_pairs;
80 }
81 
82 int
colmgr_get_pair(int fg,int bg)83 colmgr_get_pair(int fg, int bg)
84 {
85 	assert(initialized && "colmgr_init() must be called before this function!");
86 
87 	if(fg < 0)
88 	{
89 		fg = -1;
90 	}
91 
92 	if(bg < 0)
93 	{
94 		bg = -1;
95 	}
96 
97 	int p = find_pair(fg, bg);
98 	if(p != -1)
99 	{
100 		return p;
101 	}
102 
103 	p = allocate_pair(fg, bg);
104 	return (p == -1) ? 0 : p;
105 }
106 
107 /* Tries to find pair with specified colors among already allocated pairs.
108  * Returns -1 when search fails. */
109 static int
find_pair(int fg,int bg)110 find_pair(int fg, int bg)
111 {
112 	if(fg < 0)
113 	{
114 		fg = def_fg;
115 	}
116 	if(bg < 0)
117 	{
118 		bg = def_bg;
119 	}
120 
121 	int i;
122 	for(i = PREALLOCATED_COUNT; i < used_pairs; ++i)
123 	{
124 		if(color_pair_matches(i, fg, bg))
125 		{
126 			return i;
127 		}
128 	}
129 
130 	return -1;
131 }
132 
133 /* Checks whether color pair number pair has specified foreground (fg) and
134  * background (bg) colors. */
135 static int
color_pair_matches(int pair,int fg,int bg)136 color_pair_matches(int pair, int fg, int bg)
137 {
138 	short pair_fg, pair_bg;
139 	conf.pair_content(pair, &pair_fg, &pair_bg);
140 	return (pair_fg == fg && pair_bg == bg);
141 }
142 
143 /* Allocates new color pair.  Returns new pair index, or -1 on failure. */
144 static int
allocate_pair(int fg,int bg)145 allocate_pair(int fg, int bg)
146 {
147 	if(avail_pairs == 0)
148 	{
149 		/* Out of pairs, free unused ones. */
150 		if(compress_pair_space() != 0)
151 		{
152 			return -1;
153 		}
154 	}
155 
156 	if(conf.init_pair(used_pairs, fg, bg) != 0)
157 	{
158 		return -1;
159 	}
160 
161 	--avail_pairs;
162 	return used_pairs++;
163 }
164 
165 /* Returns zero if at least one pair is now available, otherwise non-zero is
166  * returned. */
167 static int
compress_pair_space(void)168 compress_pair_space(void)
169 {
170 	/* TODO: in case of performance issues cache pair_in_use() in the first loop
171 	 * or change the function to fill in bit field of pairs. */
172 
173 	int i;
174 	int j;
175 	int in_use;
176 	int first_unused;
177 
178 	in_use = 0;
179 	first_unused = -1;
180 	for(i = PREALLOCATED_COUNT; i < used_pairs; ++i)
181 	{
182 		if(conf.pair_in_use(i))
183 		{
184 			++in_use;
185 		}
186 		else if(first_unused == -1)
187 		{
188 			first_unused = i;
189 		}
190 	}
191 
192 	if(first_unused == -1)
193 	{
194 		/* No unused pairs. */
195 		return -1;
196 	}
197 
198 	j = first_unused;
199 	for(i = PREALLOCATED_COUNT + in_use; i < used_pairs; ++i)
200 	{
201 		if(conf.pair_in_use(i))
202 		{
203 			conf.move_pair(i, j);
204 
205 			/* Advance to next unused pair. */
206 			do
207 			{
208 				++j;
209 			}
210 			while(conf.pair_in_use(j));
211 		}
212 	}
213 
214 	used_pairs = j;
215 	avail_pairs = conf.max_color_pairs - used_pairs;
216 
217 	return 0;
218 }
219 
220 /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */
221 /* vim: set cinoptions+=t0 filetype=c : */
222