Skip to content

Commit 434f95a

Browse files
committed
Added Linux GUI reference
- Added Linux GUI reference - Fixed race condition breaking the Linux CLI
1 parent ef0db38 commit 434f95a

File tree

11 files changed

+277
-41
lines changed

11 files changed

+277
-41
lines changed

README.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,17 @@ Argument | Description
2626

2727
`-` can be used in place of `<file>` to designate standard output as the destination, but cannot be used in place of a directory for extracting tar archives.
2828

29-
Without any arguments, the embedded Kanzi stream will be decompressed into the working directory to a file of the same name as the executable, except with the `.exe` or `.app` extension removed. Or, if the Kanzi stream contains a tar archive, the tar archive will be both decompressed and unarchived into a folder within the working directory of the same name as the executable, except with the `.exe` or `.app` extension removed. So, command-line usage is only optional and the end user can just execute the application as they would any other application for this default behavior.
29+
Without any arguments, the embedded Kanzi stream will be decompressed into the working directory to a file of the same name as the executable, except with the extension removed. Or, if the Kanzi stream contains a tar archive, the tar archive will be both decompressed and unarchived into a folder within the working directory of the same name as the executable, except with the extension removed. So, command-line usage is only optional and the end user can just execute the application as they would any other application for this default behavior.
30+
31+
**NOTE FOR LINUX USERS: Executables on Linux generally don't have an extension. However, it is recommended to use an extension anyway just for ease-of-use for whoever you are sending the kanziSFX to, so generally either `.elf` or `.bin`.**
3032

3133
# GUI Reference Implementation
3234

33-
[![ScriptTiger/kanziSFX](https://scripttiger.github.io/images/kanziSFX-Interface.png)](https://github.com/ScriptTiger/kanziSFX)
35+
[![ScriptTiger/kanziSFX](https://scripttiger.github.io/images/kanziSFX-Interface-Windows.png)](https://github.com/ScriptTiger/kanziSFX)
36+
37+
[![ScriptTiger/kanziSFX](https://scripttiger.github.io/images/kanziSFX-Interface-Linux.png)](https://github.com/ScriptTiger/kanziSFX)
3438

35-
The cno GUI package was used for its extremely minimal and lightweight nature to allow kanziSFX to retain a small footprint, as is necessary for a self-extracting archive decompressor. However, cno only supports native Windows GUIs at the moment, so this reference implementation is only available for Windows.
39+
The cno GUI package was used for its extremely minimal and lightweight nature to allow kanziSFX to retain a small footprint, as is necessary for a self-extracting archive decompressor. However, cno only supports native GUIs for Windows and Linux at the moment, so there are only GUI reference implementations available for those platforms.
3640

3741
For additional notes on cno, please refer to its documentation:
3842
https://github.com/ScriptTiger/cno
@@ -50,7 +54,7 @@ copy /b "kanziSFX.exe"+"file.knz" "MyKanziSFX.exe"
5054

5155
For Linux and Mac:
5256
```
53-
cat "kanziSFX" "file.knz" > "MyKanziSFX"
57+
cat "kanziSFX.bin" "file.knz" > "MyKanziSFX.bin"
5458
```
5559

5660
# More About ScriptTiger

_ref/Build.cmd

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ call :Build_App
3939
if %dev% == 1 exit /b
4040

4141
set GOOS=linux
42-
set EXT=
42+
set EXT=.elf
4343
set INCLUDE=include_other.go
4444
call :Build_App
4545

@@ -53,17 +53,18 @@ exit /b
5353
:Build_App
5454

5555
set app=CLI
56-
set source=CLI
5756
set flags=-s -w
57+
set RELEASE=../Release
5858
call :Build
5959

6060
if %dev% == 1 exit /b
61-
if not %GOOS% == windows exit /b
6261

63-
cd ..\GUI
62+
if %GOOS% == darwin exit /b
63+
if %GOOS% == windows cd ..\GUI\windows
64+
if %GOOS% == linux cd ..\GUI\linux
6465

6566
if exist go.mod (
66-
choice /m "Rebuild GUI go.mod and go.sum?"
67+
choice /m "Rebuild %GOOS% GUI go.mod and go.sum?"
6768
if !errorlevel! == 1 (del go.mod go.sum)
6869
)
6970

@@ -74,17 +75,18 @@ if not exist go.mod (
7475
)
7576

7677
set app=GUI
77-
set source=GUI_Windows
78-
set flags=-s -w -H=windowsgui
78+
if %GOOS% == windows set flags=-s -w -H=windowsgui
79+
if %GOOS% == linux set flags=-s -w
7980
set INCLUDE=
81+
set RELEASE=../../Release
8082
call :Build
8183

82-
cd ..\CLI
84+
cd ..\..\CLI
8385

8486
exit /b
8587

8688
:Build
8789
echo Building %mod%_%app%_%GOOS%_%GOARCH%%EXT%...
88-
go build -ldflags="%flags%" -o "../Release/%mod%_%app%_%GOOS%_%GOARCH%%EXT%" %source%.go %INCLUDE%
89-
if %errorlevel% == 0 if not %GOOS% == darwin call upx --lzma "../Release/%mod%_%app%_%GOOS%_%GOARCH%%EXT%" 1> nul
90+
go build -ldflags="%flags%" -o "%RELEASE%/%mod%_%app%_%GOOS%_%GOARCH%%EXT%" %mod%.go %INCLUDE%
91+
if %errorlevel% == 0 if not %GOOS% == darwin call upx --lzma "%RELEASE%/%mod%_%app%_%GOOS%_%GOARCH%%EXT%" 1> nul
9092
exit /b

_ref/CLI/CLI.go renamed to _ref/CLI/kanziSFX.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,8 @@ func main() {
103103
err = Extract(outNamePtr, accelerator, ctx, nil, ops)
104104
if err != nil {displayError(err)}
105105
} else {
106+
running = true
106107
go func() {
107-
running = true
108108
err = Extract(outNamePtr, accelerator, nil, progress, ops)
109109
if err != nil {displayError(err)}
110110
running = false

_ref/GUI/go.mod

Lines changed: 0 additions & 10 deletions
This file was deleted.

_ref/GUI/go.sum

Lines changed: 0 additions & 6 deletions
This file was deleted.

_ref/GUI/linux/go.mod

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
module main
2+
3+
go 1.25.4
4+
5+
require (
6+
github.com/ScriptTiger/cno v0.0.0-20251208040705-1cfdf4ebf0d1
7+
github.com/ScriptTiger/kanziSFX v0.0.0-20251208052004-ef0db38ff524
8+
github.com/ebitengine/purego v0.9.1
9+
)
10+
11+
require github.com/flanglet/kanzi-go/v2 v2.4.0 // indirect

_ref/GUI/linux/go.sum

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
github.com/ScriptTiger/cno v0.0.0-20251208040705-1cfdf4ebf0d1 h1:x95Depmq43hoaVqnNE8HKl55A+Wokq8vX8E5t+6cnas=
2+
github.com/ScriptTiger/cno v0.0.0-20251208040705-1cfdf4ebf0d1/go.mod h1:Sn30fn9sJoeTf8zogrkItogFPhLKBAr8mSVZBc/RvjY=
3+
github.com/ScriptTiger/kanziSFX v0.0.0-20251208052004-ef0db38ff524 h1:cXTWVVi/lSGuqQnXh3Jj9TeS3BFuTunlO+JBiBTGVyw=
4+
github.com/ScriptTiger/kanziSFX v0.0.0-20251208052004-ef0db38ff524/go.mod h1:K384ImoA4qHwlgYSiWqPX1DT6P8M8VlLF4GksoGd44Q=
5+
github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A=
6+
github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
7+
github.com/flanglet/kanzi-go/v2 v2.4.0 h1:CFYcqeM5djhVXd1/Rn1VmwP4e7ovKgdJTh9V5lkm0FI=
8+
github.com/flanglet/kanzi-go/v2 v2.4.0/go.mod h1:11DfrNICimOP1RWO0SO/PkC3iAPIVXPYtJHf70+8fjQ=

_ref/GUI/linux/kanziSFX.go

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
package main
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"runtime"
7+
"strings"
8+
9+
. "github.com/ScriptTiger/kanziSFX"
10+
. "github.com/ScriptTiger/cno"
11+
. "github.com/ScriptTiger/cno/lnx/gui"
12+
"github.com/ebitengine/purego"
13+
)
14+
15+
// kanziSFX accelerator
16+
const ACCELERATOR int64 = 500000
17+
18+
var (
19+
// Widget handles
20+
hwnd, labelHwnd,
21+
pathBoxHwnd, entryHwnd, browseButtonHwnd,
22+
buttonBoxHwnd, cancelButtonHwnd, extractButtonHwnd,
23+
pbHwnd uintptr
24+
25+
// Path string
26+
path string
27+
28+
// Tar boolean denoting if Kanzi bit stream contains tar or not
29+
tar bool
30+
31+
// Progress tracker provided to kanziSFX
32+
progress [2]int64
33+
34+
// Status of extraction
35+
running bool
36+
37+
// Instructions
38+
instructions string
39+
40+
// Errors
41+
err error
42+
)
43+
44+
// Function to report error and exit
45+
func errExit(err error, code int) {
46+
errorDialog := Gtk_message_dialog_new(
47+
0,
48+
0,
49+
GTK_MESSAGE_ERROR,
50+
GTK_BUTTONS_OK,
51+
CStr(err.Error()),
52+
)
53+
Gtk_window_set_title(errorDialog, CStr("Error"))
54+
Gtk_dialog_run(errorDialog)
55+
os.Exit(code)
56+
}
57+
58+
// Callback for browse button "clicked" signal
59+
func browseButtonClick() {
60+
61+
var action uintptr
62+
if tar {action = GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
63+
} else {action = GTK_FILE_CHOOSER_ACTION_SAVE}
64+
65+
browseDialog := Gtk_file_chooser_dialog_new(
66+
CStr(instructions+"."),
67+
hwnd,
68+
action,
69+
CStr("Cancel"), GTK_RESPONSE_CANCEL,
70+
CStr("Okay"), GTK_RESPONSE_ACCEPT,
71+
0, 0,
72+
)
73+
74+
if Gtk_dialog_run(browseDialog) == GTK_RESPONSE_ACCEPT {
75+
pathPtr := Gtk_file_chooser_get_filename(browseDialog)
76+
if pathPtr != 0 {
77+
path = GStr(pathPtr)
78+
G_free(pathPtr)
79+
Gtk_entry_set_text(entryHwnd, CStr(path))
80+
}
81+
}
82+
83+
Gtk_widget_destroy(browseDialog)
84+
return
85+
}
86+
87+
// Callback for extract button "clicked" signal
88+
func extractButtonClick() {
89+
pathPtr := Gtk_entry_get_text(entryHwnd)
90+
if pathPtr != 0 {path = GStr(pathPtr)}
91+
Gtk_label_set_text(labelHwnd, CStr("Extracting..."))
92+
Gtk_widget_destroy(entryHwnd)
93+
Gtk_widget_destroy(browseButtonHwnd)
94+
Gtk_widget_destroy(extractButtonHwnd)
95+
if progress[1] != 0 {
96+
pbHwnd = Gtk_progress_bar_new()
97+
Gtk_box_pack_start(pathBoxHwnd, pbHwnd, 1, 1, 5)
98+
Gtk_widget_show(pbHwnd)
99+
}
100+
running = true
101+
G_idle_add(purego.NewCallback(progressCallback), 0)
102+
go func() {
103+
err = Extract(&path, ACCELERATOR, nil, &progress, REWRITE_PATH)
104+
running = false
105+
}()
106+
}
107+
108+
// Callback for tracking progress of extraction and updating window accordingly
109+
func progressCallback() bool {
110+
if running {
111+
if progress[1] != 0 {Gtk_progress_bar_set_fraction(pbHwnd, float64(progress[0])/float64(progress[1]))}
112+
return true
113+
}
114+
if err != nil {
115+
if progress[1] != 0 {Gtk_progress_bar_set_fraction(pbHwnd, 0)}
116+
Gtk_label_set_text(labelHwnd, CStr("A problem occurred during extraction!"))
117+
Gtk_button_set_label(cancelButtonHwnd, CStr("Okay"))
118+
errorDialog := Gtk_message_dialog_new(
119+
hwnd,
120+
GTK_DIALOG_MODAL,
121+
GTK_MESSAGE_ERROR,
122+
GTK_BUTTONS_OK,
123+
CStr(err.Error()),
124+
)
125+
Gtk_window_set_title(errorDialog, CStr("Error"))
126+
Gtk_dialog_run(errorDialog)
127+
Gtk_widget_destroy(errorDialog)
128+
} else {
129+
if progress[1] != 0 {Gtk_progress_bar_set_fraction(pbHwnd, 1)}
130+
Gtk_label_set_text(labelHwnd, CStr("Extraction complete!"))
131+
Gtk_button_set_label(cancelButtonHwnd, CStr("Okay"))
132+
}
133+
return false
134+
}
135+
136+
137+
func main() {
138+
139+
// Lock this main go routine to the current OS thread
140+
runtime.LockOSThread()
141+
142+
// Initialize GTK
143+
Gtk_init(0, 0)
144+
145+
// Set up variables for kanziSFX
146+
outNamePtr := new(string)
147+
ctx := make(map[string]any)
148+
149+
// Call kanziSFX
150+
err = Extract(outNamePtr, ACCELERATOR, ctx, nil, INFO)
151+
152+
// If there was an error, report it and exit
153+
if err != nil {errExit(err, 1)}
154+
155+
// Check if Kanzi bit stream contains tar or not
156+
if ctx["tar"].(bool) {
157+
tar = true
158+
instructions = "Specify the directory to extract to"
159+
} else {instructions = "Specify the file to extract to"}
160+
161+
// Check if output size is present to use with progress tracking
162+
if value, hasKey := ctx["outputSize"]; hasKey {progress[1] = value.(int64)}
163+
164+
path, err := os.Executable()
165+
if err != nil {errExit(err, 2)
166+
} else {
167+
path, err = filepath.EvalSymlinks(path)
168+
if err != nil {errExit(err, 3)}
169+
}
170+
path = strings.TrimSuffix(filepath.Base(path), filepath.Ext(path))
171+
172+
// Create and set up main window
173+
hwnd = Gtk_window_new(0)
174+
Gtk_window_set_title(hwnd, CStr("kanziSFX: Kanzi self-extracting archive"))
175+
Gtk_window_set_resizable(hwnd, 0)
176+
177+
// Create main box with vertical orientation and add it to the window
178+
mainBoxHwnd := Gtk_box_new(1, 5)
179+
Gtk_container_add(hwnd, mainBoxHwnd)
180+
181+
// Main box widgets
182+
labelHwnd = Gtk_label_new(CStr(instructions+":"))
183+
pathBoxHwnd = Gtk_box_new(0, 5)
184+
buttonBoxHwnd = Gtk_box_new(0, 5)
185+
Gtk_box_pack_start(mainBoxHwnd, labelHwnd, 1, 1, 5)
186+
Gtk_box_pack_start(mainBoxHwnd, pathBoxHwnd, 0, 0, 5)
187+
Gtk_box_pack_start(mainBoxHwnd, buttonBoxHwnd, 0, 0, 5)
188+
189+
// Path box widgets
190+
entryHwnd = Gtk_entry_new()
191+
Gtk_entry_set_text(entryHwnd, CStr(path))
192+
browseButtonHwnd = Gtk_button_new_with_label(CStr("..."))
193+
Gtk_box_pack_start(pathBoxHwnd, entryHwnd, 1, 1, 5)
194+
Gtk_box_pack_start(pathBoxHwnd, browseButtonHwnd, 0, 0, 5)
195+
196+
// Button box widgets
197+
cancelButtonHwnd = Gtk_button_new_with_label(CStr("Cancel"))
198+
extractButtonHwnd = Gtk_button_new_with_label(CStr("Extract"))
199+
Gtk_box_pack_end(buttonBoxHwnd, cancelButtonHwnd, 0, 0, 5)
200+
Gtk_box_pack_end(buttonBoxHwnd, extractButtonHwnd, 0, 0, 5)
201+
202+
// Connect event signals to callbacks
203+
G_signal_connect_data(browseButtonHwnd, CStr("clicked"), purego.NewCallback(browseButtonClick), 0, 0, 0)
204+
G_signal_connect_data(extractButtonHwnd, CStr("clicked"), purego.NewCallback(extractButtonClick), 0, 0, 0)
205+
G_signal_connect_data(cancelButtonHwnd, CStr("clicked"), purego.NewCallback(Gtk_main_quit), 0, 0, 0)
206+
G_signal_connect_data(hwnd, CStr("destroy"), purego.NewCallback(Gtk_main_quit), 0, 0, 0)
207+
208+
// Show everything and start the main loop
209+
Gtk_widget_show_all(hwnd)
210+
Gtk_main()
211+
}

_ref/GUI/windows/go.mod

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
module main
2+
3+
go 1.25.4
4+
5+
require (
6+
github.com/ScriptTiger/cno v0.0.0-20251208040705-1cfdf4ebf0d1
7+
github.com/ScriptTiger/kanziSFX v0.0.0-20251208052004-ef0db38ff524
8+
)
9+
10+
require github.com/flanglet/kanzi-go/v2 v2.4.0 // indirect

_ref/GUI/windows/go.sum

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
github.com/ScriptTiger/cno v0.0.0-20251208040705-1cfdf4ebf0d1 h1:x95Depmq43hoaVqnNE8HKl55A+Wokq8vX8E5t+6cnas=
2+
github.com/ScriptTiger/cno v0.0.0-20251208040705-1cfdf4ebf0d1/go.mod h1:Sn30fn9sJoeTf8zogrkItogFPhLKBAr8mSVZBc/RvjY=
3+
github.com/ScriptTiger/kanziSFX v0.0.0-20251208052004-ef0db38ff524 h1:cXTWVVi/lSGuqQnXh3Jj9TeS3BFuTunlO+JBiBTGVyw=
4+
github.com/ScriptTiger/kanziSFX v0.0.0-20251208052004-ef0db38ff524/go.mod h1:K384ImoA4qHwlgYSiWqPX1DT6P8M8VlLF4GksoGd44Q=
5+
github.com/flanglet/kanzi-go/v2 v2.4.0 h1:CFYcqeM5djhVXd1/Rn1VmwP4e7ovKgdJTh9V5lkm0FI=
6+
github.com/flanglet/kanzi-go/v2 v2.4.0/go.mod h1:11DfrNICimOP1RWO0SO/PkC3iAPIVXPYtJHf70+8fjQ=

0 commit comments

Comments
 (0)