247373e9d4f8a116e12eb8e314bd40f042b15282
[fw/stlink] / src / tools / gui / stlink-gui.c
1 #include <string.h>
2 #include <stdlib.h>
3 #include <stdint.h>
4 #include <errno.h>
5 #include <gtk/gtk.h>
6
7 #include <stlink.h>
8 #include "stlink-gui.h"
9
10 #define MEM_READ_SIZE 1024
11
12 #ifndef G_VALUE_INIT
13 #define G_VALUE_INIT {0, {{0}}}
14 #endif
15
16 G_DEFINE_TYPE (STlinkGUI, stlink_gui, G_TYPE_OBJECT);
17
18 static void
19 stlink_gui_dispose (GObject *gobject)
20 {
21     G_OBJECT_CLASS (stlink_gui_parent_class)->dispose (gobject);
22 }
23
24 static void
25 stlink_gui_finalize (GObject *gobject)
26 {
27     G_OBJECT_CLASS (stlink_gui_parent_class)->finalize (gobject);
28 }
29
30 static void
31 stlink_gui_class_init (STlinkGUIClass *klass)
32 {
33     GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
34
35     gobject_class->dispose  = stlink_gui_dispose;
36     gobject_class->finalize = stlink_gui_finalize;
37 }
38
39 static void
40 stlink_gui_init (STlinkGUI *self)
41 {
42     self->sl       = NULL;
43     self->filename = NULL;
44
45     self->progress.activity_mode = FALSE;
46     self->progress.fraction      = 0;
47
48     self->flash_mem.memory = NULL;
49     self->flash_mem.size   = 0;
50     self->flash_mem.base   = 0;
51
52     self->file_mem.memory = NULL;
53     self->file_mem.size   = 0;
54     self->file_mem.base   = 0;
55 }
56
57 static gboolean
58 set_info_error_message_idle (STlinkGUI *gui)
59 {
60     if (gui->error_message != NULL) {
61         gchar *markup;
62
63         markup = g_markup_printf_escaped ("<b>%s</b>", gui->error_message);
64         gtk_label_set_markup (gui->infolabel, markup);
65         gtk_info_bar_set_message_type (gui->infobar, GTK_MESSAGE_ERROR);
66         gtk_widget_show (GTK_WIDGET (gui->infobar));
67
68         g_free (markup);
69         g_free (gui->error_message);
70         gui->error_message = NULL;
71     }
72     return FALSE;
73 }
74
75 static void
76 stlink_gui_set_info_error_message (STlinkGUI *gui, const gchar *message)
77 {
78     gui->error_message = g_strdup (message);
79     g_idle_add ((GSourceFunc) set_info_error_message_idle, gui);
80 }
81
82 static void
83 stlink_gui_set_sensitivity (STlinkGUI *gui, gboolean sensitivity)
84 {
85     gtk_widget_set_sensitive (GTK_WIDGET (gui->open_button), sensitivity);
86
87     if (sensitivity && gui->sl)
88         gtk_widget_set_sensitive (GTK_WIDGET (gui->disconnect_button), sensitivity);
89
90     if (sensitivity && !gui->sl)
91         gtk_widget_set_sensitive (GTK_WIDGET (gui->connect_button), sensitivity);
92
93     if (sensitivity && gui->sl && gui->filename)
94         gtk_widget_set_sensitive (GTK_WIDGET (gui->flash_button), sensitivity);
95 }
96
97 static void
98 mem_view_init_headers (GtkTreeView *view)
99 {
100     GtkCellRenderer *renderer;
101     gint             i;
102
103     g_return_if_fail (view != NULL);
104
105     renderer = gtk_cell_renderer_text_new ();
106     gtk_tree_view_insert_column_with_attributes (view,
107             -1,
108             "Address",
109             renderer,
110             "text",
111             0, /* column */
112             NULL);
113     for (i = 0; i < 4; i++) {
114         gchar *label;
115
116         label = g_strdup_printf ("%X", i * 4);
117         renderer = gtk_cell_renderer_text_new ();
118         gtk_tree_view_insert_column_with_attributes (view,
119                 -1,
120                 label,
121                 renderer,
122                 "text",
123                 (i + 1), /* column */
124                 NULL);
125         g_free (label);
126     }
127
128     for (i = 0; i < 5; i++) {
129         GtkTreeViewColumn *column = gtk_tree_view_get_column (view, i);
130         gtk_tree_view_column_set_expand (column, TRUE);
131     }
132 }
133
134 static void
135 mem_view_add_as_hex (GtkListStore *store,
136                      GtkTreeIter  *iter,
137                      gint          column,
138                      guint32       value)
139 {
140     gchar     *hex_str;
141
142     hex_str = g_strdup_printf ("0x%08X", value);
143     gtk_list_store_set (store, iter, column, hex_str, -1);
144     g_free (hex_str);
145 }
146
147 static void
148 mem_view_add_buffer (GtkListStore *store,
149                      GtkTreeIter  *iter,
150                      guint32       address,
151                      guchar       *buffer,
152                      gint          len)
153 {
154     guint32 *word;
155     gint     i, step;
156     gint     column = 0;
157
158     step = sizeof (*word);
159
160     for (i = 0; i < len; i += step) {
161         word = (guint *) &buffer[i];
162
163         if (column == 0) {
164             /* new row */
165             gtk_list_store_append (store, iter);
166
167             /* add address */
168             mem_view_add_as_hex (store, iter, column, (address + i));
169         }
170         mem_view_add_as_hex (store, iter, (column + 1), *word);
171         column = (column + 1) % step;
172     }
173 }
174
175 static guint32
176 hexstr_to_guint32 (const gchar *str, GError **err)
177 {
178     guint32  val;
179     gchar   *end_ptr;
180
181     val = strtoul (str, &end_ptr, 16);
182     if ((errno == ERANGE && val == LONG_MAX) || (errno != 0 && val == 0)) {
183         g_set_error (err,
184                 g_quark_from_string ("hextou32"),
185                 1,
186                 "Invalid hexstring");
187         return UINT32_MAX;
188     }
189     if (end_ptr == str) {
190         g_set_error (err,
191                 g_quark_from_string ("hextou32"),
192                 2,
193                 "Invalid hexstring");
194         return UINT32_MAX;
195     }
196     return val;
197 }
198
199
200 static void
201 stlink_gui_update_mem_view (STlinkGUI *gui, struct mem_t *mem, GtkTreeView *view) {
202     GtkListStore *store;
203     GtkTreeIter   iter;
204
205     store = GTK_LIST_STORE (gtk_tree_view_get_model (view));
206
207     mem_view_add_buffer (store,
208             &iter,
209             mem->base,
210             mem->memory,
211             mem->size);
212
213     gtk_widget_hide (GTK_WIDGET (gui->progress.bar));
214     gtk_progress_bar_set_fraction (gui->progress.bar, 0);
215     stlink_gui_set_sensitivity (gui, TRUE);
216 }
217
218 static gboolean
219 stlink_gui_update_devmem_view (STlinkGUI *gui)
220 {
221     stlink_gui_update_mem_view (gui, &gui->flash_mem, gui->devmem_treeview);
222     return FALSE;
223 }
224
225
226 static void
227 stlink_gui_populate_devmem_view (STlinkGUI *gui)
228 {
229     guint            off;
230     stm32_addr_t     addr;
231
232     g_return_if_fail (gui != NULL);
233     g_return_if_fail (gui->sl != NULL);
234
235     addr = gui->sl->flash_base;
236
237     if (gui->flash_mem.memory) {
238         g_free (gui->flash_mem.memory);
239     }
240     gui->flash_mem.memory = g_malloc (gui->sl->flash_size);
241     gui->flash_mem.size   = gui->sl->flash_size;
242     gui->flash_mem.base   = gui->sl->flash_base;
243
244     for (off = 0; off < gui->sl->flash_size; off += MEM_READ_SIZE) {
245         guint   n_read = MEM_READ_SIZE;
246
247         if (off + MEM_READ_SIZE > gui->sl->flash_size) {
248             n_read = gui->sl->flash_size - off;
249
250             /* align if needed */
251             if (n_read & 3) {
252                 n_read = (n_read + 4) & ~(3);
253             }
254         }
255         /* reads to sl->q_buf */
256         stlink_read_mem32(gui->sl, addr + off, n_read);
257         if (gui->sl->q_len < 0) {
258             stlink_gui_set_info_error_message (gui, "Failed to read memory");
259             g_free (gui->flash_mem.memory);
260             gui->flash_mem.memory = NULL;
261             return;
262         }
263         memcpy (gui->flash_mem.memory + off, gui->sl->q_buf, n_read);
264         gui->progress.fraction = (gdouble) (off + n_read) / gui->sl->flash_size;
265     }
266     g_idle_add ((GSourceFunc) stlink_gui_update_devmem_view, gui);
267 }
268
269 static gboolean
270 stlink_gui_update_filemem_view (STlinkGUI *gui)
271 {
272     gchar *basename;
273
274     basename = g_path_get_basename (gui->filename);
275     gtk_notebook_set_tab_label_text (gui->notebook,
276             GTK_WIDGET (gtk_notebook_get_nth_page (gui->notebook, 1)),
277             basename);
278     g_free (basename);
279
280     stlink_gui_update_mem_view (gui, &gui->file_mem, gui->filemem_treeview);
281
282     return FALSE;
283 }
284
285 static gpointer
286 stlink_gui_populate_filemem_view (STlinkGUI *gui)
287 {
288     guchar        buffer[MEM_READ_SIZE];
289     GFile        *file;
290     GFileInfo    *file_info;
291     GInputStream *input_stream;
292     gint          off;
293     GError       *err = NULL;
294
295     g_return_val_if_fail (gui != NULL, NULL);
296     g_return_val_if_fail (gui->filename != NULL, NULL);
297
298     file = g_file_new_for_path (gui->filename);
299     input_stream = G_INPUT_STREAM (g_file_read (file, NULL, &err));
300     if (err) {
301         stlink_gui_set_info_error_message (gui, err->message);
302         g_error_free (err);
303         goto out;
304     }
305
306     file_info = g_file_input_stream_query_info (G_FILE_INPUT_STREAM (input_stream),
307             G_FILE_ATTRIBUTE_STANDARD_SIZE, NULL, &err);
308     if (err) {
309         stlink_gui_set_info_error_message (gui, err->message);
310         g_error_free (err);
311         goto out_input;
312     }
313     if (gui->file_mem.memory) {
314         g_free (gui->file_mem.memory);
315     }
316     gui->file_mem.size   = g_file_info_get_size (file_info);
317     gui->file_mem.memory = g_malloc (gui->file_mem.size);
318
319     for (off = 0; off < gui->file_mem.size; off += MEM_READ_SIZE) {
320         guint   n_read = MEM_READ_SIZE;
321
322         if (off + MEM_READ_SIZE > gui->file_mem.size) {
323             n_read = gui->file_mem.size - off;
324         }
325
326         if (g_input_stream_read (G_INPUT_STREAM (input_stream),
327                     &buffer, n_read, NULL, &err) == -1) {
328             stlink_gui_set_info_error_message (gui, err->message);
329             g_error_free (err);
330             goto out_input;
331         }
332         memcpy (gui->file_mem.memory + off, buffer, n_read);
333         gui->progress.fraction = (gdouble) (off + n_read) / gui->file_mem.size;
334     }
335     g_idle_add ((GSourceFunc) stlink_gui_update_filemem_view, gui);
336
337 out_input:
338     g_object_unref (input_stream);
339 out:
340     g_object_unref (file);
341     return NULL;
342 }
343
344 static void mem_jmp (GtkTreeView *view,
345                      GtkEntry    *entry,
346                      guint32      base_addr,
347                      gsize        size,
348                      GError     **err)
349 {
350     GtkTreeModel *model;
351     guint32       jmp_addr;
352     GtkTreeIter   iter;
353
354     jmp_addr = hexstr_to_guint32 (gtk_entry_get_text (entry), err);
355     if (err && *err) {
356         return;
357     }
358
359     if (jmp_addr < base_addr || jmp_addr > base_addr + size) {
360         g_set_error (err,
361                 g_quark_from_string ("mem_jmp"),
362                 1,
363                 "Invalid address");
364         return;
365     }
366
367     model = gtk_tree_view_get_model (view);
368     if (!model) {
369         return;
370     }
371
372     if (gtk_tree_model_get_iter_first (model, &iter)) {
373         do {
374             guint32 addr;
375             GValue  value = G_VALUE_INIT;
376             GError *err   = NULL;
377
378             gtk_tree_model_get_value (model, &iter, 0, &value);
379             if (G_VALUE_HOLDS_STRING (&value)) {
380                 addr = hexstr_to_guint32 (g_value_get_string (&value), &err);
381                 if (!err) {
382                     if (addr == (jmp_addr & 0xFFFFFFF0)) {
383                         GtkTreeSelection *selection;
384                         GtkTreePath      *path;
385
386                         selection = gtk_tree_view_get_selection (view);
387                         path      = gtk_tree_model_get_path (model, &iter);
388
389                         gtk_tree_selection_select_iter (selection, &iter);
390                         gtk_tree_view_scroll_to_cell (view,
391                                 path,
392                                 NULL,
393                                 TRUE,
394                                 0.0,
395                                 0.0);
396                         gtk_tree_path_free (path);
397                     }
398                 }
399             }
400             g_value_unset (&value);
401         } while (gtk_tree_model_iter_next (model, &iter));
402     }
403 }
404
405 static void
406 devmem_jmp_cb (GtkWidget *widget, gpointer data)
407 {
408     STlinkGUI *gui;
409     GError    *err = NULL;
410
411     gui = STLINK_GUI (data);
412
413     mem_jmp (gui->devmem_treeview,
414             gui->devmem_jmp_entry,
415             gui->sl->flash_base,
416             gui->sl->flash_size,
417             &err);
418
419     if (err) {
420         stlink_gui_set_info_error_message (gui, err->message);
421         g_error_free (err);
422     }
423 }
424
425 static void
426 filemem_jmp_cb (GtkWidget *widget, gpointer data)
427 {
428     STlinkGUI *gui;
429     GError    *err = NULL;
430
431     gui = STLINK_GUI (data);
432
433     g_return_if_fail (gui->filename != NULL);
434
435     mem_jmp (gui->filemem_treeview,
436             gui->filemem_jmp_entry,
437             0,
438             gui->file_mem.size,
439             &err);
440
441     if (err) {
442         stlink_gui_set_info_error_message (gui, err->message);
443         g_error_free (err);
444     }
445 }
446
447 static gchar *
448 dev_format_chip_id (guint32 chip_id)
449 {
450     const struct stlink_chipid_params *params;
451     gint i;
452
453     params = stlink_chipid_get_params(chip_id);
454     if (!params)
455         return g_strdup_printf ("0x%x", chip_id);
456
457     return g_strdup (params->description);
458 }
459
460 static gchar *
461 dev_format_mem_size (gsize flash_size)
462 {
463     return g_strdup_printf ("%u kB", flash_size / 1024);
464 }
465
466
467 static void
468 stlink_gui_set_connected (STlinkGUI *gui)
469 {
470     gchar        *tmp_str;
471     GtkListStore *store;
472     GtkTreeIter   iter;
473
474     gtk_statusbar_push (gui->statusbar,
475             gtk_statusbar_get_context_id (gui->statusbar, "conn"),
476             "Connected");
477
478     gtk_widget_set_sensitive (GTK_WIDGET (gui->device_frame), TRUE);
479     gtk_widget_set_sensitive (GTK_WIDGET (gui->devmem_box), TRUE);
480     gtk_widget_set_sensitive (GTK_WIDGET (gui->connect_button), FALSE);
481
482     if (gui->filename) {
483         gtk_widget_set_sensitive (GTK_WIDGET (gui->flash_button), TRUE);
484     }
485
486     tmp_str = dev_format_chip_id (gui->sl->chip_id);
487     gtk_label_set_text (gui->chip_id_label, tmp_str);
488     g_free (tmp_str);
489
490     tmp_str = g_strdup_printf ("0x%x", gui->sl->core_id);
491     gtk_label_set_text (gui->core_id_label, tmp_str);
492     g_free (tmp_str);
493
494     tmp_str = dev_format_mem_size (gui->sl->flash_size);
495     gtk_label_set_text (gui->flash_size_label, tmp_str);
496     g_free (tmp_str);
497
498     tmp_str = dev_format_mem_size (gui->sl->sram_size);
499     gtk_label_set_text (gui->ram_size_label, tmp_str);
500     g_free (tmp_str);
501
502     tmp_str = g_strdup_printf ("0x%08X", gui->sl->flash_base);
503     gtk_entry_set_text (gui->devmem_jmp_entry, tmp_str);
504     gtk_editable_set_editable (GTK_EDITABLE (gui->devmem_jmp_entry), TRUE);
505     g_free (tmp_str);
506
507     store = GTK_LIST_STORE (gtk_tree_view_get_model (gui->devmem_treeview));
508     if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter)) {
509         gtk_list_store_clear (store);
510     }
511
512     stlink_gui_set_sensitivity (gui, FALSE);
513     gtk_notebook_set_current_page (gui->notebook, PAGE_DEVMEM);
514     gtk_widget_show (GTK_WIDGET (gui->progress.bar));
515     gtk_progress_bar_set_text (gui->progress.bar, "Reading memory");
516
517     g_thread_new ("devmem", (GThreadFunc) stlink_gui_populate_devmem_view, gui);
518 }
519
520 static void
521 connect_button_cb (GtkWidget *widget, gpointer data)
522 {
523     STlinkGUI *gui;
524     gint       i;
525
526     gui = STLINK_GUI (data);
527
528     if (gui->sl != NULL)
529         return;
530
531     /* try version 1 then version 2 */
532     gui->sl = stlink_v1_open(0, 1);
533     if (gui->sl == NULL) {
534             gui->sl = stlink_open_usb(0, 1, NULL);
535     }
536     if (gui->sl == NULL) {
537         stlink_gui_set_info_error_message (gui, "Failed to connect to STLink.");                return;
538     }
539
540     /* code below taken from flash/main.c, refactoring might be in order */
541     if (stlink_current_mode(gui->sl) == STLINK_DEV_DFU_MODE)
542         stlink_exit_dfu_mode(gui->sl);
543
544     if (stlink_current_mode(gui->sl) != STLINK_DEV_DEBUG_MODE)
545         stlink_enter_swd_mode(gui->sl);
546
547     /* Disable DMA - Set All DMA CCR Registers to zero. - AKS 1/7/2013 */
548     if (gui->sl->chip_id == STLINK_CHIPID_STM32_F4) {
549         memset(gui->sl->q_buf, 0, 4);
550         for (i = 0; i < 8; i++) {
551             stlink_write_mem32(gui->sl, 0x40026000 + 0x10 + 0x18 * i, 4);
552             stlink_write_mem32(gui->sl, 0x40026400 + 0x10 + 0x18 * i, 4);
553             stlink_write_mem32(gui->sl, 0x40026000 + 0x24 + 0x18 * i, 4);
554             stlink_write_mem32(gui->sl, 0x40026400 + 0x24 + 0x18 * i, 4);
555         }
556     }
557     stlink_gui_set_connected (gui);
558 }
559
560 static void stlink_gui_set_disconnected (STlinkGUI *gui)
561 {
562     gtk_statusbar_push (gui->statusbar,
563             gtk_statusbar_get_context_id (gui->statusbar, "conn"),
564             "Disconnected");
565
566     gtk_widget_set_sensitive (GTK_WIDGET (gui->device_frame), FALSE);
567     gtk_widget_set_sensitive (GTK_WIDGET (gui->flash_button), FALSE);
568     gtk_widget_set_sensitive (GTK_WIDGET (gui->disconnect_button), FALSE);
569     gtk_widget_set_sensitive (GTK_WIDGET (gui->connect_button), TRUE);
570 }
571
572 static void
573 disconnect_button_cb (GtkWidget *widget, gpointer data)
574 {
575     STlinkGUI *gui;
576
577     gui = STLINK_GUI (data);
578
579     if (gui->sl != NULL) {
580         stlink_exit_debug_mode(gui->sl);
581         stlink_close(gui->sl);
582         gui->sl = NULL;
583     }
584     stlink_gui_set_disconnected (gui);
585 }
586
587
588 static void
589 stlink_gui_open_file (STlinkGUI *gui)
590 {
591     GtkWidget    *dialog;
592     GtkListStore *store;
593     GtkTreeIter   iter;
594
595     dialog = gtk_file_chooser_dialog_new ("Open file",
596             gui->window,
597             GTK_FILE_CHOOSER_ACTION_OPEN,
598             GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
599             GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
600             NULL);
601
602     if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
603         gui->filename =
604             gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
605
606         store = GTK_LIST_STORE (gtk_tree_view_get_model (gui->filemem_treeview));
607         if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter)) {
608             gtk_list_store_clear (store);
609         }
610
611         stlink_gui_set_sensitivity (gui, FALSE);
612         gtk_notebook_set_current_page (gui->notebook, PAGE_FILEMEM);
613         gtk_widget_show (GTK_WIDGET (gui->progress.bar));
614         gtk_progress_bar_set_text (gui->progress.bar, "Reading file");
615         g_thread_new ("file", (GThreadFunc) stlink_gui_populate_filemem_view, gui);
616     }
617     gtk_widget_destroy (dialog);
618 }
619
620 static void
621 open_button_cb (GtkWidget *widget, gpointer data)
622 {
623     STlinkGUI    *gui;
624
625     gui = STLINK_GUI (data);
626
627     stlink_gui_open_file (gui);
628 }
629
630 static gboolean
631 stlink_gui_write_flash_update (STlinkGUI *gui)
632 {
633     stlink_gui_set_sensitivity (gui, TRUE);
634     gui->progress.activity_mode = FALSE;
635     gtk_widget_hide (GTK_WIDGET (gui->progress.bar));
636
637     return FALSE;
638 }
639
640 static void
641 stlink_gui_write_flash (STlinkGUI *gui)
642 {
643     g_return_if_fail (gui->sl != NULL);
644     g_return_if_fail (gui->filename != NULL);
645
646     if (stlink_fwrite_flash(gui->sl, gui->filename, gui->sl->flash_base) < 0) {
647         stlink_gui_set_info_error_message (gui, "Failed to write to flash");
648     }
649
650     g_idle_add ((GSourceFunc) stlink_gui_write_flash_update, gui);
651 }
652
653 static void
654 flash_button_cb (GtkWidget *widget, gpointer data)
655 {
656     STlinkGUI *gui;
657     gchar     *tmp_str;
658     guint32    address;
659     gint       result;
660     GError    *err = NULL;
661
662     gui = STLINK_GUI (data);
663     g_return_if_fail (gui->sl != NULL);
664
665     if (!g_strcmp0 (gtk_entry_get_text (gui->flash_dialog_entry), "")) {
666         tmp_str = g_strdup_printf ("0x%08X", gui->sl->flash_base);
667         gtk_entry_set_text (gui->flash_dialog_entry, tmp_str);
668         g_free (tmp_str);
669     }
670
671     result = gtk_dialog_run (gui->flash_dialog);
672     if (result == GTK_RESPONSE_OK) {
673         address = hexstr_to_guint32 (gtk_entry_get_text (gui->flash_dialog_entry),
674                 &err);
675         if (err) {
676             stlink_gui_set_info_error_message (gui, err->message);
677         } else {
678             if (address > gui->sl->flash_base + gui->sl->flash_size ||
679                     address < gui->sl->flash_base) {
680                 stlink_gui_set_info_error_message (gui, "Invalid address");
681             }
682             else if (address + gui->file_mem.size >
683                     gui->sl->flash_base + gui->sl->flash_size) {
684                 stlink_gui_set_info_error_message (gui, "Binary overwrites flash");
685             } else {
686                 stlink_gui_set_sensitivity (gui, FALSE);
687                 gtk_progress_bar_set_text (gui->progress.bar,
688                         "Writing to flash");
689                 gui->progress.activity_mode = TRUE;
690                 gtk_widget_show (GTK_WIDGET (gui->progress.bar));
691                 g_thread_new ("flash",
692                         (GThreadFunc) stlink_gui_write_flash, gui);
693             }
694         }
695     }
696 }
697
698 static gboolean
699 progress_pulse_timeout (STlinkGUI *gui) {
700     if (gui->progress.activity_mode) {
701         gtk_progress_bar_pulse (gui->progress.bar);
702     } else {
703         gtk_progress_bar_set_fraction (gui->progress.bar, gui->progress.fraction);
704     }
705     return TRUE;
706 }
707
708 static void
709 notebook_switch_page_cb (GtkNotebook *notebook,
710                          GtkWidget   *widget,
711                          guint        page_num,
712                          gpointer     data)
713 {
714     STlinkGUI *gui;
715
716     gui = STLINK_GUI (data);
717
718     if (page_num == 1) {
719         if (gui->filename == NULL) {
720             stlink_gui_open_file (gui);
721         }
722     }
723 }
724
725 static void
726 dnd_received_cb (GtkWidget *widget,
727                  GdkDragContext *context,
728                  gint x,
729                  gint y,
730                  GtkSelectionData *selection_data,
731                  guint target_type,
732                  guint time,
733                  gpointer data)
734 {
735     GFile        *file_uri;
736     gchar       **file_list;
737     const guchar *file_data;
738     STlinkGUI    *gui = STLINK_GUI (data);
739     GtkListStore *store;
740     GtkTreeIter   iter;
741
742     if (selection_data != NULL &&
743             gtk_selection_data_get_length (selection_data) > 0) {
744         switch (target_type) {
745         case TARGET_FILENAME:
746
747             if (gui->filename) {
748                 g_free (gui->filename);
749             }
750
751             file_data = gtk_selection_data_get_data (selection_data);
752             file_list = g_strsplit ((gchar *)file_data, "\r\n", 0);
753
754             file_uri = g_file_new_for_uri (file_list[0]);
755             gui->filename = g_file_get_path (file_uri);
756
757             g_strfreev (file_list);
758             g_object_unref (file_uri);
759
760
761             store = GTK_LIST_STORE (gtk_tree_view_get_model (gui->devmem_treeview));
762             if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter)) {
763                 gtk_list_store_clear (store);
764             }
765
766             stlink_gui_set_sensitivity (gui, FALSE);
767             gtk_notebook_set_current_page (gui->notebook, PAGE_FILEMEM);
768             gtk_widget_show (GTK_WIDGET (gui->progress.bar));
769             gtk_progress_bar_set_text (gui->progress.bar, "Reading file");
770             g_thread_new ("file", (GThreadFunc) stlink_gui_populate_filemem_view, gui);
771             break;
772         }
773     }
774     gtk_drag_finish (context,
775                      TRUE,
776                      gdk_drag_context_get_suggested_action (context) == GDK_ACTION_MOVE,
777                      time);
778 }
779
780 void
781 stlink_gui_init_dnd (STlinkGUI *gui)
782 {
783     GtkTargetEntry target_list[] = {
784         { "text/uri-list", 0, TARGET_FILENAME },
785     };
786
787     gtk_drag_dest_set (GTK_WIDGET (gui->window),
788                        GTK_DEST_DEFAULT_ALL,
789                        target_list,
790                        G_N_ELEMENTS (target_list),
791                        GDK_ACTION_COPY);
792
793     g_signal_connect (gui->window, "drag-data-received",
794                       G_CALLBACK (dnd_received_cb), gui);
795 }
796
797 static void
798 stlink_gui_build_ui (STlinkGUI *gui) {
799     GtkBuilder   *builder;
800     GtkListStore *devmem_store;
801     GtkListStore *filemem_store;
802     gchar *ui_file = STLINK_UI_DIR "/stlink-gui.ui";
803
804     if (!g_file_test (ui_file, G_FILE_TEST_EXISTS)) {
805         ui_file = "stlink-gui.ui";
806     }
807     builder = gtk_builder_new ();
808     if (!gtk_builder_add_from_file (builder, ui_file, NULL)) {
809         g_printerr ("Failed to load UI file: %s\n", ui_file);
810         exit (1);
811     }
812
813     gui->window = GTK_WINDOW (gtk_builder_get_object (builder, "window"));
814     g_signal_connect (G_OBJECT (gui->window), "destroy",
815             G_CALLBACK (gtk_main_quit), NULL);
816
817     /* set up toolutton clicked callbacks */
818     gui->open_button =
819         GTK_TOOL_BUTTON (gtk_builder_get_object (builder, "open_button"));
820     g_signal_connect (G_OBJECT (gui->open_button), "clicked",
821             G_CALLBACK (open_button_cb), gui);
822
823     gui->connect_button =
824         GTK_TOOL_BUTTON (gtk_builder_get_object (builder, "connect_button"));
825     g_signal_connect (G_OBJECT (gui->connect_button), "clicked",
826             G_CALLBACK (connect_button_cb), gui);
827
828     gui->disconnect_button =
829         GTK_TOOL_BUTTON (gtk_builder_get_object (builder, "disconnect_button"));
830     g_signal_connect (G_OBJECT (gui->disconnect_button), "clicked",
831             G_CALLBACK (disconnect_button_cb), gui);
832
833     gui->flash_button =
834         GTK_TOOL_BUTTON (gtk_builder_get_object (builder, "flash_button"));
835     g_signal_connect (G_OBJECT (gui->flash_button), "clicked",
836             G_CALLBACK (flash_button_cb), gui);
837
838     gui->devmem_treeview =
839         GTK_TREE_VIEW (gtk_builder_get_object (builder, "devmem_treeview"));
840     gtk_tree_view_set_rules_hint (gui->devmem_treeview, TRUE);
841     mem_view_init_headers (gui->devmem_treeview);
842     devmem_store = gtk_list_store_new (5,
843             G_TYPE_STRING,
844             G_TYPE_STRING,
845             G_TYPE_STRING,
846             G_TYPE_STRING,
847             G_TYPE_STRING);
848     gtk_tree_view_set_model (gui->devmem_treeview, GTK_TREE_MODEL (devmem_store));
849     g_object_unref (devmem_store);
850
851     gui->filemem_treeview =
852         GTK_TREE_VIEW (gtk_builder_get_object (builder, "filemem_treeview"));
853     gtk_tree_view_set_rules_hint (gui->filemem_treeview, TRUE);
854     mem_view_init_headers (gui->filemem_treeview);
855     filemem_store = gtk_list_store_new (5,
856             G_TYPE_STRING,
857             G_TYPE_STRING,
858             G_TYPE_STRING,
859             G_TYPE_STRING,
860             G_TYPE_STRING);
861     gtk_tree_view_set_model (gui->filemem_treeview, GTK_TREE_MODEL (filemem_store));
862     g_object_unref (filemem_store);
863
864     gui->core_id_label =
865         GTK_LABEL (gtk_builder_get_object (builder, "core_id_value"));
866
867     gui->chip_id_label =
868         GTK_LABEL (gtk_builder_get_object (builder, "chip_id_value"));
869
870     gui->flash_size_label =
871         GTK_LABEL (gtk_builder_get_object (builder, "flash_size_value"));
872
873     gui->ram_size_label =
874         GTK_LABEL (gtk_builder_get_object (builder, "ram_size_value"));
875
876     gui->device_frame =
877         GTK_FRAME (gtk_builder_get_object (builder, "device_frame"));
878
879     gui->notebook =
880         GTK_NOTEBOOK (gtk_builder_get_object (builder, "mem_notebook"));
881     g_signal_connect (gui->notebook, "switch-page",
882             G_CALLBACK (notebook_switch_page_cb), gui);
883
884     gui->devmem_box =
885         GTK_BOX (gtk_builder_get_object (builder, "devmem_box"));
886
887     gui->filemem_box =
888         GTK_BOX (gtk_builder_get_object (builder, "filemem_box"));
889
890     gui->devmem_jmp_entry =
891         GTK_ENTRY (gtk_builder_get_object (builder, "devmem_jmp_entry"));
892     g_signal_connect (gui->devmem_jmp_entry, "activate",
893             G_CALLBACK (devmem_jmp_cb), gui);
894
895     gui->filemem_jmp_entry =
896         GTK_ENTRY (gtk_builder_get_object (builder, "filemem_jmp_entry"));
897     g_signal_connect (gui->filemem_jmp_entry, "activate",
898             G_CALLBACK (filemem_jmp_cb), gui);
899     gtk_editable_set_editable (GTK_EDITABLE (gui->filemem_jmp_entry), TRUE);
900
901     gui->progress.bar =
902         GTK_PROGRESS_BAR (gtk_builder_get_object (builder, "progressbar"));
903     gtk_progress_bar_set_show_text (gui->progress.bar, TRUE);
904     gui->progress.timer = g_timeout_add (100,
905             (GSourceFunc) progress_pulse_timeout,
906             gui);
907
908     gui->statusbar =
909         GTK_STATUSBAR (gtk_builder_get_object (builder, "statusbar"));
910
911     gui->infobar =
912         GTK_INFO_BAR (gtk_builder_get_object (builder, "infobar"));
913     gtk_info_bar_add_button (gui->infobar, GTK_STOCK_OK, GTK_RESPONSE_OK);
914     gui->infolabel = GTK_LABEL (gtk_label_new (""));
915     gtk_container_add (GTK_CONTAINER (gtk_info_bar_get_content_area (gui->infobar)),
916             GTK_WIDGET (gui->infolabel));
917     g_signal_connect (gui->infobar, "response", G_CALLBACK (gtk_widget_hide), NULL);
918
919     /* flash dialog */
920     gui->flash_dialog =
921         GTK_DIALOG (gtk_builder_get_object (builder, "flash_dialog"));
922     g_signal_connect_swapped (gui->flash_dialog, "response",
923             G_CALLBACK (gtk_widget_hide), gui->flash_dialog);
924
925     gui->flash_dialog_ok =
926         GTK_BUTTON (gtk_builder_get_object (builder, "flash_dialog_ok_button"));
927
928     gui->flash_dialog_cancel =
929         GTK_BUTTON (gtk_builder_get_object (builder, "flash_dialog_cancel_button"));
930
931     gui->flash_dialog_entry =
932         GTK_ENTRY (gtk_builder_get_object (builder, "flash_dialog_entry"));
933
934     /* make it so */
935     gtk_widget_show_all (GTK_WIDGET (gui->window));
936     gtk_widget_hide (GTK_WIDGET (gui->infobar));
937     gtk_widget_hide (GTK_WIDGET (gui->progress.bar));
938
939     stlink_gui_set_disconnected (gui);
940 }
941
942 int
943 main (int argc, char **argv)
944 {
945     STlinkGUI *gui;
946
947     gtk_init (&argc, &argv);
948
949     gui = g_object_new (STLINK_TYPE_GUI, NULL);
950     stlink_gui_build_ui (gui);
951     stlink_gui_init_dnd (gui);
952
953     gtk_main ();
954
955     return 0;
956 }