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