1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
3
* On-screen-display (OSD) window for gnome-settings-daemon's plugins
5
* Copyright (C) 2006-2007 William Jon McCann <mccann@jhu.edu>
6
* Copyright (C) 2009 Novell, Inc
9
* William Jon McCann <mccann@jhu.edu>
10
* Federico Mena-Quintero <federico@novell.com>
12
* This program is free software; you can redistribute it and/or
13
* modify it under the terms of the GNU Lesser General Public
14
* License as published by the Free Software Foundation; either
15
* version 2, or (at your option) any later version.
17
* This program is distributed in the hope that it will be
18
* useful, but WITHOUT ANY WARRANTY; without even the implied
19
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
20
* PURPOSE. See the GNU Lesser General Public License for more
23
* You should have received a copy of the GNU Lesser General
24
* Public License along with this program; if not, write to the
25
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
26
* Boston, MA 02111-1307, USA.
36
#include <glib/gi18n.h>
38
#include <gdk-pixbuf/gdk-pixbuf.h>
40
#include "gsd-osd-window.h"
42
#define DIALOG_TIMEOUT 2000 /* dialog timeout in ms */
43
#define DIALOG_FADE_TIMEOUT 1500 /* timeout before fade starts */
44
#define FADE_TIMEOUT 10 /* timeout in ms between each frame of the fade */
48
#define GSD_OSD_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_OSD_WINDOW, GsdOsdWindowPrivate))
50
struct GsdOsdWindowPrivate
52
guint is_composited : 1;
53
guint hide_timeout_id;
54
guint fade_timeout_id;
55
double fade_out_alpha;
63
static guint signals[LAST_SIGNAL] = { 0 };
65
G_DEFINE_TYPE (GsdOsdWindow, gsd_osd_window, GTK_TYPE_WINDOW)
68
fade_timeout (GsdOsdWindow *window)
70
if (window->priv->fade_out_alpha <= 0.0) {
71
gtk_widget_hide (GTK_WIDGET (window));
73
/* Reset it for the next time */
74
window->priv->fade_out_alpha = 1.0;
75
window->priv->fade_timeout_id = 0;
80
GtkWidget *win = GTK_WIDGET (window);
81
GtkAllocation allocation;
83
window->priv->fade_out_alpha -= 0.10;
87
gtk_widget_get_allocation (win, &allocation);
88
rect.width = allocation.width;
89
rect.height = allocation.height;
91
gtk_widget_realize (win);
92
gdk_window_invalidate_rect (gtk_widget_get_window (win), &rect, FALSE);
99
hide_timeout (GsdOsdWindow *window)
101
if (window->priv->is_composited) {
102
window->priv->hide_timeout_id = 0;
103
window->priv->fade_timeout_id = g_timeout_add (FADE_TIMEOUT,
104
(GSourceFunc) fade_timeout,
107
gtk_widget_hide (GTK_WIDGET (window));
114
remove_hide_timeout (GsdOsdWindow *window)
116
if (window->priv->hide_timeout_id != 0) {
117
g_source_remove (window->priv->hide_timeout_id);
118
window->priv->hide_timeout_id = 0;
121
if (window->priv->fade_timeout_id != 0) {
122
g_source_remove (window->priv->fade_timeout_id);
123
window->priv->fade_timeout_id = 0;
124
window->priv->fade_out_alpha = 1.0;
129
add_hide_timeout (GsdOsdWindow *window)
133
if (window->priv->is_composited) {
134
timeout = DIALOG_FADE_TIMEOUT;
136
timeout = DIALOG_TIMEOUT;
138
window->priv->hide_timeout_id = g_timeout_add (timeout,
139
(GSourceFunc) hide_timeout,
144
gsd_osd_window_draw_rounded_rectangle (cairo_t* cr,
148
gdouble corner_radius,
152
gdouble radius = corner_radius / aspect;
154
cairo_move_to (cr, x + radius, y);
163
-90.0f * G_PI / 180.0f,
164
0.0f * G_PI / 180.0f);
167
y + height - radius);
172
0.0f * G_PI / 180.0f,
173
90.0f * G_PI / 180.0f);
181
90.0f * G_PI / 180.0f,
182
180.0f * G_PI / 180.0f);
190
180.0f * G_PI / 180.0f,
191
270.0f * G_PI / 180.0f);
192
cairo_close_path (cr);
196
rgb_to_hls (gdouble *r,
244
s = (max - min) / (max + min);
246
s = (max - min) / (2 - max - min);
250
h = (green - blue) / delta;
251
else if (green == max)
252
h = 2 + (blue - red) / delta;
253
else if (blue == max)
254
h = 4 + (red - green) / delta;
267
hls_to_rgb (gdouble *h,
280
if (lightness <= 0.5)
281
m2 = lightness * (1 + saturation);
283
m2 = lightness + saturation - lightness * saturation;
284
m1 = 2 * lightness - m2;
301
r = m1 + (m2 - m1) * hue / 60;
305
r = m1 + (m2 - m1) * (240 - hue) / 60;
316
g = m1 + (m2 - m1) * hue / 60;
320
g = m1 + (m2 - m1) * (240 - hue) / 60;
331
b = m1 + (m2 - m1) * hue / 60;
335
b = m1 + (m2 - m1) * (240 - hue) / 60;
346
gsd_osd_window_color_shade (GdkRGBA *a,
357
rgb_to_hls (&red, &green, &blue);
362
else if (green < 0.0)
371
hls_to_rgb (&red, &green, &blue);
380
gsd_osd_window_color_reverse (GdkRGBA *a)
393
gtk_rgb_to_hsv (red, green, blue, &h, &s, &v);
401
gtk_hsv_to_rgb (h, s, v, &red, &green, &blue);
408
/* This is our draw handler when the window is in a compositing manager.
409
* We draw everything by hand, using Cairo, so that we can have a nice
410
* transparent/rounded look.
413
draw_when_composited (GtkWidget *widget, cairo_t *orig_cr)
415
GsdOsdWindow *window;
417
cairo_surface_t *surface;
420
GtkStyleContext *context;
423
window = GSD_OSD_WINDOW (widget);
425
context = gtk_widget_get_style_context (widget);
426
cairo_set_operator (orig_cr, CAIRO_OPERATOR_SOURCE);
427
gtk_window_get_size (GTK_WINDOW (widget), &width, &height);
429
surface = cairo_surface_create_similar (cairo_get_target (orig_cr),
430
CAIRO_CONTENT_COLOR_ALPHA,
434
if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS) {
438
cr = cairo_create (surface);
439
if (cairo_status (cr) != CAIRO_STATUS_SUCCESS) {
442
cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.0);
443
cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
447
gsd_osd_window_draw_rounded_rectangle (cr, 1.0, 0.5, 0.5, height / 10, width-1, height-1);
448
gtk_style_context_get_background_color (context, GTK_STATE_NORMAL, &acolor);
449
gsd_osd_window_color_reverse (&acolor);
450
acolor.alpha = BG_ALPHA;
451
gdk_cairo_set_source_rgba (cr, &acolor);
452
cairo_fill_preserve (cr);
454
/* FIXME use &style->text_aa[GTK_STATE_NORMAL] instead? */
455
gtk_style_context_get_color (context, GTK_STATE_NORMAL, &acolor);
456
gsd_osd_window_color_reverse (&acolor);
457
acolor.alpha = BG_ALPHA / 2;
458
gdk_cairo_set_source_rgba (cr, &acolor);
459
cairo_set_line_width (cr, 1);
462
g_signal_emit (window, signals[DRAW_WHEN_COMPOSITED], 0, cr);
466
/* Make sure we have a transparent background */
467
cairo_rectangle (orig_cr, 0, 0, width, height);
468
cairo_set_source_rgba (orig_cr, 0.0, 0.0, 0.0, 0.0);
469
cairo_fill (orig_cr);
471
cairo_set_source_surface (orig_cr, surface, 0, 0);
472
cairo_paint_with_alpha (orig_cr, window->priv->fade_out_alpha);
475
if (surface != NULL) {
476
cairo_surface_destroy (surface);
480
/* This is our draw handler when the window is *not* in a compositing manager.
481
* We just draw a rectangular frame by hand. We do this with hardcoded drawing code,
482
* instead of GtkFrame, to avoid changing the window's internal widget hierarchy: in
483
* either case (composited or non-composited), callers can assume that this works
484
* identically to a GtkWindow without any intermediate widgetry.
487
draw_when_not_composited (GtkWidget *widget, cairo_t *cr)
489
GtkStyleContext *context;
493
width = gtk_widget_get_allocated_width (widget);
494
height = gtk_widget_get_allocated_width (widget);
495
context = gtk_widget_get_style_context (widget);
497
gtk_style_context_set_state (context, GTK_STATE_FLAG_ACTIVE);
498
gtk_render_frame (context,
507
gsd_osd_window_draw (GtkWidget *widget,
510
GsdOsdWindow *window;
513
window = GSD_OSD_WINDOW (widget);
515
if (window->priv->is_composited)
516
draw_when_composited (widget, cr);
518
draw_when_not_composited (widget, cr);
520
child = gtk_bin_get_child (GTK_BIN (window));
522
gtk_container_propagate_draw (GTK_CONTAINER (window), child, cr);
528
gsd_osd_window_real_show (GtkWidget *widget)
530
GsdOsdWindow *window;
532
if (GTK_WIDGET_CLASS (gsd_osd_window_parent_class)->show) {
533
GTK_WIDGET_CLASS (gsd_osd_window_parent_class)->show (widget);
536
window = GSD_OSD_WINDOW (widget);
537
remove_hide_timeout (window);
538
add_hide_timeout (window);
542
gsd_osd_window_real_hide (GtkWidget *widget)
544
GsdOsdWindow *window;
546
if (GTK_WIDGET_CLASS (gsd_osd_window_parent_class)->hide) {
547
GTK_WIDGET_CLASS (gsd_osd_window_parent_class)->hide (widget);
550
window = GSD_OSD_WINDOW (widget);
551
remove_hide_timeout (window);
555
gsd_osd_window_real_realize (GtkWidget *widget)
557
cairo_region_t *region;
561
screen = gtk_widget_get_screen (widget);
562
visual = gdk_screen_get_rgba_visual (screen);
563
if (visual == NULL) {
564
visual = gdk_screen_get_system_visual (screen);
567
gtk_widget_set_visual (widget, visual);
569
if (GTK_WIDGET_CLASS (gsd_osd_window_parent_class)->realize) {
570
GTK_WIDGET_CLASS (gsd_osd_window_parent_class)->realize (widget);
573
/* make the whole window ignore events */
574
region = cairo_region_create ();
575
gtk_widget_input_shape_combine_region (widget, region);
576
cairo_region_destroy (region);
580
gsd_osd_window_style_updated (GtkWidget *widget)
582
GtkStyleContext *context;
585
GTK_WIDGET_CLASS (gsd_osd_window_parent_class)->style_updated (widget);
587
/* We set our border width to 12 (per the GNOME standard), plus the
588
* thickness of the frame that we draw in our draw handler. This will
589
* make our child be 12 pixels away from the frame.
592
context = gtk_widget_get_style_context (widget);
593
gtk_style_context_get_padding (context, GTK_STATE_NORMAL, &padding);
594
gtk_container_set_border_width (GTK_CONTAINER (widget), 12 + MAX (padding.left, padding.top));
598
gsd_osd_window_get_preferred_width (GtkWidget *widget,
602
GtkStyleContext *context;
605
GTK_WIDGET_CLASS (gsd_osd_window_parent_class)->get_preferred_width (widget, minimum, natural);
607
/* See the comment in gsd_osd_window_style_updated() for why we add the padding here */
609
context = gtk_widget_get_style_context (widget);
610
gtk_style_context_get_padding (context, GTK_STATE_NORMAL, &padding);
612
*minimum += padding.left;
613
*natural += padding.left;
617
gsd_osd_window_get_preferred_height (GtkWidget *widget,
621
GtkStyleContext *context;
624
GTK_WIDGET_CLASS (gsd_osd_window_parent_class)->get_preferred_height (widget, minimum, natural);
626
/* See the comment in gsd_osd_window_style_updated() for why we add the padding here */
628
context = gtk_widget_get_style_context (widget);
629
gtk_style_context_get_padding (context, GTK_STATE_NORMAL, &padding);
631
*minimum += padding.top;
632
*natural += padding.top;
636
gsd_osd_window_constructor (GType type,
637
guint n_construct_properties,
638
GObjectConstructParam *construct_params)
642
object = G_OBJECT_CLASS (gsd_osd_window_parent_class)->constructor (type, n_construct_properties, construct_params);
644
g_object_set (object,
645
"type", GTK_WINDOW_POPUP,
646
"type-hint", GDK_WINDOW_TYPE_HINT_NOTIFICATION,
647
"skip-taskbar-hint", TRUE,
648
"skip-pager-hint", TRUE,
649
"focus-on-map", FALSE,
656
gsd_osd_window_class_init (GsdOsdWindowClass *klass)
658
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
659
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
661
gobject_class->constructor = gsd_osd_window_constructor;
663
widget_class->show = gsd_osd_window_real_show;
664
widget_class->hide = gsd_osd_window_real_hide;
665
widget_class->realize = gsd_osd_window_real_realize;
666
widget_class->style_updated = gsd_osd_window_style_updated;
667
widget_class->get_preferred_width = gsd_osd_window_get_preferred_width;
668
widget_class->get_preferred_height = gsd_osd_window_get_preferred_height;
669
widget_class->draw = gsd_osd_window_draw;
671
signals[DRAW_WHEN_COMPOSITED] = g_signal_new ("draw-when-composited",
672
G_TYPE_FROM_CLASS (gobject_class),
674
G_STRUCT_OFFSET (GsdOsdWindowClass, draw_when_composited),
676
g_cclosure_marshal_VOID__POINTER,
680
g_type_class_add_private (klass, sizeof (GsdOsdWindowPrivate));
684
* gsd_osd_window_is_composited:
685
* @window: a #GsdOsdWindow
687
* Return value: whether the window was created on a composited screen.
690
gsd_osd_window_is_composited (GsdOsdWindow *window)
692
return window->priv->is_composited;
696
* gsd_osd_window_is_valid:
697
* @window: a #GsdOsdWindow
699
* Return value: TRUE if the @window's idea of being composited matches whether
700
* its current screen is actually composited.
703
gsd_osd_window_is_valid (GsdOsdWindow *window)
705
GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (window));
706
return gdk_screen_is_composited (screen) == window->priv->is_composited;
710
gsd_osd_window_init (GsdOsdWindow *window)
714
window->priv = GSD_OSD_WINDOW_GET_PRIVATE (window);
716
screen = gtk_widget_get_screen (GTK_WIDGET (window));
718
window->priv->is_composited = gdk_screen_is_composited (screen);
720
if (window->priv->is_composited) {
721
gdouble scalew, scaleh, scale;
724
gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
725
gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
727
/* assume 130x130 on a 640x480 display and scale from there */
728
scalew = gdk_screen_get_width (screen) / 640.0;
729
scaleh = gdk_screen_get_height (screen) / 480.0;
730
scale = MIN (scalew, scaleh);
731
size = 130 * MAX (1, scale);
733
gtk_window_set_default_size (GTK_WINDOW (window), size, size);
735
window->priv->fade_out_alpha = 1.0;
737
gtk_container_set_border_width (GTK_CONTAINER (window), 12);
742
gsd_osd_window_new (void)
744
return g_object_new (GSD_TYPE_OSD_WINDOW, NULL);
748
* gsd_osd_window_update_and_hide:
749
* @window: a #GsdOsdWindow
751
* Queues the @window for immediate drawing, and queues a timer to hide the window.
754
gsd_osd_window_update_and_hide (GsdOsdWindow *window)
756
remove_hide_timeout (window);
757
add_hide_timeout (window);
759
if (window->priv->is_composited) {
760
gtk_widget_queue_draw (GTK_WIDGET (window));