GnuCash  5.6-150-g038405b370+
gnc-flicker-gui.c
1 /*
2  * gnc-flicker-gui.c --
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of
7  * the License, or (at your option) any later version.
8  *
9  * This program 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  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, contact:
16  *
17  * Free Software Foundation Voice: +1-617-542-5942
18  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
19  * Boston, MA 02110-1301, USA gnu@gnu.org
20  */
21 
29 #include <config.h>
30 
31 #include <gtk/gtk.h>
32 #include <glib/gi18n.h>
33 
34 #include "dialog-utils.h"
35 #include "gnc-flicker-gui.h"
36 #include "gnc-state.h"
37 #include "gnc-ui.h"
38 
39 #define GNC_PREFS_GROUP "dialogs.flicker"
40 #define GNC_STATE_SECTION "Flicker"
41 #define STATE_KEY_BAR_WIDTH "barwidth"
42 #define STATE_KEY_DELAY "delay"
43 
44 #define BAR_WIDTH 44 /* Width of the flicker bars */
45 #define BAR_HEIGHT 200 /* Height of the flicker bars */
46 #define MARGIN 12 /* Distance between the flicker bars */
47 #define DELAY 50 /* Pause between the flickering painting */
48 
49 static guint flicker_data (char const *challenge);
50 static gboolean time_handler (GtkWidget *widget);
51 static void do_marker_drawing (cairo_t *cr);
52 static void draw_bit (cairo_t *cr, _Bool bit, int i);
53 static void do_flicker_drawing (GtkWidget *widget, cairo_t *cr);
54 static void do_flicker_load_state (GtkWidget *dialog);
55 static void do_flicker_store_state (GtkWidget *dialog);
56 static gboolean on_flicker_challenge_draw (GtkWidget *widget, cairo_t *cr,
57  gpointer user_data);
58 static void on_flicker_challenge_map (GtkWidget *widget);
59 static void on_flicker_challenge_destroy (GtkWidget *widget, gpointer user_data);
60 static gboolean on_flicker_marker_draw (GtkWidget *widget, cairo_t *cr,
61  gpointer user_data);
62 static void on_flicker_marker_map (GtkWidget *widget);
63 static void on_spin_barwidth_value_changed (GtkSpinButton *spin, GtkWidget *widget);
64 static void on_spin_delay_value_changed (GtkSpinButton *spin, GtkWidget *widget);
65 static void on_dialog_destroy (GtkWidget *dialog, gpointer user_data);
66 
67 /* structured data for the flicker variables */
68 struct Flickerdraw {
69  const char *challenge;
70  guint challenge_length;
71  guint margin; /* Distance between bars */
72  guint barwidth; /* Bar width */
73  guint barheight; /* Bar height */
74  guint x_barpos; /* x-value for the position of the bar */
75  guint y_barpos; /* y-value for the position of the bar */
76  guint x_drawpos; /* x-value of the first painting position */
77  guint y_drawpos; /* y-value of the first painting position */
78  guint height; /* Height of the drawing area */
79  guint width; /* Width of the drawing area */
80  guint delay; /* Waiting time between frames in milliseconds */
81  guint halfbyteid;
82  guint clock;
83  guint interval;
84  gboolean change_interval;
85 };
86 static struct Flickerdraw flickerdraw;
87 
88 static GncFlickerGui *flickergui = NULL;
89 static _Bool bitarray[255][5];
90 
91 /* this function will return number corresponding 0,1,2..,9,A,B,C,D,E,F */
92 static guint
93 get_num (const char ch)
94 {
95  int num =0;
96  if (ch >= '0' && ch <= '9')
97  num = ch - 0x30;
98  else
99  {
100  switch (ch)
101  {
102  case 'A': case 'a': num = 10; break;
103  case 'B': case 'b': num = 11; break;
104  case 'C': case 'c': num = 12; break;
105  case 'D': case 'd': num = 13; break;
106  case 'E': case 'e': num = 14; break;
107  case 'F': case 'f': num = 15; break;
108  default: num = 0;
109  }
110  }
111  /* The bank challenge has been verified by Aqbanking,
112  * so I assume that no error can occur. */
113  return num;
114 }
115 
116 /* convert the bank challenge into the 5 bits for the flicker data */
117 static guint
118 flicker_data (const char *challenge)
119 {
120  /* bitfield is a clock bit and a 4-bit code with the bits reversed
121  (bit 1 is the least significant and bit 4 the most
122  so 0x1 is 1000 and 0x8 is 0001) */
123  static const _Bool bits[16][5] =
124  {
125  {0, 0, 0, 0, 0}, {0, 1, 0, 0, 0}, {0, 0, 1, 0, 0}, {0, 1, 1, 0, 0},
126  {0, 0, 0, 1, 0}, {0, 1, 0, 1, 0}, {0, 0, 1, 1, 0}, {0, 1, 1, 1, 0},
127  {0, 0, 0, 0, 1}, {0, 1, 0, 0, 1}, {0, 0, 1, 0, 1}, {0, 1, 1, 0, 1},
128  {0, 0, 0, 1, 1}, {0, 1, 0, 1, 1}, {0, 0, 1, 1, 1}, {0, 1, 1, 1, 1}
129  };
130 
131  /* prepend synchronization identifier */
132  char* code = g_strdup_printf ("0FFF%s", challenge);
133  guint challenge_length = strlen (code);
134 
135  /* Swap the position of the bits in pairs throughout the bank challenge
136  (low-order nibble first). */
137  for (guint i = 0; i < challenge_length; i += 2)
138  {
139  guint val1 = get_num (code[i]);
140  guint val2 = get_num (code[i+1]);
141 
142  memcpy (&bitarray[i], bits[val2], sizeof(bits[val2]));
143  memcpy (&bitarray[i+1], bits[val1], sizeof(bits[val1]));
144  }
145  g_free (code);
146 
147  return challenge_length;
148 }
149 
150 /* A timer for redrawing the flickering painting, is started here and
151  * called up again when the "Delay" value is changed */
152 static gboolean
153 time_handler (GtkWidget *widget)
154 {
155  /* Change of waiting time */
156  if (flickerdraw.change_interval)
157  {
158  g_source_remove (flickerdraw.interval);
159  flickerdraw.interval = g_timeout_add (flickerdraw.delay,
160  (GSourceFunc) time_handler,
161  (gpointer) widget);
162  flickerdraw.change_interval = FALSE;
163  return FALSE;
164  }
165  gtk_widget_queue_draw (widget);
166 
167  return TRUE;
168 }
169 
170 /* Show the colored triangle as a pointer for the position of the TAN generator */
171 static void
172 do_marker_drawing (cairo_t *cr)
173 {
174  guint pos1;
175  guint pos2;
176 
177  /* Initialize the drawing area to black */
178  cairo_set_source_rgb (cr, 0, 0, 0);
179  cairo_paint (cr);
180 
181  cairo_set_source_rgb (cr, 0.9, 0.1, 0.1);
182  /* draw the left triangle */
183  pos1 = flickerdraw.x_drawpos + flickerdraw.barwidth / 2;
184  cairo_move_to (cr, pos1, 20);
185  cairo_line_to (cr, pos1 + 10, 2);
186  cairo_line_to (cr, pos1 - 10, 2);
187  cairo_close_path (cr);
188  cairo_stroke_preserve (cr);
189  cairo_fill (cr);
190 
191  /* draw the right triangle */
192  pos2 = flickerdraw.x_drawpos + 4 * flickerdraw.margin + 4 * flickerdraw.barwidth +
193  flickerdraw.barwidth / 2;
194  cairo_move_to (cr, pos2, 20);
195  cairo_line_to (cr, pos2 + 10, 2);
196  cairo_line_to (cr, pos2 - 10, 2);
197  cairo_close_path (cr);
198  cairo_stroke_preserve (cr);
199  cairo_fill (cr);
200 }
201 
202 /* draws the 5 flickering bars of the bank data into the drawing area */
203 static void
204 draw_bit (cairo_t *cr, _Bool bit, int i)
205 {
206  if (bit & 1)
207  cairo_set_source_rgb (cr, 1, 1, 1);
208  else
209  cairo_set_source_rgb (cr, 0, 0, 0);
210 
211  flickerdraw.x_barpos = flickerdraw.x_drawpos + i * flickerdraw.margin +
212  i * flickerdraw.barwidth;
213  cairo_rectangle (cr, flickerdraw.x_barpos, flickerdraw.y_barpos,
214  flickerdraw.barwidth, flickerdraw.barheight);
215  cairo_fill (cr);
216 }
217 
218 /* Prepares the drawing area for the flicker graphic. */
219 static void
220 do_flicker_drawing (GtkWidget *widget, cairo_t *cr)
221 {
222  /* Always align the flicker display in the middle of the drawing area */
223  flickerdraw.width = gtk_widget_get_allocated_width (widget);
224 
225  /* Start position of the first bar */
226  flickerdraw.x_drawpos = (flickerdraw.width - 4 * flickerdraw.margin -
227  5 * flickerdraw.barwidth) / 2;
228 
229  /* Initialize the drawing area to black */
230  cairo_set_source_rgb (cr, 0, 0, 0);
231  cairo_paint (cr);
232 
233  /* paint the flicker graphic */
234  bitarray[flickerdraw.halfbyteid][0] = flickerdraw.clock;
235  draw_bit (cr, flickerdraw.clock, 0);
236  for (int i = 1; i <= 4; i++)
237  draw_bit (cr, bitarray[flickerdraw.halfbyteid][i], i);
238 
239  /* Each flicker point is drawn twice. Once with clock = 1 and once with clock = 0 */
240  if (!flickerdraw.clock)
241  {
242  flickerdraw.clock = 1;
243  flickerdraw.halfbyteid++;
244  if (flickerdraw.halfbyteid >= flickerdraw.challenge_length)
245  flickerdraw.halfbyteid = 0;
246  }
247  else if (flickerdraw.clock)
248  flickerdraw.clock = 0;
249 }
250 
251 /* Load the state of the GUI (Size of the Dialog, Value of the Spinbutton) */
252 static void
253 do_flicker_load_state (GtkWidget *dialog)
254 {
255  /* Load the values in the spin button */
256  GKeyFile *state_file = gnc_state_get_current ();
257 
258  if (g_key_file_has_key (state_file, GNC_STATE_SECTION, STATE_KEY_BAR_WIDTH, NULL))
259  flickerdraw.barwidth = g_key_file_get_integer (state_file,
260  GNC_STATE_SECTION,
261  STATE_KEY_BAR_WIDTH, NULL);
262  else
263  flickerdraw.barwidth = BAR_WIDTH;
264 
265  if (g_key_file_has_key (state_file, GNC_STATE_SECTION, STATE_KEY_DELAY, NULL))
266  flickerdraw.delay = g_key_file_get_integer (state_file,
267  GNC_STATE_SECTION,
268  STATE_KEY_DELAY, NULL);
269  else
270  flickerdraw.delay = DELAY;
271 
272  /* Load window size and position */
273  gnc_restore_window_size (GNC_PREFS_GROUP, GTK_WINDOW (dialog),
274  gnc_ui_get_main_window (NULL));
275 }
276 
277 /* Stores the state of the GUI (Size of the Dialog, Value of the Spinbutton) */
278 static void
279 do_flicker_store_state (GtkWidget *dialog)
280 {
281  /* Save the values in the spin button */
282  GKeyFile *state_file = gnc_state_get_current ();
283 
284  if (flickerdraw.barwidth != BAR_WIDTH)
285  g_key_file_set_integer (state_file, GNC_STATE_SECTION,
286  STATE_KEY_BAR_WIDTH, flickerdraw.barwidth);
287  else if (g_key_file_has_key (state_file, GNC_STATE_SECTION,
288  STATE_KEY_BAR_WIDTH, NULL))
289  g_key_file_remove_key (state_file, GNC_STATE_SECTION,
290  STATE_KEY_BAR_WIDTH, NULL);
291 
292  if (flickerdraw.delay != DELAY)
293  g_key_file_set_integer (state_file, GNC_STATE_SECTION,
294  STATE_KEY_DELAY, flickerdraw.delay);
295  else if (g_key_file_has_key (state_file, GNC_STATE_SECTION,
296  STATE_KEY_DELAY, NULL))
297  g_key_file_remove_key (state_file, GNC_STATE_SECTION,
298  STATE_KEY_DELAY, NULL);
299 
300  /* Save window size and position */
301  gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW (dialog));
302 }
303 
304 /* This signal is emitted when the drawing area "flicker challenge" is visible */
305 static void
306 on_flicker_challenge_map (GtkWidget *widget)
307 {
308  flickerdraw.challenge_length = flicker_data (flickerdraw.challenge);
309 
310  /* Set the height of the drawing area */
311  flickerdraw.height = flickerdraw.barheight + 2 * flickerdraw.y_barpos;
312  gtk_widget_set_size_request (widget, -1, flickerdraw.height);
313 
314  /* Call up the time function and start the flicker display */
315  flickerdraw.interval = g_timeout_add (flickerdraw.delay,
316  (GSourceFunc) time_handler,
317  (gpointer) widget);
318 }
319 
320 /* Initialize the drawingarea to black and paint the flickerchallenge */
321 static gboolean
322 on_flicker_challenge_draw (GtkWidget *widget, cairo_t *cr,
323  __attribute__((unused)) gpointer user_data)
324 {
325  do_flicker_drawing (widget, cr);
326 
327  return FALSE;
328 }
329 
330 /* called when the drawing area is destroyed */
331 static void
332 on_flicker_challenge_destroy (GtkWidget *widget,
333  __attribute__((unused)) gpointer user_data)
334 {
335  /* remove the timeout function */
336  g_source_remove (flickerdraw.interval);
337 }
338 
339 /* Initialize the drawing area "flicker marker" in black and draw the marker for
340  * the position of the TAN-Generator */
341 static gboolean
342 on_flicker_marker_draw (__attribute__((unused)) GtkWidget *widget, cairo_t *cr,
343  __attribute__((unused)) gpointer data)
344 {
345  do_marker_drawing (cr);
346 
347  return FALSE;
348 }
349 
350 /* This signal is emitted when the drawing area "flicker marker" is visible */
351 static void
352 on_flicker_marker_map (GtkWidget *widget)
353 {
354  /* Set the height of the drawing area */
355  gtk_widget_set_size_request (widget, -1, flickerdraw.y_barpos);
356 }
357 
358 /* The value for "Field width" has been changed on the spin button and the
359  * flicker display is updated */
360 static void
361 on_spin_barwidth_value_changed (GtkSpinButton *spin, GtkWidget *widget)
362 {
363  flickerdraw.barwidth = gtk_spin_button_get_value_as_int (spin);
364  flickerdraw.x_drawpos = (flickerdraw.width - 4 * flickerdraw.margin -
365  5 * flickerdraw.barwidth) / 2;
366 
367  /* Moving the position triangles */
368  gtk_widget_queue_draw (widget);
369 }
370 
371 /* The value for "waiting time" was changed on the spin button and
372  * the speed of the flickering display is updated */
373 static void
374 on_spin_delay_value_changed (GtkSpinButton *spin, GtkWidget *widget)
375 {
376  flickerdraw.delay = gtk_spin_button_get_value_as_int (spin);
377 
378  flickerdraw.change_interval = TRUE;
379  time_handler (widget);
380 }
381 
382 static void
383 on_dialog_destroy (GtkWidget *dialog, __attribute__((unused)) gpointer user_data)
384 {
385  /* Store window size and initial setting values */
386  do_flicker_store_state (dialog);
387 }
388 
389 /* The widgets for the GUI are prepared and the first parameters are set */
390 void
391 ini_flicker_gui (const char *pChallenge, GncFlickerGui *gui)
392 {
393  /* Establish reference to the dialog widgets created in gnc_gwen_gui */
394  flickergui = gui;
395 
396  /* Load window size and initial setting values */
397  do_flicker_load_state (GTK_WIDGET (flickergui->dialog));
398 
399  /* Initialize application */
400  flickerdraw.barheight = BAR_HEIGHT;
401  flickerdraw.margin = MARGIN;
402  flickerdraw.y_barpos = 20; /* First painting position */
403  flickerdraw.halfbyteid = 0;
404  flickerdraw.clock = 1;
405 
406  flickerdraw.challenge = pChallenge;
407 
408  g_signal_connect (GTK_WINDOW (flickergui->dialog), "destroy",
409  G_CALLBACK (on_dialog_destroy), NULL);
410 
411  g_signal_connect (GTK_WIDGET (flickergui->flicker_challenge), "map",
412  G_CALLBACK (on_flicker_challenge_map), NULL);
413  g_signal_connect (GTK_WIDGET (flickergui->flicker_challenge), "draw",
414  G_CALLBACK (on_flicker_challenge_draw), NULL);
415  g_signal_connect (GTK_WIDGET (flickergui->flicker_challenge), "destroy",
416  G_CALLBACK (on_flicker_challenge_destroy), NULL);
417 
418  g_signal_connect (GTK_WIDGET (flickergui->flicker_marker), "map",
419  G_CALLBACK (on_flicker_marker_map), NULL);
420  g_signal_connect (GTK_WIDGET (flickergui->flicker_marker), "draw",
421  G_CALLBACK (on_flicker_marker_draw), NULL);
422 
423  flickergui->adj_barwidth = gtk_adjustment_new (0.0, 10.0, 80.0, 1.0, 10.0, 0.0);
424  gtk_spin_button_set_adjustment (flickergui->spin_barwidth, flickergui->adj_barwidth);
425  gtk_spin_button_set_value (GTK_SPIN_BUTTON (flickergui->spin_barwidth),
426  flickerdraw.barwidth);
427  g_signal_connect (GTK_WIDGET (flickergui->spin_barwidth), "value-changed",
428  G_CALLBACK (on_spin_barwidth_value_changed),
429  flickergui->flicker_marker);
430  gtk_widget_set_focus_on_click (GTK_WIDGET (flickergui->spin_barwidth), FALSE);
431 
432  flickergui->adj_delay = gtk_adjustment_new (0.0, 10.0, 100.0, 10.0, 10.0, 0.0);
433  gtk_spin_button_set_adjustment (flickergui->spin_delay, flickergui->adj_delay);
434  gtk_spin_button_set_value (GTK_SPIN_BUTTON (flickergui->spin_delay),
435  flickerdraw.delay);
436  g_signal_connect (GTK_WIDGET (flickergui->spin_delay), "value-changed",
437  G_CALLBACK (on_spin_delay_value_changed),
438  flickergui->flicker_challenge);
439  gtk_widget_set_focus_on_click (GTK_WIDGET (flickergui->spin_delay), FALSE);
440 
441  gtk_widget_grab_focus (GTK_WIDGET (flickergui->input_entry));
442 }
Functions to load, save and get gui state.
GtkWindow * gnc_ui_get_main_window(GtkWidget *widget)
Get a pointer to the final GncMainWindow widget is rooted in.
GKeyFile * gnc_state_get_current(void)
Returns a pointer to the most recently loaded state.
Definition: gnc-state.c:248
GUI callbacks for Flicker and ChipTAN(optisch)
void ini_flicker_gui(const char *pChallenge, GncFlickerGui *gui)
Initialize the dialog and drawing area.