1 /*
2     Foo-YC20 UI
3     Copyright (C) 2010  Sampo Savolainen <v2@iki.fi>
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 3 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 
19 #include <foo-yc20-ui.h>
20 
21 #include <string.h>
22 
23 #ifdef __cplusplus
24 extern "C" {
25 #endif
26 
27 // C-functions to wrap gtk/gdk signal handlers to C++ functions
28 
yc20ui_size_request(GtkWidget * widget,GtkRequisition * event,gpointer data)29 static void yc20ui_size_request(GtkWidget *widget, GtkRequisition *event, gpointer data)
30 {
31         YC20UI *ui = (YC20UI *)data;
32         ui->size_request(event);
33 }
34 
yc20ui_size_allocate(GtkWidget * widget,GtkAllocation * event,gpointer data)35 static void yc20ui_size_allocate(GtkWidget *widget, GtkAllocation *event, gpointer data)
36 {
37         YC20UI *ui = (YC20UI *)data;
38         ui->size_allocate(event);
39 }
40 
yc20ui_expose_event(GtkWidget * widget,GdkEventExpose * event,gpointer data)41 static gboolean yc20ui_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data)
42 {
43         YC20UI *ui = (YC20UI *)data;
44 	if (event) {
45 		ui->draw(event->area.x, event->area.y, event->area.width, event->area.height, true);
46 	} else {
47 		ui->draw(-1, -1, -1, -1, true);
48 
49 	}
50 
51 	return true;
52 }
53 
yc20ui_realize(GtkWidget * widget,gpointer data)54 static void yc20ui_realize(GtkWidget *widget, gpointer data)
55 {
56         YC20UI *ui = (YC20UI *)data;
57         ui->realize();
58 }
59 
yc20ui_motion_notify_event(GtkWidget * widget,GdkEventMotion * event,gpointer data)60 static gboolean yc20ui_motion_notify_event(GtkWidget *widget, GdkEventMotion *event, gpointer data)
61 {
62         YC20UI *ui = (YC20UI *)data;
63 	ui->mouse_movement(event->x, event->y);
64 	return true;
65 }
66 
yc20ui_button_press_event(GtkWidget * widget,GdkEventButton * event,gpointer data)67 static gboolean yc20ui_button_press_event(GtkWidget *widget, GdkEventButton *event, gpointer data)
68 {
69         YC20UI *ui = (YC20UI *)data;
70         ui->button_pressed(event->x, event->y);
71 	return true;
72 }
73 
yc20ui_button_release_event(GtkWidget * widget,GdkEventButton * event,gpointer data)74 static gboolean yc20ui_button_release_event(GtkWidget *widget, GdkEventButton *event, gpointer data)
75 {
76         YC20UI *ui = (YC20UI *)data;
77         ui->button_released(event->x, event->y);
78         return true;
79 }
80 
81 
82 #ifdef __cplusplus
83 }
84 #endif
85 
86 
87 
88 
YC20UI(YC20Processor * parent)89 YC20UI::YC20UI(YC20Processor *parent)
90 	: yc20(parent)
91 {
92 	drawingArea = GTK_WIDGET(gtk_drawing_area_new());
93 
94 	memset(draggablePerCC, 0, sizeof(Wdgt::Draggable *)*127);
95 
96         // Gtk signals
97         g_signal_connect (drawingArea, "size-request",         G_CALLBACK( yc20ui_size_request ), this);
98         g_signal_connect (drawingArea, "size-allocate",        G_CALLBACK( yc20ui_size_allocate ), this);
99         g_signal_connect (drawingArea, "expose-event",         G_CALLBACK( yc20ui_expose_event ), this);
100 	g_signal_connect (drawingArea, "realize",              G_CALLBACK( yc20ui_realize ), this);
101 
102         g_signal_connect (drawingArea, "motion-notify-event",  G_CALLBACK( yc20ui_motion_notify_event ), this);
103         g_signal_connect (drawingArea, "button-press-event",   G_CALLBACK( yc20ui_button_press_event ), this);
104         g_signal_connect (drawingArea, "button-release-event", G_CALLBACK( yc20ui_button_release_event ), this);
105 
106         // Event mask
107         gint mask = gtk_widget_get_events(drawingArea);
108 
109         mask |= GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK;
110 
111         gtk_widget_set_events(drawingArea, mask);
112 
113 	// Make the map
114 	for (std::list<Wdgt::Draggable *>::iterator i = wdgts.begin(); i !=  wdgts.end(); ++i) {
115 		Wdgt::Draggable *draggable = (*i);
116 
117 		Control *control = yc20->getControl(draggable->getName());
118 		draggablePerCC[control->getCC()] = draggable;
119 		draggable->setZone(control->getZone());
120 
121 	}
122 
123 
124 	// Create the ringbuffer and start the timeout thread
125 	exposeRingbuffer = jack_ringbuffer_create(sizeof(Wdgt::Draggable *)*1000);
126 	if (exposeRingbuffer == NULL) {
127 		throw "Could not create ringbuffer";
128 	}
129 	idleSignalTag = g_timeout_add(10, idleTimeout, this);
130 
131 }
132 
133 void
updateControlsFromState()134 YC20UI::updateControlsFromState()
135 {
136 	for (std::list<Wdgt::Draggable *>::iterator i = wdgts.begin(); i != wdgts.end(); ++i) {
137 		Wdgt::Draggable *o = (*i);
138 		Wdgt::Draggable *draggable = dynamic_cast<Wdgt::Draggable *>(o);
139 		if (draggable != NULL) {
140 			draggable->setValue( *draggable->getZone() );
141 		}
142 	}
143 }
144 
145 
146 void
size_request(GtkRequisition * req)147 YC20UI::size_request(GtkRequisition *req)
148 {
149 	//std::cerr << "size_request: " << req->width << " x " << req->height << std::endl;
150 
151 	if (req->width > 1280) {
152 		req->width = 1280;
153 	} else if (req->width < 768) {
154 		req->width = 768;
155 	}
156 
157 	float scale = (float)req->width/1280.0;
158 
159 	set_scale(scale);
160 
161 	req->height = 200.0 * scale;
162 }
163 
164 
165 void
size_allocate(GtkAllocation * alloc)166 YC20UI::size_allocate(GtkAllocation *alloc)
167 {
168         if (alloc->width > 1280) {
169                 alloc->width = 1280;
170         } else if (alloc->width < 768) {
171                 alloc->width = 768;
172         }
173 
174 	float scale = (float)alloc->width/1280.0;
175 
176 	set_scale(scale);
177 
178         alloc->height = 200.0 * scale;
179 }
180 
181 void
realize()182 YC20UI::realize()
183 {
184 	GdkGeometry geom;
185 	geom.min_width  = 768;
186 	geom.min_height = 120; // 200.0 * (768.0 / 1280.0);
187 	geom.max_width  = 1280;
188 	geom.max_height = 200;
189 
190 	geom.min_aspect = 1280.0/200.0;
191 	geom.max_aspect = 1280.0/200.0;
192 
193 	geom.width_inc  = 64;
194 	geom.height_inc = 10;
195 
196 	GtkWidget *top = gtk_widget_get_toplevel(drawingArea);
197 	if (top == NULL) {
198 		std::cerr << "No toplevel widget?!?!" << std::endl;
199 		return;
200 	}
201 
202 	GtkWindow *window = GTK_WINDOW(top);
203 	if (window == NULL) {
204 		std::cerr << "could not find the toplevel window. weird." << std::endl;
205 		return;
206 	}
207 
208 	GdkWindowHints hints = (GdkWindowHints)(GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE | GDK_HINT_ASPECT | GDK_HINT_RESIZE_INC);
209 	gtk_window_set_geometry_hints(window, NULL, &geom, hints);
210 }
211 
212 gboolean
idleTimeout(gpointer data)213 YC20UI::idleTimeout(gpointer data)
214 {
215 	YC20UI *obj = (YC20UI *)data;
216 	obj->handleExposeEvents();
217 
218 	return true;
219 }
220 
221 void
handleExposeEvents()222 YC20UI::handleExposeEvents()
223 {
224 	Wdgt::Draggable *obj;
225 
226 	while ( jack_ringbuffer_read(exposeRingbuffer,
227 	                             (char *)&obj,
228 	                             sizeof(Wdgt::Draggable *)) == sizeof(Wdgt::Draggable *)) {
229 		obj->setValue( *obj->getZone() );
230 		draw_wdgt(obj);
231 	}
232 }
233 
234 void
queueExpose(int cc)235 YC20UI::queueExpose(int cc)
236 {
237 	Wdgt::Draggable *obj = draggablePerCC[cc];
238 	if (obj == NULL) {
239 		std::cerr << "Tried to queue an expose event for a non-existent control" << std::endl;
240 		return;
241 	}
242 
243 	int i = jack_ringbuffer_write(exposeRingbuffer, (char *)&obj, sizeof(Wdgt::Draggable *));
244 	if (i != sizeof(Wdgt::Draggable *)) {
245 		std::cerr << "Ringbuffer full!" << std::endl;
246 	}
247 }
248 
~YC20UI()249 YC20UI::~YC20UI()
250 {
251 	g_source_remove(idleSignalTag);
252 
253 	jack_ringbuffer_free(exposeRingbuffer);
254 }
255 
256