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