summaryrefslogtreecommitdiffstats
path: root/utils/notify-osd/leolik.patch
diff options
context:
space:
mode:
Diffstat (limited to 'utils/notify-osd/leolik.patch')
-rw-r--r--utils/notify-osd/leolik.patch1563
1 files changed, 1563 insertions, 0 deletions
diff --git a/utils/notify-osd/leolik.patch b/utils/notify-osd/leolik.patch
new file mode 100644
index 0000000000..befb88f877
--- /dev/null
+++ b/utils/notify-osd/leolik.patch
@@ -0,0 +1,1563 @@
+diff -Naur old_src/bubble.c src/bubble.c
+--- old_src/bubble.c 2012-03-23 10:49:59.000000000 -0400
++++ src/bubble.c 2012-11-10 08:44:21.000000000 -0500
+@@ -119,6 +119,14 @@
+ A
+ };
+
++typedef struct _NotifyHSVColor NotifyHSVColor;
++
++struct _NotifyHSVColor {
++ gdouble hue;
++ gdouble saturation;
++ gdouble value;
++};
++
+ #define TEMPORARY_ICON_PREFIX_WORKAROUND 1
+ #ifdef TEMPORARY_ICON_PREFIX_WORKAROUND
+ #warning "--== Using the icon-name-substitution! This is a temp. workaround not going to be maintained for long! ==--"
+@@ -128,25 +136,25 @@
+ // FIXME: this is in class Defaults already, but not yet hooked up so for the
+ // moment we use the macros here, these values reflect the visual-guideline
+ // for jaunty notifications
+-#define TEXT_TITLE_COLOR_R 1.0f
+-#define TEXT_TITLE_COLOR_G 1.0f
+-#define TEXT_TITLE_COLOR_B 1.0f
+-#define TEXT_TITLE_COLOR_A 1.0f
+-
+-#define TEXT_BODY_COLOR_R 0.91f
+-#define TEXT_BODY_COLOR_G 0.91f
+-#define TEXT_BODY_COLOR_B 0.91f
+-#define TEXT_BODY_COLOR_A 1.0f
++float TEXT_TITLE_COLOR_R = 1.0f;
++float TEXT_TITLE_COLOR_G = 1.0f;
++float TEXT_TITLE_COLOR_B = 1.0f;
++float TEXT_TITLE_COLOR_A = 1.0f;
++
++float TEXT_BODY_COLOR_R = 0.91f;
++float TEXT_BODY_COLOR_G = 0.91f;
++float TEXT_BODY_COLOR_B = 0.91f;
++float TEXT_BODY_COLOR_A = 1.0f;
+
+ #define TEXT_SHADOW_COLOR_R 0.0f
+ #define TEXT_SHADOW_COLOR_G 0.0f
+ #define TEXT_SHADOW_COLOR_B 0.0f
+-#define TEXT_SHADOW_COLOR_A 1.0f
++float TEXT_SHADOW_COLOR_A = 1.0f;
+
+-#define BUBBLE_BG_COLOR_R 0.15f
+-#define BUBBLE_BG_COLOR_G 0.15f
+-#define BUBBLE_BG_COLOR_B 0.15f
+-#define BUBBLE_BG_COLOR_A 0.9f
++float BUBBLE_BG_COLOR_R = 0.07f;
++float BUBBLE_BG_COLOR_G = 0.07f;
++float BUBBLE_BG_COLOR_B = 0.07f;
++float BUBBLE_BG_COLOR_A = 0.9f;
+
+ #define INDICATOR_UNLIT_R 1.0f
+ #define INDICATOR_UNLIT_G 1.0f
+@@ -167,6 +175,10 @@
+ #define BUBBLE_CONTENT_BLUR_RADIUS 4
+ #define TEXT_DROP_SHADOW_SIZE 2
+
++gboolean BUBBLE_PREVENT_FADE = TRUE;
++gboolean BUBBLE_CLOSE_ON_CLICK = FALSE;
++gboolean BUBBLE_AS_DESKTOP_BG = FALSE;
++
+ //-- private functions ---------------------------------------------------------
+
+ static guint g_bubble_signals[LAST_SIGNAL] = { 0 };
+@@ -487,6 +499,22 @@
+ cairo_surface_destroy (new_surface);
+ }
+
++static gdouble
++get_shadow_size (Bubble *bubble)
++{
++ BubblePrivate* priv = GET_PRIVATE (bubble);
++ Defaults* d = bubble->defaults;
++ return defaults_get_bubble_shadow_size (d, priv->composited);
++}
++
++static gdouble
++get_corner_radius (Bubble *bubble)
++{
++ BubblePrivate* priv = GET_PRIVATE (bubble);
++ Defaults* d = bubble->defaults;
++ return defaults_get_bubble_corner_radius (d, priv->composited);
++}
++
+ static void
+ _draw_layout_grid (cairo_t* cr,
+ Bubble* bubble)
+@@ -501,112 +529,112 @@
+
+ // all vertical grid lines
+ cairo_move_to (cr,
+- 0.5f + EM2PIXELS (defaults_get_bubble_shadow_size (d), d),
+- 0.5f + EM2PIXELS (defaults_get_bubble_shadow_size (d), d));
++ 0.5f + EM2PIXELS (get_shadow_size (bubble), d),
++ 0.5f + EM2PIXELS (get_shadow_size (bubble), d));
+ cairo_line_to (cr,
+- 0.5f + EM2PIXELS (defaults_get_bubble_shadow_size (d), d),
++ 0.5f + EM2PIXELS (get_shadow_size (bubble), d),
+ 0.5f + (gdouble) bubble_get_height (bubble) -
+- EM2PIXELS (defaults_get_bubble_shadow_size (d), d));
++ EM2PIXELS (get_shadow_size (bubble), d));
+ cairo_move_to (cr,
+- 0.5f + EM2PIXELS (defaults_get_bubble_shadow_size (d), d) +
++ 0.5f + EM2PIXELS (get_shadow_size (bubble), d) +
+ EM2PIXELS (defaults_get_margin_size (d), d),
+- 0.5f + EM2PIXELS (defaults_get_bubble_shadow_size (d), d));
++ 0.5f + EM2PIXELS (get_shadow_size (bubble), d));
+ cairo_line_to (cr,
+- 0.5f + EM2PIXELS (defaults_get_bubble_shadow_size (d), d) +
++ 0.5f + EM2PIXELS (get_shadow_size (bubble), d) +
+ EM2PIXELS (defaults_get_margin_size (d), d),
+ 0.5f + (gdouble) bubble_get_height (bubble) -
+- EM2PIXELS (defaults_get_bubble_shadow_size (d), d));
++ EM2PIXELS (get_shadow_size (bubble), d));
+ cairo_move_to (cr,
+- 0.5f + EM2PIXELS (defaults_get_bubble_shadow_size (d), d) +
++ 0.5f + EM2PIXELS (get_shadow_size (bubble), d) +
+ EM2PIXELS (defaults_get_margin_size (d), d) +
+ EM2PIXELS (defaults_get_icon_size (d), d),
+- 0.5f + EM2PIXELS (defaults_get_bubble_shadow_size (d), d));
++ 0.5f + EM2PIXELS (get_shadow_size (bubble), d));
+ cairo_line_to (cr,
+- 0.5f + EM2PIXELS (defaults_get_bubble_shadow_size (d), d) +
++ 0.5f + EM2PIXELS (get_shadow_size (bubble), d) +
+ EM2PIXELS (defaults_get_margin_size (d), d) +
+ EM2PIXELS (defaults_get_icon_size (d), d),
+ 0.5f + (gdouble) bubble_get_height (bubble) -
+- EM2PIXELS (defaults_get_bubble_shadow_size (d), d));
++ EM2PIXELS (get_shadow_size (bubble), d));
+ cairo_move_to (cr,
+- 0.5f + EM2PIXELS (defaults_get_bubble_shadow_size (d), d) +
++ 0.5f + EM2PIXELS (get_shadow_size (bubble), d) +
+ EM2PIXELS (2 * defaults_get_margin_size (d), d) +
+ EM2PIXELS (defaults_get_icon_size (d), d),
+- 0.5f + EM2PIXELS (defaults_get_bubble_shadow_size (d), d));
++ 0.5f + EM2PIXELS (get_shadow_size (bubble), d));
+ cairo_line_to (cr,
+- 0.5f + EM2PIXELS (defaults_get_bubble_shadow_size (d), d) +
++ 0.5f + EM2PIXELS (get_shadow_size (bubble), d) +
+ EM2PIXELS (2 * defaults_get_margin_size (d), d) +
+ EM2PIXELS (defaults_get_icon_size (d), d),
+ 0.5f + (gdouble) bubble_get_height (bubble) -
+- EM2PIXELS (defaults_get_bubble_shadow_size (d), d));
++ EM2PIXELS (get_shadow_size (bubble), d));
+ cairo_move_to (cr,
+- 0.5f + EM2PIXELS (defaults_get_bubble_shadow_size (d), d) +
++ 0.5f + EM2PIXELS (get_shadow_size (bubble), d) +
+ EM2PIXELS (defaults_get_bubble_width (d), d) -
+ EM2PIXELS (defaults_get_margin_size (d), d),
+- 0.5f + EM2PIXELS (defaults_get_bubble_shadow_size (d), d));
++ 0.5f + EM2PIXELS (get_shadow_size (bubble), d));
+ cairo_line_to (cr,
+- 0.5f + EM2PIXELS (defaults_get_bubble_shadow_size (d), d) +
++ 0.5f + EM2PIXELS (get_shadow_size (bubble), d) +
+ EM2PIXELS (defaults_get_bubble_width (d), d) -
+ EM2PIXELS (defaults_get_margin_size (d), d),
+ 0.5f + (gdouble) bubble_get_height (bubble) -
+- EM2PIXELS (defaults_get_bubble_shadow_size (d), d));
++ EM2PIXELS (get_shadow_size (bubble), d));
+ cairo_move_to (cr,
+- 0.5f + EM2PIXELS (defaults_get_bubble_shadow_size (d), d) +
++ 0.5f + EM2PIXELS (get_shadow_size (bubble), d) +
+ EM2PIXELS (defaults_get_bubble_width (d), d),
+- 0.5f + EM2PIXELS (defaults_get_bubble_shadow_size (d), d));
++ 0.5f + EM2PIXELS (get_shadow_size (bubble), d));
+ cairo_line_to (cr,
+- 0.5f + EM2PIXELS (defaults_get_bubble_shadow_size (d), d) +
++ 0.5f + EM2PIXELS (get_shadow_size (bubble), d) +
+ EM2PIXELS (defaults_get_bubble_width (d), d),
+ 0.5f + (gdouble) bubble_get_height (bubble) -
+- EM2PIXELS (defaults_get_bubble_shadow_size (d), d));
++ EM2PIXELS (get_shadow_size (bubble), d));
+
+ // all horizontal grid lines
+ cairo_move_to (cr,
+- 0.5f + EM2PIXELS (defaults_get_bubble_shadow_size (d), d),
+- 0.5f + EM2PIXELS (defaults_get_bubble_shadow_size (d), d));
++ 0.5f + EM2PIXELS (get_shadow_size (bubble), d),
++ 0.5f + EM2PIXELS (get_shadow_size (bubble), d));
+ cairo_line_to (cr,
+- 0.5f + EM2PIXELS (defaults_get_bubble_shadow_size (d), d) +
++ 0.5f + EM2PIXELS (get_shadow_size (bubble), d) +
+ EM2PIXELS (defaults_get_bubble_width (d), d),
+- 0.5f + EM2PIXELS (defaults_get_bubble_shadow_size (d), d));
++ 0.5f + EM2PIXELS (get_shadow_size (bubble), d));
+ cairo_move_to (cr,
+- 0.5f + EM2PIXELS (defaults_get_bubble_shadow_size (d), d),
+- 0.5f + EM2PIXELS (defaults_get_bubble_shadow_size (d), d) +
++ 0.5f + EM2PIXELS (get_shadow_size (bubble), d),
++ 0.5f + EM2PIXELS (get_shadow_size (bubble), d) +
+ EM2PIXELS (defaults_get_margin_size (d), d));
+ cairo_line_to (cr,
+- 0.5f + EM2PIXELS (defaults_get_bubble_shadow_size (d), d) +
++ 0.5f + EM2PIXELS (get_shadow_size (bubble), d) +
+ EM2PIXELS (defaults_get_bubble_width (d), d),
+- 0.5f + EM2PIXELS (defaults_get_bubble_shadow_size (d), d) +
++ 0.5f + EM2PIXELS (get_shadow_size (bubble), d) +
+ EM2PIXELS (defaults_get_margin_size (d), d));
+ cairo_move_to (cr,
+- 0.5f + EM2PIXELS (defaults_get_bubble_shadow_size (d), d),
+- 0.5f + EM2PIXELS (defaults_get_bubble_shadow_size (d), d) +
++ 0.5f + EM2PIXELS (get_shadow_size (bubble), d),
++ 0.5f + EM2PIXELS (get_shadow_size (bubble), d) +
+ EM2PIXELS (defaults_get_margin_size (d), d) +
+ EM2PIXELS (defaults_get_icon_size (d), d));
+ cairo_line_to (cr,
+- 0.5f + EM2PIXELS (defaults_get_bubble_shadow_size (d), d) +
++ 0.5f + EM2PIXELS (get_shadow_size (bubble), d) +
+ EM2PIXELS (defaults_get_bubble_width (d), d),
+- 0.5f + EM2PIXELS (defaults_get_bubble_shadow_size (d), d) +
++ 0.5f + EM2PIXELS (get_shadow_size (bubble), d) +
+ EM2PIXELS (defaults_get_margin_size (d), d) +
+ EM2PIXELS (defaults_get_icon_size (d), d));
+ cairo_move_to (cr,
+- 0.5f + EM2PIXELS (defaults_get_bubble_shadow_size (d), d),
++ 0.5f + EM2PIXELS (get_shadow_size (bubble), d),
+ 0.5f + (gdouble) bubble_get_height (bubble) -
+- EM2PIXELS (defaults_get_bubble_shadow_size (d), d) -
++ EM2PIXELS (get_shadow_size (bubble), d) -
+ EM2PIXELS (defaults_get_margin_size (d), d));
+ cairo_line_to (cr,
+- 0.5f + EM2PIXELS (defaults_get_bubble_shadow_size (d), d) +
++ 0.5f + EM2PIXELS (get_shadow_size (bubble), d) +
+ EM2PIXELS (defaults_get_bubble_width (d), d),
+ 0.5f + (gdouble) bubble_get_height (bubble) -
+- EM2PIXELS (defaults_get_bubble_shadow_size (d), d) -
++ EM2PIXELS (get_shadow_size (bubble), d) -
+ EM2PIXELS (defaults_get_margin_size (d), d));
+ cairo_move_to (cr,
+- 0.5f + EM2PIXELS (defaults_get_bubble_shadow_size (d), d),
++ 0.5f + EM2PIXELS (get_shadow_size (bubble), d),
+ 0.5f + (gdouble) bubble_get_height (bubble) -
+- EM2PIXELS (defaults_get_bubble_shadow_size (d), d));
++ EM2PIXELS (get_shadow_size (bubble), d));
+ cairo_line_to (cr,
+- 0.5f + EM2PIXELS (defaults_get_bubble_shadow_size (d), d) +
++ 0.5f + EM2PIXELS (get_shadow_size (bubble), d) +
+ EM2PIXELS (defaults_get_bubble_width (d), d),
+ 0.5f + (gdouble) bubble_get_height (bubble) -
+- EM2PIXELS (defaults_get_bubble_shadow_size (d), d));
++ EM2PIXELS (get_shadow_size (bubble), d));
+
+ cairo_stroke (cr);
+ }
+@@ -625,20 +653,28 @@
+ raico_blur_t* blur = NULL;
+ gint width;
+ gint height;
++ gint scratch_shadow_size;
+
+ bubble_get_size (self, &width, &height);
+
+ // create temp. scratch surface for top-left shadow/background part
+ if (priv->composited)
++ {
++ scratch_shadow_size = EM2PIXELS (get_shadow_size (self), d);
+ scratch = cairo_image_surface_create (
+ CAIRO_FORMAT_ARGB32,
+- 3 * EM2PIXELS (defaults_get_bubble_shadow_size (d), d),
+- 3 * EM2PIXELS (defaults_get_bubble_shadow_size (d), d));
++ 3 * scratch_shadow_size,
++ 3 * scratch_shadow_size);
++ }
+ else
++ {
++ // We must have at least some width to this scratch surface.
++ scratch_shadow_size = 1;
+ scratch = cairo_image_surface_create (
+ CAIRO_FORMAT_RGB24,
+- 3 * EM2PIXELS (defaults_get_bubble_shadow_size (d), d),
+- 3 * EM2PIXELS (defaults_get_bubble_shadow_size (d), d));
++ 3 * scratch_shadow_size,
++ 3 * scratch_shadow_size);
++ }
+
+ g_return_if_fail (scratch);
+
+@@ -673,48 +709,72 @@
+ color_string = defaults_get_bubble_bg_color (d);
+ gdk_rgba_parse (&color, color_string);
+ g_free (color_string);
+-
++
++ // Apply color tweaks
++ NotifyHSVColor hsv_color;
++ gtk_rgb_to_hsv (color.red, color.green, color.blue,
++ &hsv_color.hue, &hsv_color.saturation, &hsv_color.value);
++ hsv_color.saturation *= (2.0f - hsv_color.saturation);
++ hsv_color.value = MIN (hsv_color.value, 0.4f);
++ gtk_hsv_to_rgb (hsv_color.hue, hsv_color.saturation, hsv_color.value,
++ &color.red, &color.green, &color.blue);
++
+ if (priv->composited)
+ {
+ _draw_shadow (
+ cr,
+ width,
+ height,
+- EM2PIXELS (defaults_get_bubble_shadow_size (d), d),
+- EM2PIXELS (defaults_get_bubble_corner_radius (d), d));
++ EM2PIXELS (get_shadow_size (self), d),
++ EM2PIXELS (get_corner_radius (self), d));
+ cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
+ draw_round_rect (
+ cr,
+ 1.0f,
+- EM2PIXELS (defaults_get_bubble_shadow_size (d), d),
+- EM2PIXELS (defaults_get_bubble_shadow_size (d), d),
+- EM2PIXELS (defaults_get_bubble_corner_radius (d), d),
++ EM2PIXELS (get_shadow_size (self), d),
++ EM2PIXELS (get_shadow_size (self), d),
++ EM2PIXELS (get_corner_radius (self), d),
+ EM2PIXELS (defaults_get_bubble_width (d), d),
+ (gdouble) bubble_get_height (self) -
+- 2.0f * EM2PIXELS (defaults_get_bubble_shadow_size (d), d));
++ 2.0f * EM2PIXELS (get_shadow_size (self), d));
+ cairo_fill (cr);
+ cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+- cairo_set_source_rgba (cr,
+- 0.75 * color.red,
+- 0.75 * color.green,
+- 0.75 * color.blue,
+- BUBBLE_BG_COLOR_A);
++ if (BUBBLE_AS_DESKTOP_BG) {
++ cairo_set_source_rgba (cr,
++ color.red,
++ color.green,
++ color.blue,
++ BUBBLE_BG_COLOR_A);
++ } else {
++ cairo_set_source_rgba (cr,
++ BUBBLE_BG_COLOR_R,
++ BUBBLE_BG_COLOR_G,
++ BUBBLE_BG_COLOR_B,
++ BUBBLE_BG_COLOR_A);
++ }
+ }
+ else
+- cairo_set_source_rgb (cr,
+- 0.75 * color.red,
+- 0.75 * color.green,
+- 0.75 * color.blue);
++ if (BUBBLE_AS_DESKTOP_BG) {
++ cairo_set_source_rgb (cr,
++ color.red,
++ color.green,
++ color.blue);
++ } else {
++ cairo_set_source_rgb (cr,
++ BUBBLE_BG_COLOR_R,
++ BUBBLE_BG_COLOR_G,
++ BUBBLE_BG_COLOR_B);
++ }
+
+ draw_round_rect (
+ cr,
+ 1.0f,
+- EM2PIXELS (defaults_get_bubble_shadow_size (d), d),
+- EM2PIXELS (defaults_get_bubble_shadow_size (d), d),
+- EM2PIXELS (defaults_get_bubble_corner_radius (d), d),
++ EM2PIXELS (get_shadow_size (self), d),
++ EM2PIXELS (get_shadow_size (self), d),
++ EM2PIXELS (get_corner_radius (self), d),
+ EM2PIXELS (defaults_get_bubble_width (d), d),
+ (gdouble) bubble_get_height (self) -
+- 2.0f * EM2PIXELS (defaults_get_bubble_shadow_size (d), d));
++ 2.0f * EM2PIXELS (get_shadow_size (self), d));
+ cairo_fill (cr);
+ cairo_destroy (cr);
+
+@@ -722,8 +782,8 @@
+ dummy = cairo_image_surface_create_for_data (
+ cairo_image_surface_get_data (scratch),
+ cairo_image_surface_get_format (scratch),
+- 3 * EM2PIXELS (defaults_get_bubble_shadow_size (d), d),
+- 3 * EM2PIXELS (defaults_get_bubble_shadow_size (d), d),
++ 3 * scratch_shadow_size,
++ 3 * scratch_shadow_size,
+ cairo_image_surface_get_stride (scratch));
+ clone = copy_surface (dummy);
+ cairo_surface_destroy (dummy);
+@@ -732,8 +792,8 @@
+ dummy = cairo_image_surface_create_for_data (
+ cairo_image_surface_get_data (clone),
+ cairo_image_surface_get_format (clone),
+- 2 * EM2PIXELS (defaults_get_bubble_shadow_size (d), d),
+- 2 * EM2PIXELS (defaults_get_bubble_shadow_size (d), d),
++ 2 * scratch_shadow_size,
++ 2 * scratch_shadow_size,
+ cairo_image_surface_get_stride (clone));
+ normal = copy_surface (dummy);
+ cairo_surface_destroy (dummy);
+@@ -748,8 +808,8 @@
+ dummy = cairo_image_surface_create_for_data (
+ cairo_image_surface_get_data (clone),
+ cairo_image_surface_get_format (clone),
+- 2 * EM2PIXELS (defaults_get_bubble_shadow_size (d), d),
+- 2 * EM2PIXELS (defaults_get_bubble_shadow_size (d), d),
++ 2 * scratch_shadow_size,
++ 2 * scratch_shadow_size,
+ cairo_image_surface_get_stride (clone));
+ blurred = copy_surface (dummy);
+ cairo_surface_destroy (dummy);
+@@ -1265,11 +1325,11 @@
+ draw_round_rect (
+ cr,
+ 1.0f,
+- EM2PIXELS (defaults_get_bubble_shadow_size (d), d) + 2.0f,
+- EM2PIXELS (defaults_get_bubble_shadow_size (d), d) + 2.0f,
+- EM2PIXELS (defaults_get_bubble_corner_radius (d), d) - 2.0f,
++ EM2PIXELS (get_shadow_size (self), d) + 2.0f,
++ EM2PIXELS (get_shadow_size (self), d) + 2.0f,
++ EM2PIXELS (get_corner_radius (self), d) - 2.0f,
+ EM2PIXELS (defaults_get_bubble_width (d), d) - 4.0f,
+- 2.0f * EM2PIXELS (defaults_get_bubble_shadow_size (d), d) - 2.0f);
++ 2.0f * EM2PIXELS (get_shadow_size (self), d) - 2.0f);
+ cairo_fill (cr);
+
+ cairo_set_source_rgb (cr, 0.0f, 0.0f, 0.0f);
+@@ -1279,12 +1339,12 @@
+ cairo_move_to (
+ cr,
+ EM2PIXELS (defaults_get_text_body_size (d), d) +
+- EM2PIXELS (defaults_get_bubble_shadow_size (d), d) +
++ EM2PIXELS (get_shadow_size (self), d) +
+ 2.0f,
+ EM2PIXELS (defaults_get_text_body_size (d), d) +
+- EM2PIXELS (defaults_get_bubble_shadow_size (d), d) +
++ EM2PIXELS (get_shadow_size (self), d) +
+ 2.0f +
+- ((2.0f * EM2PIXELS (defaults_get_bubble_shadow_size (d), d) - 2.0f) -
++ ((2.0f * EM2PIXELS (get_shadow_size (self), d) - 2.0f) -
+ EM2PIXELS (defaults_get_text_body_size (d), d)) / 2);
+
+ switch (bubble_get_urgency (self))
+@@ -1428,7 +1488,7 @@
+ gdouble alpha_blur)
+ {
+ Defaults* d = self->defaults;
+- gint shadow = EM2PIXELS (defaults_get_bubble_shadow_size (d), d);
++ gint shadow = EM2PIXELS (get_shadow_size (self), d);
+ gint icon_half = EM2PIXELS (defaults_get_icon_size (d), d) / 2;
+ gint width_half = EM2PIXELS (defaults_get_bubble_width (d), d) / 2;
+ gint height_half = EM2PIXELS (defaults_get_bubble_min_height (d), d) / 2;
+@@ -1605,15 +1665,23 @@
+ // sanity check
+ if (!window)
+ return;
+-
+- // set an 1x1 input-region to allow click-through
+- region = cairo_region_create_rectangle (&rect);
+- if (cairo_region_status (region) == CAIRO_STATUS_SUCCESS)
++
++ if (!BUBBLE_CLOSE_ON_CLICK)
+ {
+- gtk_widget_input_shape_combine_region (window, NULL);
+- gtk_widget_input_shape_combine_region (window, region);
++ // set an 1x1 input-region to allow click-through
++ region = cairo_region_create_rectangle (&rect);
++ if (cairo_region_status (region) == CAIRO_STATUS_SUCCESS)
++ {
++ gtk_widget_input_shape_combine_region (window, NULL);
++ gtk_widget_input_shape_combine_region (window, region);
++ }
++ cairo_region_destroy (region);
++ }
++ else
++ {
++ GdkWindow *window_ = gtk_widget_get_window (window);
++ gdk_window_set_events (window_, gdk_window_get_events (window_) | GDK_BUTTON_PRESS);
+ }
+- cairo_region_destroy (region);
+ }
+
+ static void
+@@ -1703,7 +1771,7 @@
+ cairo_paint (cr);
+ cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+
+- if (priv->prevent_fade || !priv->composited)
++ if (!BUBBLE_PREVENT_FADE || priv->prevent_fade || !priv->composited)
+ {
+ // render drop-shadow and bubble-background
+ _render_background (bubble, cr, 1.0f, 0.0f);
+@@ -1730,12 +1798,35 @@
+
+ _set_bg_blur (window,
+ TRUE,
+- EM2PIXELS (defaults_get_bubble_shadow_size (d), d));
++ EM2PIXELS (get_shadow_size (bubble), d));
+
+ return TRUE;
+ }
+
+ static gboolean
++button_press_event_handler (GtkWidget* window G_GNUC_UNUSED,
++ GdkEventButton* event,
++ Bubble* bubble)
++{
++ BubblePrivate *priv = GET_PRIVATE (bubble);
++
++ if (priv->mouse_over && event->button == 1)
++ {
++ bubble_hide (bubble);
++
++ dbus_send_close_signal (bubble_get_sender (bubble),
++ bubble_get_id (bubble),
++ 1);
++
++ g_signal_emit (bubble, g_bubble_signals[TIMED_OUT], 0);
++
++ return TRUE;
++ }
++
++ return FALSE;
++}
++
++static gboolean
+ redraw_handler (Bubble* bubble)
+ {
+ GtkWindow* window;
+@@ -1759,7 +1850,7 @@
+
+ if (priv->alpha == NULL)
+ {
+- if (priv->distance < 1.0f && !priv->prevent_fade)
++ if (priv->distance < 1.0f && !priv->prevent_fade && BUBBLE_PREVENT_FADE)
+ {
+ gtk_window_set_opacity (window,
+ WINDOW_MIN_OPACITY +
+@@ -1914,7 +2005,7 @@
+
+ // mark mouse-pointer having left bubble and proximity-area
+ // after inital show-up of bubble
+- if (priv->prevent_fade && priv->distance > 1.0f)
++ if (BUBBLE_PREVENT_FADE && priv->prevent_fade && priv->distance > 1.0f)
+ priv->prevent_fade = FALSE;
+ }
+
+@@ -2210,6 +2301,14 @@
+ G_CALLBACK (expose_handler),
+ this);
+
++ if (BUBBLE_CLOSE_ON_CLICK)
++ {
++ g_signal_connect (window,
++ "button-press-event",
++ G_CALLBACK (button_press_event_handler),
++ this);
++ }
++
+ // "clear" input-mask, set title/icon/attributes
+ gtk_widget_set_app_paintable (window, TRUE);
+ gtk_window_set_title (GTK_WINDOW (window), "notify-osd");
+@@ -2689,7 +2788,7 @@
+
+ priv = GET_PRIVATE (self);
+
+- if (priv->prevent_fade)
++ if (BUBBLE_PREVENT_FADE && priv->prevent_fade)
+ return FALSE;
+
+ return priv->mouse_over;
+@@ -2793,7 +2892,7 @@
+
+ // check if mouse-pointer is over bubble (and proximity-area) initially
+ pointer_update (self);
+- if (priv->distance <= 1.0f)
++ if (priv->distance <= 1.0f || !BUBBLE_PREVENT_FADE)
+ priv->prevent_fade = TRUE;
+ else
+ priv->prevent_fade = FALSE;
+@@ -3351,6 +3450,8 @@
+ gint old_bubble_height = 0;
+ gint new_bubble_width = 0;
+ gint new_bubble_height = 0;
++ gint x;
++ gint y;
+ Defaults* d;
+ BubblePrivate* priv;
+
+@@ -3379,7 +3480,7 @@
+
+ new_bubble_width =
+ EM2PIXELS (defaults_get_bubble_width (d), d) +
+- 2 * EM2PIXELS (defaults_get_bubble_shadow_size (d), d);
++ 2 * EM2PIXELS (get_shadow_size (self), d);
+
+ switch (priv->layout)
+ {
+@@ -3397,7 +3498,7 @@
+
+ new_bubble_height =
+ EM2PIXELS (defaults_get_bubble_min_height (d), d) +
+- 2.0f * EM2PIXELS (defaults_get_bubble_shadow_size (d), d);
++ 2.0f * EM2PIXELS (get_shadow_size (self), d);
+ break;
+
+ case LAYOUT_ICON_TITLE_BODY:
+@@ -3446,7 +3547,7 @@
+ new_bubble_height =
+ EM2PIXELS (defaults_get_icon_size (d), d) +
+ 2.0f * EM2PIXELS (defaults_get_margin_size (d), d) +
+- 2.0f * EM2PIXELS (defaults_get_bubble_shadow_size (d), d);
++ 2.0f * EM2PIXELS (get_shadow_size (self), d);
+ }
+ else
+ {
+@@ -3454,7 +3555,7 @@
+ priv->body_height +
+ priv->title_height +
+ 2.0f * EM2PIXELS (defaults_get_margin_size (d), d) +
+- 2.0f * EM2PIXELS (defaults_get_bubble_shadow_size (d), d);
++ 2.0f * EM2PIXELS (get_shadow_size (self), d);
+ }
+ }
+ }
+@@ -3501,7 +3602,7 @@
+ priv->body_height +
+ priv->title_height +
+ 2.0f * EM2PIXELS (defaults_get_margin_size (d), d) +
+- 2.0f * EM2PIXELS (defaults_get_bubble_shadow_size (d), d);
++ 2.0f * EM2PIXELS (get_shadow_size (self), d);
+ }
+ }
+ break;
+@@ -3518,7 +3619,7 @@
+
+ new_bubble_height = priv->title_height +
+ 2.0f * EM2PIXELS (defaults_get_margin_size (d), d) +
+- 2.0f * EM2PIXELS (defaults_get_bubble_shadow_size (d), d);
++ 2.0f * EM2PIXELS (get_shadow_size (self), d);
+ }
+ break;
+
+@@ -3545,6 +3646,13 @@
+ _refresh_body (self);
+
+ update_shape (self);
++
++ if (defaults_get_gravity (d) == GRAVITY_SOUTH_EAST)
++ {
++ bubble_get_position(self, &x, &y);
++ bubble_move(self, x, y - (new_bubble_height - old_bubble_height));
++ }
++
+ }
+
+ void
+diff -Naur old_src/defaults.c src/defaults.c
+--- old_src/defaults.c 2012-03-23 10:49:59.000000000 -0400
++++ src/defaults.c 2012-11-10 08:44:21.000000000 -0500
+@@ -110,40 +110,41 @@
+ /* these values are interpreted as em-measurements and do comply to the
+ * visual guide for jaunty-notifications */
+ #define DEFAULT_DESKTOP_BOTTOM_GAP 6.0f
+-#define DEFAULT_BUBBLE_WIDTH 24.0f
+-#define DEFAULT_BUBBLE_MIN_HEIGHT 5.0f
+-#define DEFAULT_BUBBLE_MAX_HEIGHT 12.2f
+-#define DEFAULT_BUBBLE_VERT_GAP 0.5f
+-#define DEFAULT_BUBBLE_HORZ_GAP 0.5f
++float DEFAULT_BUBBLE_WIDTH = 24.0f;
++float DEFAULT_BUBBLE_MIN_HEIGHT = 5.0f;
++float DEFAULT_BUBBLE_MAX_HEIGHT = 12.2f;
++float DEFAULT_BUBBLE_VERT_GAP = 0.5f;
++float DEFAULT_BUBBLE_HORZ_GAP = 0.5f;
+ #define DEFAULT_BUBBLE_SHADOW_SIZE 0.7f
+ #define DEFAULT_BUBBLE_SHADOW_COLOR "#000000"
+ #define DEFAULT_BUBBLE_BG_COLOR "#131313"
+ #define DEFAULT_BUBBLE_BG_OPACITY "#cc"
+ #define DEFAULT_BUBBLE_HOVER_OPACITY "#66"
+-#define DEFAULT_BUBBLE_CORNER_RADIUS 0.375f
++float DEFAULT_BUBBLE_CORNER_RADIUS = 0.375f;
+ #define DEFAULT_CONTENT_SHADOW_SIZE 0.125f
+ #define DEFAULT_CONTENT_SHADOW_COLOR "#000000"
+-#define DEFAULT_MARGIN_SIZE 1.0f
+-#define DEFAULT_ICON_SIZE 3.0f
+-#define DEFAULT_GAUGE_SIZE 0.625f
+-#define DEFAULT_GAUGE_OUTLINE_WIDTH 0.125f
++float DEFAULT_MARGIN_SIZE = 1.0f;
++float DEFAULT_ICON_SIZE = 3.0f;
++float DEFAULT_GAUGE_SIZE = 0.625f;
++#define DEFAULT_GAUGE_OUTLINE_WIDTH 0.125f
+ #define DEFAULT_TEXT_FONT_FACE "Sans"
+ #define DEFAULT_TEXT_TITLE_COLOR "#ffffff"
+-#define DEFAULT_TEXT_TITLE_WEIGHT TEXT_WEIGHT_BOLD
+-#define DEFAULT_TEXT_TITLE_SIZE 1.0f
++short DEFAULT_TEXT_TITLE_WEIGHT = TEXT_WEIGHT_BOLD;
++float DEFAULT_TEXT_TITLE_SIZE = 1.0f;
+ #define DEFAULT_TEXT_BODY_COLOR "#eaeaea"
+-#define DEFAULT_TEXT_BODY_WEIGHT TEXT_WEIGHT_NORMAL
+-#define DEFAULT_TEXT_BODY_SIZE 0.9f
++short DEFAULT_TEXT_BODY_WEIGHT = TEXT_WEIGHT_NORMAL;
++float DEFAULT_TEXT_BODY_SIZE = 0.9f;
+ #define DEFAULT_PIXELS_PER_EM 10.0f
+ #define DEFAULT_SYSTEM_FONT_SIZE 10.0f
+ #define DEFAULT_SCREEN_DPI 96.0f
+ #define DEFAULT_GRAVITY GRAVITY_NORTH_EAST
++short SLOT_ALLOCATION = SLOT_ALLOCATION_DYNAMIC;
+
+ /* these values are interpreted as milliseconds-measurements and do comply to
+ * the visual guide for jaunty-notifications */
+-#define DEFAULT_FADE_IN_TIMEOUT 250
+-#define DEFAULT_FADE_OUT_TIMEOUT 1000
+-#define DEFAULT_ON_SCREEN_TIMEOUT 10000
++float DEFAULT_FADE_IN_TIMEOUT = 250;
++float DEFAULT_FADE_OUT_TIMEOUT = 1000;
++float DEFAULT_ON_SCREEN_TIMEOUT = 10000;
+
+ /* notify-osd settings */
+ #define NOTIFY_OSD_SCHEMA "com.canonical.notify-osd"
+@@ -242,7 +243,7 @@
+ gravity = g_settings_get_int (self->nosd_settings, GSETTINGS_GRAVITY_KEY);
+
+ // protect against out-of-bounds values for gravity
+- if (gravity != GRAVITY_EAST && gravity != GRAVITY_NORTH_EAST)
++ if (gravity != GRAVITY_EAST && gravity != GRAVITY_NORTH_EAST && gravity != GRAVITY_WEST && gravity != GRAVITY_NORTH_WEST && gravity != GRAVITY_SOUTH_EAST && gravity != GRAVITY_SOUTH_WEST)
+ gravity = DEFAULT_GRAVITY;
+
+ // update stored DPI-value
+@@ -546,7 +547,7 @@
+ self);
+
+ // use fixed slot-allocation for async. and sync. bubbles
+- self->slot_allocation = SLOT_ALLOCATION_FIXED;
++ self->slot_allocation = SLOT_ALLOCATION;
+ }
+
+ static void
+@@ -1546,7 +1547,7 @@
+ "gravity",
+ "Positional hint for placing bubbles",
+ 0,
+- 2,
++ 6,
+ DEFAULT_GRAVITY,
+ G_PARAM_CONSTRUCT |
+ G_PARAM_READWRITE |
+@@ -1762,11 +1763,11 @@
+ }
+
+ gdouble
+-defaults_get_bubble_shadow_size (Defaults* self)
++defaults_get_bubble_shadow_size (Defaults* self, gboolean is_composited)
+ {
+ gdouble bubble_shadow_size;
+
+- if (!self || !IS_DEFAULTS (self))
++ if (!self || !IS_DEFAULTS (self) || !is_composited)
+ return 0.0f;
+
+ g_object_get (self, "bubble-shadow-size", &bubble_shadow_size, NULL);
+@@ -1841,11 +1842,11 @@
+ }
+
+ gdouble
+-defaults_get_bubble_corner_radius (Defaults* self)
++defaults_get_bubble_corner_radius (Defaults* self, gboolean is_composited)
+ {
+ gdouble bubble_corner_radius;
+
+- if (!self || !IS_DEFAULTS (self))
++ if (!self || !IS_DEFAULTS (self) || !is_composited)
+ return 0.0f;
+
+ g_object_get (self,
+@@ -2205,11 +2206,10 @@
+ }
+
+ void
+-defaults_get_top_corner (Defaults *self, gint *x, gint *y)
++defaults_get_top_corner (Defaults *self, GdkScreen **screen, gint *x, gint *y)
+ {
+ GdkRectangle rect;
+ GdkRectangle panel_rect = {0, 0, 0, 0};
+- GdkScreen* screen = NULL;
+ GdkWindow* active_window = NULL;
+ GdkWindow* panel_window = NULL;
+ gint mx;
+@@ -2219,21 +2219,23 @@
+ gint aw_monitor;
+ gboolean has_panel_window = FALSE;
+ gboolean follow_focus = defaults_multihead_does_focus_follow (self);
++ gboolean is_composited = FALSE;
+
+ g_return_if_fail (self != NULL && IS_DEFAULTS (self));
+
+ gdk_display_get_pointer (gdk_display_get_default (),
+- &screen,
++ screen,
+ &mx,
+ &my,
+ NULL);
+
++ is_composited = gdk_screen_is_composited (*screen);
+ panel_window = get_panel_window ();
+
+ if (panel_window != NULL)
+ {
+ gdk_window_get_frame_extents (panel_window, &panel_rect);
+- panel_monitor = gdk_screen_get_monitor_at_window (screen,
++ panel_monitor = gdk_screen_get_monitor_at_window (*screen,
+ panel_window);
+ monitor = panel_monitor;
+ g_debug ("found panel (%d,%d) - %dx%d on monitor %d",
+@@ -2249,12 +2251,12 @@
+ if (follow_focus)
+ {
+ g_debug ("multi_head_focus_follow mode");
+- monitor = gdk_screen_get_monitor_at_point (screen, mx, my);
+- active_window = gdk_screen_get_active_window (screen);
++ monitor = gdk_screen_get_monitor_at_point (*screen, mx, my);
++ active_window = gdk_screen_get_active_window (*screen);
+ if (active_window != NULL)
+ {
+ aw_monitor = gdk_screen_get_monitor_at_window (
+- screen,
++ *screen,
+ active_window);
+
+ if (monitor != aw_monitor)
+@@ -2270,7 +2272,7 @@
+ }
+ }
+
+- gdk_screen_get_monitor_geometry (screen, monitor, &rect);
++ gdk_screen_get_monitor_geometry (*screen, monitor, &rect);
+ g_debug ("selecting monitor %d at (%d,%d) - %dx%d",
+ monitor,
+ rect.x,
+@@ -2299,7 +2301,7 @@
+
+ *y = rect.y;
+ *y += EM2PIXELS (defaults_get_bubble_vert_gap (self), self)
+- - EM2PIXELS (defaults_get_bubble_shadow_size (self), self);
++ - EM2PIXELS (defaults_get_bubble_shadow_size (self, is_composited), self);
+
+ /* correct potential offset in multi-monitor setups with two (or more)
+ * monitors side by side, all having different vertical resolutions and
+@@ -2307,9 +2309,9 @@
+ * the top edge of the monitor with the lowest vertical resolution,
+ * LP: #716458 */
+ GdkRectangle cur_geo = {0, 0, 0, 0};
+- int num_monitors = gdk_screen_get_n_monitors (screen);
+- int screen_width = gdk_screen_get_width (screen);
+- int screen_height = gdk_screen_get_height (screen);
++ int num_monitors = gdk_screen_get_n_monitors (*screen);
++ int screen_width = gdk_screen_get_width (*screen);
++ int screen_height = gdk_screen_get_height (*screen);
+
+ if (!follow_focus && num_monitors > 1)
+ {
+@@ -2319,10 +2321,10 @@
+ {
+ int right_most_monitor = 0;
+
+- right_most_monitor = gdk_screen_get_monitor_at_point (screen,
++ right_most_monitor = gdk_screen_get_monitor_at_point (*screen,
+ screen_width,
+ screen_height / 2);
+- gdk_screen_get_monitor_geometry (screen,
++ gdk_screen_get_monitor_geometry (*screen,
+ right_most_monitor,
+ &cur_geo);
+ if (cur_geo.y != 0)
+@@ -2332,10 +2334,10 @@
+ {
+ int left_most_monitor = 0;
+
+- left_most_monitor = gdk_screen_get_monitor_at_point (screen,
++ left_most_monitor = gdk_screen_get_monitor_at_point (*screen,
+ 0,
+ screen_height / 2);
+- gdk_screen_get_monitor_geometry (screen,
++ gdk_screen_get_monitor_geometry (*screen,
+ left_most_monitor,
+ &cur_geo);
+ if (cur_geo.y != 0)
+@@ -2348,12 +2350,12 @@
+ if (gtk_widget_get_default_direction () == GTK_TEXT_DIR_LTR)
+ {
+ *x = rect.x + rect.width;
+- *x -= EM2PIXELS (defaults_get_bubble_shadow_size (self), self)
++ *x -= EM2PIXELS (defaults_get_bubble_shadow_size (self, is_composited), self)
+ + EM2PIXELS (defaults_get_bubble_horz_gap (self), self)
+ + EM2PIXELS (defaults_get_bubble_width (self), self);
+ } else {
+ *x = rect.x
+- - EM2PIXELS (defaults_get_bubble_shadow_size (self), self)
++ - EM2PIXELS (defaults_get_bubble_shadow_size (self, is_composited), self)
+ + EM2PIXELS (defaults_get_bubble_horz_gap (self), self);
+ }
+
+diff -Naur old_src/defaults.h src/defaults.h
+--- old_src/defaults.h 2012-03-23 10:49:59.000000000 -0400
++++ src/defaults.h 2012-11-10 08:44:21.000000000 -0500
+@@ -31,6 +31,7 @@
+
+ #include <glib-object.h>
+ #include <gio/gio.h>
++#include <gdk/gdk.h>
+
+ G_BEGIN_DECLS
+
+@@ -58,7 +59,13 @@
+ {
+ GRAVITY_NONE = 0,
+ GRAVITY_NORTH_EAST, // top-right of screen
+- GRAVITY_EAST // vertically centered at right of screen
++ GRAVITY_EAST, // vertically centered at right of screen
++ GRAVITY_SOUTH_EAST, // bottom-right of screen
++ GRAVITY_SOUTH_WEST, // bottom-left of screen
++ GRAVITY_WEST, // vertically centered at left of screen
++ GRAVITY_NORTH_WEST // top-left of screen
++
++
+ } Gravity;
+
+ typedef enum
+@@ -177,7 +184,7 @@
+ defaults_get_bubble_horz_gap (Defaults* self);
+
+ gdouble
+-defaults_get_bubble_shadow_size (Defaults* self);
++defaults_get_bubble_shadow_size (Defaults* self, gboolean is_composited);
+
+ gchar*
+ defaults_get_bubble_shadow_color (Defaults* self);
+@@ -192,7 +199,7 @@
+ defaults_get_bubble_hover_opacity (Defaults* self);
+
+ gdouble
+-defaults_get_bubble_corner_radius (Defaults* self);
++defaults_get_bubble_corner_radius (Defaults* self, gboolean is_composited);
+
+ gdouble
+ defaults_get_content_shadow_size (Defaults* self);
+@@ -258,7 +265,7 @@
+ defaults_refresh_screen_dimension_properties (Defaults *self);
+
+ void
+-defaults_get_top_corner (Defaults *self, gint *x, gint *y);
++defaults_get_top_corner (Defaults *self, GdkScreen **screen, gint *x, gint *y);
+
+ Gravity
+ defaults_get_gravity (Defaults *self);
+diff -Naur old_src/display.c src/display.c
+--- old_src/display.c 2012-03-23 10:49:59.000000000 -0400
++++ src/display.c 2012-11-10 08:44:21.000000000 -0500
+@@ -60,12 +60,13 @@
+ static gboolean
+ stack_is_at_top_corner (Stack *self, Bubble *bubble)
+ {
++ GdkScreen* screen;
+ gint x, y1, y2;
+
+ g_assert (IS_STACK (self));
+ g_assert (IS_BUBBLE (bubble));
+
+- defaults_get_top_corner (self->defaults, &x, &y1);
++ defaults_get_top_corner (self->defaults, &screen, &x, &y1);
+ bubble_get_position (bubble, &x, &y2);
+
+ return y1 == y2;
+@@ -74,11 +75,12 @@
+ static void
+ stack_display_position_sync_bubble (Stack *self, Bubble *bubble)
+ {
+- Defaults* d = self->defaults;
+- gint y = 0;
+- gint x = 0;
++ Defaults* d = self->defaults;
++ GdkScreen* screen;
++ gint y = 0;
++ gint x = 0;
+
+- defaults_get_top_corner (d, &x, &y);
++ defaults_get_top_corner (d, &screen, &x, &y);
+
+ // TODO: with multi-head, in focus follow mode, there may be enough
+ // space left on the top monitor
+@@ -279,10 +281,11 @@
+ static void
+ stack_layout (Stack* self)
+ {
+- Bubble* bubble = NULL;
+- Defaults* d;
+- gint y = 0;
+- gint x = 0;
++ Bubble* bubble = NULL;
++ Defaults* d;
++ GdkScreen* screen;
++ gint y = 0;
++ gint x = 0;
+
+ g_return_if_fail (self != NULL);
+
+@@ -310,10 +313,12 @@
+ return;
+ }
+
++ /*
+ bubble_set_timeout (bubble,
+ defaults_get_on_screen_timeout (self->defaults));
++ */
+
+- defaults_get_top_corner (self->defaults, &x, &y);
++ defaults_get_top_corner (self->defaults, &screen, &x, &y);
+
+ d = self->defaults;
+
+diff -Naur old_src/main.c src/main.c
+--- old_src/main.c 2012-03-23 10:49:59.000000000 -0400
++++ src/main.c 2012-11-10 08:44:21.000000000 -0500
+@@ -25,6 +25,10 @@
+ ** with this program. If not, see <http://www.gnu.org/licenses/>.
+ **
+ *******************************************************************************/
++#define _GNU_SOURCE /* getline */
++#include <unistd.h> /* getuid */
++#include <pwd.h> /* getpwuid */
++#include <sys/types.h>
+
+ #include <string.h>
+ #include <stdlib.h>
+@@ -39,6 +43,213 @@
+
+ #define ICONS_DIR (DATADIR G_DIR_SEPARATOR_S "notify-osd" G_DIR_SEPARATOR_S "icons")
+
++/* begin hack */
++extern float TEXT_TITLE_COLOR_R;
++extern float TEXT_TITLE_COLOR_G;
++extern float TEXT_TITLE_COLOR_B;
++extern float TEXT_TITLE_COLOR_A;
++
++extern float TEXT_BODY_COLOR_R;
++extern float TEXT_BODY_COLOR_G;
++extern float TEXT_BODY_COLOR_B;
++extern float TEXT_BODY_COLOR_A;
++
++extern float TEXT_SHADOW_COLOR_A;
++
++extern float BUBBLE_BG_COLOR_R;
++extern float BUBBLE_BG_COLOR_G;
++extern float BUBBLE_BG_COLOR_B;
++extern float BUBBLE_BG_COLOR_A;
++
++extern float DEFAULT_TEXT_TITLE_SIZE;
++extern float DEFAULT_TEXT_BODY_SIZE;
++extern float DEFAULT_ON_SCREEN_TIMEOUT;
++
++extern short DEFAULT_TEXT_TITLE_WEIGHT;
++extern short DEFAULT_TEXT_BODY_WEIGHT;
++extern short SLOT_ALLOCATION;
++
++extern float DEFAULT_MARGIN_SIZE;
++extern float DEFAULT_BUBBLE_CORNER_RADIUS;
++extern float DEFAULT_BUBBLE_WIDTH;
++extern float DEFAULT_BUBBLE_VERT_GAP;
++extern float DEFAULT_BUBBLE_HORZ_GAP;
++extern float DEFAULT_ICON_SIZE;
++extern float DEFAULT_GAUGE_SIZE;
++
++extern gboolean BUBBLE_PREVENT_FADE;
++extern gboolean BUBBLE_CLOSE_ON_CLICK;
++extern gboolean BUBBLE_AS_DESKTOP_BG;
++
++void parse_color(unsigned int c, float* r, float* g, float* b)
++{
++ *b = (float)(c & 0xFF) / (float)(0xFF);
++ c >>= 8;
++ *g = (float)(c & 0xFF) / (float)(0xFF);
++ c >>= 8;
++ *r = (float)(c & 0xFF) / (float)(0xFF);
++}
++
++
++void load_settings(void)
++{
++ char file[PATH_MAX];
++ uid_t uid = getuid();
++ const char* settings_file_name = ".notify-osd";
++
++ struct passwd* pw = getpwuid(uid);
++ if (!pw) {
++ fprintf(stderr,
++ "failed to retrieve home directory. using default settings.\n");
++ return;
++ }
++ /* $HOME/.notify-osd */
++ snprintf(file, sizeof(file), "%s%s%s", pw->pw_dir,
++ G_DIR_SEPARATOR_S, settings_file_name);
++
++ FILE* fp = fopen(file, "r");
++
++ if (!fp) {
++ fprintf(stderr, "could not open '%s'. using default settings.\n", file);
++ return;
++
++ }
++ printf("reading settings from '%s'\n", file);
++
++ char* buf = NULL;
++ size_t size = 0;
++ char key[32], value[32];
++ float fvalue;
++ unsigned int ivalue;
++
++ while(getline(&buf, &size, fp) != -1) {
++ if (sscanf(buf, "%31s = %31s", key, value) != 2)
++ continue;
++ if (!strcmp(key, "bubble-background-color") &&
++ sscanf(value, "%x", &ivalue)) {
++
++ parse_color(ivalue, &BUBBLE_BG_COLOR_R, &BUBBLE_BG_COLOR_G,
++ &BUBBLE_BG_COLOR_B);
++
++
++ } else if (!strcmp(key, "bubble-background-opacity") &&
++ sscanf(value, "%f", &fvalue)) {
++
++ BUBBLE_BG_COLOR_A = fvalue*0.01;
++
++ } else if (!strcmp(key, "text-title-color") &&
++ sscanf(value, "%x", &ivalue) ) {
++
++ parse_color(ivalue, &TEXT_TITLE_COLOR_R, &TEXT_TITLE_COLOR_G,
++ &TEXT_TITLE_COLOR_B);
++
++ } else if (!strcmp(key, "text-title-opacity") &&
++ sscanf(value, "%f", &fvalue) ) {
++
++ TEXT_TITLE_COLOR_A = fvalue*0.01;
++
++ } else if (!strcmp(key, "text-body-color") &&
++ sscanf(value, "%x", &ivalue) ) {
++
++ parse_color(ivalue, &TEXT_BODY_COLOR_R, &TEXT_BODY_COLOR_G,
++ &TEXT_BODY_COLOR_B);
++
++ } else if (!strcmp(key, "text-body-opacity") &&
++ sscanf(value, "%f", &fvalue) ) {
++
++ TEXT_BODY_COLOR_A = fvalue*0.01;
++
++ } else if (!strcmp(key, "text-shadow-opacity") &&
++ sscanf(value, "%f", &fvalue) ) {
++
++ TEXT_SHADOW_COLOR_A = fvalue*0.01;
++
++ } else if (!strcmp(key, "text-title-size") &&
++ sscanf(value, "%f", &fvalue) ) {
++ DEFAULT_TEXT_TITLE_SIZE = fvalue*0.01;
++
++ } else if (!strcmp(key, "text-body-size") &&
++ sscanf(value, "%f", &fvalue) ) {
++ DEFAULT_TEXT_BODY_SIZE = fvalue*0.01;
++
++ } else if (!strcmp(key, "bubble-expire-timeout") &&
++ sscanf(value, "%f", &fvalue) ) {
++ DEFAULT_ON_SCREEN_TIMEOUT = fvalue*1000;
++
++ } else if (!strcmp(key, "text-title-weight")) {
++ if (!strcmp(value, "bold")) {
++ DEFAULT_TEXT_TITLE_WEIGHT = 700;
++ } else if (!strcmp(value, "normal")) {
++ DEFAULT_TEXT_TITLE_WEIGHT = 400;
++ } else if (!strcmp(value, "light")) {
++ DEFAULT_TEXT_TITLE_WEIGHT = 300;
++ }
++ } else if (!strcmp(key, "text-body-weight")) {
++ if (!strcmp(value, "bold")) {
++ DEFAULT_TEXT_BODY_WEIGHT = 700;
++ } else if (!strcmp(value, "normal")) {
++ DEFAULT_TEXT_BODY_WEIGHT = 400;
++ } else if (!strcmp(value, "light")) {
++ DEFAULT_TEXT_BODY_WEIGHT = 300;
++ }
++ } else if (!strcmp(key, "text-margin-size") &&
++ sscanf(value, "%f", &fvalue) ) {
++ DEFAULT_MARGIN_SIZE = fvalue*0.1;
++
++ } else if (!strcmp(key, "bubble-corner-radius") &&
++ sscanf(value, "%f", &fvalue) ) {
++ DEFAULT_BUBBLE_CORNER_RADIUS = fvalue*0.01;
++
++ } else if (!strcmp(key, "bubble-width") &&
++ sscanf(value, "%f", &fvalue) ) {
++ DEFAULT_BUBBLE_WIDTH = fvalue*0.1;
++
++ } else if (!strcmp(key, "slot-allocation")) {
++ if (!strcmp(value, "dynamic")) {
++ SLOT_ALLOCATION = SLOT_ALLOCATION_DYNAMIC;
++ } else if (!strcmp(value, "fixed")) {
++ SLOT_ALLOCATION = SLOT_ALLOCATION_FIXED;
++ }
++ } else if (!strcmp(key, "bubble-vertical-gap") &&
++ sscanf(value, "%f", &fvalue) ) {
++ DEFAULT_BUBBLE_VERT_GAP = fvalue*0.1;
++
++ } else if (!strcmp(key, "bubble-horizontal-gap") &&
++ sscanf(value, "%f", &fvalue) ) {
++ DEFAULT_BUBBLE_HORZ_GAP = fvalue*0.1;
++
++ } else if (!strcmp(key, "bubble-icon-size") &&
++ sscanf(value, "%f", &fvalue) ) {
++ DEFAULT_ICON_SIZE = fvalue*0.1;
++
++ } else if (!strcmp(key, "bubble-gauge-size") &&
++ sscanf(value, "%f", &fvalue) ) {
++ DEFAULT_GAUGE_SIZE = fvalue*0.1;
++
++ } else if (!strcmp(key, "bubble-prevent-fade") &&
++ sscanf(value, "%d", &ivalue) ) {
++ BUBBLE_PREVENT_FADE = ivalue;
++
++ } else if (!strcmp(key, "bubble-close-on-click") &&
++ sscanf(value, "%d", &ivalue) ) {
++ BUBBLE_CLOSE_ON_CLICK = ivalue;
++
++ } else if (!strcmp(key, "bubble-as-desktop-bg") &&
++ sscanf(value, "%d", &ivalue) ) {
++ BUBBLE_AS_DESKTOP_BG = ivalue;
++
++ }
++
++ }
++
++ if (buf) {
++ free(buf);
++ }
++
++ fclose(fp);
++}
++/* end hack */
++
+ int
+ main (int argc,
+ char** argv)
+@@ -58,6 +269,8 @@
+ gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(),
+ ICONS_DIR);
+
++ load_settings();
++
+ defaults = defaults_new ();
+ observer = observer_new ();
+ stack = stack_new (defaults, observer);
+diff -Naur old_src/send-test-notification.sh src/send-test-notification.sh
+--- old_src/send-test-notification.sh 1969-12-31 19:00:00.000000000 -0500
++++ src/send-test-notification.sh 2012-11-10 08:44:09.000000000 -0500
+@@ -0,0 +1,63 @@
++#!/bin/sh
++
++notify-send "Take note" "The next example will test the icon-only layout-case" -i dialog-info
++sleep 2
++notify-send "Eject" -i notification-device-eject -h string:x-canonical-private-icon-only:
++sleep 2
++notify-send "WiFi signal found" -i notification-network-wireless-medium
++sleep 2
++notify-send "WiFi signal lost" -i notification-network-wireless-disconnected
++sleep 2
++notify-send "Volume" -i notification-audio-volume-medium -h int:value:75 -h string:x-canonical-private-synchronous:
++sleep 2
++notify-send "Volume" -i notification-audio-volume-low -h int:value:30 -h string:x-canonical-private-synchronous:
++sleep 2
++notify-send "Brightness" -i notification-display-brightness-high -h int:value:101 -h string:x-canonical-private-synchronous:
++sleep 2
++notify-send "Brightness" -i notification-keyboard-brightness-medium -h int:value:45 -h string:x-canonical-private-synchronous:
++sleep 2
++notify-send "Testing markup" "Some <b>bold</b>, <u>underlined</u>, <i>italic</i> text. Note, you should not see any marked up text."
++sleep 2
++notify-send "Jamshed Kakar" "Hey, what about this restaurant? http://www.blafasel.org
++
++Would you go from your place by train or should I pick you up from work? What do you think?"
++sleep 2
++notify-send "English bubble" "The quick brown fox jumps over the lazy dog." -i network
++sleep 2
++notify-send "Bubble from Germany" "Polyfon zwitschernd aßen Mäxchens Vögel Rüben, Joghurt und Quark." -i gnome-system
++sleep 2
++notify-send "Very russian" "Съешь ещё этих мягких французских булок, да выпей чаю." -i dialog-info
++sleep 2
++notify-send "More from Germany" "Oje, Qualm verwölkt Dix zig Farbtriptychons." -i gnome-globe
++sleep 2
++notify-send "Filter the world 1/3" "<a href=\"http://www.ubuntu.com/\">Ubuntu</a>
++Don't rock the boat
++Kick him while he&apos;s down
++\"Film spectators are quiet vampires.\"
++Peace &amp; Love
++War & Peace
++Law &#38; Order
++Love &#x26; War
++7 > 3
++7 &gt; 3"
++sleep 2
++notify-send "Filter the world 2/3" "7 &#62; 3
++7 &#x3e; 3
++14 < 42
++14 &lt; 42
++14 &#60; 42
++14 &#x3c; 42
++><
++<>
++< this is not a tag >
++<i>Not italic</i>"
++sleep 2
++notify-send "Filter the world 3/3" "<b>So broken</i>
++<img src=\"foobar.png\" />Nothing to see
++<u>Test</u>
++<b>Bold</b>
++<span>Span</span>
++<s>E-flat</s>
++<sub>Sandwich</sub>
++<small>Fry</small>
++<tt>Testing tag</tt>"
+diff -Naur old_src/stack.c src/stack.c
+--- old_src/stack.c 2012-03-23 10:49:59.000000000 -0400
++++ src/stack.c 2012-11-10 08:44:21.000000000 -0500
+@@ -44,6 +44,7 @@
+ G_DEFINE_TYPE (Stack, stack, G_TYPE_OBJECT);
+
+ #define FORCED_SHUTDOWN_THRESHOLD 500
++#define NOTIFY_EXPIRES_DEFAULT -1
+
+ /* fwd declaration */
+ void close_handler (GObject* n, Stack* stack);
+@@ -646,6 +647,15 @@
+ if (body)
+ bubble_set_message_body (bubble, body);
+
++ if (timeout == NOTIFY_EXPIRES_DEFAULT) {
++ bubble_set_timeout (bubble,
++ defaults_get_on_screen_timeout (self->defaults));
++ }
++ else {
++ bubble_set_timeout (bubble, timeout);
++ }
++
++
+ if (new_bubble && bubble_is_append_allowed(bubble)) {
+ app_bubble = find_bubble_for_append(self, bubble);
+
+@@ -885,6 +895,9 @@
+ gint* x,
+ gint* y)
+ {
++ GdkScreen* screen = NULL;
++ gboolean is_composited = FALSE;
++
+ // sanity checks
+ if (!x && !y)
+ return;
+@@ -904,7 +917,9 @@
+ }
+
+ // initialize x and y
+- defaults_get_top_corner (self->defaults, x, y);
++ defaults_get_top_corner (self->defaults, &screen, x, y);
++
++ is_composited = gdk_screen_is_composited (screen);
+
+ // differentiate returned top-left corner for top and bottom slot
+ // depending on the placement
+@@ -920,12 +935,12 @@
+ *y += defaults_get_desktop_height (d) / 2 -
+ EM2PIXELS (defaults_get_bubble_vert_gap (d) / 2.0f, d) -
+ bubble_height +
+- EM2PIXELS (defaults_get_bubble_shadow_size (d), d);
++ EM2PIXELS (defaults_get_bubble_shadow_size (d, is_composited), d);
+ // the position for the async. bubble
+ else if (slot == SLOT_BOTTOM)
+ *y += defaults_get_desktop_height (d) / 2 +
+ EM2PIXELS (defaults_get_bubble_vert_gap (d) / 2.0f, d) -
+- EM2PIXELS (defaults_get_bubble_shadow_size (d), d);
++ EM2PIXELS (defaults_get_bubble_shadow_size (d, is_composited), d);
+ break;
+
+ case GRAVITY_NORTH_EAST:
+@@ -944,15 +959,61 @@
+ case SLOT_ALLOCATION_FIXED:
+ *y += EM2PIXELS (defaults_get_icon_size (d), d) +
+ 2 * EM2PIXELS (defaults_get_margin_size (d), d) +
+- EM2PIXELS (defaults_get_bubble_vert_gap (d), d); /* +
+- 2 * EM2PIXELS (defaults_get_bubble_shadow_size (d), d);*/
++ EM2PIXELS (defaults_get_bubble_vert_gap (d), d) + 2;
++ break;
++
++ case SLOT_ALLOCATION_DYNAMIC:
++ g_assert (stack_is_slot_vacant (self, SLOT_TOP) == OCCUPIED);
++ *y += bubble_get_height (self->slots[SLOT_TOP]) +
++ EM2PIXELS (defaults_get_bubble_vert_gap (d), d) -
++ 2 * EM2PIXELS (defaults_get_bubble_shadow_size (d, is_composited), d);
++ break;
++
++ default:
++ break;
++ }
++
++ }
++ break;
++
++ case GRAVITY_WEST:
++ d = self->defaults;
++
++ *x = defaults_get_desktop_left (d);
++
++ // the position for the sync./feedback bubble
++ if (slot == SLOT_TOP)
++ *y += defaults_get_desktop_height (d) / 2 -
++ EM2PIXELS (defaults_get_bubble_vert_gap (d) / 2.0f, d) -
++ bubble_height +
++ EM2PIXELS (defaults_get_bubble_shadow_size (d, is_composited), d);
++ // the position for the async. bubble
++ else if (slot == SLOT_BOTTOM)
++ *y += defaults_get_desktop_height (d) / 2 +
++ EM2PIXELS (defaults_get_bubble_vert_gap (d) / 2.0f, d) -
++ EM2PIXELS (defaults_get_bubble_shadow_size (d, is_composited), d);
++ break;
++
++ case GRAVITY_NORTH_WEST:
++ d = self->defaults;
++
++ *x = defaults_get_desktop_left (d);
++
++ if (slot == SLOT_BOTTOM)
++ {
++ switch (defaults_get_slot_allocation (d))
++ {
++ case SLOT_ALLOCATION_FIXED:
++ *y += EM2PIXELS (defaults_get_icon_size (d), d) +
++ 2 * EM2PIXELS (defaults_get_margin_size (d), d) +
++ EM2PIXELS (defaults_get_bubble_vert_gap (d), d) + 2;
+ break;
+
+ case SLOT_ALLOCATION_DYNAMIC:
+ g_assert (stack_is_slot_vacant (self, SLOT_TOP) == OCCUPIED);
+ *y += bubble_get_height (self->slots[SLOT_TOP]) +
+ EM2PIXELS (defaults_get_bubble_vert_gap (d), d) -
+- 2 * EM2PIXELS (defaults_get_bubble_shadow_size (d), d);
++ 2 * EM2PIXELS (defaults_get_bubble_shadow_size (d, is_composited), d);
+ break;
+
+ default:
+@@ -961,6 +1022,114 @@
+
+ }
+ break;
++
++ case GRAVITY_SOUTH_EAST:
++ d = self->defaults;
++
++ switch (defaults_get_slot_allocation (d))
++ {
++ case SLOT_ALLOCATION_FIXED:
++ if (slot == SLOT_TOP)
++ {
++ *y += defaults_get_desktop_height (d) -
++ 2 * EM2PIXELS (defaults_get_bubble_vert_gap (d), d) -
++ bubble_height +
++ 2 * EM2PIXELS (defaults_get_bubble_shadow_size (d, is_composited), d);
++
++ }
++
++ if (slot == SLOT_BOTTOM)
++ {
++ *y += defaults_get_desktop_height (d) -
++ bubble_height -
++ EM2PIXELS (defaults_get_icon_size (d), d) -
++ 2 * EM2PIXELS (defaults_get_margin_size (d), d) -
++ 3 * EM2PIXELS (defaults_get_bubble_vert_gap (d), d) +
++ 2 * EM2PIXELS (defaults_get_bubble_shadow_size (d, is_composited), d) - 2;
++
++ }
++ break;
++
++ case SLOT_ALLOCATION_DYNAMIC:
++ if (slot == SLOT_TOP)
++ {
++ *y += defaults_get_desktop_height (d) -
++ 2 * EM2PIXELS (defaults_get_bubble_vert_gap (d), d) -
++ bubble_height +
++ 2 * EM2PIXELS (defaults_get_bubble_shadow_size (d, is_composited), d);
++ }
++
++ if (slot == SLOT_BOTTOM)
++ {
++ g_assert (stack_is_slot_vacant (self, SLOT_TOP) == OCCUPIED);
++ *y += defaults_get_desktop_height (d) -
++ 3 * EM2PIXELS (defaults_get_bubble_vert_gap (d), d) -
++ bubble_height +
++ 4 * EM2PIXELS (defaults_get_bubble_shadow_size (d, is_composited), d) -
++ bubble_get_height (self->slots[SLOT_TOP]);
++ }
++ break;
++
++ default:
++ break;
++ }
++
++ break;
++
++ case GRAVITY_SOUTH_WEST:
++ d = self->defaults;
++
++ *x = defaults_get_desktop_left (d);
++
++ switch (defaults_get_slot_allocation (d))
++ {
++ case SLOT_ALLOCATION_FIXED:
++ if (slot == SLOT_TOP)
++ {
++ *y += defaults_get_desktop_height (d) -
++ 2 * EM2PIXELS (defaults_get_bubble_vert_gap (d), d) -
++ bubble_height +
++ 2 * EM2PIXELS (defaults_get_bubble_shadow_size (d, is_composited), d);
++
++ }
++
++ if (slot == SLOT_BOTTOM)
++ {
++ *y += defaults_get_desktop_height (d) -
++ bubble_height -
++ EM2PIXELS (defaults_get_icon_size (d), d) -
++ 2 * EM2PIXELS (defaults_get_margin_size (d), d) -
++ 3 * EM2PIXELS (defaults_get_bubble_vert_gap (d), d) +
++ 2 * EM2PIXELS (defaults_get_bubble_shadow_size (d, is_composited), d) - 2;
++
++ }
++ break;
++
++ case SLOT_ALLOCATION_DYNAMIC:
++ if (slot == SLOT_TOP)
++ {
++ *y += defaults_get_desktop_height (d) -
++ 2 * EM2PIXELS (defaults_get_bubble_vert_gap (d), d) -
++ bubble_height +
++ 2 * EM2PIXELS (defaults_get_bubble_shadow_size (d, is_composited), d);
++ }
++
++ if (slot == SLOT_BOTTOM)
++ {
++ g_assert (stack_is_slot_vacant (self, SLOT_TOP) == OCCUPIED);
++ *y += defaults_get_desktop_height (d) -
++ 3 * EM2PIXELS (defaults_get_bubble_vert_gap (d), d) -
++ bubble_height +
++ 4 * EM2PIXELS (defaults_get_bubble_shadow_size (d, is_composited), d) -
++ bubble_get_height (self->slots[SLOT_TOP]);
++ }
++ break;
++
++ default:
++ break;
++ }
++
++ break;
+
+ default:
+ g_warning ("Unhandled placement!\n");