Fixed a bug that caused a segfault on stopping playback with minimized
[terminatorX.git] / terminatorX / src / tX_widget.c
1 /*
2     terminatorX - realtime audio scratching software
3     Copyright (C) 1999-2002  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, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  
19     File: tX_widget.c
20  
21     Description: This contains the implementation of the tx_widget.
22                  This file is based on the GTK+ widget example from
23                  the GTK+ 1.2 tutorial.
24 */
25
26 #define FR_SIZE 3
27 #define DBL_FR_SIZE 6
28
29 #include <math.h>
30
31 #include <gtk/gtkwindow.h>
32 #include "tX_widget.h"
33 #include "tX_types.h"
34 #include <malloc.h>
35
36 #ifndef WIN32
37 #include <unistd.h>
38 #endif
39
40 #ifdef __cplusplus
41 extern "C" {
42 #endif                          /* __cplusplus */
43
44 #define TX_DEFAULT_SIZE_X 100
45 #define TX_DEFAULT_SIZE_Y 30
46
47 /* pre dec */
48     static void gtk_tx_class_init(GtkTxClass *);
49     static void gtk_tx_init(GtkTx * tx);
50     GtkWidget *gtk_tx_new(int16_t * wavdata, int wavsamples);
51     static void gtk_tx_destroy(GtkObject * object);
52     void gtk_tx_set_data(GtkTx * tx, int16_t * wavdata, int wavsamples);
53     static void gtk_tx_realize(GtkWidget * widget);
54     static void gtk_tx_size_request(GtkWidget * widget,
55                                     GtkRequisition * requisition);
56     static void gtk_tx_size_allocate(GtkWidget * widget,
57                                      GtkAllocation * allocation);
58     static gint gtk_tx_expose(GtkWidget * widget, GdkEventExpose * event);
59     static void gtk_tx_update(GtkTx * tx);
60     static void gtk_tx_prepare(GtkWidget * widget);
61
62 /* Local data */
63
64     static GtkWidgetClass *parent_class = NULL;
65
66      guint gtk_tx_get_type() {
67         static guint tx_type = 0;
68
69         if (!tx_type) {
70             GtkTypeInfo tx_info = {
71                 "GtkTx",
72                 sizeof(GtkTx),
73                 sizeof(GtkTxClass),
74                 (GtkClassInitFunc) gtk_tx_class_init,
75                 (GtkObjectInitFunc) gtk_tx_init,
76                 /* reserved */ NULL,
77                 /* reserved */ NULL,
78                 /* reserved */ NULL
79             };
80
81              tx_type = gtk_type_unique(gtk_widget_get_type(), &tx_info);
82         }
83
84         return tx_type;
85     }
86
87     static void
88      gtk_tx_class_init(GtkTxClass * gclass) {
89         GtkObjectClass *object_class;
90         GtkWidgetClass *widget_class;
91
92         object_class = (GtkObjectClass *) gclass;
93         widget_class = (GtkWidgetClass *) gclass;
94
95         parent_class = gtk_type_class(gtk_widget_get_type());
96
97         object_class->destroy = gtk_tx_destroy;
98
99         widget_class->realize = gtk_tx_realize;
100         widget_class->expose_event = gtk_tx_expose;
101         widget_class->size_request = gtk_tx_size_request;
102         widget_class->size_allocate = gtk_tx_size_allocate;
103 //  widget_class->button_press_event = gtk_tx_button_press;
104 //  widget_class->button_release_event = gtk_tx_button_release;
105 //  widget_class->motion_notify_event = gtk_tx_motion_notify;
106     }
107
108     void gtk_tx_mk_col(GtkTx * tx, GdkColor * col, float r, float g,
109                        float b) {
110         float max = 65535.0;
111
112         col->red = (gint) (r * max);
113         col->green = (gint) (g * max);
114         col->blue = (gint) (b * max);
115 //      printf("r: %8i, g: %8i, b: %8i\n", col->red, col->green, col->blue);
116         gdk_colormap_alloc_color(gtk_widget_get_colormap(GTK_WIDGET(tx)),
117                                  col, 1, 1);
118     }
119
120     static void
121      gtk_tx_init(GtkTx * tx) {
122         GdkColormap *priv;
123
124         tx->disp_data = NULL;
125
126         tx->data = NULL;
127         tx->samples = 0;
128
129         tx->do_showframe = 0;
130
131         priv = gdk_colormap_new(gtk_widget_get_visual(GTK_WIDGET(tx)), 6);
132         gtk_widget_set_colormap(GTK_WIDGET(tx), priv);
133
134         gtk_tx_mk_col(tx, &tx->bg, 0, 0, 0);
135
136         gtk_tx_mk_col(tx, &tx->fg, 0, 1, 0);
137
138         gtk_tx_mk_col(tx, &tx->busy_fg, 1, 1, 1);
139         gtk_tx_mk_col(tx, &tx->busy_bg, 1, 0.4, 0.4);
140
141         gtk_tx_mk_col(tx, &tx->mute_fg, 0, 1, 1);
142         gtk_tx_mk_col(tx, &tx->mute_bg, 0, 0, 1);
143         gtk_tx_mk_col(tx, &tx->framecol, 1, 0, 0);
144     }
145
146     GtkWidget *gtk_tx_new(int16_t * wavdata, int wavsamples) {
147         GtkTx *tx;
148
149         tx = gtk_type_new(gtk_tx_get_type());
150
151         tx->data = wavdata;
152         tx->samples = wavsamples;
153
154 //  gtk_tx_prepare(GTK_WIDGET(tx));     
155
156         return GTK_WIDGET(tx);
157     }
158
159     static void
160      gtk_tx_destroy(GtkObject * object) {
161         g_return_if_fail(object != NULL);
162         g_return_if_fail(GTK_IS_TX(object));
163
164         if (GTK_OBJECT_CLASS(parent_class)->destroy)
165             (*GTK_OBJECT_CLASS(parent_class)->destroy) (object);
166     }
167
168
169     void gtk_tx_set_data(GtkTx * tx, int16_t * wavdata, int wavsamples) {
170         g_return_if_fail(tx != NULL);
171         g_return_if_fail(GTK_IS_TX(tx));
172
173         tx->data = wavdata;
174         tx->samples = wavsamples;
175
176         gtk_tx_prepare(GTK_WIDGET(tx));
177         gtk_tx_update(tx);
178     }
179
180     static void
181      gtk_tx_realize(GtkWidget * widget) {
182         GtkTx *tx;
183         GdkWindowAttr attributes;
184         gint attributes_mask;
185
186         g_return_if_fail(widget != NULL);
187         g_return_if_fail(GTK_IS_TX(widget));
188
189         GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
190         tx = GTK_TX(widget);
191
192         attributes.x = widget->allocation.x;
193         attributes.y = widget->allocation.y;
194         attributes.width = widget->allocation.width;
195         attributes.height = widget->allocation.height;
196         attributes.wclass = GDK_INPUT_OUTPUT;
197         attributes.window_type = GDK_WINDOW_CHILD;
198         attributes.event_mask = gtk_widget_get_events(widget) |
199             GDK_EXPOSURE_MASK;
200         attributes.visual = gtk_widget_get_visual(widget);
201         attributes.colormap = gtk_widget_get_colormap(widget);
202
203         attributes_mask =
204             GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
205
206         widget->window =
207             gdk_window_new(widget->parent->window, &attributes,
208                            attributes_mask);
209
210         widget->style = gtk_style_attach(widget->style, widget->window);
211
212         gdk_window_set_user_data(widget->window, widget);
213
214         gtk_style_set_background(widget->style, widget->window,
215                                  GTK_STATE_NORMAL);
216     }
217
218     static void
219      gtk_tx_size_request(GtkWidget * widget, GtkRequisition * requisition) {
220         requisition->width = TX_DEFAULT_SIZE_X;
221         requisition->height = TX_DEFAULT_SIZE_Y;
222     }
223
224     static void
225      gtk_tx_prepare(GtkWidget * widget) {
226         int x, sample;
227         f_prec temp;
228         int16_t *ptr;
229         int16_t value;
230
231         GtkTx *tx;
232
233         g_return_if_fail(widget != NULL);
234         g_return_if_fail(GTK_IS_TX(widget));
235
236         tx = GTK_TX(widget);
237
238         tx->spp = tx->samples / (widget->allocation.width - DBL_FR_SIZE);
239         tx->yc = widget->allocation.height / 2;
240
241         if (tx->disp_data)
242             free(tx->disp_data);
243
244         if (tx->data) {
245
246             tx->disp_data =
247                 (int16_t *) malloc((widget->allocation.width - DBL_FR_SIZE)
248                                    * sizeof(int16_t));
249
250             if (tx->disp_data)
251                 for (x = 0, ptr = tx->disp_data;
252                      x < widget->allocation.width - DBL_FR_SIZE;
253                      ptr++, x++) {
254                     value = tx->data[x * tx->spp];
255                     for (sample = x * tx->spp; sample < (x + 1) * tx->spp;
256                          sample++) {
257                         value = (value + tx->data[sample]) / 2;
258                     }
259                     temp = ((f_prec) value) / 32767.0;
260                     tx->disp_data[x] =
261                         (int) (temp * (f_prec) (tx->yc - FR_SIZE));
262                 }
263
264         } else
265             tx->disp_data = NULL;
266     }
267
268     static void
269      gtk_tx_size_allocate(GtkWidget * widget, GtkAllocation * allocation) {
270         GtkTx *tx;
271
272         g_return_if_fail(widget != NULL);
273         g_return_if_fail(GTK_IS_TX(widget));
274         g_return_if_fail(allocation != NULL);
275
276         widget->allocation = *allocation;
277
278         gtk_tx_prepare(widget);
279
280         if (GTK_WIDGET_REALIZED(widget)) {
281             tx = GTK_TX(widget);
282
283             gdk_window_move_resize(widget->window,
284                                    allocation->x, allocation->y,
285                                    allocation->width, allocation->height);
286
287         }
288     }
289
290     static gint gtk_tx_expose(GtkWidget * widget, GdkEventExpose * event) {
291         GtkTx *tx;
292         gint x, pos;
293
294         g_return_val_if_fail(widget != NULL, FALSE);
295         g_return_val_if_fail(GTK_IS_TX(widget), FALSE);
296         g_return_val_if_fail(event != NULL, FALSE);
297
298         if (event->count > 0)
299             return FALSE;
300
301         tx = GTK_TX(widget);
302
303         gdk_gc_set_foreground(widget->style->fg_gc[widget->state],
304                               &tx->bg);
305
306         gdk_draw_rectangle(widget->window,
307                            widget->style->fg_gc[widget->state], 1, 0, 0,
308                            widget->allocation.width,
309                            widget->allocation.height);
310
311         gdk_gc_set_foreground(widget->style->fg_gc[widget->state],
312                               &tx->fg);
313
314         if (tx->disp_data) {
315             for (x = FR_SIZE, pos = 0;
316                  x < widget->allocation.width - FR_SIZE; x++, pos++) {
317                 gdk_draw_line(widget->window,
318                               widget->style->fg_gc[widget->state], x,
319                               tx->yc - tx->disp_data[pos], x,
320                               tx->yc + tx->disp_data[pos]);
321             }
322         } else {
323             gdk_draw_line(widget->window,
324                           widget->style->fg_gc[widget->state],
325                           FR_SIZE,
326                           tx->yc,
327                           widget->allocation.width - FR_SIZE, tx->yc);
328         }
329
330         gtk_tx_show_frame(tx, tx->do_showframe);
331         return FALSE;
332     }
333
334     static void
335      gtk_tx_update(GtkTx * tx) {
336         g_return_if_fail(tx != NULL);
337         g_return_if_fail(GTK_IS_TX(tx));
338
339         gtk_widget_draw(GTK_WIDGET(tx), NULL);
340     }
341
342     void
343      gtk_tx_prepare_pos_display(GtkTx * tx) {
344         tx->lastpos = -1;
345     }
346
347     void
348      gtk_tx_update_pos_display(GtkTx * tx, int sample, int mute) {
349         GtkWidget *widget;
350         GdkWindow *window;
351         GdkGC *gc;
352 //      GdkColor *fg;
353 //      GdkColor *bg;
354
355         int current_x, x, y, yc, ymax;
356
357         /* Don't update if not required */
358
359         current_x = sample / tx->spp + FR_SIZE;
360
361         if ((current_x == tx->lastpos) && (tx->lastmute == mute))
362             return;
363         tx->lastmute = mute;
364
365         /* speedup + easyness */
366
367         widget = GTK_WIDGET(tx);
368         window = widget->window;
369
370         if (current_x > widget->allocation.width - FR_SIZE - 2)
371             return;
372
373         gc = widget->style->fg_gc[widget->state];
374         yc = tx->yc;
375         ymax = widget->allocation.height - FR_SIZE - 1;
376
377         /* Clean up last pos */
378
379         x = tx->lastpos;
380
381         if (x >= FR_SIZE) {
382             gdk_gc_set_foreground(gc, &tx->bg);
383             gdk_draw_line(window, gc, x, FR_SIZE, x, ymax);
384
385             gdk_gc_set_foreground(gc, &tx->fg);
386             y = tx->disp_data[x - FR_SIZE];
387             gdk_draw_line(window, gc, x, yc + y, x, yc - y);
388         }
389         /* store current_pos */
390
391         tx->lastpos = current_x;
392
393         /* draw current_pos */
394
395         x = current_x;
396
397         if (mute)
398             gdk_gc_set_foreground(gc, &tx->mute_bg);
399         else
400             gdk_gc_set_foreground(gc, &tx->busy_bg);
401
402         gdk_draw_line(window, gc, x, FR_SIZE, x, ymax);
403     }
404
405     void gtk_tx_cleanup_pos_display(GtkTx * tx) {
406         GtkWidget *widget;
407         GdkWindow *window;
408         GdkGC *gc;
409         int x, y, ymax, yc;
410
411         widget = GTK_WIDGET(tx);
412         window = widget->window;
413         gc = widget->style->fg_gc[widget->state];
414         yc = tx->yc;
415         ymax = widget->allocation.height - FR_SIZE - 1;
416
417         x = tx->lastpos;
418
419         if (x >= FR_SIZE) {
420             gdk_gc_set_foreground(gc, &tx->bg);
421             gdk_draw_line(window, gc, x, FR_SIZE, x, ymax);
422
423             gdk_gc_set_foreground(gc, &tx->fg);
424             y = tx->disp_data[x - FR_SIZE];
425             gdk_draw_line(window, gc, x, yc + y, x, yc - y);
426         }
427     }
428
429     void gtk_tx_show_frame(GtkTx * tx, int show) {
430         GtkWidget *widget;
431         GdkWindow *window;
432         GdkGC *gc;
433         int i;
434
435         widget = GTK_WIDGET(tx);
436         window = widget->window;
437         gc = widget->style->fg_gc[widget->state];
438
439
440         tx->do_showframe = show;
441
442         if (show) {
443             gdk_gc_set_foreground(gc, &tx->framecol);
444         } else {
445             gdk_gc_set_foreground(gc, &tx->bg);
446         }
447
448         for (i = 0; i < FR_SIZE; i++) {
449             gdk_draw_rectangle(window, gc, 0, i, i,
450                                widget->allocation.width - (2 * i + 1),
451                                widget->allocation.height - (2 * i + 1));
452         }
453     }
454
455 #ifdef __cplusplus
456 }
457 #endif                          /* __cplusplus */