Workaround compiler warnings and gtk+ deprecations
[terminatorX.git] / src / tX_flash.c
1 /*
2     terminatorX - realtime audio scratching software
3     Copyright (C) 1999-2020  Alexander K├Ânig
4  
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9  
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14  
15     You should have received a copy of the GNU General Public License
16     along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  
18     File: tX_flash.c
19  
20     Description: This contains the implementation of the tx_flash_flash widget.
21 */    
22
23 #include <math.h>
24
25
26 #define IS_TX_FLASH 1
27 #include "tX_flash.h"
28 #include "tX_types.h"
29 #include "tX_global.h"
30 #include <malloc.h>
31
32 #ifndef WIN32
33 #include <unistd.h>
34 #endif
35
36 #ifdef __cplusplus
37 extern "C" {
38 #endif /* __cplusplus */
39
40 #define MAX_VALUE 1.0f
41 // 32767
42 #define RED_BORDER 0.8f
43 // 25000
44 #define TX_FLASH_DEFAULT_SIZE_X 25
45 #define TX_FLASH_DEFAULT_SIZE_Y 30
46 #define DY 5
47 #define DX 5
48 #define DMINIX 2
49 #define S_MINIX 2
50 #define L_MINIX 3
51 #define DLEVEL 3
52 #define MAX_MAX_CYCLES 30;
53
54 /* pre dec */
55 static void gtk_tx_flash_class_init (GtkTxFlashClass *);
56 static void gtk_tx_flash_init (GtkTxFlash *tx_flash);
57 GtkWidget* gtk_tx_flash_new ();
58 static void gtk_tx_flash_destroy (GtkWidget *widget);
59 static void gtk_tx_flash_realize (GtkWidget *widget);
60 static void gtk_tx_flash_get_preferred_width (GtkWidget *widget, gint *minimal_height, gint *natural_height);
61 static void gtk_tx_flash_get_preferred_height (GtkWidget *widget, gint *minimal_height, gint *natural_height);
62
63 static void gtk_tx_flash_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
64 static gboolean gtk_tx_flash_draw (GtkWidget *widget, cairo_t *cairo);
65
66 /* Local data */
67
68 static GtkWidgetClass *parent_class = NULL;
69
70 GType gtk_tx_flash_get_type ()
71 {
72         static GType tx_flash_type = 0;
73
74         if (!tx_flash_type) {
75                 static const GTypeInfo tx_flash_info = {
76                         sizeof (GtkTxFlashClass),
77                         NULL,
78                         NULL,
79                         (GClassInitFunc) gtk_tx_flash_class_init, 
80                         NULL,
81                         NULL,
82                         sizeof (GtkTxFlash),
83                 0,
84                         (GInstanceInitFunc) gtk_tx_flash_init,
85                 };
86
87                 tx_flash_type = g_type_register_static(GTK_TYPE_WIDGET, "GtkTxFlash", &tx_flash_info, 0);
88     }
89         
90         return tx_flash_type;
91 }
92
93 static void gtk_tx_flash_class_init (GtkTxFlashClass *gclass)
94 {
95         GtkWidgetClass *widget_class;
96         
97         widget_class = (GtkWidgetClass*) gclass;
98         
99         parent_class = (GtkWidgetClass*) g_type_class_peek (gtk_widget_get_type ());
100         
101         widget_class->destroy = gtk_tx_flash_destroy;
102         
103         widget_class->realize = gtk_tx_flash_realize;
104         widget_class->draw = gtk_tx_flash_draw;
105         
106         widget_class->get_preferred_height = gtk_tx_flash_get_preferred_height;
107         widget_class->get_preferred_width = gtk_tx_flash_get_preferred_width;
108         
109         widget_class->size_allocate = gtk_tx_flash_size_allocate;
110 }
111
112 enum {
113         COL_BG,
114         COL_NORM,
115         COL_NORM_HALF,
116         COL_LOUD,
117         COL_LOUD_HALF,
118         NO_COLS
119 };
120
121 /* c=a+(a-b)*x; */
122
123 void mk_half(double s, GdkRGBA *a, GdkRGBA *b, GdkRGBA *c)
124 {
125         c->red=a->red-(a->red-b->red)*s;
126         c->green=a->green-(a->green-b->green)*s;
127         c->blue=a->blue-(a->blue-b->blue)*s;
128         c->alpha=1.0;
129 }
130
131 void gtk_tx_flash_update_colors(GtkTxFlash *tx)
132 {
133         gdk_rgba_parse(&tx->colors[COL_BG], globals.vu_meter_bg);
134         tx->colors[COL_BG].alpha = 1.0;
135         gdk_rgba_parse(&tx->colors[COL_LOUD], globals.vu_meter_loud);
136         tx->colors[COL_LOUD].alpha = 1.0;
137         gdk_rgba_parse(&tx->colors[COL_NORM], globals.vu_meter_normal);
138         tx->colors[COL_NORM].alpha = 1.0;
139         mk_half(globals.vu_meter_border_intensity, &tx->colors[COL_BG], &tx->colors[COL_LOUD], &tx->colors[COL_LOUD_HALF]);
140         mk_half(globals.vu_meter_border_intensity, &tx->colors[COL_BG], &tx->colors[COL_NORM], &tx->colors[COL_NORM_HALF]);
141 }
142
143 static void gtk_tx_flash_init (GtkTxFlash *tx_flash)
144 {
145         tx_flash->surface = NULL;
146
147         gtk_tx_flash_update_colors(tx_flash);
148 }
149
150 GtkWidget* gtk_tx_flash_new ()
151 {
152         GtkTxFlash *tx_flash;
153         tx_flash = (GtkTxFlash *)g_object_new(gtk_tx_flash_get_type (), NULL);  
154         
155         // tX_msg("creating a new flash: %08x\n", tx_flash);
156         return GTK_WIDGET (tx_flash);
157 }
158
159 static void gtk_tx_flash_destroy (GtkWidget *widget)
160 {
161         g_return_if_fail (widget != NULL);
162         g_return_if_fail (GTK_IS_TX_FLASH (widget));
163         
164         if (GTK_WIDGET_CLASS (parent_class)->destroy)
165                 (*GTK_WIDGET_CLASS (parent_class)->destroy) (widget);
166 }
167
168 static void gtk_tx_flash_realize (GtkWidget *widget)
169 {
170         GtkTxFlash *tx_flash;
171         GdkWindowAttr attributes;
172         gint attributes_mask;
173         
174         g_return_if_fail (widget != NULL);
175         g_return_if_fail (GTK_IS_TX_FLASH (widget));
176         
177         tx_flash=GTK_TX_FLASH(widget);
178
179         gtk_widget_set_realized(widget, TRUE);
180         GtkAllocation allocation;
181         gtk_widget_get_allocation(widget, &allocation);
182         
183         attributes.x = allocation.x;
184         attributes.y = allocation.y;
185         attributes.width = allocation.width;
186         attributes.height = allocation.height;
187         attributes.wclass = GDK_INPUT_OUTPUT;
188         attributes.window_type = GDK_WINDOW_CHILD;
189         attributes.event_mask = gtk_widget_get_events (widget) | 
190                 GDK_EXPOSURE_MASK;
191         attributes.visual = gtk_widget_get_visual (widget);
192         attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
193         
194         gtk_widget_set_window(widget, gdk_window_new (gtk_widget_get_parent_window(widget), &attributes, attributes_mask));
195         
196         gdk_window_set_user_data (gtk_widget_get_window(widget), widget);
197         
198         if (tx_flash->surface) {
199                 cairo_surface_destroy (tx_flash->surface);
200         }
201         
202         tx_flash->surface = gdk_window_create_similar_surface (gtk_widget_get_window(widget), CAIRO_CONTENT_COLOR, allocation.width, allocation.height);
203 }
204
205 static void gtk_tx_flash_get_preferred_width (GtkWidget *widget, gint *minimal_width, gint *natural_width)
206 {
207   *minimal_width = *natural_width = TX_FLASH_DEFAULT_SIZE_X;
208 }
209
210 static void gtk_tx_flash_get_preferred_height (GtkWidget *widget, gint *minimal_height, gint *natural_height)
211 {
212     *minimal_height = *natural_height = TX_FLASH_DEFAULT_SIZE_Y;
213 }
214
215
216 static void gtk_tx_flash_prepare(GtkWidget *widget)
217 {
218         GtkTxFlash *tx_flash;
219
220         g_return_if_fail (widget != NULL);
221         g_return_if_fail (GTK_IS_TX_FLASH (widget));
222         
223         tx_flash=GTK_TX_FLASH(widget);
224
225         GtkAllocation allocation;
226         gtk_widget_get_allocation(widget, &allocation);
227         
228         tx_flash->levels=(allocation.height-(2*DY))/DLEVEL;
229         tx_flash->channel[0].last_level=0;
230         tx_flash->channel[1].last_level=0;
231         tx_flash->channel[0].max=0;
232         tx_flash->channel[1].max=0;
233         tx_flash->level_value=MAX_VALUE/(f_prec) tx_flash->levels;
234         tx_flash->red_level=(RED_BORDER/tx_flash->level_value);
235         
236         tx_flash->channel[0].x1=DMINIX+S_MINIX+2;
237         tx_flash->channel[1].x2=allocation.width-tx_flash->channel[0].x1;
238                 
239         if (allocation.width%2>0) {
240                 // odd
241                 tx_flash->center_expand=0;
242                 tx_flash->channel[0].x2=allocation.width/2-1;
243         } else {
244                 // even
245                 tx_flash->center_expand=1;
246                 tx_flash->channel[0].x2=allocation.width/2-2;
247         }
248         tx_flash->channel[1].x1=allocation.width/2+2;
249         
250         //tX_msg("flash: width %i: left %i, right %i", widget->allocation.width, tx_flash->channel[0].x2-tx_flash->channel[0].x1, tx_flash->channel[1].x2-tx_flash->channel[1].x1);
251 }
252
253 static void gtk_tx_flash_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
254 {
255         g_return_if_fail (widget != NULL);
256         g_return_if_fail (GTK_IS_TX_FLASH (widget));
257         g_return_if_fail (allocation != NULL);
258         
259         gtk_widget_set_allocation(widget, allocation);
260         
261         gtk_tx_flash_prepare(widget);
262         
263         if (gtk_widget_get_realized (widget)) {
264                 gdk_window_move_resize (gtk_widget_get_window(widget),
265                                   allocation->x, allocation->y,
266                                   allocation->width, allocation->height);
267         }
268 }
269
270 static void gtk_tx_flash_paint_yourself(GtkWidget *widget, cairo_t *cr)
271 {
272         GtkTxFlash *tx_flash;
273         GtkAllocation allocation;
274         
275         gint i, x11, x12,x21, x22, y, middle;
276         int long_level;
277         
278         tx_flash = GTK_TX_FLASH (widget);
279         gtk_widget_get_allocation(widget, &allocation);
280         
281         cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
282         cairo_set_source_surface (cr, tx_flash->surface, 0, 0);
283
284         gdk_cairo_set_source_rgba (cr, &tx_flash->colors[COL_BG]);
285         cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
286         cairo_fill(cr);
287         
288         cairo_set_line_width(cr,1);
289         gdk_cairo_set_source_rgba (cr, &tx_flash->colors[COL_NORM]);
290         
291         x12=DMINIX+S_MINIX;
292         x21=allocation.width-1-x12;
293         middle=allocation.width/2;
294         
295         for (i=0, y=allocation.height-DY; i<=tx_flash->levels; y-=DLEVEL, i++) {
296                 if (i==0) {
297                         long_level=1;
298                 } else if (i==tx_flash->red_level-1) {
299                         long_level=1;
300                 } else if (i==tx_flash->red_level) {
301                         long_level=1;
302                         gdk_cairo_set_source_rgba (cr, &tx_flash->colors[COL_LOUD_HALF]);       
303                 } else if (i==tx_flash->levels) {
304                         long_level=1;
305                 } else long_level=0;
306                 
307                 if (long_level) {
308                         x11=x12-L_MINIX;
309                         x22=x21+L_MINIX;
310                 } else {
311                         x11=x12-S_MINIX;
312                         x22=x21+S_MINIX;                
313                 }
314                 
315                 cairo_move_to (cr, x11, y);
316                 cairo_line_to (cr, x12+1, y);
317                 
318                 cairo_move_to (cr, x21, y);
319                 cairo_line_to (cr, x22+1, y);
320
321                 if (tx_flash->center_expand) {
322                         cairo_move_to (cr, middle-1, y);
323                         cairo_line_to (cr, middle+1, y);
324                 } else {
325                         cairo_move_to (cr, middle, y);
326                         cairo_line_to (cr, middle+1, y);
327                 }
328                 cairo_stroke (cr);
329         }
330 }
331
332 static gboolean gtk_tx_flash_draw(GtkWidget *widget, cairo_t *cr)
333 {
334         g_return_val_if_fail (widget != NULL, FALSE);
335         g_return_val_if_fail (GTK_IS_TX_FLASH (widget), FALSE);
336         
337         gtk_tx_flash_prepare(widget);
338         gtk_tx_flash_paint_yourself(widget, cr); 
339         
340         return FALSE;
341
342
343
344 static void gtk_tx_flash_set_channel_level(GtkTxFlash *tx_flash, f_prec new_value, struct flash_channel *channel);
345
346 void gtk_tx_flash_set_level(GtkWidget *widget, f_prec left_channel, f_prec right_channel)
347 {
348         GtkTxFlash *tx_flash;
349         
350         g_return_if_fail (widget != NULL);
351         g_return_if_fail (GTK_IS_TX_FLASH (widget));
352         
353         tx_flash = GTK_TX_FLASH (widget);
354         
355         gtk_tx_flash_set_channel_level(tx_flash, left_channel, &tx_flash->channel[0]);
356         gtk_tx_flash_set_channel_level(tx_flash, right_channel, &tx_flash->channel[1]);
357 }
358
359 static void gtk_tx_flash_set_channel_level(GtkTxFlash *tx_flash, f_prec new_value, struct flash_channel *channel)
360 {
361         GtkWidget *widget=GTK_WIDGET(tx_flash);
362         GtkAllocation allocation;
363         GdkDrawingContext *context;
364         GdkWindow *window;
365         cairo_t *cr;
366         cairo_region_t *region;
367         gint i, y;
368         int new_level;
369         int red=0;
370         
371         new_level=(int) (new_value/tx_flash->level_value);
372         gtk_widget_get_allocation(widget, &allocation);
373         
374         region = cairo_region_create();
375         window = gtk_widget_get_window(widget);
376         context = gdk_window_begin_draw_frame(window, region);
377         cr =  gdk_drawing_context_get_cairo_context(context);
378         cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
379         cairo_set_line_width(cr,1);
380         cairo_set_source_surface (cr, tx_flash->surface, 0, 0);
381
382         gdk_cairo_set_source_rgba (cr, &tx_flash->colors[COL_BG]);
383
384         // tX_msg("setting level: %5d for widget %08x channel %08x\n", new_level, tx_flash, channel);
385         
386         if (new_level>tx_flash->levels) 
387                 new_level=tx_flash->levels;
388         
389         if (new_level>channel->max) {
390                 channel->max=new_level;
391                 tx_flash->max_cycles=MAX_MAX_CYCLES;
392         } else {
393                 tx_flash->max_cycles--;
394         }
395         
396         if (tx_flash->max_cycles <= 0) {
397                 y=allocation.height-(DY+(channel->max)*DLEVEL);
398                 gdk_cairo_set_source_rgba (cr, &tx_flash->colors[COL_BG]);
399                 cairo_move_to (cr, channel->x1, y);
400                 cairo_line_to (cr, channel->x2, y);
401                 cairo_stroke (cr);
402                 
403                 if (channel->max>0) {
404                         channel->max--;
405                         y+=DLEVEL;
406                         if (channel->max>tx_flash->red_level) {
407                                 gdk_cairo_set_source_rgba (cr, &tx_flash->colors[COL_LOUD]);
408                         } else {
409                                 gdk_cairo_set_source_rgba (cr, &tx_flash->colors[COL_NORM]);
410                         }
411                         
412                         cairo_move_to (cr, channel->x1, y);
413                         cairo_line_to (cr, channel->x2, y);
414                         cairo_stroke (cr);
415                 }
416         }
417         
418         if (new_level!=channel->last_level)  {
419             if (new_level<channel->last_level) {
420                     new_level=channel->last_level*globals.flash_response;
421             }
422
423             if (new_level>channel->last_level) {
424                     gdk_cairo_set_source_rgba (cr, &tx_flash->colors[COL_NORM]);
425
426                     for (i=channel->last_level, y=allocation.height-(DY+channel->last_level*DLEVEL); i<=new_level; y-=DLEVEL, i++) {
427                             if (!red) {
428                                     if (i>=tx_flash->red_level) {
429                                             gdk_cairo_set_source_rgba (cr, &tx_flash->colors[COL_LOUD]);
430                                             red=1;
431                                     }
432                             }
433                             cairo_move_to (cr, channel->x1, y);
434                             cairo_line_to (cr, channel->x2, y);
435                             cairo_stroke (cr);
436                     }
437             } else {
438                     gdk_cairo_set_source_rgba (cr, &tx_flash->colors[COL_BG]);
439
440                     if (channel->last_level==channel->max) {
441                             i=channel->last_level-1;
442                     } else {
443                             i=channel->last_level;
444                     }
445
446                     for (y=allocation.height-(DY+i*DLEVEL); i>new_level; y+=DLEVEL, i--) {
447                             cairo_move_to (cr, channel->x1, y);
448                             cairo_line_to (cr, channel->x2, y);
449                             cairo_stroke (cr);
450                     }
451             }
452             channel->last_level=new_level;
453         }
454
455         gdk_window_end_draw_frame(window, context);
456         cairo_region_destroy(region);
457 }
458
459 void
460 gtk_tx_flash_clear (GtkWidget *widget)
461 {
462         GtkTxFlash *tx_flash;
463
464         tx_flash = GTK_TX_FLASH (widget);
465         
466         tx_flash->channel[0].max=0;
467         tx_flash->channel[1].max=0;
468         tx_flash->channel[0].last_level=0;
469         tx_flash->channel[1].last_level=0;
470         tx_flash->max_cycles=0;
471         
472         gtk_widget_queue_draw(widget);
473 }
474
475 #ifdef __cplusplus
476 }
477 #endif /* __cplusplus */