1 /* Metageek WiSPY interface
2 * Mike Kershaw/Dragorn <dragorn@kismetwireless.net>
3 *
4 * This code is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This code is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * Extra thanks to Ryan Woodings @ Metageek for interface documentation
15 */
16
17 #include <math.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21
22 #include "spectool_gtk_channel.h"
23
24 static void spectool_channel_class_init(SpectoolChannelClass *class);
25 static void spectool_channel_init(SpectoolChannel *graph);
26 static void spectool_channel_destroy(GtkObject *object);
27
28 static gint spectool_channel_configure(GtkWidget *widget,
29 GdkEventConfigure *event);
30 static gboolean spectool_channel_expose(GtkWidget *widget,
31 GdkEventExpose *event,
32 gpointer *aux);
33 static gboolean spectool_channel_button_press(GtkWidget *widget,
34 GdkEventButton *event,
35 gpointer *aux);
36 static gboolean spectool_channel_mouse_move(GtkWidget *widget,
37 GdkEventMotion *event,
38 gpointer *aux);
39
40 G_DEFINE_TYPE(SpectoolChannel, spectool_channel, SPECTOOL_TYPE_WIDGET);
41
spectool_channel_find_chan_pt(SpectoolChannel * cwidget,int x,int y)42 static inline int spectool_channel_find_chan_pt(SpectoolChannel *cwidget, int x, int y) {
43 int dbm, maxy, nchannels;
44 SpectoolWidget *wwidget;
45
46 g_return_val_if_fail(cwidget != NULL, 0);
47 g_return_val_if_fail(IS_SPECTOOL_WIDGET(cwidget), 0);
48 wwidget = SPECTOOL_WIDGET(cwidget);
49
50 /* Only compute if we know a chanset and if we're inside the bounding
51 * box */
52 if (wwidget->chanopts == NULL)
53 return -1;
54
55 if (wwidget->chanopts->chanset == NULL)
56 return -2;
57
58 if (x < cwidget->chan_start_x || x > cwidget->chan_end_x ||
59 y < cwidget->chan_start_y || y > cwidget->chan_end_y) {
60 return -2;
61 }
62
63 /* Search for the channel positions */
64 maxy = 0;
65 nchannels = wwidget->chanopts->chanset->chan_num;
66 for (dbm = 0; dbm < nchannels; dbm++) {
67 if (cwidget->chan_points[dbm + nchannels].y > maxy)
68 maxy = cwidget->chan_points[dbm + nchannels].y;
69
70 if (x > cwidget->chan_points[dbm].x &&
71 x < cwidget->chan_points[dbm + nchannels].x &&
72 y > cwidget->chan_points[dbm].y &&
73 y < cwidget->chan_points[dbm + nchannels].y) {
74
75 return dbm;
76 }
77 }
78
79 return -1;
80 }
81
spectool_channel_draw(GtkWidget * widget,cairo_t * cr,SpectoolWidget * wwidget)82 void spectool_channel_draw(GtkWidget *widget, cairo_t *cr, SpectoolWidget *wwidget) {
83 SpectoolChannel *channel;
84 cairo_text_extents_t extents;
85 int x, chpix;
86 char mtext[128];
87
88 g_return_if_fail(widget != NULL);
89
90 channel = SPECTOOL_CHANNEL(wwidget);
91
92 cairo_save(cr);
93
94 channel->chan_h = extents.height + ((double) extents.height * 0.1) + 5;
95
96 /* Try to figure out the channels we use for this spectrum */
97 for (x = 0; wwidget->chanopts != NULL &&
98 channel_list[x].name != NULL && wwidget->chanopts->chanset == NULL; x++) {
99 if (channel_list[x].startkhz >=
100 wwidget->sweepcache->latest->start_khz &&
101 channel_list[x].endkhz <= wwidget->sweepcache->latest->end_khz) {
102 int cpos;
103 double r, g, b;
104
105 wwidget->chanopts->chanset = &(channel_list[x]);
106
107 /* Allocate the channels that are 'hit' or highlighted */
108 if (wwidget->chanopts->chanhit)
109 free(wwidget->chanopts->chanhit);
110 wwidget->chanopts->chanhit =
111 malloc(sizeof(int) * wwidget->chanopts->chanset->chan_num);
112 memset(wwidget->chanopts->chanhit, 0,
113 sizeof(int) * wwidget->chanopts->chanset->chan_num);
114
115 /* Allocate color sweep */
116 if (wwidget->chanopts->chancolors)
117 free(wwidget->chanopts->chancolors);
118 wwidget->chanopts->chancolors = malloc(sizeof(double) *
119 wwidget->chanopts->chanset->chan_num * 3);
120
121 for (cpos = 0; cpos < wwidget->chanopts->chanset->chan_num; cpos++) {
122 /* Get the RGB values of a full-intensity color somewhere
123 * along the H slider derived from the channel position */
124 hsv_to_rgb(&r, &g, &b,
125 (360 / wwidget->chanopts->chanset->chan_num) * cpos, 1, 1);
126 /* Convert the hex colors to cairo colors */
127 wwidget->chanopts->chancolors[(3 * cpos) + 0] = HC2CC(r);
128 wwidget->chanopts->chancolors[(3 * cpos) + 1] = HC2CC(g);
129 wwidget->chanopts->chancolors[(3 * cpos) + 2] = HC2CC(b);
130 }
131
132 break;
133 }
134 }
135
136 /* Plot the channels if we know how */
137 if (wwidget->chanopts != NULL && wwidget->chanopts->chanset != NULL &&
138 wwidget->show_channels) {
139 /* Allocate the channel point array if we haven't yet, so the mouse
140 * handlers know where we've clicked. Points allocated inside the
141 * channel widget itself. */
142 if (channel->chan_points == NULL) {
143 channel->chan_points =
144 (GdkPoint *) malloc(sizeof(GdkPoint) *
145 wwidget->chanopts->chanset->chan_num * 2);
146 }
147
148 /* Draw the channel text along the bottom */
149 cairo_save(cr);
150 for (x = 0; x < wwidget->chanopts->chanset->chan_num; x++) {
151 chpix = ((float) wwidget->g_len_x /
152 (wwidget->sweepcache->latest->end_khz -
153 wwidget->sweepcache->latest->start_khz)) *
154 (wwidget->chanopts->chanset->chan_freqs[x] -
155 wwidget->sweepcache->latest->start_khz);
156
157 if (x == wwidget->chanopts->hi_chan) {
158 cairo_set_source_rgb(cr, HC2CC(0xFF), HC2CC(0xF6), HC2CC(0x00));
159 snprintf(mtext, 128, "%s",
160 wwidget->chanopts->chanset->chan_text[x]);
161 } else {
162 cairo_set_source_rgb(cr, 1, 1, 1);
163 snprintf(mtext, 128, "%s",
164 wwidget->chanopts->chanset->chan_text[x]);
165 }
166
167 cairo_move_to(cr, wwidget->g_start_x + chpix,
168 wwidget->g_start_y);
169 cairo_line_to(cr, wwidget->g_start_x + chpix, wwidget->g_start_y + 5);
170 cairo_stroke(cr);
171
172 cairo_select_font_face(cr, "Helvetica",
173 CAIRO_FONT_SLANT_NORMAL,
174 CAIRO_FONT_WEIGHT_BOLD);
175 cairo_set_font_size(cr, 14);
176 cairo_text_extents(cr, mtext, &extents);
177 cairo_move_to(cr,
178 wwidget->g_start_x + chpix - (extents.width / 2),
179 wwidget->g_start_y + 10 + extents.height);
180 cairo_show_text(cr, mtext);
181
182 channel->chan_points[x].x =
183 wwidget->g_start_x + chpix - (extents.width / 2) - 4;
184 channel->chan_points[x].y = wwidget->g_start_y + 10 - 4;
185 channel->chan_points[x + wwidget->chanopts->chanset->chan_num].x =
186 channel->chan_points[x].x + extents.width + 8;
187 channel->chan_points[x + wwidget->chanopts->chanset->chan_num].y =
188 channel->chan_points[x].y + extents.height + 10;
189
190 if (wwidget->chanopts->chanhit[x]) {
191 cairo_save(cr);
192 cairo_set_source_rgba(cr,
193 wwidget->chanopts->chancolors[(3 * x) + 0],
194 wwidget->chanopts->chancolors[(3 * x) + 1],
195 wwidget->chanopts->chancolors[(3 * x) + 2], 0.60);
196 cairo_rectangle(cr,
197 wwidget->g_start_x + chpix - (extents.width / 2) - 3.5,
198 wwidget->g_start_y + 10 - 3.5,
199 extents.width + 8,
200 extents.height + 10);
201 cairo_fill(cr);
202 /* cairo_stroke(cr); */
203 cairo_restore(cr);
204 }
205 }
206
207 channel->chan_start_x = channel->chan_points[0].x - 1;
208 channel->chan_end_x =
209 channel->chan_points[(wwidget->chanopts->chanset->chan_num * 2) - 1].x + 1;
210 channel->chan_start_y = channel->chan_points[0].y - 1;
211 channel->chan_end_y =
212 channel->chan_points[(wwidget->chanopts->chanset->chan_num * 2) - 1].y + 1;
213
214 cairo_restore(cr);
215
216 }
217 }
218
spectool_channel_button_press(GtkWidget * widget,GdkEventButton * event,gpointer * aux)219 static gint spectool_channel_button_press(GtkWidget *widget,
220 GdkEventButton *event,
221 gpointer *aux) {
222 SpectoolChannel *channel;
223 SpectoolWidget *wwidget;
224 int ch;
225 GList *upd_iter;
226
227 g_return_val_if_fail(aux != NULL, 0);
228 g_return_val_if_fail(IS_SPECTOOL_CHANNEL(aux), 0);
229 g_return_val_if_fail(IS_SPECTOOL_WIDGET(aux), 0);
230
231 channel = SPECTOOL_CHANNEL(aux);
232 wwidget = SPECTOOL_WIDGET(aux);
233
234 g_return_val_if_fail(wwidget->chanopts != NULL, 0);
235 g_return_val_if_fail(wwidget->chanopts->chanset != NULL, 0);
236 g_return_val_if_fail(wwidget->sweepcache != NULL, 0);
237 g_return_val_if_fail(wwidget->sweepcache->latest != NULL, 0);
238
239 if (event->button != 1)
240 return TRUE;
241
242 if ((ch = spectool_channel_find_chan_pt(channel, event->x, event->y)) > -1) {
243 /* Should never get here if we don't have a chanset, do some
244 * checking anyhow though */
245 if (ch < 0 || ch > wwidget->chanopts->chanset->chan_num)
246 return TRUE;
247
248 if (wwidget->chanopts->chanhit[ch]) {
249 wwidget->chanopts->chanhit[ch] = 0;
250 } else {
251 wwidget->chanopts->chanhit[ch] = 1;
252 }
253
254 wwidget->chanopts->hi_chan = -1;
255
256 }
257
258 spectool_widget_update(GTK_WIDGET(wwidget));
259
260 upd_iter = channel->update_list;
261 while (upd_iter != NULL) {
262 spectool_widget_update(GTK_WIDGET(upd_iter->data));
263
264 upd_iter = g_list_next(upd_iter);
265 continue;
266 }
267
268 return TRUE;
269 }
270
spectool_channel_mouse_move(GtkWidget * widget,GdkEventMotion * event,gpointer * aux)271 static gboolean spectool_channel_mouse_move(GtkWidget *widget,
272 GdkEventMotion *event,
273 gpointer *aux) {
274 int x, y;
275 int ch;
276 GdkModifierType state;
277 SpectoolChannel *channel;
278 SpectoolWidget *wwidget;
279 GList *upd_iter;
280
281 g_return_val_if_fail(aux != NULL, FALSE);
282 g_return_val_if_fail(IS_SPECTOOL_CHANNEL(aux), FALSE);
283 g_return_val_if_fail(IS_SPECTOOL_WIDGET(aux), FALSE);
284
285 channel = SPECTOOL_CHANNEL(aux);
286 wwidget = SPECTOOL_WIDGET(aux);
287
288 g_return_val_if_fail(wwidget->sweepcache != NULL, FALSE);
289 g_return_val_if_fail(wwidget->sweepcache->latest != NULL, FALSE);
290
291 if (event->is_hint) {
292 gdk_window_get_pointer(event->window, &x, &y, &state);
293 } else {
294 x = (int) event->x;
295 y = (int) event->y;
296 state = event->state;
297 }
298
299 /* Search for the channel positions, update the graph if we've changed
300 * the highlighted channel */
301 g_return_val_if_fail(wwidget->chanopts != NULL, FALSE);
302 g_return_val_if_fail(wwidget->chanopts->chanset != NULL, FALSE);
303
304 if ((ch = spectool_channel_find_chan_pt(channel, x, y)) >= -1) {
305 if (ch != wwidget->chanopts->hi_chan) {
306 wwidget->chanopts->hi_chan = ch;
307 spectool_widget_update(GTK_WIDGET(wwidget));
308
309 upd_iter = channel->update_list;
310 while (upd_iter != NULL) {
311 spectool_widget_update(GTK_WIDGET(upd_iter->data));
312
313 upd_iter = g_list_next(upd_iter);
314 continue;
315 }
316 }
317 } else if (wwidget->chanopts->hi_chan > -1) {
318 wwidget->chanopts->hi_chan = -1;
319 spectool_widget_update(GTK_WIDGET(wwidget));
320
321 upd_iter = channel->update_list;
322 while (upd_iter != NULL) {
323 spectool_widget_update(GTK_WIDGET(upd_iter->data));
324
325 upd_iter = g_list_next(upd_iter);
326 continue;
327 }
328 }
329
330 return TRUE;
331 }
332
spectool_channel_update(GtkWidget * widget)333 void spectool_channel_update(GtkWidget *widget) {
334 SpectoolWidget *wwidget;
335 SpectoolChannel *channel;
336
337 g_return_if_fail(widget != NULL);
338 g_return_if_fail(IS_SPECTOOL_WIDGET(widget));
339 g_return_if_fail(IS_SPECTOOL_CHANNEL(widget));
340
341 wwidget = SPECTOOL_WIDGET(widget);
342 channel = SPECTOOL_CHANNEL(widget);
343
344 g_return_if_fail(wwidget->draw != NULL);
345
346 /* Bail out if we don't have any sweep data */
347 if (wwidget->sweepcache == NULL)
348 return;
349 if (wwidget->sweepcache->pos < 0)
350 return;
351 }
352
spectool_channel_size_request(GtkWidget * widget,GtkRequisition * requisition)353 static void spectool_channel_size_request (GtkWidget *widget, GtkRequisition *requisition) {
354 SpectoolWidget *wwidget = SPECTOOL_WIDGET(widget);
355
356 requisition->width = 0;
357 requisition->height = 25;
358
359 if (GTK_BIN(wwidget)->child && GTK_WIDGET_VISIBLE(GTK_BIN(wwidget)->child)) {
360 GtkRequisition child_requisition;
361
362 gtk_widget_size_request(GTK_BIN(wwidget)->child, &child_requisition);
363
364 requisition->width += child_requisition.width;
365 requisition->height += child_requisition.height;
366 }
367 }
368
spectool_channel_class_init(SpectoolChannelClass * class)369 static void spectool_channel_class_init(SpectoolChannelClass *class) {
370 GObjectClass *gobject_class;
371 GtkObjectClass *object_class;
372 GtkWidgetClass *widget_class;
373
374 gobject_class = G_OBJECT_CLASS(class);
375 object_class = GTK_OBJECT_CLASS(class);
376 widget_class = GTK_WIDGET_CLASS(class);
377
378 object_class->destroy = spectool_channel_destroy;
379
380 widget_class->size_request = spectool_channel_size_request;
381 }
382
spectool_channel_destroy(GtkObject * object)383 static void spectool_channel_destroy(GtkObject *object) {
384 SpectoolChannel *channel = SPECTOOL_CHANNEL(object);
385 SpectoolWidget *wwidget;
386
387 wwidget = SPECTOOL_WIDGET(channel);
388
389 GTK_OBJECT_CLASS(spectool_channel_parent_class)->destroy(object);
390 }
391
spectool_channel_new()392 GtkWidget *spectool_channel_new() {
393 SpectoolChannel *channel;
394 SpectoolWidget *wwidget;
395
396 channel = gtk_type_new(spectool_channel_get_type());
397 printf("debug - channel new %p widget %p\n", channel, GTK_WIDGET(channel));
398
399 wwidget = SPECTOOL_WIDGET(channel);
400
401 return GTK_WIDGET(channel);
402 }
403
spectool_channel_wdr_sweep(int slot,int mode,spectool_sample_sweep * sweep,void * aux)404 static void spectool_channel_wdr_sweep(int slot, int mode,
405 spectool_sample_sweep *sweep, void *aux) {
406 SpectoolChannel *cwidget;
407 SpectoolWidget *wwidget;
408
409 g_return_if_fail(aux != NULL);
410 g_return_if_fail(IS_SPECTOOL_CHANNEL(aux));
411
412 cwidget = SPECTOOL_CHANNEL(aux);
413 wwidget = SPECTOOL_WIDGET(aux);
414
415 if ((mode & SPECTOOL_POLL_CONFIGURED)) {
416 if (wwidget->chanopts == NULL)
417 return;
418
419 wwidget->chanopts->chanset = NULL;
420 if (wwidget->chanopts->chanhit)
421 free(wwidget->chanopts->chanhit);
422 wwidget->chanopts->chanhit = NULL;
423 if (wwidget->chanopts->chancolors)
424 free(wwidget->chanopts->chancolors);
425 wwidget->chanopts->chancolors = NULL;
426 }
427 }
428
spectool_channel_init(SpectoolChannel * channel)429 static void spectool_channel_init(SpectoolChannel *channel) {
430 SpectoolWidget *wwidget;
431 GtkWidget *temp;
432
433 wwidget = SPECTOOL_WIDGET(channel);
434
435 channel->chan_points = NULL;
436 channel->chan_h = -1;
437 channel->chan_start_x = channel->chan_end_x =
438 channel->chan_start_y = channel->chan_end_y = 0;
439
440 channel->update_list = NULL;
441
442 wwidget->sweep_num_samples = 1;
443 wwidget->graph_title = "channel";
444 wwidget->graph_title_bg = "#ABBBBB";
445 wwidget->graph_control_bg = "#ABBBBB";
446 wwidget->show_channels = 1;
447
448 wwidget->wdr_sweep_func = spectool_channel_wdr_sweep;
449 wwidget->wdr_devbind_func = NULL;
450 wwidget->draw_mouse_move_func = spectool_channel_mouse_move;
451 wwidget->draw_mouse_click_func = spectool_channel_button_press;
452
453 wwidget->draw_timeout = 1000;
454 wwidget->draw_func = spectool_channel_draw;
455 wwidget->update_func = spectool_channel_update;
456
457 wwidget->timeout_ref =
458 g_timeout_add(wwidget->draw_timeout,
459 (GSourceFunc) spectool_widget_timeout, wwidget);
460
461 spectool_widget_buildgui(wwidget);
462 }
463
spectool_channel_append_update(GtkWidget * widget,GtkWidget * update)464 void spectool_channel_append_update(GtkWidget *widget, GtkWidget *update) {
465 SpectoolChannel *channel;
466
467 g_return_if_fail(widget != NULL);
468 g_return_if_fail(IS_SPECTOOL_CHANNEL(widget));
469
470 channel = SPECTOOL_CHANNEL(widget);
471
472 channel->update_list = g_list_append(channel->update_list, update);
473 }
474
475