1 /* Calf DSP Library
2 * Light emitting diode-like control.
3 *
4 * Copyright (C) 2008 Krzysztof Foltman
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General
17 * Public License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301 USA
20 */
21 #include <calf/ctl_led.h>
22 #include <math.h>
23 #include <stdint.h>
24 #include <stdlib.h>
25 #include <calf/drawingutils.h>
26
27 GtkWidget *
calf_led_new()28 calf_led_new()
29 {
30 GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_LED, NULL ));
31 return widget;
32 }
33
34 static gboolean
calf_led_expose(GtkWidget * widget,GdkEventExpose * event)35 calf_led_expose (GtkWidget *widget, GdkEventExpose *event)
36 {
37 g_assert(CALF_IS_LED(widget));
38
39 CalfLed *self = CALF_LED(widget);
40 GdkWindow *window = widget->window;
41 cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(window));
42
43 int width = widget->allocation.width;
44 int height = widget->allocation.height;
45 int x = widget->allocation.x;
46 int y = widget->allocation.y;
47 int ox = widget->style->xthickness;
48 int oy = widget->style->ythickness;
49 int sx = width - ox * 2;
50 int sy = height - oy * 2;
51 int xc = x + width / 2;
52 int yc = y + height / 2;
53 float r, g, b;
54
55 if( self->cache_surface == NULL ) {
56 // looks like its either first call or the widget has been resized.
57 // create the cache_surface.
58 self->cache_surface = cairo_image_surface_create( CAIRO_FORMAT_ARGB32, width, height );
59 cairo_t *cache_cr = cairo_create( self->cache_surface );
60
61 float radius, bevel;
62 get_bg_color(widget, NULL, &r, &g, &b);
63 gtk_widget_style_get(widget, "border-radius", &radius, "bevel", &bevel, NULL);
64 create_rectangle(cache_cr, 0, 0, width, height, radius);
65 cairo_set_source_rgb(cache_cr, r, g, b);
66 cairo_fill(cache_cr);
67 draw_bevel(cache_cr, 0, 0, width, height, radius, bevel);
68 cairo_rectangle(cache_cr, ox, oy, sx, sy);
69 cairo_set_source_rgb (cache_cr, 0, 0, 0);
70 cairo_fill(cache_cr);
71
72 cairo_destroy( cache_cr );
73 }
74
75 cairo_set_source_surface( c, self->cache_surface, x, y );
76 cairo_paint( c );
77
78 ox += x;
79 oy += y;
80
81 cairo_pattern_t *pt = cairo_pattern_create_radial(xc, yc, 0, xc, yc, sx > sy ? sx/2 : sy/2);
82
83 float value = self->led_value;
84
85 if(self->led_mode >= 4 && self->led_mode <= 5 && value > 1.f) {
86 value = 1.f;
87 }
88 switch (self->led_mode) {
89 default:
90 case 0:
91 // blue-on/off
92 cairo_pattern_add_color_stop_rgb(pt, 0.0, value > 0.f ? 0.2 : 0.0, value > 0.f ? 1.0 : 0.25, value > 0.f ? 1.0 : 0.35);
93 cairo_pattern_add_color_stop_rgb(pt, 0.5, value > 0.f ? 0.1 : 0.0, value > 0.f ? 0.6 : 0.15, value > 0.f ? 0.75 : 0.2);
94 cairo_pattern_add_color_stop_rgb(pt, 1.0, 0.0, value > 0.f ? 0.3 : 0.1, value > 0.f ? 0.5 : 0.1);
95 break;
96 case 1:
97 // red-on/off
98 cairo_pattern_add_color_stop_rgb(pt, 0.0, value > 0.f ? 1.0 : 0.35, value > 0.f ? 0.5 : 0.0, value > 0.f ? 0.2 : 0.0);
99 cairo_pattern_add_color_stop_rgb(pt, 0.5, value > 0.f ? 0.80 : 0.2, value > 0.f ? 0.2 : 0.0, value > 0.f ? 0.1 : 0.0);
100 cairo_pattern_add_color_stop_rgb(pt, 1.0, value > 0.f ? 0.65 : 0.1, value > 0.f ? 0.1 : 0.0, 0.0);
101 break;
102 case 2:
103 case 4:
104 // blue-dynamic (limited)
105 cairo_pattern_add_color_stop_rgb(pt, 0.0, value * 0.2, value * 0.75 + 0.25, value * 0.65 + 0.35);
106 cairo_pattern_add_color_stop_rgb(pt, 0.5, value * 0.1, value * 0.45 + 0.15, value * 0.55 + 0.2);
107 cairo_pattern_add_color_stop_rgb(pt, 1.0, 0.0, value * 0.2 + 0.1, value * 0.4 + 0.1);
108 break;
109 case 3:
110 case 5:
111 // red-dynamic (limited)
112 cairo_pattern_add_color_stop_rgb(pt, 0.0, value * 0.65 + 0.35, value * 0.5, value * 0.2);
113 cairo_pattern_add_color_stop_rgb(pt, 0.5, value * 0.6 + 0.2, value * 0.2, value * 0.1);
114 cairo_pattern_add_color_stop_rgb(pt, 1.0, value * 0.66 + 0.1, value * 0.1, 0.0);
115 break;
116 case 6:
117 // blue-dynamic with red peak at >= 1.f
118 if(value < 1.0) {
119 cairo_pattern_add_color_stop_rgb(pt, 0.0, value * 0.2, value * 0.75 + 0.25, value * 0.65 + 0.35);
120 cairo_pattern_add_color_stop_rgb(pt, 0.5, value * 0.1, value * 0.45 + 0.15, value * 0.55 + 0.2);
121 cairo_pattern_add_color_stop_rgb(pt, 1.0, 0.0, value * 0.2 + 0.1, value * 0.4 + 0.1);
122 } else {
123 cairo_pattern_add_color_stop_rgb(pt, 0.0, 1.0, 0.5, 0.2);
124 cairo_pattern_add_color_stop_rgb(pt, 0.5, 0.80, 0.2, 0.1);
125 cairo_pattern_add_color_stop_rgb(pt, 1.0, 0.66, 0.1, 0.0);
126 }
127 break;
128 case 7:
129 // off @ 0.0, blue < 1.0, red @ 1.0
130 if(value < 1.f and value > 0.f) {
131 // blue
132 cairo_pattern_add_color_stop_rgb(pt, 0.0, 0.2, 1.0, 1.0);
133 cairo_pattern_add_color_stop_rgb(pt, 0.5, 0.1, 0.6, 0.75);
134 cairo_pattern_add_color_stop_rgb(pt, 1.0, 0.0, 0.3, 0.5);
135 } else if(value == 0.f) {
136 // off
137 cairo_pattern_add_color_stop_rgb(pt, 0.0, 0.0, 0.25, 0.35);
138 cairo_pattern_add_color_stop_rgb(pt, 0.5, 0.0, 0.15, 0.2);
139 cairo_pattern_add_color_stop_rgb(pt, 1.0, 0.0, 0.1, 0.1);
140 } else {
141 // red
142 cairo_pattern_add_color_stop_rgb(pt, 0.0, 1.0, 0.5, 0.2);
143 cairo_pattern_add_color_stop_rgb(pt, 0.5, 0.80, 0.2, 0.1);
144 cairo_pattern_add_color_stop_rgb(pt, 1.0, 0.66, 0.1, 0.0);
145 }
146 break;
147 }
148
149 cairo_rectangle(c, ox + 1, oy + 1, sx - 2, sy - 2);
150 cairo_set_source (c, pt);
151 cairo_fill_preserve(c);
152
153 float glass;
154 gtk_widget_style_get(widget, "glass", &glass, NULL);
155
156 if (glass > 0.f) {
157 pt = cairo_pattern_create_linear (ox, oy, ox, oy + sy);
158 cairo_pattern_add_color_stop_rgba (pt, 0, 1, 1, 1, 0.4 * glass);
159 cairo_pattern_add_color_stop_rgba (pt, 0.4, 1, 1, 1, 0.1 * glass);
160 cairo_pattern_add_color_stop_rgba (pt, 0.401, 0, 0, 0, 0.0);
161 cairo_pattern_add_color_stop_rgba (pt, 1, 0, 0, 0, 0.2 * glass);
162 cairo_set_source (c, pt);
163 cairo_fill(c);
164 cairo_pattern_destroy(pt);
165 }
166
167 cairo_destroy(c);
168
169 return TRUE;
170 }
171
172 static void
calf_led_size_request(GtkWidget * widget,GtkRequisition * requisition)173 calf_led_size_request (GtkWidget *widget,
174 GtkRequisition *requisition)
175 {
176 g_assert(CALF_IS_LED(widget));
177 CalfLed *self = CALF_LED(widget);
178 requisition->width = self->size ? 24 : 19;
179 requisition->height = self->size ? 18 : 14;
180 }
181
182 static void
calf_led_size_allocate(GtkWidget * widget,GtkAllocation * allocation)183 calf_led_size_allocate (GtkWidget *widget,
184 GtkAllocation *allocation)
185 {
186 g_assert(CALF_IS_LED(widget));
187 CalfLed *led = CALF_LED(widget);
188
189 GtkWidgetClass *parent_class = (GtkWidgetClass *) g_type_class_peek_parent( CALF_LED_GET_CLASS( led ) );
190 parent_class->size_allocate( widget, allocation );
191
192 if( led->cache_surface )
193 cairo_surface_destroy( led->cache_surface );
194 led->cache_surface = NULL;
195 }
196
197 static gboolean
calf_led_button_press(GtkWidget * widget,GdkEventButton * event)198 calf_led_button_press (GtkWidget *widget, GdkEventButton *event)
199 {
200 return TRUE;
201 }
202
203 static void
calf_led_class_init(CalfLedClass * klass)204 calf_led_class_init (CalfLedClass *klass)
205 {
206 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
207 widget_class->expose_event = calf_led_expose;
208 widget_class->size_request = calf_led_size_request;
209 widget_class->size_allocate = calf_led_size_allocate;
210 widget_class->button_press_event = calf_led_button_press;
211 gtk_widget_class_install_style_property(
212 widget_class, g_param_spec_float("border-radius", "Border Radius", "Generate round edges",
213 0, 24, 4, GParamFlags(G_PARAM_READWRITE)));
214 gtk_widget_class_install_style_property(
215 widget_class, g_param_spec_float("bevel", "Bevel", "Bevel the object",
216 -2, 2, 0.2, GParamFlags(G_PARAM_READWRITE)));
217 gtk_widget_class_install_style_property(
218 widget_class, g_param_spec_float("glass", "Glass", "Glass effect on top",
219 0, 1, 1, GParamFlags(G_PARAM_READWRITE)));
220 }
221
222 static void
calf_led_init(CalfLed * self)223 calf_led_init (CalfLed *self)
224 {
225 GtkWidget *widget = GTK_WIDGET(self);
226 // GtkWidget *widget = GTK_WIDGET(self);
227 // GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
228 self->led_mode = 0;
229 self->size = 0;
230 self->led_value = 0.f;
231 self->cache_surface = NULL;
232 widget->requisition.width = self->size ? 24 : 19;
233 widget->requisition.height = self->size ? 18 : 14;
234 gtk_widget_set_has_window(widget, FALSE);
235 }
236
calf_led_set_value(CalfLed * led,float value)237 void calf_led_set_value(CalfLed *led, float value)
238 {
239 if (value != led->led_value)
240 {
241 float old_value = led->led_value;
242 led->led_value = value;
243 if (led->led_mode >= 2 || (old_value > 0) != (value > 0))
244 {
245 GtkWidget *widget = GTK_WIDGET (led);
246 if (GTK_WIDGET_REALIZED(widget))
247 gtk_widget_queue_draw (widget);
248 }
249 }
250 }
251
calf_led_get_value(CalfLed * led)252 gboolean calf_led_get_value(CalfLed *led)
253 {
254 return led->led_value;
255 }
256
257 GType
calf_led_get_type(void)258 calf_led_get_type (void)
259 {
260 static GType type = 0;
261 if (!type) {
262 static const GTypeInfo type_info = {
263 sizeof(CalfLedClass),
264 NULL, /* base_init */
265 NULL, /* base_finalize */
266 (GClassInitFunc)calf_led_class_init,
267 NULL, /* class_finalize */
268 NULL, /* class_data */
269 sizeof(CalfLed),
270 0, /* n_preallocs */
271 (GInstanceInitFunc)calf_led_init
272 };
273
274 for (int i = 0; ; i++) {
275 const char *name = "CalfLed";
276 //char *name = g_strdup_printf("CalfLed%u%d",
277 //((unsigned int)(intptr_t)calf_led_class_init) >> 16, i);
278 if (g_type_from_name(name)) {
279 //free(name);
280 continue;
281 }
282 type = g_type_register_static(GTK_TYPE_DRAWING_AREA,
283 name,
284 &type_info,
285 (GTypeFlags)0);
286 //free(name);
287 break;
288 }
289 }
290 return type;
291 }
292