diff --git a/pidgin/gtkblist.c b/pidgin/gtkblist.c
--- a/pidgin/gtkblist.c
+++ b/pidgin/gtkblist.c
@@ -3904,7 +3904,7 @@
theme = pidgin_blist_get_theme();
name_color = NULL;
- dim_grey = pidgin_style_context_is_dark(NULL) ? "light slate grey" : "dim grey";
+ dim_grey = pidgin_style_context_is_dark() ? "light slate grey" : "dim grey";
if (theme) {
if (purple_presence_is_idle(presence)) {
@@ -5909,7 +5909,7 @@
textcolor = pidgin_theme_font_get_color_describe(pair);
else
/* If no theme them default to making idle buddy names grey */
- textcolor = pidgin_style_context_is_dark(NULL) ? "light slate grey" : "dim grey";
+ textcolor = pidgin_style_context_is_dark() ? "light slate grey" : "dim grey";
if (textcolor) {
idle = g_strdup_printf("%d:%02d",
diff --git a/pidgin/gtkconv.h b/pidgin/gtkconv.h
--- a/pidgin/gtkconv.h
+++ b/pidgin/gtkconv.h
@@ -100,7 +100,6 @@
GtkWidget *tabby;
GtkWidget *menu_tabby;
- GArray *nick_colors;
PurpleMessageFlags last_flags;
GtkWidget *history_sw;
GtkWidget *history;
diff --git a/pidgin/gtkconv.c b/pidgin/gtkconv.c
--- a/pidgin/gtkconv.c
+++ b/pidgin/gtkconv.c
@@ -63,8 +63,6 @@
#include "pidgintooltip.h"
#include "pidginwindow.h"
-#include "gtknickcolors.h"
-
#define GTK_TOOLTIPS_VAR gtkconv->tooltips
#define ADD_MESSAGE_HISTORY_AT_ONCE 100
@@ -135,11 +133,6 @@
#define BUDDYICON_SIZE_MIN 32
#define BUDDYICON_SIZE_MAX 96
-#define MIN_LUMINANCE_CONTRAST_RATIO 4.5
-
-#define NICK_COLOR_GENERATE_COUNT 220
-static GArray *generated_nick_colors = NULL;
-
/* These probably won't conflict with any WebKit values. */
#define PIDGIN_DRAG_BLIST_NODE (1337)
#define PIDGIN_DRAG_IM_CONTACT (31337)
@@ -172,9 +165,6 @@
static void update_typing_icon(PidginConversation *gtkconv);
static void update_typing_message(PidginConversation *gtkconv, const char *message);
gboolean pidgin_conv_has_focus(PurpleConversation *conv);
-static GArray* generate_nick_colors(guint numcolors, GdkRGBA background);
-gdouble luminance(GdkRGBA color);
-static gboolean color_is_visible(GdkRGBA foreground, GdkRGBA background, gdouble min_contrast_ratio);
static GtkTextTag *get_buddy_tag(PurpleChatConversation *chat, const char *who, PurpleMessageFlags flag, gboolean create);
static void pidgin_conv_update_fields(PurpleConversation *conv, PidginConvFields fields);
static void focus_out_from_menubar(GtkWidget *wid, PidginConvWindow *win);
@@ -186,21 +176,52 @@
int width, int height);
static gboolean pidgin_conv_xy_to_right_infopane(PidginConvWindow *win, int x, int y);
-static const GdkRGBA *
-get_nick_color(PidginConversation *gtkconv, const gchar *name)
-{
- static GdkRGBA col;
-
- if (name == NULL) {
- col.red = col.green = col.blue = 0;
- col.alpha = 1;
- return &col;
- }
-
- col = g_array_index(gtkconv->nick_colors, GdkRGBA,
- g_str_hash(name) % gtkconv->nick_colors->len);
-
- return &col;
+/*
+ * get_nick_color:
+ * @name: The name or text to get a color for.
+ * @color: (out): The return address for a #GdkRGBA that will recieve the
+ * color.
+ *
+ * This function is based heavily on the implementation that gajim uses from
+ * python-nbxmpp in nbxmpp.util.text_to_color. However, we don't have an
+ * implementation of HSL let alone HSLuv, so we're using HSV which is why
+ * the value is 1.0 instead of a luminance of 0.5.
+ *
+ * Currently there is no caching as GCache is deprecated and writing a fast LRU
+ * in glib is going to take a bit of finesse. Also we'll need to figure out how
+ * to scale to ginormous Twitch channels which will constantly break the cache.
+ */
+static void
+get_nick_color(const gchar *name, GdkRGBA *color) {
+ GdkRGBA background;
+ GChecksum *checksum = NULL;
+ guchar digest[20];
+ gsize digest_len = sizeof(digest);
+ gdouble hue = 0, red = 0, green = 0, blue = 0;
+
+ pidgin_style_context_get_background_color(&background);
+
+ /* hash the string and get the first 2 bytes of the digest */
+ checksum = g_checksum_new(G_CHECKSUM_SHA1);
+ g_checksum_update(checksum, (const guchar *)name, -1);
+ g_checksum_get_digest(checksum, digest, &digest_len);
+ g_checksum_free(checksum);
+
+ /* Calculate the hue based on the digest. We need a value between 0 and 1
+ * so we divide the value by 65535 which is the maximum value for 2 bytes.
+ */
+ hue = (digest[0] << 8 | digest[1]) / 65535.0;
+
+ /* Get the rgb values for the hue at full saturation and value. */
+ gtk_hsv_to_rgb(hue, 1.0, 1.0, &red, &green, &blue);
+
+ /* Finally calculate the color summing 20% of the inverted background color
+ * with 80% of the color.
+ */
+ color->red = (0.2 * (1 - background.red)) + (0.8 * red);
+ color->green = (0.2 * (1 - background.green)) + (0.8 * green);
+ color->blue = (0.2 * (1 - background.blue)) + (0.8 * blue);
+ color->alpha = 1.0;
}
static PurpleBlistNode *
@@ -3310,7 +3331,7 @@
const gchar *name, *alias;
gchar *tmp, *alias_key;
PurpleChatUserFlags flags;
- GdkRGBA *color = NULL;
+ GdkRGBA color;
alias = purple_chat_user_get_alias(cb);
name = purple_chat_user_get_name(cb);
@@ -3352,7 +3373,8 @@
g_object_set(G_OBJECT(tag), "style", PANGO_STYLE_NORMAL, NULL);
if ((tag = get_buddy_tag(chat, name, PURPLE_MESSAGE_NICK, FALSE)))
g_object_set(G_OBJECT(tag), "style", PANGO_STYLE_NORMAL, NULL);
- color = (GdkRGBA*)get_nick_color(gtkconv, name);
+
+ get_nick_color(name, &color);
}
gtk_list_store_insert_with_values(ls, &iter,
@@ -3369,7 +3391,7 @@
CHAT_USERS_ALIAS_KEY_COLUMN, alias_key,
CHAT_USERS_NAME_COLUMN, name,
CHAT_USERS_FLAGS_COLUMN, flags,
- CHAT_USERS_COLOR_COLUMN, color,
+ CHAT_USERS_COLOR_COLUMN, &color,
CHAT_USERS_WEIGHT_COLUMN, is_buddy ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL,
-1);
@@ -3379,10 +3401,6 @@
(GDestroyNotify)gtk_tree_row_reference_free);
gtk_tree_path_free(newpath);
-#if 0
- if (is_me && color)
- gdk_rgba_free(color);
-#endif
g_free(alias_key);
}
@@ -4156,19 +4174,6 @@
pidgin_conv_window_add_gtkconv(hidden_convwin, gtkconv);
else
pidgin_conv_placement_place(gtkconv);
-
- if (generated_nick_colors == NULL) {
- GdkColor color;
- GdkRGBA rgba;
- color = gtk_widget_get_style(gtkconv->history)->base[GTK_STATE_NORMAL];
- rgba.red = color.red / 65535.0;
- rgba.green = color.green / 65535.0;
- rgba.blue = color.blue / 65535.0;
- rgba.alpha = 1.0;
- generated_nick_colors = generate_nick_colors(NICK_COLOR_GENERATE_COUNT, rgba);
- }
-
- gtkconv->nick_colors = g_array_ref(generated_nick_colors);
}
static void
@@ -4275,8 +4280,6 @@
g_source_remove(gtkconv->attach_timer);
}
- g_array_unref(gtkconv->nick_colors);
-
g_free(gtkconv);
}
@@ -7793,51 +7796,6 @@
return (gtkconv->win == hidden_convwin);
}
-
-gdouble luminance(GdkRGBA color)
-{
- gdouble r, g, b;
- gdouble rr, gg, bb;
- gdouble cutoff = 0.03928, scale = 12.92;
- gdouble a = 0.055, d = 1.055, p = 2.2;
-
- rr = color.red;
- gg = color.green;
- bb = color.blue;
-
- r = (rr > cutoff) ? pow((rr+a)/d, p) : rr/scale;
- g = (gg > cutoff) ? pow((gg+a)/d, p) : gg/scale;
- b = (bb > cutoff) ? pow((bb+a)/d, p) : bb/scale;
-
- return (r*0.2126 + g*0.7152 + b*0.0722);
-}
-
-/* Algorithm from https://www.w3.org/TR/2008/REC-WCAG20-20081211/relative-luminance.xml */
-static gboolean
-color_is_visible(GdkRGBA foreground, GdkRGBA background, gdouble min_contrast_ratio)
-{
- gdouble lfg, lbg, lmin, lmax;
- gdouble luminosity_ratio;
- gdouble nr, dr;
-
- lfg = luminance(foreground);
- lbg = luminance(background);
-
- if (lfg > lbg)
- lmax = lfg, lmin = lbg;
- else
- lmax = lbg, lmin = lfg;
-
- nr = lmax + 0.05, dr = lmin - 0.05;
- if (dr < 0.005 && dr > -0.005)
- dr += 0.01;
-
- luminosity_ratio = nr/dr;
- if ( luminosity_ratio < 0)
- luminosity_ratio *= -1.0;
- return (luminosity_ratio > min_contrast_ratio);
-}
-
void
pidgin_conv_placement_place(PidginConversation *conv) {
PidginConvWindow *win;
@@ -7857,71 +7815,3 @@
pidgin_conv_window_add_gtkconv(win, conv);
}
}
-
-static GArray*
-generate_nick_colors(guint numcolors, GdkRGBA background)
-{
- guint i = 0, j = 0;
- GArray *colors = g_array_new(FALSE, FALSE, sizeof(GdkRGBA));
- GdkRGBA nick_highlight;
- GdkRGBA send_color;
- time_t breakout_time;
-
- gdk_rgba_parse(&nick_highlight, DEFAULT_HIGHLIGHT_COLOR);
- gdk_rgba_parse(&send_color, DEFAULT_SEND_COLOR);
-
- pidgin_style_context_adjust_contrast(NULL, &nick_highlight);
- pidgin_style_context_adjust_contrast(NULL, &send_color);
-
- srand(background.red * 65535 + background.green * 65535 + background.blue * 65535 + 1);
-
- breakout_time = time(NULL) + 3;
-
- /* first we look through the list of "good" colors: colors that differ from every other color in the
- * list. only some of them will differ from the background color though. lets see if we can find
- * numcolors of them that do
- */
- while (i < numcolors && j < PIDGIN_NUM_NICK_SEED_COLORS && time(NULL) < breakout_time)
- {
- GdkRGBA color = nick_seed_colors[j];
-
- if (color_is_visible(color, background, MIN_LUMINANCE_CONTRAST_RATIO) &&
- color_is_visible(color, nick_highlight, MIN_LUMINANCE_CONTRAST_RATIO) &&
- color_is_visible(color, send_color, MIN_LUMINANCE_CONTRAST_RATIO))
- {
- g_array_append_val(colors, color);
- i++;
- }
- j++;
- }
-
- /* we might not have found numcolors in the last loop. if we did, we'll never enter this one.
- * if we did not, lets just find some colors that don't conflict with the background. its
- * expensive to find colors that not only don't conflict with the background, but also do not
- * conflict with each other.
- */
- while(i < numcolors && time(NULL) < breakout_time)
- {
- GdkRGBA color = {g_random_double_range(0, 1), g_random_double_range(0, 1), g_random_double_range(0, 1), 1};
-
- if (color_is_visible(color, background, MIN_LUMINANCE_CONTRAST_RATIO) &&
- color_is_visible(color, nick_highlight, MIN_LUMINANCE_CONTRAST_RATIO) &&
- color_is_visible(color, send_color, MIN_LUMINANCE_CONTRAST_RATIO))
- {
- g_array_append_val(colors, color);
- i++;
- }
- }
-
- if (i < numcolors) {
- purple_debug_warning("gtkconv", "Unable to generate enough random colors before timeout. %u colors found.\n", i);
- }
-
- if( i == 0 ) {
- /* To remove errors caused by an empty array. */
- GdkRGBA color = {0.5, 0.5, 0.5, 1.0};
- g_array_append_val(colors, color);
- }
-
- return colors;
-}
diff --git a/pidgin/gtknickcolors.h b/pidgin/gtknickcolors.h
deleted file mode 100644
--- a/pidgin/gtknickcolors.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/* pidgin
- * Pidgin is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
- */
-
-#if !defined(PIDGIN_GLOBAL_HEADER_INSIDE) && !defined(PIDGIN_COMPILATION)
-# error "only may be included directly"
-#endif
-
-#ifndef _PIDGINNICKCOLORS_H_
-#define _PIDGINNICKCOLORS_H_
-/**
- * SECTION:gtknickcolors
- * @section_id: pidgin-gtknickcolors
- * @short_description: gtknickcolors.h
- * @title: Conversation Nick Colors
- */
-
-static const GdkRGBA nick_seed_colors[] = {
- {64764/65535.0, 59881/65535.0, 20303/65535.0, 1}, /* Butter #1 */
- {60909/65535.0, 54484/65535.0, 0/65535.0, 1}, /* Butter #2 */
- {50372/65535.0, 41120/65535.0, 0/65535.0, 1}, /* Butter #3 */
- {64764/65535.0, 44975/65535.0, 15934/65535.0, 1}, /* Orange #1 */
- {62965/65535.0, 31097/65535.0, 0/65535.0, 1}, /* Orange #2 */
- {52942/65535.0, 23644/65535.0, 0/65535.0, 1}, /* Orange #3 */
- {59811/65535.0, 47545/65535.0, 28270/65535.0, 1}, /* Chocolate #1 */
- {49601/65535.0, 32125/65535.0, 4369/65535.0, 1}, /* Chocolate #2 */
- {36751/65535.0, 22873/65535.0, 514/65535.0, 1}, /* Chocolate #3 */
- {35466/65535.0, 58082/65535.0, 13364/65535.0, 1}, /* Chameleon #1 */
- {29555/65535.0, 53970/65535.0, 5654/65535.0, 1}, /* Chameleon #2 */
- {20046/65535.0, 39578/65535.0, 1542/65535.0, 1}, /* Chameleon #3 */
- {29289/65535.0, 40863/65535.0, 53199/65535.0, 1}, /* Sky Blue #1 */
- {13364/65535.0, 25957/65535.0, 42148/65535.0, 1}, /* Sky Blue #2 */
- { 8224/65535.0, 19018/65535.0, 34695/65535.0, 1}, /* Sky Blue #3 */
- {44461/65535.0, 32639/65535.0, 43167/65535.0, 1}, /* Plum #1 */
- {30069/65535.0, 20560/65535.0, 31611/65535.0, 1}, /* Plum #2 */
- {23644/65535.0, 13621/65535.0, 26214/65535.0, 1}, /* Plum #3 */
- {61423/65535.0, 10537/65535.0, 10537/65535.0, 1}, /* Scarlet Red #1 */
- {52428/65535.0, 0/65535.0, 0/65535.0, 1}, /* Scarlet Red #2 */
- {42148/65535.0, 0/65535.0, 0/65535.0, 1}, /* Scarlet Red #3 */
- {34952/65535.0, 35466/65535.0, 34181/65535.0, 1}, /* Aluminium #4*/
- {21845/65535.0, 22359/65535.0, 21331/65535.0, 1}, /* Aluminium #5*/
- {11822/65535.0, 13364/65535.0, 13878/65535.0, 1} /* Aluminium #6*/
-};
-
-#define PIDGIN_NUM_NICK_SEED_COLORS (sizeof(nick_seed_colors) / sizeof(nick_seed_colors[0]))
-
-#endif
diff --git a/pidgin/meson.build b/pidgin/meson.build
--- a/pidgin/meson.build
+++ b/pidgin/meson.build
@@ -80,7 +80,6 @@
'gtkicon-theme-loader.h',
'gtkidle.h',
'gtkmedia.h',
- 'gtknickcolors.h',
'gtknotify.h',
'gtkpluginpref.h',
'gtkprefs.h',
diff --git a/pidgin/pidginstylecontext.h b/pidgin/pidginstylecontext.h
--- a/pidgin/pidginstylecontext.h
+++ b/pidgin/pidginstylecontext.h
@@ -41,27 +41,27 @@
G_BEGIN_DECLS
/**
+ * pidgin_style_context_get_background_color:
+ * @color: (out): A return address of a #GdkRGBA for the background color.
+ *
+ * Gets the background color for #GtkWindow in the currently selected theme
+ * and sets @color to that value.
+ *
+ * Since: 3.0.0
+ */
+void pidgin_style_context_get_background_color(GdkRGBA *color);
+
+/**
* pidgin_style_context_is_dark:
- * @context: The #GtkStyleContext to use, or %NULL to use a cached version.
*
* Gets whether or not dark mode is enabled.
*
* Returns: %TRUE if dark mode is enabled and foreground colours should be
* inverted.
+ *
+ * Since: 3.0.0
*/
-
-gboolean pidgin_style_context_is_dark(GtkStyleContext *context);
-
-/**
- * pidgin_style_context_adjust_contrast:
- * @context: The #GtkStyleContext in use.
- * @color: (inout): Color to be lightened. Transformed color will be written
- * here.
- *
- * Lighten a color if dark mode is enabled.
- */
-
-void pidgin_style_context_adjust_contrast(GtkStyleContext *context, GdkRGBA *color);
+gboolean pidgin_style_context_is_dark(void);
G_END_DECLS
diff --git a/pidgin/pidginstylecontext.c b/pidgin/pidginstylecontext.c
--- a/pidgin/pidginstylecontext.c
+++ b/pidgin/pidginstylecontext.c
@@ -29,48 +29,53 @@
/******************************************************************************
* Public API
*****************************************************************************/
+void
+pidgin_style_context_get_background_color(GdkRGBA *color) {
+ /* This value will leak, we could put a shutdown function in but right now
+ * that seems like a bit much for a few bytes.
+ */
+ static GdkRGBA *background = NULL;
+
+ if(g_once_init_enter(&background)) {
+ GdkRGBA *bg = NULL;
+ GtkStyleContext *context = NULL;
+ GtkWidget *window = NULL;
+
+ /* We create a window to get its background color from its style
+ * context. This _is_ doable without creating a window, but you still
+ * need the window class and about four times as much code, so that's
+ * why we do it this way.
+ */
+ window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ context = gtk_widget_get_style_context(window);
+
+ gtk_style_context_get(context, GTK_STATE_FLAG_NORMAL,
+ GTK_STYLE_PROPERTY_BACKGROUND_COLOR, &bg,
+ NULL);
+ g_object_unref(G_OBJECT(window));
+
+ g_once_init_leave(&background, bg);
+ }
+
+ *color = *background;
+}
+
gboolean
-pidgin_style_context_is_dark(GtkStyleContext *context) {
+pidgin_style_context_is_dark(void) {
GdkRGBA bg;
gdouble luminance = 0.0;
- if(context == NULL) {
- if(dark_mode_have_cache) {
- return dark_mode_cached_value;
- }
-
- context = gtk_style_context_new();
- } else {
- g_object_ref(G_OBJECT(context));
+ if(dark_mode_have_cache) {
+ return dark_mode_cached_value;
}
- gtk_style_context_get(context, GTK_STATE_FLAG_NORMAL,
- GTK_STYLE_PROPERTY_BACKGROUND_COLOR, &bg,
- NULL);
- g_object_unref(G_OBJECT(context));
+ pidgin_style_context_get_background_color(&bg);
- /* magic values are taken from https://en.wikipedia.org/wiki/Luma_(video)
- * Rec._601_luma_versus_Rec._709_luma_coefficients.
- */
- luminance = (0.299 * bg.red) + (0.587 * bg.green) + (0.114 * bg.blue);
+ /* 709 coefficients from https://en.wikipedia.org/wiki/Luma_(video) */
+ luminance = (0.2126 * bg.red) + (0.7152 * bg.green) + (0.0722 * bg.blue);
- dark_mode_cached_value = (luminance < 0x7FFF);
+ dark_mode_cached_value = (luminance < 0.5);
dark_mode_have_cache = TRUE;
return dark_mode_cached_value;
}
-
-void
-pidgin_style_context_adjust_contrast(GtkStyleContext *context, GdkRGBA *rgba) {
- if(pidgin_style_context_is_dark(context)) {
- gdouble h, s, v;
-
- gtk_rgb_to_hsv(rgba->red, rgba->green, rgba->blue, &h, &s, &v);
-
- v += 0.3;
- v = v > 1.0 ? 1.0 : v;
- s = 0.7;
-
- gtk_hsv_to_rgb(h, s, v, &rgba->red, &rgba->green, &rgba->blue);
- }
-}