--- a/pidgin/gtkblist.c Fri Jan 15 01:17:42 2021 -0600
+++ b/pidgin/gtkblist.c Mon Jan 18 00:05:05 2021 -0600
@@ -3904,7 +3904,7 @@
theme = pidgin_blist_get_theme();
- 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 (purple_presence_is_idle(presence)) {
@@ -5909,7 +5909,7 @@
textcolor = pidgin_theme_font_get_color_describe(pair);
/* 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"; idle = g_strdup_printf("<span color='%s' font_desc='%s'>%d:%02d</span>",
--- a/pidgin/gtkconv.c Fri Jan 15 01:17:42 2021 -0600
+++ b/pidgin/gtkconv.c Mon Jan 18 00:05:05 2021 -0600
@@ -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 @@
static gboolean pidgin_conv_xy_to_right_infopane(PidginConvWindow *win, int x, int y);
-get_nick_color(PidginConversation *gtkconv, const gchar *name)
- col.red = col.green = col.blue = 0;
- col = g_array_index(gtkconv->nick_colors, GdkRGBA,
- g_str_hash(name) % gtkconv->nick_colors->len);
+ * @name: The name or text to get a color for. + * @color: (out): The return address for a #GdkRGBA that will recieve the + * 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. +get_nick_color(const gchar *name, GdkRGBA *color) { + GChecksum *checksum = NULL; + 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); @@ -3310,7 +3331,7 @@
const gchar *name, *alias;
PurpleChatUserFlags flags;
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,
@@ -3379,10 +3401,6 @@
(GDestroyNotify)gtk_tree_row_reference_free);
gtk_tree_path_free(newpath);
@@ -4156,19 +4174,6 @@
pidgin_conv_window_add_gtkconv(hidden_convwin, gtkconv);
pidgin_conv_placement_place(gtkconv);
- if (generated_nick_colors == NULL) {
- 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;
- generated_nick_colors = generate_nick_colors(NICK_COLOR_GENERATE_COUNT, rgba);
- gtkconv->nick_colors = g_array_ref(generated_nick_colors);
@@ -4275,8 +4280,6 @@
g_source_remove(gtkconv->attach_timer);
- g_array_unref(gtkconv->nick_colors);
@@ -7793,51 +7796,6 @@
return (gtkconv->win == hidden_convwin);
-gdouble luminance(GdkRGBA color)
- gdouble cutoff = 0.03928, scale = 12.92;
- gdouble a = 0.055, d = 1.055, p = 2.2;
- 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 */
-color_is_visible(GdkRGBA foreground, GdkRGBA background, gdouble min_contrast_ratio)
- gdouble lfg, lbg, lmin, lmax;
- gdouble luminosity_ratio;
- lfg = luminance(foreground);
- lbg = luminance(background);
- lmax = lfg, lmin = lbg;
- lmax = lbg, lmin = lfg;
- nr = lmax + 0.05, dr = lmin - 0.05;
- if (dr < 0.005 && dr > -0.005)
- luminosity_ratio = nr/dr;
- if ( luminosity_ratio < 0)
- luminosity_ratio *= -1.0;
- return (luminosity_ratio > min_contrast_ratio);
pidgin_conv_placement_place(PidginConversation *conv) {
@@ -7857,71 +7815,3 @@
pidgin_conv_window_add_gtkconv(win, conv);
-generate_nick_colors(guint numcolors, GdkRGBA background)
- GArray *colors = g_array_new(FALSE, FALSE, sizeof(GdkRGBA));
- GdkRGBA nick_highlight;
- 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);
- /* 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);
- purple_debug_warning("gtkconv", "Unable to generate enough random colors before timeout. %u colors found.\n", i);
- /* To remove errors caused by an empty array. */
- GdkRGBA color = {0.5, 0.5, 0.5, 1.0};
- g_array_append_val(colors, color);
--- a/pidgin/pidginstylecontext.c Fri Jan 15 01:17:42 2021 -0600
+++ b/pidgin/pidginstylecontext.c Mon Jan 18 00:05:05 2021 -0600
@@ -29,48 +29,53 @@
/******************************************************************************
*****************************************************************************/
+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)) { + 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, + g_object_unref(G_OBJECT(window)); + g_once_init_leave(&background, bg); -pidgin_style_context_is_dark(GtkStyleContext *context) {
+pidgin_style_context_is_dark(void) {
- if(dark_mode_have_cache) {
- return dark_mode_cached_value;
- context = gtk_style_context_new();
- 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,
- 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;
-pidgin_style_context_adjust_contrast(GtkStyleContext *context, GdkRGBA *rgba) {
- if(pidgin_style_context_is_dark(context)) {
- gtk_rgb_to_hsv(rgba->red, rgba->green, rgba->blue, &h, &s, &v);
- gtk_hsv_to_rgb(h, s, v, &rgba->red, &rgba->green, &rgba->blue);
--- a/pidgin/pidginstylecontext.h Fri Jan 15 01:17:42 2021 -0600
+++ b/pidgin/pidginstylecontext.h Mon Jan 18 00:05:05 2021 -0600
@@ -41,27 +41,27 @@
+ * 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. +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
-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
- * 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);