1 /*
2  windows.c : irssi
3 
4     Copyright (C) 1999-2000 Timo Sirainen
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License along
17     with this program; if not, write to the Free Software Foundation, Inc.,
18     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20 
21 #include "module.h"
22 #include "module-formats.h"
23 #include "modules.h"
24 #include "signals.h"
25 #include "commands.h"
26 #include "servers.h"
27 #include "misc.h"
28 #include "settings.h"
29 
30 #include "levels.h"
31 
32 #include "printtext.h"
33 #include "fe-windows.h"
34 #include "window-items.h"
35 
36 GSList *windows; /* first in the list is the active window,
37                     next is the last active, etc. */
38 GSequence *windows_seq;
39 WINDOW_REC *active_win;
40 
41 static int daytag;
42 static int daycheck; /* 0 = don't check, 1 = time is 00:00, check,
43                         2 = time is 00:00, already checked */
44 
window_refnum_lookup(WINDOW_REC * window,void * refnum_p)45 static int window_refnum_lookup(WINDOW_REC *window, void *refnum_p)
46 {
47 	int refnum = GPOINTER_TO_INT(refnum_p);
48 	return window->refnum == refnum ? 0 : window->refnum < refnum ? -1 : 1;
49 }
50 
windows_seq_begin(void)51 static GSequenceIter *windows_seq_begin(void)
52 {
53 	return g_sequence_get_begin_iter(windows_seq);
54 }
55 
windows_seq_end(void)56 static GSequenceIter *windows_seq_end(void)
57 {
58 	return g_sequence_get_end_iter(windows_seq);
59 }
60 
windows_seq_insert(WINDOW_REC * rec)61 static GSequenceIter *windows_seq_insert(WINDOW_REC *rec)
62 {
63 	return g_sequence_insert_sorted(windows_seq, rec, (GCompareDataFunc)window_refnum_cmp, NULL);
64 }
65 
windows_seq_refnum_lookup(int refnum)66 static GSequenceIter *windows_seq_refnum_lookup(int refnum)
67 {
68 	return g_sequence_lookup(windows_seq, GINT_TO_POINTER(refnum), (GCompareDataFunc)window_refnum_lookup, NULL);
69 }
70 
windows_seq_changed(GSequenceIter * iter)71 static void windows_seq_changed(GSequenceIter *iter)
72 {
73 	g_sequence_sort_changed(iter, (GCompareDataFunc)window_refnum_cmp, NULL);
74 }
75 
windows_seq_window_lookup(WINDOW_REC * rec)76 static GSequenceIter *windows_seq_window_lookup(WINDOW_REC *rec)
77 {
78 	return g_sequence_lookup(windows_seq, rec, (GCompareDataFunc)window_refnum_cmp, NULL);
79 }
80 
81 /* search to the numerically right iterator of refnum */
windows_seq_refnum_search_right(int refnum)82 static GSequenceIter *windows_seq_refnum_search_right(int refnum)
83 {
84 	return g_sequence_search(windows_seq, GINT_TO_POINTER(refnum), (GCompareDataFunc)window_refnum_lookup, NULL);
85 }
86 
87 /* we want to find the numerically left iterator of refnum, so we
88    search the right of the previous refnum. but we need to figure out
89    the case where the iterator is already at the beginning, i.e
90    iter->refnum >= refnum */
windows_seq_refnum_search_left(int refnum)91 static GSequenceIter *windows_seq_refnum_search_left(int refnum)
92 {
93 	GSequenceIter *iter = windows_seq_refnum_search_right(refnum - 1);
94 	return iter == windows_seq_begin() ? NULL : g_sequence_iter_prev(iter);
95 }
96 
window_get_new_refnum(void)97 static int window_get_new_refnum(void)
98 {
99 	WINDOW_REC *win;
100 	GSequenceIter *iter, *end;
101 	int refnum;
102 
103 	refnum = 1;
104 	iter = windows_seq_begin();
105 	end = windows_seq_end();
106 
107 	while (iter != end) {
108 		win = g_sequence_get(iter);
109 
110 		if (refnum != win->refnum)
111 			return refnum;
112 
113 		refnum++;
114 		iter = g_sequence_iter_next(iter);
115 	}
116 
117 	return refnum;
118 }
119 
window_create(WI_ITEM_REC * item,int automatic)120 WINDOW_REC *window_create(WI_ITEM_REC *item, int automatic)
121 {
122 	WINDOW_REC *rec;
123 
124 	rec = g_new0(WINDOW_REC, 1);
125 	rec->refnum = window_get_new_refnum();
126 	rec->level = settings_get_level("window_default_level");
127 
128 	windows = g_slist_prepend(windows, rec);
129 	windows_seq_insert(rec);
130 	signal_emit("window created", 2, rec, GINT_TO_POINTER(automatic));
131 
132 	if (item != NULL) window_item_add(rec, item, automatic);
133 	if (windows->next == NULL || !automatic || settings_get_bool("window_auto_change")) {
134 		if (automatic && windows->next != NULL)
135 			signal_emit("window changed automatic", 1, rec);
136 		window_set_active(rec);
137 	}
138 	return rec;
139 }
140 
window_set_refnum0(WINDOW_REC * window,int refnum)141 static void window_set_refnum0(WINDOW_REC *window, int refnum)
142 {
143 	int old_refnum;
144 
145 	g_return_if_fail(window != NULL);
146 	g_return_if_fail(refnum >= 1);
147 	if (window->refnum == refnum) return;
148 
149 	old_refnum = window->refnum;
150 	window->refnum = refnum;
151 	signal_emit("window refnum changed", 2, window, GINT_TO_POINTER(old_refnum));
152 }
153 
154 /* removed_refnum was removed from the windows list, pack the windows so
155    there won't be any holes. If there is any holes after removed_refnum,
156    leave the windows behind it alone. */
windows_pack(int removed_refnum)157 static void windows_pack(int removed_refnum)
158 {
159 	WINDOW_REC *window;
160 	int refnum;
161 	GSequenceIter *iter, *end;
162 
163 	refnum = removed_refnum + 1;
164 	end = windows_seq_end();
165 	iter = windows_seq_refnum_lookup(refnum);
166 	if (iter == NULL) return;
167 
168 	while (iter != end) {
169 		window = g_sequence_get(iter);
170 
171 		if (window == NULL || window->sticky_refnum || window->refnum != refnum)
172 			break;
173 
174 		window_set_refnum0(window, refnum - 1);
175 		windows_seq_changed(iter);
176 
177 		refnum++;
178 		iter = g_sequence_iter_next(iter);
179 	}
180 }
181 
window_destroy(WINDOW_REC * window)182 void window_destroy(WINDOW_REC *window)
183 {
184 	GSequenceIter *iter;
185 	g_return_if_fail(window != NULL);
186 
187 	if (window->destroying) return;
188 	window->destroying = TRUE;
189 	windows = g_slist_remove(windows, window);
190 	iter = windows_seq_window_lookup(window);
191 	if (iter != NULL) g_sequence_remove(iter);
192 
193 	if (active_win == window) {
194 		active_win = NULL; /* it's corrupted */
195 		if (windows != NULL)
196 			window_set_active(windows->data);
197 	}
198 
199 	while (window->items != NULL)
200 		window_item_destroy(window->items->data);
201 
202         if (settings_get_bool("windows_auto_renumber"))
203 		windows_pack(window->refnum);
204 
205 	signal_emit("window destroyed", 1, window);
206 
207 	while (window->bound_items != NULL)
208                 window_bind_destroy(window, window->bound_items->data);
209 
210 	g_free_not_null(window->hilight_color);
211 	g_free_not_null(window->servertag);
212 	g_free_not_null(window->theme_name);
213 	g_free_not_null(window->name);
214 	g_free(window);
215 }
216 
window_auto_destroy(WINDOW_REC * window)217 void window_auto_destroy(WINDOW_REC *window)
218 {
219 	if (settings_get_bool("autoclose_windows") && windows->next != NULL &&
220 	    window->items == NULL && window->bound_items == NULL &&
221 	    window->level == 0 && !window->immortal)
222                 window_destroy(window);
223 }
224 
window_set_active(WINDOW_REC * window)225 void window_set_active(WINDOW_REC *window)
226 {
227 	WINDOW_REC *old_window;
228 
229 	if (window == active_win)
230 		return;
231 
232 	old_window = active_win;
233 	active_win = window;
234 	if (active_win != NULL) {
235 		windows = g_slist_remove(windows, active_win);
236 		windows = g_slist_prepend(windows, active_win);
237 	}
238 
239         if (active_win != NULL)
240 		signal_emit("window changed", 2, active_win, old_window);
241 }
242 
window_change_server(WINDOW_REC * window,void * server)243 void window_change_server(WINDOW_REC *window, void *server)
244 {
245 	SERVER_REC *active, *connect;
246 
247 	if (server != NULL && SERVER(server)->disconnected)
248 		return;
249 
250 	if (server == NULL) {
251 		active = connect = NULL;
252 	} else if (g_slist_find(servers, server) != NULL) {
253 		active = server;
254 		connect = NULL;
255 	} else {
256 		active = NULL;
257 		connect = server;
258 	}
259 
260 	if (window->connect_server != connect) {
261 		window->connect_server = connect;
262 		signal_emit("window connect changed", 2, window, connect);
263 	}
264 
265 	if (window->active_server != active) {
266 		window->active_server = active;
267 		signal_emit("window server changed", 2, window, active);
268 	}
269 }
270 
window_set_refnum(WINDOW_REC * window,int refnum)271 void window_set_refnum(WINDOW_REC *window, int refnum)
272 {
273 	GSequenceIter *other_iter, *window_iter;
274 	int old_refnum;
275 
276 	g_return_if_fail(window != NULL);
277 	g_return_if_fail(refnum >= 1);
278 	if (window->refnum == refnum) return;
279 
280 	other_iter = windows_seq_refnum_lookup(refnum);
281 	window_iter = windows_seq_refnum_lookup(window->refnum);
282 
283 	if (other_iter != NULL) {
284 		WINDOW_REC *rec = g_sequence_get(other_iter);
285 
286 		rec->refnum = window->refnum;
287 		signal_emit("window refnum changed", 2, rec, GINT_TO_POINTER(refnum));
288 	}
289 
290 	old_refnum = window->refnum;
291 	window->refnum = refnum;
292 	signal_emit("window refnum changed", 2, window, GINT_TO_POINTER(old_refnum));
293 
294 	if (window_iter != NULL && other_iter != NULL) {
295 		g_sequence_swap(other_iter, window_iter);
296 	} else {
297 		windows_seq_changed(window_iter);
298 	}
299 }
300 
window_set_name(WINDOW_REC * window,const char * name)301 void window_set_name(WINDOW_REC *window, const char *name)
302 {
303 	g_free_not_null(window->name);
304 	window->name = name == NULL || *name == '\0' ? NULL : g_strdup(name);
305 
306 	signal_emit("window name changed", 1, window);
307 }
308 
window_set_history(WINDOW_REC * window,const char * name)309 void window_set_history(WINDOW_REC *window, const char *name)
310 {
311 	char *oldname;
312 	oldname = window->history_name;
313 
314 	if (name == NULL || *name == '\0')
315 		window->history_name = NULL;
316 	else
317 		window->history_name = g_strdup(name);
318 
319 	signal_emit("window history changed", 2, window, oldname);
320 
321 	g_free_not_null(oldname);
322 }
323 
window_clear_history(WINDOW_REC * window,const char * name)324 void window_clear_history(WINDOW_REC *window, const char *name)
325 {
326 	signal_emit("window history cleared", 2, window, name);
327 }
328 
window_set_level(WINDOW_REC * window,int level)329 void window_set_level(WINDOW_REC *window, int level)
330 {
331 	g_return_if_fail(window != NULL);
332 
333 	window->level = level;
334         signal_emit("window level changed", 1, window);
335 }
336 
window_set_immortal(WINDOW_REC * window,int immortal)337 void window_set_immortal(WINDOW_REC *window, int immortal)
338 {
339 	g_return_if_fail(window != NULL);
340 
341 	window->immortal = immortal;
342         signal_emit("window immortal changed", 1, window);
343 }
344 
345 /* return active item's name, or if none is active, window's name */
window_get_active_name(WINDOW_REC * window)346 const char *window_get_active_name(WINDOW_REC *window)
347 {
348 	g_return_val_if_fail(window != NULL, NULL);
349 
350 	if (window->active != NULL)
351 		return window->active->visible_name;
352 
353 	return window->name;
354 }
355 
356 #define WINDOW_LEVEL_MATCH(window, server, level) \
357 	(((window)->level & level) && \
358 	 (server == NULL || (window)->active_server == server))
359 
window_find_level(void * server,int level)360 WINDOW_REC *window_find_level(void *server, int level)
361 {
362 	GSList *tmp;
363 	WINDOW_REC *match;
364 
365 	match = NULL;
366 	for (tmp = windows; tmp != NULL; tmp = tmp->next) {
367 		WINDOW_REC *rec = tmp->data;
368 
369 		if (WINDOW_LEVEL_MATCH(rec, server, level)) {
370 			/* prefer windows without any items */
371 			if (rec->items == NULL)
372 				return rec;
373 
374 			if (match == NULL)
375 				match = rec;
376 			else if (active_win == rec) {
377 				/* prefer active window over others */
378 				match = rec;
379 			}
380 		}
381 	}
382 
383 	return match;
384 }
385 
window_find_closest(void * server,const char * name,int level)386 WINDOW_REC *window_find_closest(void *server, const char *name, int level)
387 {
388 	WINDOW_REC *window,*namewindow=NULL;
389 	WI_ITEM_REC *item;
390 	int i;
391 
392 	/* match by name */
393 	item = name == NULL ? NULL :
394 		window_item_find(server, name);
395 	if (item != NULL) {
396 		namewindow = window_item_window(item);
397 		if (namewindow != NULL &&
398 		    ((namewindow->level & level) != 0 ||
399 		     !settings_get_bool("window_check_level_first"))) {
400 			/* match, but if multiple windows have the same level
401 			   we could be choosing a bad one here, eg.
402 			   name=nick1 would get nick2's query instead of
403 			   generic msgs window.
404 
405 	                   And check for prefixed !channel name --Borys  */
406 			if (g_ascii_strcasecmp(name, item->visible_name) == 0 ||
407 			    g_ascii_strcasecmp(name, (char *) window_item_get_target((WI_ITEM_REC *) item)) == 0)
408 				return namewindow;
409 		}
410 	}
411 
412 	/* prefer windows without items */
413 	for (i = 0; i < 2; i++) {
414 		/* match by level */
415 		if (level != MSGLEVEL_HILIGHT)
416 			level &= ~(MSGLEVEL_HILIGHT | MSGLEVEL_NOHILIGHT);
417 		window = window_find_level(server, level);
418 		if (window != NULL && (i == 1 || window->items == NULL))
419 			return window;
420 
421 		/* match by level - ignore server */
422 		window = window_find_level(NULL, level);
423 		if (window != NULL && (i == 1 || window->items == NULL))
424 			return window;
425 	}
426 
427 	/* still return item's window if we didnt find anything */
428 	if (namewindow != NULL) return namewindow;
429 
430 	/* fallback to active */
431 	return active_win;
432 }
433 
window_find_refnum(int refnum)434 WINDOW_REC *window_find_refnum(int refnum)
435 {
436 	GSequenceIter *iter;
437 
438 	iter = windows_seq_refnum_lookup(refnum);
439 	if (iter != NULL) {
440 		WINDOW_REC *rec = g_sequence_get(iter);
441 
442 		return rec;
443 	}
444 
445 	return NULL;
446 }
447 
window_find_name(const char * name)448 WINDOW_REC *window_find_name(const char *name)
449 {
450 	GSList *tmp;
451 
452 	g_return_val_if_fail(name != NULL, NULL);
453 
454 	for (tmp = windows; tmp != NULL; tmp = tmp->next) {
455 		WINDOW_REC *rec = tmp->data;
456 
457 		if (rec->name != NULL &&
458 		    g_ascii_strcasecmp(rec->name, name) == 0)
459 			return rec;
460 	}
461 
462 	return NULL;
463 }
464 
window_find_item(SERVER_REC * server,const char * name)465 WINDOW_REC *window_find_item(SERVER_REC *server, const char *name)
466 {
467 	WINDOW_REC *rec;
468 	WI_ITEM_REC *item;
469 
470 	g_return_val_if_fail(name != NULL, NULL);
471 
472 	rec = window_find_name(name);
473 	if (rec != NULL) return rec;
474 
475 	item = server == NULL ? NULL :
476 		window_item_find(server, name);
477 	if (item == NULL) {
478 		/* not found from the active server - any server? */
479 		item = window_item_find(NULL, name);
480 	}
481 
482 	if (item == NULL)
483 		return NULL;
484 
485 	return window_item_window(item);
486 }
487 
window_refnum_prev(int refnum,int wrap)488 int window_refnum_prev(int refnum, int wrap)
489 {
490 	WINDOW_REC *rec;
491 	GSequenceIter *iter, *end;
492 
493 	iter = windows_seq_refnum_search_left(refnum);
494 	end = windows_seq_end();
495 
496 	if (iter != NULL) {
497 		rec = g_sequence_get(iter);
498 		return rec->refnum;
499 	}
500 
501 	if (wrap) {
502 		iter = g_sequence_iter_prev(end);
503 		if (iter != end) {
504 			rec = g_sequence_get(iter);
505 			return rec->refnum;
506 		}
507 	}
508 
509 	return -1;
510 }
511 
window_refnum_next(int refnum,int wrap)512 int window_refnum_next(int refnum, int wrap)
513 {
514 	WINDOW_REC *rec;
515 	GSequenceIter *iter, *end;
516 
517 	iter = windows_seq_refnum_search_right(refnum);
518 	end = windows_seq_end();
519 
520 	if (iter != end) {
521 		rec = g_sequence_get(iter);
522 		return rec->refnum;
523 	}
524 
525 	if (wrap) {
526 		iter = windows_seq_begin();
527 		if (iter != end) {
528 			rec = g_sequence_get(iter);
529 			return rec->refnum;
530 		}
531 	}
532 
533 	return -1;
534 }
535 
windows_refnum_last(void)536 int windows_refnum_last(void)
537 {
538 	WINDOW_REC *rec;
539 	GSequenceIter *end, *iter;
540 
541 	end = windows_seq_end();
542 	iter = g_sequence_iter_prev(end);
543 	if (iter != end) {
544 		rec = g_sequence_get(iter);
545 		return rec->refnum;
546 	}
547 
548 	return -1;
549 }
550 
window_refnum_cmp(WINDOW_REC * w1,WINDOW_REC * w2)551 int window_refnum_cmp(WINDOW_REC *w1, WINDOW_REC *w2)
552 {
553 	return w1 == w2 ? 0 : w1->refnum < w2->refnum ? -1 : 1;
554 }
555 
windows_get_sorted(void)556 GSList *windows_get_sorted(void)
557 {
558 	GSequenceIter *iter, *begin;
559 	GSList *sorted;
560 
561         sorted = NULL;
562 	iter = windows_seq_end();
563 	begin = windows_seq_begin();
564 
565 	while (iter != begin) {
566 		WINDOW_REC *rec;
567 
568 		iter = g_sequence_iter_prev(iter);
569 		rec = g_sequence_get(iter);
570 
571 		sorted = g_slist_prepend(sorted, rec);
572 	}
573 
574         return sorted;
575 }
576 
577 /* Add a new bind to window - if duplicate is found it's returned */
window_bind_add(WINDOW_REC * window,const char * servertag,const char * name)578 WINDOW_BIND_REC *window_bind_add(WINDOW_REC *window, const char *servertag,
579 				 const char *name)
580 {
581 	WINDOW_BIND_REC *rec;
582 
583         g_return_val_if_fail(window != NULL, NULL);
584         g_return_val_if_fail(servertag != NULL, NULL);
585 	g_return_val_if_fail(name != NULL, NULL);
586 
587 	rec = window_bind_find(window, servertag, name);
588 	if (rec != NULL)
589 		return rec;
590 
591 	rec = g_new0(WINDOW_BIND_REC, 1);
592         rec->name = g_strdup(name);
593         rec->servertag = g_strdup(servertag);
594 
595 	window->bound_items = g_slist_append(window->bound_items, rec);
596         return rec;
597 }
598 
window_bind_destroy(WINDOW_REC * window,WINDOW_BIND_REC * rec)599 void window_bind_destroy(WINDOW_REC *window, WINDOW_BIND_REC *rec)
600 {
601 	g_return_if_fail(window != NULL);
602         g_return_if_fail(rec != NULL);
603 
604 	window->bound_items = g_slist_remove(window->bound_items, rec);
605 
606         g_free(rec->servertag);
607         g_free(rec->name);
608         g_free(rec);
609 }
610 
window_bind_find(WINDOW_REC * window,const char * servertag,const char * name)611 WINDOW_BIND_REC *window_bind_find(WINDOW_REC *window, const char *servertag,
612 				  const char *name)
613 {
614 	GSList *tmp;
615 
616         g_return_val_if_fail(window != NULL, NULL);
617         g_return_val_if_fail(servertag != NULL, NULL);
618         g_return_val_if_fail(name != NULL, NULL);
619 
620 	for (tmp = window->bound_items; tmp != NULL; tmp = tmp->next) {
621 		WINDOW_BIND_REC *rec = tmp->data;
622 
623 		if (g_ascii_strcasecmp(rec->name, name) == 0 &&
624 		    g_ascii_strcasecmp(rec->servertag, servertag) == 0)
625                         return rec;
626 	}
627 
628         return NULL;
629 }
630 
window_bind_remove_unsticky(WINDOW_REC * window)631 void window_bind_remove_unsticky(WINDOW_REC *window)
632 {
633 	GSList *tmp, *next;
634 
635 	for (tmp = window->bound_items; tmp != NULL; tmp = next) {
636 		WINDOW_BIND_REC *rec = tmp->data;
637 
638 		next = tmp->next;
639 		if (!rec->sticky)
640                         window_bind_destroy(window, rec);
641 	}
642 }
643 
sig_server_connected(SERVER_REC * server)644 static void sig_server_connected(SERVER_REC *server)
645 {
646 	GSList *tmp;
647 
648 	g_return_if_fail(server != NULL);
649 
650 	/* Try to keep some server assigned to windows..
651 	   Also change active window's server if the window is empty */
652 	for (tmp = windows; tmp != NULL; tmp = tmp->next) {
653 		WINDOW_REC *rec = tmp->data;
654 
655 		if ((rec->servertag == NULL ||
656 		     g_ascii_strcasecmp(rec->servertag, server->tag) == 0) &&
657 		    (rec->active_server == NULL ||
658 		     (rec == active_win && rec->items == NULL)))
659 			window_change_server(rec, server);
660 	}
661 }
662 
sig_server_disconnected(SERVER_REC * server)663 static void sig_server_disconnected(SERVER_REC *server)
664 {
665 	GSList *tmp;
666         SERVER_REC *new_server;
667 
668 	g_return_if_fail(server != NULL);
669 
670 	new_server = servers == NULL ? NULL : servers->data;
671 	for (tmp = windows; tmp != NULL; tmp = tmp->next) {
672 		WINDOW_REC *rec = tmp->data;
673 
674 		if (rec->active_server == server ||
675 		    rec->connect_server == server) {
676 			window_change_server(rec, rec->servertag != NULL ?
677 					     NULL : new_server);
678 		}
679 	}
680 }
681 
window_print_daychange(WINDOW_REC * window,struct tm * tm)682 static void window_print_daychange(WINDOW_REC *window, struct tm *tm)
683 {
684         THEME_REC *theme;
685         TEXT_DEST_REC dest;
686 	char *format, str[256];
687 	int ret;
688 
689 	theme = active_win->theme != NULL ? active_win->theme : current_theme;
690 	format_create_dest(&dest, NULL, NULL, MSGLEVEL_NEVER, window);
691 	format = format_get_text_theme(theme, MODULE_NAME, &dest,
692 				       TXT_DAYCHANGE);
693 	ret = strftime(str, sizeof(str), format, tm);
694 	g_free(format);
695 	if (ret <= 0) return;
696 
697 	printtext_string_window(window, MSGLEVEL_NEVER, str);
698 }
699 
color_24bit_256(const unsigned char rgb[])700 short color_24bit_256 (const unsigned char rgb[])
701 {
702 	static const int cstep_size = 40;
703 	static const int cstep_start = 0x5f;
704 
705 	static const int gstep_size = 10;
706 	static const int gstep_start = 0x08;
707 
708 	int dist[3] = {0};
709 	int r[3], gr[3];
710 
711 	size_t i;
712 
713 	for (i = 0; i < 3; ++i) {
714 		const int n = rgb[i];
715 		gr[i] = -1;
716 		if (n < cstep_start /2) {
717 			r[i] = 0;
718 			dist[i] = -cstep_size/2;
719 		}
720 		else {
721 			r[i] = 1+((n-cstep_start + cstep_size /2)/cstep_size);
722 			dist[i] = ((n-cstep_start + cstep_size /2)%cstep_size - cstep_size/2);
723 		}
724 		if (n < gstep_start /2) {
725 			gr[i] = -1;
726 		}
727 		else {
728 			gr[i] = ((n-gstep_start + gstep_size /2)/gstep_size);
729 		}
730 	}
731 	if (r[0] == r[1] && r[1] == r[2] &&
732 	    4*abs(dist[0]) < gstep_size && 4*abs(dist[1]) < gstep_size && 4*abs(dist[2]) < gstep_size) {
733 		/* skip gray detection */
734 	}
735 	else {
736 		const int j = r[1] == r[2] ? 0 : 1;
737 		if ((r[0] == r[1] || r[j] == r[2]) && abs(r[j]-r[(j+1)%3]) <= 1) {
738 			const int k = gr[1] == gr[2] ? 0 : 1;
739 			if ((gr[0] == gr[1] || gr[k] == gr[2]) && abs(gr[k]-gr[(k+1)%3]) <= 2) {
740 				if (gr[k] < 0) {
741 					r[0] = r[1] = r[2] = 0;
742 				}
743 				else if (gr[k] > 23) {
744 					r[0] = r[1] = r[2] = 5;
745 				}
746 				else {
747 					r[0] = 6;
748 					r[1] = (gr[k] / 6);
749 					r[2] = gr[k]%6;
750 				}
751 			}
752 		}
753 	}
754 	return 16 + r[0]*36 + r[1] * 6 + r[2];
755 }
756 
sig_print_text(void)757 static void sig_print_text(void)
758 {
759 	GSList *tmp;
760 	time_t t;
761 	struct tm *tm;
762 
763 	t = time(NULL);
764 	tm = localtime(&t);
765 	if (tm->tm_hour != 0 || tm->tm_min != 0)
766 		return;
767 
768 	daycheck = 2;
769 	signal_remove("print text", (SIGNAL_FUNC) sig_print_text);
770 
771 	/* day changed, print notice about it to every window */
772 	for (tmp = windows; tmp != NULL; tmp = tmp->next)
773 		window_print_daychange(tmp->data, tm);
774 }
775 
sig_check_daychange(void)776 static int sig_check_daychange(void)
777 {
778 	time_t t;
779 	struct tm *tm;
780 
781 	t = time(NULL);
782 	tm = localtime(&t);
783 
784 	if (daycheck == 1 && tm->tm_hour == 0 && tm->tm_min == 0) {
785 		sig_print_text();
786 		return TRUE;
787 	}
788 
789 	if (tm->tm_hour != 23 || tm->tm_min != 59) {
790 		daycheck = 0;
791 		return TRUE;
792 	}
793 
794 	/* time is 23:59 */
795 	if (daycheck == 0) {
796 		daycheck = 1;
797 		signal_add("print text", (SIGNAL_FUNC) sig_print_text);
798 	}
799 	return TRUE;
800 }
801 
read_settings(void)802 static void read_settings(void)
803 {
804 	if (daytag != -1) {
805 		g_source_remove(daytag);
806 		daytag = -1;
807 	}
808 
809 	if (settings_get_bool("timestamps"))
810 		daytag = g_timeout_add(30000, (GSourceFunc) sig_check_daychange, NULL);
811 }
812 
windows_init(void)813 void windows_init(void)
814 {
815 	active_win = NULL;
816 	windows_seq = g_sequence_new(NULL);
817 	daycheck = 0; daytag = -1;
818 	settings_add_bool("lookandfeel", "window_auto_change", FALSE);
819 	settings_add_bool("lookandfeel", "windows_auto_renumber", TRUE);
820 	settings_add_bool("lookandfeel", "window_check_level_first", FALSE);
821 	settings_add_level("lookandfeel", "window_default_level", "NONE");
822 
823 	read_settings();
824 	signal_add("server looking", (SIGNAL_FUNC) sig_server_connected);
825 	signal_add("server connected", (SIGNAL_FUNC) sig_server_connected);
826 	signal_add("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
827 	signal_add("server connect failed", (SIGNAL_FUNC) sig_server_disconnected);
828 	signal_add("setup changed", (SIGNAL_FUNC) read_settings);
829 }
830 
windows_deinit(void)831 void windows_deinit(void)
832 {
833 	if (daytag != -1) g_source_remove(daytag);
834 	if (daycheck == 1) signal_remove("print text", (SIGNAL_FUNC) sig_print_text);
835 
836 	signal_remove("server looking", (SIGNAL_FUNC) sig_server_connected);
837 	signal_remove("server connected", (SIGNAL_FUNC) sig_server_connected);
838 	signal_remove("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
839 	signal_remove("server connect failed", (SIGNAL_FUNC) sig_server_disconnected);
840 	signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
841 	g_sequence_free(windows_seq);
842 	windows_seq = NULL;
843 }
844