1 #include "theme.h"
2 
3 #include "debug.h"
4 #include "filesys.h"
5 #include "theme_tables.h"
6 #include "ui.h"
7 
8 #include <stdlib.h>
9 #include <string.h>
10 
11 #define COLOR_PROC(a_ulColor) RGB((a_ulColor >> 16) & 0x0000FF, (a_ulColor >> 8) & 0x0000FF, a_ulColor & 0x0000FF)
12 
13 /* Solarized color scheme */
14 #define SOLAR_BASE03 0x002b36
15 #define SOLAR_BASE02 0x073642
16 #define SOLAR_BASE01 0x586e75
17 #define SOLAR_BASE00 0x657b83
18 #define SOLAR_BASE0 0x839496
19 #define SOLAR_BASE1 0x93a1a1
20 #define SOLAR_BASE2 0xeee8d5
21 #define SOLAR_BASE3 0xfdf6e3
22 #define SOLAR_YELLOW 0xb58900
23 #define SOLAR_ORANGE 0xcb4b16
24 #define SOLAR_RED 0xdc322f
25 #define SOLAR_MAGENTA 0xd33682
26 #define SOLAR_VIOLET 0x6c71c4
27 #define SOLAR_BLUE 0x268bd2
28 #define SOLAR_CYAN 0x2aa198
29 #define SOLAR_GREEN 0x859900
30 
31 uint32_t COLOR_BKGRND_MAIN;
32 uint32_t COLOR_BKGRND_ALT;
33 uint32_t COLOR_BKGRND_AUX;
34 uint32_t COLOR_BKGRND_MENU;
35 uint32_t COLOR_BKGRND_MENU_HOVER;
36 uint32_t COLOR_BKGRND_MENU_ACTIVE;
37 uint32_t COLOR_BKGRND_LIST;
38 uint32_t COLOR_BKGRND_LIST_HOVER;
39 
40 uint32_t COLOR_MAIN_TEXT;
41 uint32_t COLOR_MAIN_TEXT_CHAT;
42 uint32_t COLOR_MAIN_TEXT_SUBTEXT;
43 uint32_t COLOR_MAIN_TEXT_ACTION;
44 uint32_t COLOR_MAIN_TEXT_QUOTE;
45 uint32_t COLOR_MAIN_TEXT_RED;
46 uint32_t COLOR_MAIN_TEXT_URL;
47 uint32_t COLOR_MAIN_TEXT_HINT;
48 
49 uint32_t COLOR_MSG_USER;
50 uint32_t COLOR_MSG_USER_PEND;
51 uint32_t COLOR_MSG_USER_ERROR;
52 uint32_t COLOR_MSG_CONTACT;
53 
54 uint32_t COLOR_MENU_TEXT;
55 uint32_t COLOR_MENU_TEXT_SUBTEXT;
56 uint32_t COLOR_MENU_TEXT_ACTIVE;
57 
58 uint32_t COLOR_LIST_TEXT;
59 uint32_t COLOR_LIST_TEXT_SUBTEXT;
60 
61 uint32_t COLOR_AUX_EDGE_NORMAL;
62 uint32_t COLOR_AUX_EDGE_HOVER;
63 uint32_t COLOR_AUX_EDGE_ACTIVE;
64 uint32_t COLOR_AUX_TEXT;
65 uint32_t COLOR_AUX_ACTIVEOPTION_BKGRND;
66 uint32_t COLOR_AUX_ACTIVEOPTION_TEXT;
67 
68 uint32_t COLOR_GROUP_SELF;
69 uint32_t COLOR_GROUP_PEER;
70 uint32_t COLOR_GROUP_AUDIO;
71 uint32_t COLOR_GROUP_MUTED;
72 
73 uint32_t COLOR_SELECTION_BACKGROUND;
74 uint32_t COLOR_SELECTION_TEXT;
75 
76 uint32_t COLOR_EDGE_NORMAL;
77 uint32_t COLOR_EDGE_ACTIVE;
78 uint32_t COLOR_EDGE_HOVER;
79 
80 uint32_t COLOR_ACTIVEOPTION_BKGRND;
81 uint32_t COLOR_ACTIVEOPTION_TEXT;
82 
83 uint32_t COLOR_STATUS_ONLINE;
84 uint32_t COLOR_STATUS_AWAY;
85 
86 uint32_t COLOR_STATUS_BUSY;
87 uint32_t COLOR_BTN_SUCCESS_BKGRND;
88 uint32_t COLOR_BTN_SUCCESS_TEXT;
89 uint32_t COLOR_BTN_SUCCESS_BKGRND_HOVER;
90 uint32_t COLOR_BTN_SUCCESS_TEXT_HOVER;
91 
92 uint32_t COLOR_BTN_WARNING_BKGRND;
93 uint32_t COLOR_BTN_WARNING_TEXT;
94 uint32_t COLOR_BTN_WARNING_BKGRND_HOVER;
95 uint32_t COLOR_BTN_WARNING_TEXT_HOVER;
96 
97 uint32_t COLOR_BTN_DANGER_BACKGROUND;
98 uint32_t COLOR_BTN_DANGER_TEXT;
99 uint32_t COLOR_BTN_DANGER_BKGRND_HOVER;
100 uint32_t COLOR_BTN_DANGER_TEXT_HOVER;
101 
102 uint32_t COLOR_BTN_DISABLED_BKGRND;
103 uint32_t COLOR_BTN_DISABLED_TEXT;
104 uint32_t COLOR_BTN_DISABLED_BKGRND_HOVER;
105 uint32_t COLOR_BTN_DISABLED_TRANSFER;
106 
107 uint32_t COLOR_BTN_INPROGRESS_BKGRND;
108 uint32_t COLOR_BTN_INPROGRESS_TEXT;
109 uint32_t COLOR_BTN_DISABLED_FORGRND;
110 uint32_t COLOR_BTN_INPROGRESS_FORGRND;
111 
112 uint32_t status_color[4];
113 
114 /**
115  * Loads a custom theme and sets out to the size of the data
116  *
117  * Returns a pointer to the theme data on success, the caller needs to free this
118  * Returns NULL on failure
119  */
120 static uint8_t *utox_data_load_custom_theme(size_t *out);
121 static void read_custom_theme(const uint8_t *data, size_t length);
122 static uint32_t try_parse_hex_colour(char *color, bool *error);
123 
theme_load(const THEME loadtheme)124 void theme_load(const THEME loadtheme) {
125     // Update the settings dropdown UI
126 
127     // ==== Default theme     ====
128     // ---- Background Colors ----
129     COLOR_BKGRND_MAIN        = COLOR_PROC(0xffffff);
130     COLOR_BKGRND_ALT         = COLOR_PROC(0xaaaaaa);
131     COLOR_BKGRND_AUX         = COLOR_PROC(0x313131);
132     COLOR_BKGRND_LIST        = COLOR_PROC(0x414141);
133     COLOR_BKGRND_LIST_HOVER  = COLOR_PROC(0x505050);
134     COLOR_BKGRND_MENU        = COLOR_PROC(0x1c1c1c);
135     COLOR_BKGRND_MENU_HOVER  = COLOR_PROC(0x282828);
136     COLOR_BKGRND_MENU_ACTIVE = COLOR_PROC(0x414141);
137 
138     /* ---- Text Colors --- */
139     COLOR_MAIN_TEXT         = COLOR_PROC(0x333333);
140     COLOR_MAIN_TEXT_CHAT    = COLOR_PROC(0x000000);
141     COLOR_MAIN_TEXT_SUBTEXT = COLOR_PROC(0x414141);
142     COLOR_MAIN_TEXT_ACTION  = COLOR_PROC(0x4e4ec8);
143     COLOR_MAIN_TEXT_QUOTE   = COLOR_PROC(0x008000);
144     COLOR_MAIN_TEXT_RED     = COLOR_PROC(0xFF0000);
145     COLOR_MAIN_TEXT_URL     = COLOR_PROC(0x001fff);
146     COLOR_MAIN_TEXT_HINT    = COLOR_PROC(0x969696);
147 
148     /* Message window colors */
149     COLOR_MSG_USER       = COLOR_MAIN_TEXT_SUBTEXT;
150     COLOR_MSG_USER_PEND  = COLOR_MAIN_TEXT_ACTION;
151     COLOR_MSG_USER_ERROR = COLOR_MAIN_TEXT_RED;
152     COLOR_MSG_CONTACT    = COLOR_MAIN_TEXT;
153 
154     //---- Friend list header and bottom-left buttons ----
155     COLOR_MENU_TEXT         = COLOR_BKGRND_MAIN;
156     COLOR_MENU_TEXT_SUBTEXT = COLOR_PROC(0xd1d1d1);
157     COLOR_MENU_TEXT_ACTIVE  = COLOR_BKGRND_MAIN;
158 
159     //---- Friend list  ----
160     COLOR_LIST_TEXT         = COLOR_MENU_TEXT;
161     COLOR_LIST_TEXT_SUBTEXT = COLOR_MENU_TEXT_SUBTEXT;
162 
163     //---- Groupchat user list and title ----
164     COLOR_GROUP_SELF  = COLOR_PROC(0x6bc260);
165     COLOR_GROUP_PEER  = COLOR_MAIN_TEXT_HINT;
166     COLOR_GROUP_AUDIO = COLOR_PROC(0xc84e4e);
167     COLOR_GROUP_MUTED = COLOR_MAIN_TEXT_ACTION;
168 
169     //---- Text selection ----
170     COLOR_SELECTION_BACKGROUND = COLOR_MAIN_TEXT;
171     COLOR_SELECTION_TEXT       = COLOR_BKGRND_MAIN;
172 
173     //---- Inputs, dropdowns & tooltips ----
174     COLOR_EDGE_NORMAL         = COLOR_PROC(0xc0c0c0);
175     COLOR_EDGE_HOVER          = COLOR_PROC(0x969696);
176     COLOR_EDGE_ACTIVE         = COLOR_PROC(0x4ea6ea);
177     COLOR_ACTIVEOPTION_BKGRND = COLOR_PROC(0xd1d1d1);
178     COLOR_ACTIVEOPTION_TEXT   = COLOR_MAIN_TEXT;
179 
180     //---- Auxiliary style for inputs/dropdowns ("Search friends" bar) ----
181     COLOR_AUX_EDGE_NORMAL         = COLOR_BKGRND_AUX;
182     COLOR_AUX_EDGE_HOVER          = COLOR_PROC(0x999999);
183     COLOR_AUX_EDGE_ACTIVE         = COLOR_PROC(0x1A73B7);
184     COLOR_AUX_TEXT                = COLOR_LIST_TEXT;
185     COLOR_AUX_ACTIVEOPTION_BKGRND = COLOR_BKGRND_LIST_HOVER;
186     COLOR_AUX_ACTIVEOPTION_TEXT   = COLOR_AUX_TEXT;
187 
188     //---- Status circles ----
189     COLOR_STATUS_ONLINE = COLOR_PROC(0x6bc260);
190     COLOR_STATUS_AWAY   = COLOR_PROC(0xcebf45);
191     COLOR_STATUS_BUSY   = COLOR_PROC(0xc84e4e);
192 
193     //---- Buttons ----
194     COLOR_BTN_SUCCESS_BKGRND       = COLOR_STATUS_ONLINE;
195     COLOR_BTN_SUCCESS_BKGRND_HOVER = COLOR_PROC(0x76d56a);
196     COLOR_BTN_SUCCESS_TEXT         = COLOR_BKGRND_MAIN;
197     COLOR_BTN_SUCCESS_TEXT_HOVER   = COLOR_BKGRND_MAIN;
198 
199     COLOR_BTN_WARNING_BKGRND       = COLOR_STATUS_AWAY;
200     COLOR_BTN_WARNING_BKGRND_HOVER = COLOR_PROC(0xe3d24c);
201     COLOR_BTN_WARNING_TEXT         = COLOR_BKGRND_MAIN;
202     COLOR_BTN_WARNING_TEXT_HOVER   = COLOR_BKGRND_MAIN;
203 
204     COLOR_BTN_DANGER_BACKGROUND   = COLOR_STATUS_BUSY;
205     COLOR_BTN_DANGER_BKGRND_HOVER = COLOR_PROC(0xdc5656);
206     COLOR_BTN_DANGER_TEXT         = COLOR_BKGRND_MAIN;
207     COLOR_BTN_DANGER_TEXT_HOVER   = COLOR_BKGRND_MAIN;
208 
209     COLOR_BTN_DISABLED_BKGRND       = COLOR_PROC(0xd1d1d1);
210     COLOR_BTN_DISABLED_BKGRND_HOVER = COLOR_BKGRND_LIST_HOVER;
211     COLOR_BTN_DISABLED_TEXT         = COLOR_BKGRND_MAIN;
212     COLOR_BTN_DISABLED_TRANSFER     = COLOR_BKGRND_LIST;
213     COLOR_BTN_DISABLED_FORGRND      = COLOR_PROC(0xb3b3b3);
214 
215     COLOR_BTN_INPROGRESS_BKGRND  = COLOR_PROC(0x4ea6ea);
216     COLOR_BTN_INPROGRESS_TEXT    = COLOR_BKGRND_MAIN;
217     COLOR_BTN_INPROGRESS_FORGRND = COLOR_PROC(0x76baef);
218 
219     switch (loadtheme) {
220         case THEME_DARK: {
221             COLOR_BKGRND_MAIN        = COLOR_PROC(0x333333);
222             COLOR_BKGRND_ALT         = COLOR_PROC(0x151515);
223             COLOR_BKGRND_LIST        = COLOR_PROC(0x222222);
224             COLOR_BKGRND_LIST_HOVER  = COLOR_PROC(0x151515);
225             COLOR_BKGRND_MENU        = COLOR_PROC(0x171717);
226             COLOR_BKGRND_AUX         = COLOR_BKGRND_MENU;
227             COLOR_BKGRND_MENU_HOVER  = COLOR_BKGRND_LIST_HOVER;
228             COLOR_BKGRND_MENU_ACTIVE = COLOR_BKGRND_LIST;
229 
230             COLOR_MAIN_TEXT         = COLOR_PROC(0xdfdfdf);
231             COLOR_MAIN_TEXT_CHAT    = COLOR_PROC(0xffffff);
232             COLOR_MAIN_TEXT_SUBTEXT = COLOR_PROC(0xbbbbbb);
233             COLOR_MAIN_TEXT_ACTION  = COLOR_PROC(0x27a9bc);
234             COLOR_MAIN_TEXT_URL     = COLOR_MAIN_TEXT_ACTION;
235             COLOR_MAIN_TEXT_QUOTE   = COLOR_PROC(0x55b317);
236 
237             COLOR_MSG_USER       = COLOR_MAIN_TEXT_SUBTEXT;
238             COLOR_MSG_USER_PEND  = COLOR_PROC(0x66ccff);
239             COLOR_MSG_USER_ERROR = COLOR_MAIN_TEXT_RED;
240             COLOR_MSG_CONTACT    = COLOR_MAIN_TEXT;
241 
242             COLOR_MENU_TEXT_ACTIVE = COLOR_MAIN_TEXT;
243 
244             COLOR_GROUP_MUTED = COLOR_MAIN_TEXT_URL;
245 
246             COLOR_SELECTION_BACKGROUND = COLOR_MAIN_TEXT;
247             COLOR_SELECTION_TEXT       = COLOR_BKGRND_MAIN;
248 
249             COLOR_EDGE_NORMAL         = COLOR_PROC(0x555555);
250             COLOR_EDGE_ACTIVE         = COLOR_PROC(0x228888);
251             COLOR_EDGE_HOVER          = COLOR_PROC(0x999999);
252             COLOR_ACTIVEOPTION_BKGRND = COLOR_PROC(0x228888);
253             COLOR_ACTIVEOPTION_TEXT   = COLOR_MAIN_TEXT;
254 
255             COLOR_AUX_EDGE_NORMAL         = COLOR_BKGRND_AUX;
256             COLOR_AUX_EDGE_ACTIVE         = COLOR_EDGE_ACTIVE;
257             COLOR_AUX_ACTIVEOPTION_BKGRND = COLOR_ACTIVEOPTION_BKGRND;
258 
259             COLOR_BTN_SUCCESS_BKGRND       = COLOR_PROC(0x414141);
260             COLOR_BTN_SUCCESS_TEXT         = COLOR_PROC(0x33a63d);
261             COLOR_BTN_SUCCESS_BKGRND_HOVER = COLOR_PROC(0x455147);
262             COLOR_BTN_SUCCESS_TEXT_HOVER   = COLOR_PROC(0x6eff3a);
263 
264             COLOR_BTN_WARNING_BKGRND       = COLOR_PROC(0x414141);
265             COLOR_BTN_WARNING_TEXT         = COLOR_PROC(0xbd9e22);
266             COLOR_BTN_WARNING_BKGRND_HOVER = COLOR_PROC(0x4c493c);
267             COLOR_BTN_WARNING_TEXT_HOVER   = COLOR_PROC(0xff8d2a);
268 
269             COLOR_BTN_DANGER_BACKGROUND   = COLOR_PROC(0x414141);
270             COLOR_BTN_DANGER_TEXT         = COLOR_PROC(0xbd2525);
271             COLOR_BTN_DANGER_BKGRND_HOVER = COLOR_PROC(0x513939);
272             COLOR_BTN_DANGER_TEXT_HOVER   = COLOR_PROC(0xfa2626);
273 
274             COLOR_BTN_DISABLED_BKGRND   = COLOR_PROC(0x414141);
275             COLOR_BTN_DISABLED_TEXT     = COLOR_MAIN_TEXT;
276             COLOR_BTN_DISABLED_TRANSFER = COLOR_BTN_DISABLED_TEXT;
277             COLOR_BTN_DISABLED_FORGRND  = COLOR_PROC(0x666666);
278 
279             COLOR_BTN_INPROGRESS_BKGRND  = COLOR_BTN_DISABLED_BKGRND;
280             COLOR_BTN_INPROGRESS_TEXT    = COLOR_MAIN_TEXT_URL;
281             COLOR_BTN_INPROGRESS_FORGRND = COLOR_PROC(0x2f656a);
282             break;
283         }
284         case THEME_LIGHT: {
285             COLOR_BKGRND_AUX         = COLOR_PROC(0xe0e0e0);
286             COLOR_BKGRND_LIST        = COLOR_PROC(0xf0f0f0);
287             COLOR_BKGRND_LIST_HOVER  = COLOR_PROC(0xe0e0e0);
288             COLOR_BKGRND_MENU        = COLOR_BKGRND_LIST;
289             COLOR_BKGRND_MENU_HOVER  = COLOR_PROC(0xe0e0e0);
290             COLOR_BKGRND_MENU_ACTIVE = COLOR_PROC(0x555555);
291 
292             COLOR_LIST_TEXT         = COLOR_MAIN_TEXT;
293             COLOR_LIST_TEXT_SUBTEXT = COLOR_MAIN_TEXT_SUBTEXT;
294 
295             COLOR_MENU_TEXT         = COLOR_PROC(0x555555);
296             COLOR_MENU_TEXT_ACTIVE  = COLOR_PROC(0xffffff);
297             COLOR_MENU_TEXT_SUBTEXT = COLOR_PROC(0x414141);
298 
299             COLOR_EDGE_NORMAL         = COLOR_PROC(0xc0c0c0);
300             COLOR_EDGE_HOVER          = COLOR_PROC(0x707070);
301             COLOR_ACTIVEOPTION_BKGRND = COLOR_PROC(0xc2e0ff);
302             COLOR_ACTIVEOPTION_TEXT   = COLOR_MAIN_TEXT;
303 
304             COLOR_AUX_EDGE_NORMAL         = COLOR_BKGRND_AUX;
305             COLOR_AUX_EDGE_HOVER          = COLOR_PROC(0x999999);
306             COLOR_AUX_EDGE_ACTIVE         = COLOR_EDGE_ACTIVE;
307             COLOR_AUX_TEXT                = COLOR_LIST_TEXT;
308             COLOR_AUX_ACTIVEOPTION_BKGRND = COLOR_ACTIVEOPTION_BKGRND;
309             COLOR_AUX_ACTIVEOPTION_TEXT   = COLOR_AUX_TEXT;
310             break;
311         }
312         case THEME_HIGHCONTRAST: {
313             COLOR_BKGRND_MAIN        = COLOR_PROC(0xffffff);
314             COLOR_BKGRND_AUX         = COLOR_BKGRND_MAIN;
315             COLOR_BKGRND_LIST        = COLOR_PROC(0x444444);
316             COLOR_BKGRND_LIST_HOVER  = COLOR_PROC(0x000001);
317             COLOR_BKGRND_MENU        = COLOR_BKGRND_MAIN;
318             COLOR_BKGRND_MENU_HOVER  = COLOR_BKGRND_MAIN;
319             COLOR_BKGRND_MENU_ACTIVE = COLOR_BKGRND_LIST_HOVER;
320 
321             COLOR_MAIN_TEXT         = COLOR_PROC(0x000001);
322             COLOR_MAIN_TEXT_CHAT    = COLOR_MAIN_TEXT;
323             COLOR_MAIN_TEXT_SUBTEXT = COLOR_MAIN_TEXT;
324             COLOR_MAIN_TEXT_ACTION  = COLOR_PROC(0x0000ff);
325             COLOR_MAIN_TEXT_QUOTE   = COLOR_PROC(0x00ff00);
326             COLOR_MAIN_TEXT_URL     = COLOR_MAIN_TEXT_ACTION;
327             COLOR_MAIN_TEXT_HINT    = COLOR_MAIN_TEXT;
328 
329             COLOR_MENU_TEXT         = COLOR_MAIN_TEXT;
330             COLOR_MENU_TEXT_SUBTEXT = COLOR_MAIN_TEXT;
331             COLOR_MENU_TEXT_ACTIVE  = COLOR_BKGRND_MAIN;
332 
333             COLOR_LIST_TEXT         = COLOR_BKGRND_MAIN;
334             COLOR_LIST_TEXT_SUBTEXT = COLOR_BKGRND_MAIN;
335 
336             COLOR_GROUP_SELF  = COLOR_PROC(0x00ff00);
337             COLOR_GROUP_PEER  = COLOR_MAIN_TEXT_HINT;
338             COLOR_GROUP_AUDIO = COLOR_PROC(0xff0000);
339             COLOR_GROUP_MUTED = COLOR_MAIN_TEXT_URL;
340 
341             COLOR_SELECTION_BACKGROUND = COLOR_MAIN_TEXT;
342             COLOR_SELECTION_TEXT       = COLOR_BKGRND_MAIN;
343 
344             COLOR_EDGE_NORMAL         = COLOR_MAIN_TEXT;
345             COLOR_EDGE_ACTIVE         = COLOR_MAIN_TEXT;
346             COLOR_EDGE_HOVER          = COLOR_MAIN_TEXT;
347             COLOR_ACTIVEOPTION_BKGRND = COLOR_MAIN_TEXT;
348             COLOR_ACTIVEOPTION_TEXT   = COLOR_BKGRND_MAIN;
349 
350             COLOR_AUX_EDGE_NORMAL         = COLOR_EDGE_NORMAL;
351             COLOR_AUX_EDGE_HOVER          = COLOR_EDGE_NORMAL;
352             COLOR_AUX_EDGE_ACTIVE         = COLOR_EDGE_ACTIVE;
353             COLOR_AUX_TEXT                = COLOR_MAIN_TEXT;
354             COLOR_AUX_ACTIVEOPTION_BKGRND = COLOR_ACTIVEOPTION_BKGRND;
355             COLOR_AUX_ACTIVEOPTION_TEXT   = COLOR_ACTIVEOPTION_TEXT;
356 
357             COLOR_STATUS_ONLINE = COLOR_PROC(0x00ff00);
358             COLOR_STATUS_AWAY   = COLOR_PROC(0xffff00);
359             COLOR_STATUS_BUSY   = COLOR_PROC(0xff0000);
360 
361             COLOR_BTN_SUCCESS_BKGRND       = COLOR_PROC(0x00ff00);
362             COLOR_BTN_SUCCESS_TEXT         = COLOR_BKGRND_MAIN;
363             COLOR_BTN_SUCCESS_BKGRND_HOVER = COLOR_PROC(0x00ff00);
364             COLOR_BTN_SUCCESS_TEXT_HOVER   = COLOR_BKGRND_MAIN;
365 
366             COLOR_BTN_WARNING_BKGRND       = COLOR_PROC(0xffff00);
367             COLOR_BTN_WARNING_TEXT         = COLOR_BKGRND_MAIN;
368             COLOR_BTN_WARNING_BKGRND_HOVER = COLOR_PROC(0xffff00);
369             COLOR_BTN_WARNING_TEXT_HOVER   = COLOR_BKGRND_MAIN;
370 
371             COLOR_BTN_DANGER_BACKGROUND   = COLOR_PROC(0xff0000);
372             COLOR_BTN_DANGER_TEXT         = COLOR_BKGRND_MAIN;
373             COLOR_BTN_DANGER_BKGRND_HOVER = COLOR_PROC(0xff0000);
374             COLOR_BTN_DANGER_TEXT_HOVER   = COLOR_BKGRND_MAIN;
375 
376             COLOR_BTN_DISABLED_BKGRND   = COLOR_PROC(0x444444);
377             COLOR_BTN_DISABLED_TEXT     = COLOR_MAIN_TEXT;
378             COLOR_BTN_DISABLED_TRANSFER = COLOR_BKGRND_MAIN;
379             COLOR_BTN_DISABLED_FORGRND  = COLOR_PROC(0x000000);
380 
381             COLOR_BTN_INPROGRESS_TEXT   = COLOR_BTN_DISABLED_TEXT;
382             COLOR_BTN_INPROGRESS_BKGRND = COLOR_PROC(0x00ffff);
383             break;
384         }
385         case THEME_ZENBURN: {
386             COLOR_BKGRND_MAIN        = COLOR_PROC(0x3f3f3f);
387             COLOR_BKGRND_AUX         = COLOR_BKGRND_MAIN;
388             COLOR_BKGRND_LIST        = COLOR_PROC(0x5f5f5f);
389             COLOR_BKGRND_LIST_HOVER  = COLOR_PROC(0x7f7f7f);
390             COLOR_BKGRND_MENU        = COLOR_BKGRND_MAIN;
391             COLOR_BKGRND_MENU_HOVER  = COLOR_PROC(0x7f9f7f);
392             COLOR_BKGRND_MENU_ACTIVE = COLOR_BKGRND_MENU_HOVER;
393 
394             COLOR_MAIN_TEXT         = COLOR_PROC(0xdcdccc);
395             COLOR_MAIN_TEXT_CHAT    = COLOR_MAIN_TEXT;
396             COLOR_MAIN_TEXT_SUBTEXT = COLOR_MAIN_TEXT;
397             COLOR_MAIN_TEXT_ACTION  = COLOR_PROC(0xd0bf8f);
398             COLOR_MAIN_TEXT_QUOTE   = COLOR_PROC(0x7f9f7f);
399             COLOR_MAIN_TEXT_RED     = COLOR_PROC(0xcc9393);
400             COLOR_MAIN_TEXT_URL     = COLOR_PROC(0x6ca0a3);
401             COLOR_MAIN_TEXT_HINT    = COLOR_MAIN_TEXT;
402 
403             COLOR_MSG_USER          = COLOR_MAIN_TEXT;
404             COLOR_MSG_USER_PEND     = COLOR_MAIN_TEXT_ACTION;
405             COLOR_MSG_USER_ERROR    = COLOR_MAIN_TEXT_RED;
406             COLOR_MSG_CONTACT       = COLOR_MAIN_TEXT;
407 
408             COLOR_MENU_TEXT         = COLOR_MAIN_TEXT;
409             COLOR_MENU_TEXT_SUBTEXT = COLOR_MAIN_TEXT;
410             COLOR_MENU_TEXT_ACTIVE  = COLOR_MAIN_TEXT;
411 
412             COLOR_LIST_TEXT         = COLOR_MAIN_TEXT;
413             COLOR_LIST_TEXT_SUBTEXT = COLOR_MAIN_TEXT;
414 
415             COLOR_GROUP_SELF  = COLOR_MAIN_TEXT;
416             COLOR_GROUP_PEER  = COLOR_MAIN_TEXT;
417             COLOR_GROUP_AUDIO = COLOR_MAIN_TEXT_QUOTE;
418             COLOR_GROUP_MUTED = COLOR_MAIN_TEXT_ACTION;
419 
420             COLOR_SELECTION_BACKGROUND = COLOR_MAIN_TEXT_QUOTE;
421             COLOR_SELECTION_TEXT       = COLOR_MAIN_TEXT;
422 
423             COLOR_EDGE_NORMAL         = COLOR_BKGRND_LIST;
424             COLOR_EDGE_ACTIVE         = COLOR_MAIN_TEXT;
425             COLOR_EDGE_HOVER          = COLOR_MAIN_TEXT_QUOTE;
426             COLOR_ACTIVEOPTION_BKGRND = COLOR_MAIN_TEXT_QUOTE;
427             COLOR_ACTIVEOPTION_TEXT   = COLOR_MAIN_TEXT;
428 
429             COLOR_AUX_EDGE_NORMAL         = COLOR_BKGRND_LIST;
430             COLOR_AUX_EDGE_HOVER          = COLOR_MAIN_TEXT_QUOTE;
431             COLOR_AUX_EDGE_ACTIVE         = COLOR_MAIN_TEXT;
432             COLOR_AUX_TEXT                = COLOR_MAIN_TEXT;
433             COLOR_AUX_ACTIVEOPTION_BKGRND = COLOR_MAIN_TEXT_QUOTE;
434             COLOR_AUX_ACTIVEOPTION_TEXT   = COLOR_MAIN_TEXT;
435 
436             COLOR_STATUS_ONLINE = COLOR_MAIN_TEXT_QUOTE;
437             COLOR_STATUS_AWAY   = COLOR_MAIN_TEXT_ACTION;
438             COLOR_STATUS_BUSY   = COLOR_MAIN_TEXT_RED;
439 
440             COLOR_BTN_SUCCESS_BKGRND       = COLOR_MAIN_TEXT_QUOTE;
441             COLOR_BTN_SUCCESS_TEXT         = COLOR_MAIN_TEXT;
442             COLOR_BTN_SUCCESS_BKGRND_HOVER = COLOR_PROC(0xbfebbf);
443             COLOR_BTN_SUCCESS_TEXT_HOVER   = COLOR_PROC(0xffffff);
444 
445             COLOR_BTN_WARNING_BKGRND       = COLOR_MAIN_TEXT_ACTION;
446             COLOR_BTN_WARNING_TEXT         = COLOR_BTN_SUCCESS_TEXT_HOVER;
447             COLOR_BTN_WARNING_BKGRND_HOVER = COLOR_PROC(0xf0dfaf);
448             COLOR_BTN_WARNING_TEXT_HOVER   = COLOR_BTN_SUCCESS_TEXT_HOVER;
449 
450             COLOR_BTN_DANGER_BACKGROUND   = COLOR_STATUS_AWAY;
451             COLOR_BTN_DANGER_TEXT         = COLOR_MAIN_TEXT;
452             COLOR_BTN_DANGER_BKGRND_HOVER = COLOR_PROC(0xdca3a3);
453             COLOR_BTN_DANGER_TEXT_HOVER   = COLOR_BTN_SUCCESS_TEXT_HOVER;
454 
455             COLOR_BTN_DISABLED_BKGRND       = COLOR_BKGRND_LIST;
456             COLOR_BTN_DISABLED_TEXT         = COLOR_MAIN_TEXT;
457             COLOR_BTN_DISABLED_BKGRND_HOVER = COLOR_BKGRND_LIST_HOVER;
458             COLOR_BTN_DISABLED_TRANSFER     = COLOR_MAIN_TEXT;
459             COLOR_BTN_DISABLED_FORGRND      = COLOR_BKGRND_LIST_HOVER;
460 
461             COLOR_BTN_INPROGRESS_BKGRND  = COLOR_PROC(0xc1c1a4);
462             COLOR_BTN_INPROGRESS_TEXT    = COLOR_BKGRND_MAIN;
463             COLOR_BTN_INPROGRESS_FORGRND = COLOR_MAIN_TEXT;
464             break;
465         }
466         case THEME_SOLARIZED_DARK: {
467             COLOR_BKGRND_MAIN        = COLOR_PROC(SOLAR_BASE03);
468             COLOR_BKGRND_ALT         = COLOR_PROC(SOLAR_BASE02);
469             COLOR_BKGRND_AUX         = COLOR_BKGRND_ALT;
470             COLOR_BKGRND_LIST        = COLOR_BKGRND_ALT;
471             COLOR_BKGRND_LIST_HOVER  = COLOR_PROC(SOLAR_BASE01);
472             COLOR_BKGRND_MENU        = COLOR_PROC(SOLAR_BASE03);
473             COLOR_BKGRND_MENU_HOVER  = COLOR_PROC(SOLAR_CYAN);
474             COLOR_BKGRND_MENU_ACTIVE = COLOR_BKGRND_ALT;
475 
476             COLOR_MAIN_TEXT         = COLOR_PROC(SOLAR_BASE2);
477             COLOR_MAIN_TEXT_CHAT    = COLOR_MAIN_TEXT;
478             COLOR_MAIN_TEXT_SUBTEXT = COLOR_PROC(SOLAR_BASE1);
479             COLOR_MAIN_TEXT_ACTION  = COLOR_PROC(SOLAR_BASE3);
480             COLOR_MAIN_TEXT_QUOTE   = COLOR_MAIN_TEXT_SUBTEXT;
481             COLOR_MAIN_TEXT_RED     = COLOR_PROC(SOLAR_RED);
482             COLOR_MAIN_TEXT_URL     = COLOR_PROC(SOLAR_MAGENTA);
483             COLOR_MAIN_TEXT_HINT    = COLOR_PROC(SOLAR_VIOLET);
484 
485             COLOR_MSG_USER       = COLOR_MAIN_TEXT_SUBTEXT;
486             COLOR_MSG_USER_PEND  = COLOR_MAIN_TEXT_ACTION;
487             COLOR_MSG_USER_ERROR = COLOR_MAIN_TEXT_RED;
488             COLOR_MSG_CONTACT    = COLOR_MAIN_TEXT;
489 
490             COLOR_MENU_TEXT         = COLOR_MAIN_TEXT;
491             COLOR_MENU_TEXT_SUBTEXT = COLOR_MAIN_TEXT_SUBTEXT;
492             COLOR_MENU_TEXT_ACTIVE  = COLOR_MAIN_TEXT;
493 
494             COLOR_LIST_TEXT         = COLOR_MAIN_TEXT;
495             COLOR_LIST_TEXT_SUBTEXT = COLOR_MAIN_TEXT_SUBTEXT;
496 
497             COLOR_GROUP_SELF  = COLOR_PROC(SOLAR_GREEN);
498             COLOR_GROUP_PEER  = COLOR_MAIN_TEXT_HINT;
499             COLOR_GROUP_AUDIO = COLOR_PROC(SOLAR_RED);
500             COLOR_GROUP_MUTED = COLOR_MAIN_TEXT_ACTION;
501 
502             COLOR_SELECTION_BACKGROUND = COLOR_MAIN_TEXT;
503             COLOR_SELECTION_TEXT       = COLOR_BKGRND_MAIN;
504 
505             COLOR_EDGE_NORMAL         = COLOR_PROC(SOLAR_VIOLET);
506             COLOR_EDGE_HOVER          = COLOR_PROC(SOLAR_BLUE);
507             COLOR_EDGE_ACTIVE         = COLOR_PROC(SOLAR_ORANGE);
508             COLOR_ACTIVEOPTION_BKGRND = COLOR_BKGRND_LIST_HOVER;
509             COLOR_ACTIVEOPTION_TEXT   = COLOR_MAIN_TEXT;
510 
511             COLOR_AUX_EDGE_NORMAL         = COLOR_BKGRND_AUX;
512             COLOR_AUX_EDGE_HOVER          = COLOR_PROC(SOLAR_VIOLET);
513             COLOR_AUX_EDGE_ACTIVE         = COLOR_PROC(SOLAR_CYAN);
514             COLOR_AUX_TEXT                = COLOR_LIST_TEXT;
515             COLOR_AUX_ACTIVEOPTION_BKGRND = COLOR_BKGRND_LIST_HOVER;
516             COLOR_AUX_ACTIVEOPTION_TEXT   = COLOR_AUX_TEXT;
517 
518             COLOR_STATUS_ONLINE = COLOR_PROC(SOLAR_GREEN);
519             COLOR_STATUS_AWAY   = COLOR_PROC(SOLAR_YELLOW);
520             COLOR_STATUS_BUSY   = COLOR_PROC(SOLAR_RED);
521 
522             COLOR_BTN_SUCCESS_BKGRND        = COLOR_STATUS_ONLINE;
523             COLOR_BTN_SUCCESS_TEXT          = COLOR_MAIN_TEXT;
524             COLOR_BTN_SUCCESS_BKGRND_HOVER  = COLOR_PROC(SOLAR_CYAN);
525             COLOR_BTN_SUCCESS_TEXT_HOVER    = COLOR_BKGRND_MAIN;
526 
527             COLOR_BTN_WARNING_BKGRND        = COLOR_STATUS_AWAY;
528             COLOR_BTN_WARNING_TEXT          = COLOR_MAIN_TEXT;
529             COLOR_BTN_WARNING_BKGRND_HOVER  = COLOR_PROC(SOLAR_ORANGE);
530             COLOR_BTN_WARNING_TEXT_HOVER    = COLOR_BKGRND_MAIN;
531 
532             COLOR_BTN_DANGER_BACKGROUND     = COLOR_STATUS_BUSY;
533             COLOR_BTN_DANGER_TEXT           = COLOR_MAIN_TEXT;
534             COLOR_BTN_DANGER_BKGRND_HOVER   = COLOR_PROC(SOLAR_MAGENTA);
535             COLOR_BTN_DANGER_TEXT_HOVER     = COLOR_BKGRND_MAIN;
536 
537             COLOR_BTN_DISABLED_BKGRND       = COLOR_PROC(SOLAR_BASE00);
538             COLOR_BTN_DISABLED_TEXT         = COLOR_BKGRND_MAIN;
539             COLOR_BTN_DISABLED_BKGRND_HOVER = COLOR_BKGRND_LIST_HOVER;
540             COLOR_BTN_DISABLED_TRANSFER     = COLOR_BKGRND_LIST;
541             COLOR_BTN_DISABLED_FORGRND      = COLOR_PROC(SOLAR_ORANGE);
542 
543             COLOR_BTN_INPROGRESS_FORGRND = COLOR_PROC(SOLAR_MAGENTA);
544             COLOR_BTN_INPROGRESS_BKGRND  = COLOR_PROC(SOLAR_VIOLET);
545             COLOR_BTN_INPROGRESS_TEXT    = COLOR_BKGRND_MAIN;
546             break;
547         }
548         case THEME_SOLARIZED_LIGHT: {
549             COLOR_BKGRND_MAIN        = COLOR_PROC(SOLAR_BASE3);
550             COLOR_BKGRND_ALT         = COLOR_PROC(SOLAR_BASE2);
551             COLOR_BKGRND_AUX         = COLOR_BKGRND_ALT;
552             COLOR_BKGRND_LIST        = COLOR_BKGRND_ALT;
553             COLOR_BKGRND_LIST_HOVER  = COLOR_PROC(SOLAR_BASE1);
554             COLOR_BKGRND_MENU        = COLOR_BKGRND_ALT;
555             COLOR_BKGRND_MENU_HOVER  = COLOR_PROC(SOLAR_CYAN);
556             COLOR_BKGRND_MENU_ACTIVE = COLOR_BKGRND_ALT;
557 
558             COLOR_MAIN_TEXT         = COLOR_PROC(SOLAR_BASE02);
559             COLOR_MAIN_TEXT_CHAT    = COLOR_MAIN_TEXT;
560             COLOR_MAIN_TEXT_SUBTEXT = COLOR_PROC(SOLAR_BASE01);
561             COLOR_MAIN_TEXT_ACTION  = COLOR_PROC(SOLAR_BASE03);
562             COLOR_MAIN_TEXT_QUOTE   = COLOR_MAIN_TEXT_SUBTEXT;
563             COLOR_MAIN_TEXT_RED     = COLOR_PROC(SOLAR_RED);
564             COLOR_MAIN_TEXT_URL     = COLOR_PROC(SOLAR_MAGENTA);
565             COLOR_MAIN_TEXT_HINT    = COLOR_PROC(SOLAR_VIOLET);
566 
567             COLOR_MSG_USER       = COLOR_MAIN_TEXT_SUBTEXT;
568             COLOR_MSG_USER_PEND  = COLOR_MAIN_TEXT_ACTION;
569             COLOR_MSG_USER_ERROR = COLOR_MAIN_TEXT_RED;
570             COLOR_MSG_CONTACT    = COLOR_MAIN_TEXT;
571 
572             COLOR_MENU_TEXT         = COLOR_MAIN_TEXT;
573             COLOR_MENU_TEXT_SUBTEXT = COLOR_MAIN_TEXT_SUBTEXT;
574             COLOR_MENU_TEXT_ACTIVE  = COLOR_MAIN_TEXT;
575 
576             COLOR_LIST_TEXT         = COLOR_MAIN_TEXT;
577             COLOR_LIST_TEXT_SUBTEXT = COLOR_MAIN_TEXT_SUBTEXT;
578 
579             COLOR_GROUP_SELF  = COLOR_PROC(SOLAR_GREEN);
580             COLOR_GROUP_PEER  = COLOR_MAIN_TEXT_HINT;
581             COLOR_GROUP_AUDIO = COLOR_PROC(SOLAR_RED);
582             COLOR_GROUP_MUTED = COLOR_MAIN_TEXT_ACTION;
583 
584             COLOR_SELECTION_BACKGROUND = COLOR_MAIN_TEXT;
585             COLOR_SELECTION_TEXT       = COLOR_BKGRND_MAIN;
586 
587             COLOR_EDGE_NORMAL         = COLOR_PROC(SOLAR_VIOLET);
588             COLOR_EDGE_HOVER          = COLOR_PROC(SOLAR_BLUE);
589             COLOR_EDGE_ACTIVE         = COLOR_PROC(SOLAR_CYAN);
590             COLOR_ACTIVEOPTION_BKGRND = COLOR_BKGRND_LIST_HOVER;
591             COLOR_ACTIVEOPTION_TEXT   = COLOR_MAIN_TEXT;
592 
593             COLOR_AUX_EDGE_NORMAL         = COLOR_BKGRND_AUX;
594             COLOR_AUX_EDGE_HOVER          = COLOR_PROC(SOLAR_VIOLET);
595             COLOR_AUX_EDGE_ACTIVE         = COLOR_PROC(SOLAR_CYAN);
596             COLOR_AUX_TEXT                = COLOR_LIST_TEXT;
597             COLOR_AUX_ACTIVEOPTION_BKGRND = COLOR_BKGRND_LIST_HOVER;
598             COLOR_AUX_ACTIVEOPTION_TEXT   = COLOR_AUX_TEXT;
599 
600             COLOR_STATUS_ONLINE = COLOR_PROC(SOLAR_GREEN);
601             COLOR_STATUS_AWAY   = COLOR_PROC(SOLAR_YELLOW);
602             COLOR_STATUS_BUSY   = COLOR_PROC(SOLAR_RED);
603 
604             COLOR_BTN_SUCCESS_BKGRND       = COLOR_STATUS_ONLINE;
605             COLOR_BTN_SUCCESS_TEXT         = COLOR_MAIN_TEXT;
606             COLOR_BTN_SUCCESS_BKGRND_HOVER = COLOR_PROC(SOLAR_CYAN);
607             COLOR_BTN_SUCCESS_TEXT_HOVER   = COLOR_BKGRND_MAIN;
608 
609             COLOR_BTN_WARNING_BKGRND       = COLOR_STATUS_AWAY;
610             COLOR_BTN_WARNING_TEXT         = COLOR_MAIN_TEXT;
611             COLOR_BTN_WARNING_BKGRND_HOVER = COLOR_PROC(SOLAR_ORANGE);
612             COLOR_BTN_WARNING_TEXT_HOVER   = COLOR_BKGRND_MAIN;
613 
614             COLOR_BTN_DANGER_BACKGROUND   = COLOR_STATUS_BUSY;
615             COLOR_BTN_DANGER_TEXT         = COLOR_MAIN_TEXT;
616             COLOR_BTN_DANGER_BKGRND_HOVER = COLOR_PROC(SOLAR_MAGENTA);
617             COLOR_BTN_DANGER_TEXT_HOVER   = COLOR_BKGRND_MAIN;
618 
619             COLOR_BTN_DISABLED_BKGRND       = COLOR_PROC(SOLAR_BASE0);
620             COLOR_BTN_DISABLED_TEXT         = COLOR_BKGRND_MAIN;
621             COLOR_BTN_DISABLED_BKGRND_HOVER = COLOR_BKGRND_LIST_HOVER;
622             COLOR_BTN_DISABLED_TRANSFER     = COLOR_BKGRND_LIST;
623             COLOR_BTN_DISABLED_FORGRND      = COLOR_PROC(SOLAR_ORANGE);
624 
625             COLOR_BTN_INPROGRESS_BKGRND  = COLOR_PROC(SOLAR_VIOLET);
626             COLOR_BTN_INPROGRESS_TEXT    = COLOR_BKGRND_MAIN;
627             COLOR_BTN_INPROGRESS_FORGRND = COLOR_PROC(SOLAR_MAGENTA);
628             break;
629         }
630         case THEME_CUSTOM: {
631             size_t size;
632             uint8_t *themedata = utox_data_load_custom_theme(&size);
633             if (!themedata) {
634                 return;
635             }
636             read_custom_theme(themedata, size);
637             free(themedata);
638             break;
639         }
640         case THEME_DEFAULT: {
641             // Set above the switch.
642             break;
643         }
644     }
645 
646     status_color[0] = COLOR_STATUS_ONLINE;
647     status_color[1] = COLOR_STATUS_AWAY;
648     status_color[2] = COLOR_STATUS_BUSY;
649     status_color[3] = COLOR_STATUS_BUSY;
650 }
651 
find_colour_pointer(char * color)652 uint32_t *find_colour_pointer(char *color) {
653     while (*color == 0 || *color == ' ' || *color == '\t') {
654         ++color;
655     }
656 
657     for (int l = strlen(color) - 1; l > 0; --l) {
658         if (color[l] != ' ' && color[l] != '\t') {
659             color[l + 1] = '\0';
660             break;
661         }
662     }
663 
664     // Skip past "COLOR_" prefix
665     if (!strncmp(color, "COLOR_", 6)) {
666         color += 6;
667     }
668 
669     LOG_INFO("Theme", "Color: %s" , color);
670 
671     for (int i = 0;; ++i) {
672         const char *s = COLOUR_NAME_TABLE[i];
673 
674         if (!s) {
675             break;
676         }
677 
678         if (!strcmp(color, s)) {
679             return COLOUR_POINTER_TABLE[i];
680         }
681     }
682 
683     return NULL;
684 }
685 
try_parse_hex_colour(char * color,bool * error)686 static uint32_t try_parse_hex_colour(char *color, bool *error) {
687     while (*color == 0 || *color == ' ' || *color == '\t') {
688         color++;
689     }
690 
691     for (int l = strlen(color) - 1; l > 0; --l) {
692         if (color[l] != ' ' && color[l] != '\n') {
693             color[++l] = '\0';
694 
695             if (l != 6) {
696                 *error = true;
697                 return 0;
698             }
699             break;
700         }
701     }
702 
703     char hex[3] = { 0 };
704 
705     memcpy(hex, color, 2);
706     unsigned char red = strtol(hex, NULL, 16);
707     memcpy(hex, color + 2, 2);
708     unsigned char green = strtol(hex, NULL, 16);
709     memcpy(hex, color + 4, 2);
710     unsigned char blue = strtol(hex, NULL, 16);
711 
712     return RGB(red, green, blue);
713 }
714 
read_custom_theme(const uint8_t * data,size_t length)715 static void read_custom_theme(const uint8_t *data, size_t length) {
716     while (length) {
717         char *line = (char *)data;
718         while (*line != 0) {
719             if (*line == '#') {
720                 *line = 0;
721                 break;
722             }
723             ++line;
724             --length;
725         }
726 
727         char *color = strpbrk(line, "=");
728 
729         if (!color || color == line) {
730             continue;
731         }
732 
733         *color++ = 0;
734 
735         uint32_t *colorp = find_colour_pointer(line);
736         if (!colorp) {
737             continue;
738         }
739 
740         bool err = false;
741         const uint32_t col = try_parse_hex_colour(color, &err);
742 
743         if (err) {
744             LOG_ERR("Theme", "Error: Parsing hex color failed.");
745             continue;
746         } else {
747             *colorp = COLOR_PROC(col);
748         }
749     }
750 }
751 
utox_data_load_custom_theme(size_t * out)752 static uint8_t *utox_data_load_custom_theme(size_t *out) {
753     FILE *fp = utox_get_file("utox_theme.ini", out, UTOX_FILE_OPTS_READ);
754 
755     if (fp == NULL) {
756         LOG_ERR("Theme", "Failed to open custom theme file.");
757         return NULL;
758     }
759 
760     uint8_t *data = calloc(1, *out + 1);
761     if (data == NULL) {
762         LOG_ERR("Theme", "Failed to allocate memory for custom theme.");
763         fclose(fp);
764         return NULL;
765     }
766 
767     if (fread(data, *out, 1, fp) != 1) {
768         LOG_ERR("Theme", "Could not read custom theme from file.");
769         fclose(fp);
770         free(data);
771         return NULL;
772     }
773     fclose(fp);
774 
775     return data;
776 }
777