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