-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathApplication.php
More file actions
executable file
·117 lines (99 loc) · 3.22 KB
/
Copy pathApplication.php
File metadata and controls
executable file
·117 lines (99 loc) · 3.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
<?php
/**
* Class Application
*
* Main application class that initializes the Tcl/Tk environment,
* manages the event loop, and dispatches PHP callbacks.
*
* Integrates the Tcl/Tk event processing with PHP application logic via FFI.
*/
namespace PhpGui;
class Application
{
private ProcessTCL $tcl;
private bool $running = false;
private string $appId;
/** @var \PhpGui\Widget\WebView[] */
private array $webviews = [];
public function __construct()
{
$this->tcl = ProcessTCL::getInstance();
$this->appId = uniqid('app_');
$this->tcl->evalTcl('package require Tk');
$this->tcl->evalTcl('wm withdraw .');
// Quit signal lives in a Tcl variable. The temp-file approach used
// before v1.9 added 100ms of polling latency and collided across
// concurrent php-gui processes (shared /tmp/phpgui_quit.txt).
$this->tcl->evalTcl('proc ::exit_app {} { set ::phpgui_quit 1 }');
}
/**
* Runs the main event loop.
*
* Each iteration: pump Tcl events, drain queued PHP callbacks, drive
* any active WebView subprocesses, then sleep briefly. The sleep is
* deliberately short — a 100ms loop felt sluggish and dropped events
* when two fired within one tick.
*/
public function run(): void
{
if ($this->running) {
return;
}
$this->running = true;
while ($this->running) {
$this->tick();
// 10ms gives ~100Hz responsiveness without busy-looping. WebView
// mode polls subprocess pipes and benefits from the same cadence.
usleep(10000);
}
$this->quit();
}
public function addWebView(\PhpGui\Widget\WebView $wv): void
{
$this->webviews[$wv->getId()] = $wv;
}
public function removeWebView(\PhpGui\Widget\WebView $wv): void
{
unset($this->webviews[$wv->getId()]);
}
/**
* Run a single iteration of the event loop. Public for tests so they
* can step the loop deterministically without sleeping.
*/
public function tick(): void
{
$this->tcl->evalTcl('update');
// Drain every queued callback, in order. The drain resets the
// queue first, so closures that fire new events don't race with us.
$this->tcl->drainPendingCallbacks();
if ($this->tcl->shouldQuit()) {
$this->running = false;
}
foreach ($this->webviews as $key => $wv) {
if ($wv->isClosed()) {
unset($this->webviews[$key]);
continue;
}
$wv->processEvents();
}
}
/**
* Tear down the application. Closes WebViews and flips the run flag
* so the loop exits at its next iteration. Does NOT call `exit()` —
* `run()` returns to the caller, which can perform its own shutdown
* work (logging, cleanup) before the script ends.
*/
public function quit(): void
{
if (!$this->running) {
return;
}
foreach ($this->webviews as $wv) {
if (!$wv->isClosed()) {
$wv->destroy();
}
}
$this->webviews = [];
$this->running = false;
}
}