]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - LinuxGUI/Ventoy2Disk/GTK/ventoy_gtk.c
1.1.07 release
[Ventoy.git] / LinuxGUI / Ventoy2Disk / GTK / ventoy_gtk.c
1 /******************************************************************************
2 * ventoy_gtk.c
3 *
4 * Copyright (c) 2021, longpanda <admin@ventoy.net>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 3 of the
9 * License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <stdint.h>
24 #include <string.h>
25 #include <stdarg.h>
26 #include <errno.h>
27 #include <time.h>
28 #include <unistd.h>
29 #include <linux/limits.h>
30
31 #include <ventoy_define.h>
32 #include <ventoy_json.h>
33 #include <ventoy_util.h>
34 #include <ventoy_disk.h>
35 #include <ventoy_http.h>
36
37 #include <gtk/gtk.h>
38 #include <gdk/gdkkeysyms.h>
39 #include "ventoy_gtk.h"
40
41 int g_secure_boot_support = 0;
42 GtkWidget *g_topWindow = NULL;
43 GtkWidget *g_partCfgWindow = NULL;
44 GtkBuilder *g_pXmlBuilder = NULL;
45 GtkComboBoxText *g_dev_combobox = NULL;
46 GtkButton *g_refresh_button = NULL;
47 GtkButton *g_install_button = NULL;
48 GtkButton *g_update_button = NULL;
49 GtkMenu *g_lang_menu = NULL;;
50 GtkCheckMenuItem *g_menu_item_secure_boot = NULL;
51 GtkCheckMenuItem *g_menu_item_mbr = NULL;
52 GtkCheckMenuItem *g_menu_item_gpt = NULL;
53 GtkCheckMenuItem *g_menu_item_show_all = NULL;
54 GtkLabel *g_device_title = NULL;
55 GtkLabel *g_label_local_part_style = NULL;
56 GtkLabel *g_label_dev_part_style = NULL;
57 GtkLabel *g_label_local_ver = NULL;
58 GtkLabel *g_label_disk_ver = NULL;
59 GtkLabel *g_label_status = NULL;
60 GtkImage *g_image_secure_local = NULL;
61 GtkImage *g_image_secure_device = NULL;
62 GtkToggleButton *g_part_align_checkbox = NULL;
63 GtkToggleButton *g_part_preserve_checkbox = NULL;
64 GtkEntry *g_part_reserve_space_value = NULL;
65 GtkComboBoxText *g_part_space_unit_combox = NULL;
66 GtkProgressBar *g_progress_bar = NULL;
67 VTOY_JSON *g_languages_json = NULL;
68 int g_languages_toggled_proc = 0;
69 int g_dev_changed_proc = 0;
70 gboolean g_align_part_with_4k = TRUE;
71 gboolean g_preserve_space_check = FALSE;
72 int g_preserve_space_unit = 1;
73 int g_preserve_space_number = 0;
74 gboolean g_thread_run = FALSE;
75
76 const char *language_string(const char *id)
77 {
78 const char *pName = NULL;
79 VTOY_JSON *node = NULL;
80 const char *pCurLang = ventoy_code_get_cur_language();
81
82 for (node = g_languages_json->pstChild; node; node = node->pstNext)
83 {
84 pName = vtoy_json_get_string_ex(node->pstChild, "name");
85 if (0 == g_strcmp0(pName, pCurLang))
86 {
87 break;
88 }
89 }
90
91 if (NULL == node)
92 {
93 return "xxx";
94 }
95
96 return vtoy_json_get_string_ex(node->pstChild, id);
97 }
98
99 int msgbox(GtkMessageType type, GtkButtonsType buttons, const char *strid)
100 {
101 int ret;
102 GtkWidget *pMsgBox = NULL;
103
104 pMsgBox = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, type, buttons, "%s", language_string(strid));
105
106 ret = gtk_dialog_run(GTK_DIALOG(pMsgBox));
107 gtk_widget_destroy(pMsgBox);
108
109 return ret;
110 }
111
112
113 static void set_item_visible(const char *id, int visible)
114 {
115 GtkWidget *pWidget = NULL;
116
117 pWidget = GTK_WIDGET(gtk_builder_get_object(g_pXmlBuilder, id));
118 if (visible)
119 {
120 gtk_widget_show(pWidget);
121 }
122 else
123 {
124 gtk_widget_hide(pWidget);
125 }
126 }
127
128 static void init_part_style_menu(void)
129 {
130 int style;
131
132 style = ventoy_code_get_cur_part_style();
133 gtk_check_menu_item_set_active(g_menu_item_mbr, (0 == style));
134 gtk_check_menu_item_set_active(g_menu_item_gpt, (1 == style));
135 gtk_label_set_text(g_label_local_part_style, style ? "GPT" : "MBR");
136 }
137
138 static void select_language(const char *lang)
139 {
140 const char *pName = NULL;
141 const char *pPos = NULL;
142 const char *pDevice = NULL;
143 VTOY_JSON *node = NULL;
144 char device[256];
145
146 for (node = g_languages_json->pstChild; node; node = node->pstNext)
147 {
148 pName = vtoy_json_get_string_ex(node->pstChild, "name");
149 if (0 == g_strcmp0(pName, lang))
150 {
151 break;
152 }
153 }
154
155 if (NULL == node)
156 {
157 return;
158 }
159
160 pDevice = gtk_label_get_text(g_device_title);
161 if (pDevice && (pPos = strchr(pDevice, '[')) != NULL)
162 {
163 g_snprintf(device, sizeof(device), "%s %s", vtoy_json_get_string_ex(node->pstChild, "STR_DEVICE"), pPos);
164 gtk_label_set_text(g_device_title, device);
165 }
166 else
167 {
168 gtk_label_set_text(g_device_title, vtoy_json_get_string_ex(node->pstChild, "STR_DEVICE"));
169 }
170
171 LANG_LABEL_TEXT("label_local_ver", "STR_LOCAL_VER");
172 LANG_LABEL_TEXT("label_device_ver", "STR_DISK_VER");
173
174 LANG_LABEL_TEXT("label_status", "STR_STATUS");
175
176 LANG_BUTTON_TEXT("button_install", "STR_INSTALL");
177 LANG_BUTTON_TEXT("button_update", "STR_UPDATE");
178
179 LANG_MENU_ITEM_TEXT("menu_option", "STR_MENU_OPTION");
180 LANG_MENU_ITEM_TEXT("menu_item_secure", "STR_MENU_SECURE_BOOT");
181 LANG_MENU_ITEM_TEXT("menu_part_style", "STR_MENU_PART_STYLE");
182 LANG_MENU_ITEM_TEXT("menu_item_part_cfg", "STR_MENU_PART_CFG");
183 LANG_MENU_ITEM_TEXT("menu_item_clear", "STR_MENU_CLEAR");
184 LANG_MENU_ITEM_TEXT("menu_item_show_all", "STR_SHOW_ALL_DEV");
185
186 LANG_BUTTON_TEXT("space_check_btn", "STR_PRESERVE_SPACE");
187 LANG_BUTTON_TEXT("space_align_btn", "STR_PART_ALIGN_4KB");
188 LANG_BUTTON_TEXT("button_partcfg_ok", "STR_BTN_OK");
189 LANG_BUTTON_TEXT("button_partcfg_cancel", "STR_BTN_CANCEL");
190
191 gtk_window_set_title(GTK_WINDOW(g_partCfgWindow), vtoy_json_get_string_ex(node->pstChild, "STR_MENU_PART_CFG"));
192
193 /*
194 * refresh screen
195 */
196
197 gtk_widget_hide(g_topWindow);
198 gtk_widget_show(g_topWindow);
199 }
200
201
202 void on_secure_boot_toggled(GtkMenuItem *menuItem, gpointer data)
203 {
204 g_secure_boot_support = 1 - g_secure_boot_support;
205
206 if (g_secure_boot_support)
207 {
208 gtk_widget_show((GtkWidget *)g_image_secure_local);
209 }
210 else
211 {
212 gtk_widget_hide((GtkWidget *)g_image_secure_local);
213 }
214 }
215
216 void on_devlist_changed(GtkWidget *widget, gpointer data)
217 {
218 int active;
219 ventoy_disk *cur = NULL;
220 char version[512];
221
222 if (g_dev_changed_proc == 0)
223 {
224 return;
225 }
226
227 gtk_widget_set_sensitive(GTK_WIDGET(g_update_button), FALSE);
228
229 gtk_widget_hide((GtkWidget *)g_image_secure_device);
230 gtk_label_set_markup(g_label_disk_ver, "");
231 gtk_label_set_text(g_label_dev_part_style, "");
232
233 active = gtk_combo_box_get_active((GtkComboBox *)g_dev_combobox);
234 if (active < 0 || active >= g_disk_num)
235 {
236 vlog("invalid active combox id %d\n", active);
237 return;
238 }
239
240 cur = g_disk_list + active;
241 if (cur->vtoydata.ventoy_valid)
242 {
243 if (cur->vtoydata.secure_boot_flag)
244 {
245 gtk_widget_show((GtkWidget *)g_image_secure_device);
246 }
247 else
248 {
249 gtk_widget_hide((GtkWidget *)g_image_secure_device);
250 }
251
252 if (g_secure_boot_support != cur->vtoydata.secure_boot_flag)
253 {
254 gtk_check_menu_item_set_active(g_menu_item_secure_boot, 1 - g_secure_boot_support);
255 }
256
257 g_snprintf(version, sizeof(version), VTOY_VER_FMT, cur->vtoydata.ventoy_ver);
258 gtk_label_set_markup(g_label_disk_ver, version);
259 gtk_label_set_text(g_label_dev_part_style, cur->vtoydata.partition_style ? "GPT" : "MBR");
260
261 gtk_widget_set_sensitive(GTK_WIDGET(g_update_button), TRUE);
262 }
263 else
264 {
265 if (!g_secure_boot_support)
266 {
267 gtk_check_menu_item_set_active(g_menu_item_secure_boot, 1 - g_secure_boot_support);
268 }
269 }
270 }
271
272
273 void on_language_toggled(GtkMenuItem *menuItem, gpointer data)
274 {
275 const char *cur_lang = NULL;
276
277 if (g_languages_toggled_proc == 0)
278 {
279 return;
280 }
281
282 cur_lang = ventoy_code_get_cur_language();
283 if (g_strcmp0(cur_lang, (char *)data) != 0)
284 {
285 ventoy_code_set_cur_language((char *)data);
286 select_language((char *)data);
287 }
288 }
289
290 void on_part_style_toggled(GtkMenuItem *menuItem, gpointer data)
291 {
292 int style;
293
294 style = ventoy_code_get_cur_part_style();
295 ventoy_code_set_cur_part_style(1 - style);
296
297 gtk_label_set_text(g_label_local_part_style, style ? "MBR" : "GPT");
298 }
299
300 static ventoy_disk *select_active_dev(const char *select, int *activeid)
301 {
302 int i;
303 int alldev;
304 ventoy_disk *cur = NULL;
305
306 alldev = ventoy_code_get_cur_show_all();
307
308 /* find the match one */
309 if (select)
310 {
311 for (i = 0; i < g_disk_num; i++)
312 {
313 cur = g_disk_list + i;
314 if (alldev == 0 && cur->type != VTOY_DEVICE_USB)
315 {
316 continue;
317 }
318
319 if (strcmp(cur->disk_name, select) == 0)
320 {
321 *activeid = i;
322 return cur;
323 }
324 }
325 }
326
327 /* find the first one that installed with Ventoy */
328 for (i = 0; i < g_disk_num; i++)
329 {
330 cur = g_disk_list + i;
331 if (alldev == 0 && cur->type != VTOY_DEVICE_USB)
332 {
333 continue;
334 }
335
336 if (cur->vtoydata.ventoy_valid)
337 {
338 *activeid = i;
339 return cur;
340 }
341 }
342
343 /* find the first USB interface device */
344 for (i = 0; i < g_disk_num; i++)
345 {
346 cur = g_disk_list + i;
347 if (alldev == 0 && cur->type != VTOY_DEVICE_USB)
348 {
349 continue;
350 }
351
352 if (cur->type == VTOY_DEVICE_USB)
353 {
354 *activeid = i;
355 return cur;
356 }
357 }
358
359 /* use the first one */
360 for (i = 0; i < g_disk_num; i++)
361 {
362 cur = g_disk_list + i;
363 if (alldev == 0 && cur->type != VTOY_DEVICE_USB)
364 {
365 continue;
366 }
367
368 *activeid = i;
369 return cur;
370 }
371
372 return NULL;
373 }
374
375 static void fill_dev_list(const char *select)
376 {
377 int i;
378 int alldev;
379 int activeid;
380 int count = 0;
381 char line[512];
382 ventoy_disk *cur = NULL;
383 ventoy_disk *active = NULL;
384 GtkListStore *store = NULL;
385
386 g_dev_changed_proc = 0;
387
388 alldev = ventoy_code_get_cur_show_all();
389
390 vlog("fill_dev_list total disk: %d showall:%d\n", g_disk_num, alldev);
391
392 /* gtk_combo_box_text_remove_all */
393 store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(g_dev_combobox)));
394 gtk_list_store_clear(store);
395
396 for (i = 0; i < g_disk_num; i++)
397 {
398 cur = g_disk_list + i;
399
400 if (alldev == 0 && cur->type != VTOY_DEVICE_USB)
401 {
402 continue;
403 }
404
405 g_snprintf(line, sizeof(line), "%s [%s] %s", cur->disk_name, cur->human_readable_size, cur->disk_model);
406 gtk_combo_box_text_append_text(g_dev_combobox, line);
407 count++;
408 }
409
410 active = select_active_dev(select, &activeid);
411 if (active)
412 {
413 vlog("combox count:%d, active:%s id:%d\n", count, active->disk_name, activeid);
414 gtk_combo_box_set_active((GtkComboBox *)g_dev_combobox, activeid);
415 gtk_widget_set_sensitive(GTK_WIDGET(g_install_button), TRUE);
416 gtk_widget_set_sensitive(GTK_WIDGET(g_update_button), active->vtoydata.ventoy_valid);
417 }
418 else
419 {
420 vlog("combox count:%d, no active id\n", count);
421 gtk_widget_set_sensitive(GTK_WIDGET(g_install_button), FALSE);
422 gtk_widget_set_sensitive(GTK_WIDGET(g_update_button), FALSE);
423 }
424
425 g_dev_changed_proc = 1;
426 on_devlist_changed(NULL, NULL);
427 }
428
429
430 void on_show_all_toggled(GtkMenuItem *menuItem, gpointer data)
431 {
432 int show_all = ventoy_code_get_cur_show_all();
433
434 ventoy_code_set_cur_show_all(1 - show_all);
435 fill_dev_list(NULL);
436 }
437
438 void on_button_refresh_clicked(GtkWidget *widget, gpointer data)
439 {
440 if (g_thread_run || ventoy_code_is_busy())
441 {
442 msgbox(GTK_MESSAGE_INFO, GTK_BUTTONS_OK, "STR_WAIT_PROCESS");
443 return;
444 }
445
446 ventoy_code_refresh_device();
447 fill_dev_list(NULL);
448 }
449
450 static void set_progress_bar_percent(int percent)
451 {
452 char *pos = NULL;
453 const char *text = NULL;
454 char tmp[128];
455
456 gtk_progress_bar_set_fraction(g_progress_bar, percent * 1.0 / 100);
457 vlog("set percent %d\n", percent);
458
459 text = language_string("STR_STATUS");
460 if (percent == 0)
461 {
462 gtk_label_set_text(g_label_status, text);
463 }
464 else
465 {
466 g_snprintf(tmp, sizeof(tmp), "%s", text);
467 pos = strchr(tmp, '-');
468 if (pos)
469 {
470 g_snprintf(pos + 2, sizeof(tmp), "%d%%", percent);
471 gtk_label_set_text(g_label_status, tmp);
472 }
473 }
474 }
475
476 void on_clear_ventoy(GtkMenuItem *menuItem, gpointer data)
477 {
478 int ret;
479 int active;
480 char buf[1024];
481 char out[256];
482 char disk_name[32];
483 ventoy_disk *cur = NULL;
484
485 if (g_thread_run || ventoy_code_is_busy())
486 {
487 msgbox(GTK_MESSAGE_INFO, GTK_BUTTONS_OK, "STR_WAIT_PROCESS");
488 return;
489 }
490
491 active = gtk_combo_box_get_active((GtkComboBox *)g_dev_combobox);
492 if (active < 0 || active >= g_disk_num)
493 {
494 vlog("invalid active combox id %d\n", active);
495 return;
496 }
497
498 if (GTK_RESPONSE_OK != msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK_CANCEL, "STR_INSTALL_TIP"))
499 {
500 return;
501 }
502
503 if (GTK_RESPONSE_OK != msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK_CANCEL, "STR_INSTALL_TIP2"))
504 {
505 return;
506 }
507
508 gtk_widget_set_sensitive (GTK_WIDGET(g_refresh_button), FALSE);
509 gtk_widget_set_sensitive (GTK_WIDGET(g_install_button), FALSE);
510 gtk_widget_set_sensitive (GTK_WIDGET(g_update_button), FALSE);
511 g_thread_run = TRUE;
512
513
514 cur = g_disk_list + active;
515 g_snprintf(disk_name, sizeof(disk_name), "%s", cur->disk_name);
516 g_snprintf(buf, sizeof(buf), "{\"method\":\"clean\",\"disk\":\"%s\"}", disk_name);
517
518 out[0] = 0;
519 ventoy_func_handler(buf, out, sizeof(out));
520 vlog("func handler clean <%s>\n", out);
521
522 if (strstr(out, "success"))
523 {
524 ret = ventoy_code_get_result();
525 ventoy_code_refresh_device();
526 cur = NULL;
527 }
528 else
529 {
530 ret = 1;
531 }
532
533 if (ret == 0)
534 {
535 msgbox(GTK_MESSAGE_INFO, GTK_BUTTONS_OK, "STR_CLEAR_SUCCESS");
536 }
537 else
538 {
539 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "STR_CLEAR_FAILED");
540 }
541
542 set_progress_bar_percent(0);
543 gtk_widget_set_sensitive(GTK_WIDGET(g_refresh_button), TRUE);
544 gtk_widget_set_sensitive(GTK_WIDGET(g_install_button), TRUE);
545 gtk_widget_set_sensitive(GTK_WIDGET(g_update_button), TRUE);
546
547 fill_dev_list(disk_name);
548 g_thread_run = FALSE;
549 }
550
551 static int install_proc(ventoy_disk *cur)
552 {
553 int ret = 0;
554 int pos = 0;
555 int buflen = 0;
556 int percent = 0;
557 char buf[1024];
558 char dec[64];
559 char out[256];
560 char disk_name[32];
561 long long space;
562
563 vlog("install_thread ...\n");
564
565 g_snprintf(disk_name, sizeof(disk_name), "%s", cur->disk_name);
566
567 buflen = sizeof(buf);
568 VTOY_JSON_FMT_BEGIN(pos, buf, buflen);
569 VTOY_JSON_FMT_OBJ_BEGIN();
570 VTOY_JSON_FMT_STRN("method", "install");
571 VTOY_JSON_FMT_STRN("disk", disk_name);
572
573 if (g_preserve_space_check)
574 {
575 space = g_preserve_space_number;
576 if (g_preserve_space_unit == 1)
577 {
578 space = space * 1024 * 1024 * 1024LL;
579 }
580 else
581 {
582 space = space * 1024 * 1024LL;
583 }
584
585 snprintf(dec, sizeof(dec), "%lld", space);
586 VTOY_JSON_FMT_STRN("reserve_space", dec);
587 }
588 else
589 {
590 VTOY_JSON_FMT_STRN("reserve_space", "0");
591 }
592
593 VTOY_JSON_FMT_UINT("partstyle", ventoy_code_get_cur_part_style());
594 VTOY_JSON_FMT_UINT("secure_boot", g_secure_boot_support);
595 VTOY_JSON_FMT_UINT("align_4kb", g_align_part_with_4k);
596 VTOY_JSON_FMT_OBJ_END();
597 VTOY_JSON_FMT_END(pos);
598
599 out[0] = 0;
600 ventoy_func_handler(buf, out, sizeof(out));
601 vlog("func handler install <%s>\n", out);
602
603 if (strstr(out, "success"))
604 {
605 while (percent != 100)
606 {
607 percent = ventoy_code_get_percent();
608 set_progress_bar_percent(percent);
609 GTK_MSG_ITERATION();
610 usleep(50 * 1000);
611 }
612
613 ret = ventoy_code_get_result();
614 ventoy_code_refresh_device();
615 cur = NULL;
616 }
617 else
618 {
619 ret = 1;
620 }
621
622 if (ret)
623 {
624 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "STR_INSTALL_FAILED");
625 }
626 else
627 {
628 msgbox(GTK_MESSAGE_INFO, GTK_BUTTONS_OK, "STR_INSTALL_SUCCESS");
629 }
630
631 set_progress_bar_percent(0);
632 gtk_widget_set_sensitive(GTK_WIDGET(g_refresh_button), TRUE);
633 gtk_widget_set_sensitive(GTK_WIDGET(g_install_button), TRUE);
634 gtk_widget_set_sensitive(GTK_WIDGET(g_update_button), TRUE);
635
636 fill_dev_list(disk_name);
637 g_thread_run = FALSE;
638
639 return 0;
640 }
641
642 void on_button_install_clicked(GtkWidget *widget, gpointer data)
643 {
644 int active;
645 long long size;
646 long long space;
647 ventoy_disk *cur = NULL;
648
649 if (g_thread_run || ventoy_code_is_busy())
650 {
651 msgbox(GTK_MESSAGE_INFO, GTK_BUTTONS_OK, "STR_WAIT_PROCESS");
652 return;
653 }
654
655 active = gtk_combo_box_get_active((GtkComboBox *)g_dev_combobox);
656 if (active < 0 || active >= g_disk_num)
657 {
658 vlog("invalid active combox id %d\n", active);
659 return;
660 }
661
662 cur = g_disk_list + active;
663
664 if (cur->is4kn)
665 {
666 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "STR_4KN_UNSUPPORTED");
667 return;
668 }
669
670 if (ventoy_code_get_cur_part_style() == 0 && cur->size_in_byte > 2199023255552ULL)
671 {
672 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "STR_DISK_2TB_MBR_ERROR");
673 return;
674 }
675
676 if (g_preserve_space_check)
677 {
678 space = g_preserve_space_number;
679 if (g_preserve_space_unit == 1)
680 {
681 space = space * 1024;
682 }
683 else
684 {
685 space = space;
686 }
687
688 size = cur->size_in_byte / SIZE_1MB;
689 if (size <= space || (size - space) <= (VTOYEFI_PART_BYTES / SIZE_1MB))
690 {
691 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "STR_SPACE_VAL_INVALID");
692 vlog("reserved space value too big ...\n");
693 return;
694 }
695 }
696
697 if (GTK_RESPONSE_OK != msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK_CANCEL, "STR_INSTALL_TIP"))
698 {
699 return;
700 }
701
702 if (GTK_RESPONSE_OK != msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK_CANCEL, "STR_INSTALL_TIP2"))
703 {
704 return;
705 }
706
707 gtk_widget_set_sensitive (GTK_WIDGET(g_refresh_button), FALSE);
708 gtk_widget_set_sensitive (GTK_WIDGET(g_install_button), FALSE);
709 gtk_widget_set_sensitive (GTK_WIDGET(g_update_button), FALSE);
710
711 g_thread_run = TRUE;
712
713 install_proc(cur);
714 }
715
716
717 static int update_proc(ventoy_disk *cur)
718 {
719 int ret = 0;
720 int percent = 0;
721 char buf[1024];
722 char out[256];
723 char disk_name[32];
724
725 g_snprintf(disk_name, sizeof(disk_name), "%s", cur->disk_name);
726 g_snprintf(buf, sizeof(buf), "{\"method\":\"update\",\"disk\":\"%s\",\"secure_boot\":%d}",
727 disk_name, g_secure_boot_support);
728
729 out[0] = 0;
730 ventoy_func_handler(buf, out, sizeof(out));
731 vlog("func handler update <%s>\n", out);
732
733 if (strstr(out, "success"))
734 {
735 while (percent != 100)
736 {
737 percent = ventoy_code_get_percent();
738 set_progress_bar_percent(percent);
739 GTK_MSG_ITERATION();
740 usleep(50 * 1000);
741 }
742
743 ret = ventoy_code_get_result();
744 ventoy_code_refresh_device();
745 cur = NULL;
746 }
747 else
748 {
749 ret = 1;
750 }
751
752 if (ret)
753 {
754 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "STR_UPDATE_FAILED");
755 }
756 else
757 {
758 msgbox(GTK_MESSAGE_INFO, GTK_BUTTONS_OK, "STR_UPDATE_SUCCESS");
759 }
760
761 set_progress_bar_percent(0);
762 gtk_widget_set_sensitive(GTK_WIDGET(g_refresh_button), TRUE);
763 gtk_widget_set_sensitive(GTK_WIDGET(g_install_button), TRUE);
764 gtk_widget_set_sensitive(GTK_WIDGET(g_update_button), TRUE);
765
766 fill_dev_list(disk_name);
767 g_thread_run = FALSE;
768
769 return 0;
770 }
771
772
773 void on_button_update_clicked(GtkWidget *widget, gpointer data)
774 {
775 int active;
776 ventoy_disk *cur = NULL;
777
778 if (g_thread_run || ventoy_code_is_busy())
779 {
780 msgbox(GTK_MESSAGE_INFO, GTK_BUTTONS_OK, "STR_WAIT_PROCESS");
781 return;
782 }
783
784 active = gtk_combo_box_get_active((GtkComboBox *)g_dev_combobox);
785 if (active < 0 || active >= g_disk_num)
786 {
787 vlog("invalid active combox id %d\n", active);
788 return;
789 }
790 cur = g_disk_list + active;
791
792 if (cur->vtoydata.ventoy_valid == 0)
793 {
794 vlog("invalid ventoy version.\n");
795 return;
796 }
797
798 if (GTK_RESPONSE_OK != msgbox(GTK_MESSAGE_INFO, GTK_BUTTONS_OK_CANCEL, "STR_UPDATE_TIP"))
799 {
800 return;
801 }
802
803 gtk_widget_set_sensitive (GTK_WIDGET(g_refresh_button), FALSE);
804 gtk_widget_set_sensitive (GTK_WIDGET(g_install_button), FALSE);
805 gtk_widget_set_sensitive (GTK_WIDGET(g_update_button), FALSE);
806
807 g_thread_run = TRUE;
808
809 update_proc(cur);
810 }
811
812 static gint lang_compare(gconstpointer a, gconstpointer b)
813 {
814 const char *name1 = (const char *)a;
815 const char *name2 = (const char *)b;
816
817 if (strncmp(name1, "Chinese Simplified", 18) == 0)
818 {
819 return -1;
820 }
821 else if (strncmp(name2, "Chinese Simplified", 18) == 0)
822 {
823 return 1;
824 }
825 else
826 {
827 return g_strcmp0(name1, name2);
828 }
829 }
830
831 static int load_languages(void)
832 {
833 int size = 0;
834 char *pBuf = NULL;
835 char *pCur = NULL;
836 const char *pCurLang = NULL;
837 const char *pName = NULL;
838 VTOY_JSON *json = NULL;
839 VTOY_JSON *node = NULL;
840 VTOY_JSON *lang = NULL;
841 GSList *pGroup = NULL;
842 GList *pNameNode = NULL;
843 GList *pNameList = NULL;
844 GtkRadioMenuItem *pItem = NULL;
845
846 vlog("load_languages ...\n");
847
848 pCurLang = ventoy_code_get_cur_language();
849 if (pCurLang[0] == 0)
850 {
851 pName = getenv("LANG");
852 if (pName && strncmp(pName, "zh_CN", 5) == 0)
853 {
854 ventoy_code_set_cur_language("Chinese Simplified (简体中文)");
855 }
856 else
857 {
858 ventoy_code_set_cur_language("English (English)");
859 }
860 pCurLang = ventoy_code_get_cur_language();
861 }
862
863 vlog("current language <%s>\n", pCurLang);
864
865 ventoy_read_file_to_buf("./tool/languages.json", 1, (void **)&pBuf, &size);
866
867 json = vtoy_json_create();
868 vtoy_json_parse(json, pBuf);
869 g_languages_json = json;
870
871 for (node = json->pstChild; node; node = node->pstNext)
872 {
873 pName = vtoy_json_get_string_ex(node->pstChild, "name");
874 if (pName)
875 {
876 pNameList = g_list_append(pNameList, (gpointer)pName);
877 }
878
879 for (lang = node->pstChild; lang; lang = lang->pstNext)
880 {
881 if (lang->enDataType == JSON_TYPE_STRING)
882 {
883 pCur = lang->unData.pcStrVal;
884 while (*pCur)
885 {
886 if (*pCur == '#')
887 {
888 *pCur = '\r';
889 }
890 else if (*pCur == '@')
891 {
892 *pCur = '\n';
893 }
894 pCur++;
895 }
896 }
897 }
898 }
899
900 pNameList = g_list_sort(pNameList, lang_compare);
901
902 for (pNameNode = g_list_first(pNameList); pNameNode; pNameNode = g_list_next(pNameNode))
903 {
904 pName = (char *)(pNameNode->data);
905 pItem = (GtkRadioMenuItem *)gtk_radio_menu_item_new_with_label(pGroup, pName);
906 pGroup = gtk_radio_menu_item_get_group(pItem);
907
908 if (strcmp(pCurLang, pName) == 0)
909 {
910 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(pItem), TRUE);
911 }
912
913 g_signal_connect(pItem, "toggled", G_CALLBACK(on_language_toggled), (gpointer)pName);
914 gtk_widget_show((GtkWidget *)pItem);
915 gtk_menu_shell_append(GTK_MENU_SHELL(g_lang_menu), (GtkWidget *)pItem);
916 }
917
918 g_list_free(pNameList);
919 free(pBuf);
920
921 select_language(pCurLang);
922 g_languages_toggled_proc = 1;
923
924 return 0;
925 }
926
927 void on_part_cfg_ok(GtkWidget *widget, gpointer data)
928 {
929 const char *pos = NULL;
930 const char *input = NULL;
931 char device[256];
932 gboolean checked = gtk_toggle_button_get_active(g_part_preserve_checkbox);
933
934 if (checked)
935 {
936 input = gtk_entry_get_text(g_part_reserve_space_value);
937 if (input == NULL || input[0] == 0)
938 {
939 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "STR_SPACE_VAL_INVALID");
940 return;
941 }
942
943 for (pos = input; *pos; pos++)
944 {
945 if (*pos < '0' || *pos > '9')
946 {
947 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "STR_SPACE_VAL_INVALID");
948 return;
949 }
950 }
951
952 g_preserve_space_unit = gtk_combo_box_get_active((GtkComboBox *)g_part_space_unit_combox);
953 g_preserve_space_number = (int)strtol(input, NULL, 10);
954
955 g_snprintf(device, sizeof(device), "%s [ -%d%s ]",
956 language_string("STR_DEVICE"), g_preserve_space_number,
957 g_preserve_space_unit ? "GB" : "MB");
958 gtk_label_set_text(g_device_title, device);
959 }
960 else
961 {
962 gtk_label_set_text(g_device_title, language_string("STR_DEVICE"));
963 }
964
965 g_preserve_space_check = checked;
966 g_align_part_with_4k = gtk_toggle_button_get_active(g_part_align_checkbox);
967 gtk_widget_hide(g_partCfgWindow);
968 }
969
970 void on_part_cfg_cancel(GtkWidget *widget, gpointer data)
971 {
972 gtk_widget_hide(g_partCfgWindow);
973 }
974
975 int on_part_cfg_close(GtkWidget *widget, gpointer data)
976 {
977 gtk_widget_hide(g_partCfgWindow);
978 return TRUE;
979 }
980
981 void on_part_config(GtkMenuItem *menuItem, gpointer data)
982 {
983 char value[64];
984
985 gtk_toggle_button_set_active(g_part_align_checkbox, g_align_part_with_4k);
986 gtk_toggle_button_set_active(g_part_preserve_checkbox, g_preserve_space_check);
987 gtk_widget_set_sensitive(GTK_WIDGET(g_part_reserve_space_value), g_preserve_space_check);
988 gtk_widget_set_sensitive(GTK_WIDGET(g_part_space_unit_combox), g_preserve_space_check);
989
990 if (g_preserve_space_check)
991 {
992 g_snprintf(value, sizeof(value), "%d", g_preserve_space_number);
993 gtk_entry_set_text(g_part_reserve_space_value, value);
994 gtk_combo_box_set_active((GtkComboBox *)g_part_space_unit_combox, g_preserve_space_unit);
995 }
996
997 gtk_widget_show_all(g_partCfgWindow);
998 }
999
1000 void on_reserve_space_toggled(GtkMenuItem *menuItem, gpointer data)
1001 {
1002 gboolean checked = gtk_toggle_button_get_active(g_part_preserve_checkbox);
1003
1004 gtk_widget_set_sensitive(GTK_WIDGET(g_part_reserve_space_value), checked);
1005 gtk_widget_set_sensitive(GTK_WIDGET(g_part_space_unit_combox), checked);
1006 }
1007
1008 static void init_part_cfg_window(GtkBuilder *pBuilder)
1009 {
1010 #if GTK_CHECK_VERSION(3, 0, 0)
1011 GtkWidget *pHeader = NULL;
1012
1013 pHeader = gtk_header_bar_new();
1014 gtk_header_bar_set_show_close_button(GTK_HEADER_BAR(pHeader), FALSE);
1015 gtk_window_set_titlebar (GTK_WINDOW(g_partCfgWindow), pHeader);
1016 gtk_window_set_title(GTK_WINDOW(g_partCfgWindow), "Partition Configuration");
1017 #endif
1018
1019 gtk_combo_box_set_active((GtkComboBox *)g_part_space_unit_combox, g_preserve_space_unit);
1020 gtk_widget_set_sensitive (GTK_WIDGET(g_part_reserve_space_value), FALSE);
1021 gtk_widget_set_sensitive (GTK_WIDGET(g_part_space_unit_combox), FALSE);
1022
1023 SIGNAL("space_check_btn", "toggled", on_reserve_space_toggled);
1024
1025 SIGNAL("button_partcfg_ok", "clicked", on_part_cfg_ok);
1026 SIGNAL("button_partcfg_cancel", "clicked", on_part_cfg_cancel);
1027 SIGNAL("part_cfg_dlg", "delete_event", on_part_cfg_close);
1028 }
1029
1030 static void add_accelerator(GtkAccelGroup *agMain, void *widget, const char *signal, guint accel_key)
1031 {
1032 gtk_widget_add_accelerator(GTK_WIDGET(widget), signal, agMain, accel_key, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
1033 gtk_widget_add_accelerator(GTK_WIDGET(widget), signal, agMain, accel_key, GDK_SHIFT_MASK | GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
1034 }
1035
1036 void on_init_window(GtkBuilder *pBuilder)
1037 {
1038 GSList *pGroup = NULL;
1039 GtkAccelGroup *agMain = NULL;
1040 char version[512];
1041
1042 vlog("on_init_window ...\n");
1043
1044 g_pXmlBuilder = pBuilder;
1045 g_topWindow = BUILDER_ITEM(GtkWidget, "window");
1046 g_partCfgWindow = BUILDER_ITEM(GtkWidget, "part_cfg_dlg");
1047
1048 g_dev_combobox = BUILDER_ITEM(GtkComboBoxText, "combobox_devlist");
1049 g_refresh_button = BUILDER_ITEM(GtkButton, "button_refresh");
1050 g_install_button = BUILDER_ITEM(GtkButton, "button_install");
1051 g_update_button = BUILDER_ITEM(GtkButton, "button_update");
1052
1053 g_lang_menu = BUILDER_ITEM(GtkMenu, "submenu_language");
1054 g_menu_item_secure_boot = BUILDER_ITEM(GtkCheckMenuItem, "menu_item_secure");
1055 g_menu_item_mbr = BUILDER_ITEM(GtkCheckMenuItem, "menu_item_mbr");
1056 g_menu_item_gpt = BUILDER_ITEM(GtkCheckMenuItem, "menu_item_gpt");
1057 g_menu_item_show_all = BUILDER_ITEM(GtkCheckMenuItem, "menu_item_show_all");
1058
1059 g_device_title = BUILDER_ITEM(GtkLabel, "label_device");
1060 g_label_local_part_style = BUILDER_ITEM(GtkLabel, "label_local_part_style");
1061 g_label_dev_part_style = BUILDER_ITEM(GtkLabel, "label_dev_part_style");
1062
1063 g_label_local_ver = BUILDER_ITEM(GtkLabel, "label_local_ver_value");
1064 g_label_disk_ver = BUILDER_ITEM(GtkLabel, "label_dev_ver_value");
1065
1066 g_label_status = BUILDER_ITEM(GtkLabel, "label_status");
1067
1068 g_image_secure_local = BUILDER_ITEM(GtkImage, "image_secure_local");
1069 g_image_secure_device = BUILDER_ITEM(GtkImage, "image_secure_dev");
1070
1071 g_part_preserve_checkbox = BUILDER_ITEM(GtkToggleButton, "space_check_btn");
1072 g_part_align_checkbox = BUILDER_ITEM(GtkToggleButton, "space_align_btn");
1073
1074 g_part_reserve_space_value = BUILDER_ITEM(GtkEntry, "entry_reserve_space");
1075 g_part_space_unit_combox = BUILDER_ITEM(GtkComboBoxText, "comboboxtext_unit");
1076
1077 g_progress_bar = BUILDER_ITEM(GtkProgressBar, "progressbar1");
1078
1079 init_part_cfg_window(pBuilder);
1080
1081 /* for gtk2 */
1082 gtk_frame_set_shadow_type(BUILDER_ITEM(GtkFrame, "frame_dummy1"), GTK_SHADOW_NONE);
1083 gtk_frame_set_shadow_type(BUILDER_ITEM(GtkFrame, "frame_dummy2"), GTK_SHADOW_NONE);
1084
1085 /* join group */
1086 pGroup = gtk_radio_menu_item_get_group((GtkRadioMenuItem *)g_menu_item_mbr);
1087 gtk_radio_menu_item_set_group((GtkRadioMenuItem *)g_menu_item_gpt, pGroup);
1088
1089 gtk_widget_hide((GtkWidget *)g_image_secure_local);
1090 gtk_widget_hide((GtkWidget *)g_image_secure_device);
1091
1092 g_snprintf(version, sizeof(version), VTOY_VER_FMT, ventoy_get_local_version());
1093 gtk_label_set_markup(g_label_local_ver, version);
1094
1095 init_part_style_menu();
1096 gtk_check_menu_item_set_active(g_menu_item_show_all, ventoy_code_get_cur_show_all());
1097
1098 load_languages();
1099
1100 SIGNAL("combobox_devlist", "changed", on_devlist_changed);
1101
1102 SIGNAL("button_refresh", "clicked", on_button_refresh_clicked);
1103 SIGNAL("button_install", "clicked", on_button_install_clicked);
1104 SIGNAL("button_update", "clicked", on_button_update_clicked);
1105
1106 SIGNAL("menu_item_secure", "toggled", on_secure_boot_toggled);
1107 SIGNAL("menu_item_mbr", "toggled", on_part_style_toggled);
1108 SIGNAL("menu_item_show_all", "toggled", on_show_all_toggled);
1109
1110 SIGNAL("menu_item_part_cfg", "activate", on_part_config);
1111 SIGNAL("menu_item_clear", "activate", on_clear_ventoy);
1112
1113 agMain = gtk_accel_group_new();
1114 gtk_window_add_accel_group(GTK_WINDOW(g_topWindow), agMain);
1115 add_accelerator(agMain, g_dev_combobox, "popup", GDK_KEY_d);
1116 add_accelerator(agMain, g_install_button, "clicked", GDK_KEY_i);
1117 add_accelerator(agMain, g_update_button, "clicked", GDK_KEY_u);
1118 add_accelerator(agMain, g_refresh_button, "clicked", GDK_KEY_r);
1119
1120 gtk_check_menu_item_set_active(g_menu_item_secure_boot, 1 - g_secure_boot_support);
1121
1122 fill_dev_list(NULL);
1123
1124 return;
1125 }
1126
1127 int on_exit_window(GtkWidget *widget, gpointer data)
1128 {
1129 vlog("on_exit_window ...\n");
1130
1131 if (g_thread_run || ventoy_code_is_busy())
1132 {
1133 msgbox(GTK_MESSAGE_INFO, GTK_BUTTONS_OK, "STR_WAIT_PROCESS");
1134 return TRUE;
1135 }
1136
1137 ventoy_code_save_cfg();
1138 return FALSE;
1139 }
1140