Skip to content

Commit 1c621ca

Browse files
Merge pull request #7 from developersharif/v1.3mac
V1.3mac (lib issue resolved)
2 parents 38177c1 + 9c9a917 commit 1c621ca

246 files changed

Lines changed: 43938 additions & 37 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/tests.yml

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ on:
55
branches: [main]
66

77
jobs:
8-
test:
8+
test-linux:
99
runs-on: ubuntu-latest
1010

1111
steps:
@@ -38,3 +38,31 @@ jobs:
3838
php tests/widgets_test/MenubuttonTest.php
3939
php tests/widgets_test/MessageTest.php
4040
php tests/widgets_test/WindowTest.php
41+
42+
test-macos:
43+
runs-on: macos-latest
44+
45+
steps:
46+
- uses: actions/checkout@v4
47+
48+
- name: Setup PHP
49+
uses: shivammathur/setup-php@v2
50+
with:
51+
php-version: '8.1'
52+
extensions: ffi
53+
54+
- name: Install Composer dependencies
55+
run: composer install --no-interaction
56+
57+
- name: Run tests
58+
run: |
59+
php tests/index_test.php
60+
php tests/widgets_test/AdditionalWidgetsTest.php
61+
php tests/widgets_test/ButtonTest.php
62+
php tests/widgets_test/GeometryTest.php
63+
php tests/widgets_test/InputTest.php
64+
php tests/widgets_test/LabelTest.php
65+
php tests/widgets_test/MenuTest.php
66+
php tests/widgets_test/MenubuttonTest.php
67+
php tests/widgets_test/MessageTest.php
68+
php tests/widgets_test/WindowTest.php

src/Application.php

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,8 @@ public function __construct()
3030
$this->tcl->evalTcl("package require Tk");
3131
$this->tcl->evalTcl("wm withdraw .");
3232

33-
34-
$quitFile = (PHP_OS_FAMILY === 'Windows')
35-
? str_replace('\\', '/', sys_get_temp_dir()) . "/phpgui_quit.txt"
36-
: "/tmp/phpgui_quit.txt";
33+
$tempDir = str_replace('\\', '/', sys_get_temp_dir());
34+
$quitFile = $tempDir . "/phpgui_quit.txt";
3735
$this->tcl->evalTcl("proc ::exit_app {} { set ::forever 1; set f [open \"$quitFile\" w]; puts \$f 1; close \$f }");
3836
}
3937

@@ -50,21 +48,17 @@ public function run(): void
5048
}
5149

5250
$this->running = true;
51+
$tempDir = str_replace('\\', '/', sys_get_temp_dir());
52+
$callbackFile = $tempDir . "/phpgui_callback.txt";
53+
$quitFile = $tempDir . "/phpgui_quit.txt";
54+
5355
while ($this->running) {
5456
$this->tcl->evalTcl("update");
55-
// Use OS-based callback file path.
56-
$callbackFile = (PHP_OS_FAMILY === 'Windows')
57-
? str_replace('\\', '/', sys_get_temp_dir()) . "/phpgui_callback.txt"
58-
: "/tmp/phpgui_callback.txt";
5957
if (file_exists($callbackFile)) {
6058
$id = trim(file_get_contents($callbackFile));
6159
unlink($callbackFile);
6260
ProcessTCL::getInstance()->executeCallback($id);
6361
}
64-
// Use OS-based quit file path.
65-
$quitFile = (PHP_OS_FAMILY === 'Windows')
66-
? str_replace('\\', '/', sys_get_temp_dir()) . "/phpgui_quit.txt"
67-
: "/tmp/phpgui_quit.txt";
6862
if (file_exists($quitFile)) {
6963
unlink($quitFile);
7064
$this->running = false;

src/ProcessTCL.php

Lines changed: 82 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
class ProcessTCL
1717
{
1818
private FFI $ffi;
19+
private bool $isTcl9;
1920
private static ?ProcessTCL $instance = null;
2021
private array $callbacks = []; // registered callbacks
2122

@@ -34,47 +35,74 @@ private function __construct()
3435
// find the bundled script libraries without needing system packages.
3536
$this->setupLibraryPaths($libDir);
3637

37-
$cdef = "
38+
$this->ffi = $this->loadTclLibrary($libDir);
39+
}
40+
41+
/**
42+
* Returns the FFI C declarations for Tcl 8.6 (Linux/Windows).
43+
*/
44+
private function getCdefTcl8(): string
45+
{
46+
return "
3847
void* Tcl_CreateInterp(void);
3948
int Tcl_Init(void *interp);
4049
int Tcl_Eval(void *interp, const char *cmd);
4150
const char* Tcl_GetStringResult(void *interp);
4251
const char* Tcl_GetVar(void* interp, const char* varName, int flags);
4352
char* Tcl_SetVar(void* interp, const char* varName, const char* newValue, int flags);
4453
";
54+
}
4555

46-
$this->ffi = $this->loadTclLibrary($cdef, $libDir);
56+
/**
57+
* Returns the FFI C declarations for Tcl 9.0 (macOS).
58+
* Tcl 9 removed Tcl_Eval/Tcl_GetStringResult/Tcl_GetVar/Tcl_SetVar.
59+
*/
60+
private function getCdefTcl9(): string
61+
{
62+
return "
63+
void* Tcl_CreateInterp(void);
64+
int Tcl_Init(void *interp);
65+
int Tcl_EvalEx(void *interp, const char *cmd, int numBytes, int flags);
66+
void* Tcl_GetObjResult(void *interp);
67+
const char* Tcl_GetString(void *objPtr);
68+
const char* Tcl_GetVar2(void* interp, const char* name1, const char* name2, int flags);
69+
char* Tcl_SetVar2(void* interp, const char* name1, const char* name2, const char* newValue, int flags);
70+
";
4771
}
4872

4973
/**
5074
* Tries to load the Tcl shared library, attempting bundled first then system paths.
5175
*
5276
* @throws \RuntimeException if no loadable Tcl library is found.
5377
*/
54-
private function loadTclLibrary(string $cdef, string $libDir): FFI
78+
private function loadTclLibrary(string $libDir): FFI
5579
{
80+
// Each candidate: [path, isTcl9]
5681
$candidates = [];
5782

5883
if (PHP_OS_FAMILY === 'Windows') {
59-
$candidates[] = str_replace('/', '\\', $libDir . 'windows/bin/tcl86t.dll');
84+
$candidates[] = [str_replace('/', '\\', $libDir . 'windows/bin/tcl86t.dll'), false];
6085
} elseif (PHP_OS_FAMILY === 'Darwin') {
61-
$candidates[] = $libDir . 'libtcl9.0.dylib';
62-
$candidates[] = $libDir . 'libtcl8.6.dylib';
86+
$candidates[] = [$libDir . 'libtcl9.0.dylib', true];
87+
$candidates[] = [$libDir . 'libtcl8.6.dylib', false];
6388
} else {
64-
$candidates[] = $libDir . 'libtcl8.6.so';
65-
$candidates[] = '/usr/lib/x86_64-linux-gnu/libtcl8.6.so';
66-
$candidates[] = '/usr/lib/aarch64-linux-gnu/libtcl8.6.so';
67-
$candidates[] = '/usr/lib64/libtcl8.6.so';
68-
$candidates[] = '/usr/lib/libtcl8.6.so';
69-
$candidates[] = '/usr/local/lib/libtcl8.6.so';
89+
$candidates[] = [$libDir . 'libtcl8.6.so', false];
90+
$candidates[] = ['/usr/lib/x86_64-linux-gnu/libtcl8.6.so', false];
91+
$candidates[] = ['/usr/lib/aarch64-linux-gnu/libtcl8.6.so', false];
92+
$candidates[] = ['/usr/lib64/libtcl8.6.so', false];
93+
$candidates[] = ['/usr/lib/libtcl8.6.so', false];
94+
$candidates[] = ['/usr/local/lib/libtcl8.6.so', false];
7095
}
7196

72-
foreach ($candidates as $path) {
97+
foreach ($candidates as [$path, $isTcl9]) {
7398
if (!file_exists($path)) {
7499
continue;
75100
}
101+
$cdef = $isTcl9 ? $this->getCdefTcl9() : $this->getCdefTcl8();
76102
try {
77-
return FFI::cdef($cdef, $path);
103+
$ffi = FFI::cdef($cdef, $path);
104+
$this->isTcl9 = $isTcl9;
105+
return $ffi;
78106
} catch (\FFI\Exception $e) {
79107
// Bundled binary may be incompatible with this system, try next
80108
continue;
@@ -95,25 +123,38 @@ private function loadTclLibrary(string $cdef, string $libDir): FFI
95123
*/
96124
private function setupLibraryPaths(string $libDir): void
97125
{
98-
$tclScriptDir = $libDir . 'tcl8.6';
99-
$tkScriptDir = $libDir . 'tk8.6';
126+
// macOS uses Tcl/Tk 9.0, Linux uses 8.6
127+
if (PHP_OS_FAMILY === 'Darwin') {
128+
$tclScriptDir = $libDir . 'tcl9.0';
129+
$tkScriptDir = $libDir . 'tk9.0';
130+
} else {
131+
$tclScriptDir = $libDir . 'tcl8.6';
132+
$tkScriptDir = $libDir . 'tk8.6';
133+
}
100134

101135
if (is_dir($tclScriptDir)) {
102136
putenv('TCL_LIBRARY=' . $tclScriptDir);
103137
}
104138
if (is_dir($tkScriptDir)) {
105139
putenv('TK_LIBRARY=' . $tkScriptDir);
106-
// TCLLIBPATH tells Tcl where to search for package directories (like tk8.6/)
140+
// TCLLIBPATH tells Tcl where to search for package directories (like tk9.0/ or tk8.6/)
107141
putenv('TCLLIBPATH=' . $libDir);
108142
}
109143

110-
// Add bundled X11 libraries to LD_LIBRARY_PATH so libtk can find them
144+
// Add bundled X11 libraries to LD_LIBRARY_PATH so libtk can find them (Linux)
111145
$x11Dir = $libDir . 'x11';
112146
if (is_dir($x11Dir)) {
113147
$current = getenv('LD_LIBRARY_PATH');
114148
$newPath = $current ? $x11Dir . ':' . $current : $x11Dir;
115149
putenv('LD_LIBRARY_PATH=' . $newPath);
116150
}
151+
152+
// macOS: ensure bundled dylibs (libtommath) can be found at runtime
153+
if (PHP_OS_FAMILY === 'Darwin') {
154+
$current = getenv('DYLD_FALLBACK_LIBRARY_PATH');
155+
$newPath = $current ? $libDir . ':' . $current : $libDir;
156+
putenv('DYLD_FALLBACK_LIBRARY_PATH=' . $newPath);
157+
}
117158
}
118159

119160
/**
@@ -139,9 +180,13 @@ public static function getInstance(): self
139180
public function evalTcl(string $command)
140181
{
141182
$interp = $this->getInterp();
142-
$result = $this->ffi->Tcl_Eval($interp, $command);
143-
if ($result !== 0) { // Check for errors
144-
$error = $this->ffi->Tcl_GetStringResult($interp);
183+
if ($this->isTcl9) {
184+
$result = $this->ffi->Tcl_EvalEx($interp, $command, -1, 0);
185+
} else {
186+
$result = $this->ffi->Tcl_Eval($interp, $command);
187+
}
188+
if ($result !== 0) {
189+
$error = $this->getResult();
145190
throw new \RuntimeException("Tcl Error: " . $error);
146191
}
147192
return $this->getResult();
@@ -155,7 +200,12 @@ public function evalTcl(string $command)
155200
public function getResult(): string
156201
{
157202
$interp = $this->getInterp();
158-
$result = $this->ffi->Tcl_GetStringResult($interp);
203+
if ($this->isTcl9) {
204+
$objPtr = $this->ffi->Tcl_GetObjResult($interp);
205+
$result = $this->ffi->Tcl_GetString($objPtr);
206+
} else {
207+
$result = $this->ffi->Tcl_GetStringResult($interp);
208+
}
159209
if (is_string($result)) {
160210
return $result;
161211
}
@@ -171,7 +221,11 @@ public function getResult(): string
171221
public function getVar(string $varName): string
172222
{
173223
$interp = $this->getInterp();
174-
$result = $this->ffi->Tcl_GetVar($interp, $varName, 0);
224+
if ($this->isTcl9) {
225+
$result = $this->ffi->Tcl_GetVar2($interp, $varName, null, 0);
226+
} else {
227+
$result = $this->ffi->Tcl_GetVar($interp, $varName, 0);
228+
}
175229
if (is_string($result)) {
176230
return $result;
177231
}
@@ -191,7 +245,11 @@ public function getVar(string $varName): string
191245
public function setVar(string $varName, string $value): string
192246
{
193247
$interp = $this->getInterp();
194-
$result = $this->ffi->Tcl_SetVar($interp, $varName, $value, 0);
248+
if ($this->isTcl9) {
249+
$result = $this->ffi->Tcl_SetVar2($interp, $varName, null, $value, 0);
250+
} else {
251+
$result = $this->ffi->Tcl_SetVar($interp, $varName, $value, 0);
252+
}
195253
if (is_string($result)) {
196254
return $result;
197255
}

src/lib/libtcl9.0.dylib

-7.61 KB
Binary file not shown.

src/lib/libtcl9tk9.0.dylib

1.59 MB
Binary file not shown.

src/lib/libtommath.1.dylib

125 KB
Binary file not shown.

0 commit comments

Comments
 (0)