This commit is contained in:
Thomas Peterson 2025-11-27 13:22:59 +01:00
parent cb148dfb7c
commit e617930ca4
20 changed files with 823 additions and 53 deletions

View File

@ -36,8 +36,11 @@ class App
// Status label (referenced by tabs) // Status label (referenced by tabs)
$statusLabel = new Label( $statusLabel = new Label(
text: 'Fenster: ' . $this->window->getViewport()->windowWidth . 'x' . $this->window->getViewport()->windowHeight, text: 'Fenster: ' .
style: 'basis-4/8 text-black' $this->window->getViewport()->windowWidth .
'x' .
$this->window->getViewport()->windowHeight,
style: 'basis-4/8 text-black',
); );
// Settings variables (simple variables work better with async than object properties) // Settings variables (simple variables work better with async than object properties)
@ -50,7 +53,13 @@ class App
$mainContainer->addComponent($menuBar); $mainContainer->addComponent($menuBar);
// Create settings modal with the real menu bar // Create settings modal with the real menu bar
$settingsModal = new SettingsModal($this->settings, $menuBar, $currentApiKey, $currentPrivateKeyPath, $currentRemoteStartDir); $settingsModal = new SettingsModal(
$this->settings,
$menuBar,
$currentApiKey,
$currentPrivateKeyPath,
$currentRemoteStartDir,
);
$mainContainer->addComponent($settingsModal->getModal()); $mainContainer->addComponent($settingsModal->getModal());
// Build menu bar menus after modal is created // Build menu bar menus after modal is created
@ -69,6 +78,7 @@ class App
$this->settings, $this->settings,
$kanbanTab, $kanbanTab,
); );
$kanbanTab->setServerListTab($serverListTab);
$sftpManagerTab = new SftpManagerTab( $sftpManagerTab = new SftpManagerTab(
$currentApiKey, $currentApiKey,
$currentPrivateKeyPath, $currentPrivateKeyPath,
@ -97,10 +107,21 @@ class App
$statusBar->addSegment(new Label('v1.0', 'basis-1/8 text-center text-black border-l border-gray-300')); $statusBar->addSegment(new Label('v1.0', 'basis-1/8 text-center text-black border-l border-gray-300'));
$statusBar->addSegment(new Label( $statusBar->addSegment(new Label(
'PHPNative Framework', 'PHPNative Framework',
'basis-3/8 text-right text-black pr-2 border-l border-gray-300' 'basis-3/8 text-right text-black pr-2 border-l border-gray-300',
)); ));
$mainContainer->addComponent($statusBar); $mainContainer->addComponent($statusBar);
// Tray disabled for now due to GTK/XKB issues with static builds
// TODO: Re-enable after rebuilding static PHP with new SDL3 tray implementation
if (function_exists('tray_setup')) {
try {
tray_setup('', ['Beenden']);
} catch (\Throwable $e) {
// Tray initialization failed, continue without tray
error_log('Tray setup failed: ' . $e->getMessage());
}
}
// Set window content and run // Set window content and run
$this->window->setRoot($mainContainer); $this->window->setRoot($mainContainer);
$this->app->addWindow($this->window); $this->app->addWindow($this->window);

View File

@ -24,6 +24,7 @@ class KanbanTab
private Container $editBoardButtonsContainer; private Container $editBoardButtonsContainer;
private string $currentEditingBoard = 'neu'; private string $currentEditingBoard = 'neu';
private null|string $currentEditingTaskId = null; private null|string $currentEditingTaskId = null;
private null|ServerListTab $serverListTab = null;
public function __construct(Settings $settings) public function __construct(Settings $settings)
{ {
@ -85,6 +86,11 @@ class KanbanTab
$this->renderBoards(); $this->renderBoards();
} }
public function setServerListTab(ServerListTab $serverListTab): void
{
$this->serverListTab = $serverListTab;
}
public function getContainer(): Container public function getContainer(): Container
{ {
return $this->tab; return $this->tab;
@ -270,6 +276,10 @@ class KanbanTab
$this->editModal->setVisible(false); $this->editModal->setVisible(false);
$this->renderBoards(); $this->renderBoards();
if ($this->serverListTab !== null) {
$this->serverListTab->refreshCurrentServerTasks();
}
} }
private function renderBoards(): void private function renderBoards(): void
@ -360,6 +370,10 @@ class KanbanTab
$kanbanTab->settings->set('kanban.tasks', $tasks); $kanbanTab->settings->set('kanban.tasks', $tasks);
$kanbanTab->settings->save(); $kanbanTab->settings->save();
$kanbanTab->renderBoards(); $kanbanTab->renderBoards();
if ($kanbanTab->serverListTab !== null) {
$kanbanTab->serverListTab->refreshCurrentServerTasks();
}
}); });
$headerRow->addComponent($editButton); $headerRow->addComponent($editButton);

View File

@ -1032,4 +1032,9 @@ class ServerListTab
$this->renderTodoList(); $this->renderTodoList();
} }
public function refreshCurrentServerTasks(): void
{
$this->loadServerTasks();
}
} }

View File

@ -2,6 +2,8 @@
declare(strict_types=1); declare(strict_types=1);
putenv('XKB_CONFIG_ROOT=/usr/share/X11/xkb');
putenv('XLOCALEDIR=/usr/share/X11/locale');
// Bootstrap: Load composer autoloader // Bootstrap: Load composer autoloader
require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__ . '/../vendor/autoload.php';

View File

@ -36,11 +36,7 @@ $loadTasks = static function (string $path): array {
}; };
$saveTasks = static function (string $path, array $tasks): void { $saveTasks = static function (string $path, array $tasks): void {
file_put_contents( file_put_contents($path, json_encode($tasks, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), LOCK_EX);
$path,
json_encode($tasks, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE),
LOCK_EX,
);
}; };
$tasks = $loadTasks($storagePath); $tasks = $loadTasks($storagePath);
@ -54,7 +50,10 @@ $main = new Container('flex flex-col bg-gray-100 gap-4 p-4 h-full w-full');
$title = new Label('Todo Liste', 'text-2xl font-bold text-black'); $title = new Label('Todo Liste', 'text-2xl font-bold text-black');
$main->addComponent($title); $main->addComponent($title);
$input = new TextInput('Neue Aufgabe hinzufügen …', 'flex-1 border border-gray-300 rounded px-3 py-2 bg-white text-black'); $input = new TextInput(
'Neue Aufgabe hinzufügen …',
'flex-1 border border-gray-300 rounded px-3 py-2 bg-white text-black',
);
$addButton = new Button('Hinzufügen', 'px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700'); $addButton = new Button('Hinzufügen', 'px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700');
$inputRow = new Container('flex flex-row gap-3 w-full'); $inputRow = new Container('flex flex-row gap-3 w-full');
$inputRow->addComponent($input); $inputRow->addComponent($input);
@ -78,10 +77,10 @@ $renderTasks = function () use (&$tasks, $listContainer, $statusLabel, $storageP
} }
foreach ($tasks as $index => $task) { foreach ($tasks as $index => $task) {
$row = new Container('flex flex-row items-center gap-3 w-full border border-gray-200 rounded px-3 py-2 bg-white shadow-sm'); $row = new Container(
$taskLabelStyles = $task['done'] 'flex flex-row items-center gap-3 w-full border border-gray-200 rounded px-3 py-2 bg-white shadow-sm',
? 'flex-1 text-gray-500 line-through' );
: 'flex-1 text-black'; $taskLabelStyles = $task['done'] ? 'flex-1 text-gray-500 line-through' : 'flex-1 text-black';
$taskLabel = new Label($task['title'], $taskLabelStyles); $taskLabel = new Label($task['title'], $taskLabelStyles);
$row->addComponent($taskLabel); $row->addComponent($taskLabel);
@ -92,7 +91,14 @@ $renderTasks = function () use (&$tasks, $listContainer, $statusLabel, $storageP
: 'px-3 py-1 text-sm bg-emerald-500 text-white rounded hover:bg-emerald-600', : 'px-3 py-1 text-sm bg-emerald-500 text-white rounded hover:bg-emerald-600',
); );
$toggleButton->setOnClick(function () use (&$tasks, $task, $storagePath, $saveTasks, $statusLabel, $renderTasks) { $toggleButton->setOnClick(function () use (
&$tasks,
$task,
$storagePath,
$saveTasks,
$statusLabel,
$renderTasks,
) {
foreach ($tasks as &$entry) { foreach ($tasks as &$entry) {
if ($entry['id'] === $task['id']) { if ($entry['id'] === $task['id']) {
$entry['done'] = !$entry['done']; $entry['done'] = !$entry['done'];
@ -105,12 +111,17 @@ $renderTasks = function () use (&$tasks, $listContainer, $statusLabel, $storageP
$renderTasks(); $renderTasks();
}); });
$deleteButton = new Button( $deleteButton = new Button('Löschen', 'px-3 py-1 text-sm bg-red-500 text-white rounded hover:bg-red-600');
'Löschen',
'px-3 py-1 text-sm bg-red-500 text-white rounded hover:bg-red-600',
);
$deleteButton->setOnClick(function () use (&$tasks, $index, $task, $storagePath, $saveTasks, $statusLabel, $renderTasks) { $deleteButton->setOnClick(function () use (
&$tasks,
$index,
$task,
$storagePath,
$saveTasks,
$statusLabel,
$renderTasks,
) {
array_splice($tasks, $index, 1); array_splice($tasks, $index, 1);
$saveTasks($storagePath, $tasks); $saveTasks($storagePath, $tasks);
$statusLabel->setText('Aufgabe entfernt: ' . $task['title']); $statusLabel->setText('Aufgabe entfernt: ' . $task['title']);
@ -147,4 +158,64 @@ $addButton->setOnClick(function () use (&$tasks, $input, $saveTasks, $storagePat
$window->setRoot($main); $window->setRoot($main);
$app->addWindow($window); $app->addWindow($window);
// Setup Tray with callbacks
if (function_exists('tray_setup')) {
try {
tray_setup('', [
[
'label' => 'Neue Aufgabe',
'callback' => function ($idx) use (
&$tasks,
$input,
$saveTasks,
$storagePath,
$statusLabel,
$renderTasks,
) {
try {
error_log('Neue Aufgabe Callback called!');
$title = 'Tray: Neue Aufgabe';
error_log("Adding task: {$title}");
$tasks[] = [
'id' => uniqid('task_', true),
'title' => $title,
'done' => false,
];
error_log('Saving tasks...');
$saveTasks($storagePath, $tasks);
error_log('Updating UI...');
$statusLabel->setText('Aufgabe hinzugefügt: ' . $title);
$renderTasks();
error_log('Done!');
} catch (\Throwable $e) {
error_log('ERROR in Neue Aufgabe callback: ' . $e->getMessage());
error_log('Trace: ' . $e->getTraceAsString());
}
},
],
[
'label' => 'Fenster anzeigen',
'callback' => function ($idx) use ($window) {
// Show/focus window (TODO: implement window focus/show API)
},
],
[
'label' => 'Beenden',
'callback' => function ($idx) use ($app) {
$app->quit();
},
],
]);
} catch (\Throwable $e) {
error_log('Tray setup failed: ' . $e->getMessage());
}
}
$app->run(); $app->run();

View File

@ -1,12 +1,7 @@
[ [
{ {
"id": "task_69164e23f0d356.41043316", "id": "task_692840bf6e9035.94502029",
"title": "Test", "title": "Tray: Neue Aufgabe",
"done": false "done": false
},
{
"id": "task_69164e28dae205.72302890",
"title": "Geht",
"done": true
} }
] ]

Binary file not shown.

Binary file not shown.

View File

@ -87,6 +87,8 @@ if test "$PHP_SDL3" != "no"; then
AC_MSG_WARN([libnotify not found via pkg-config, desktop_notify() will be disabled]) AC_MSG_WARN([libnotify not found via pkg-config, desktop_notify() will be disabled])
]) ])
dnl SDL3 includes native tray support, no external dependencies needed
SDL_SOURCE_FILES="sdl3.c helper.c sdl3_image.c sdl3_ttf.c sdl3_events.c" SDL_SOURCE_FILES="sdl3.c helper.c sdl3_image.c sdl3_ttf.c sdl3_events.c"
PHP_NEW_EXTENSION(sdl3, $SDL_SOURCE_FILES, $ext_shared) PHP_NEW_EXTENSION(sdl3, $SDL_SOURCE_FILES, $ext_shared)

View File

@ -3,7 +3,4 @@
# Created by configure # Created by configure
'./configure' \ './configure' \
'--with-sdl3' \
'--with-sdl3-image' \
'--with-sdl3-ttf' \
"$@" "$@"

View File

@ -413,7 +413,7 @@ $config_headers
Report bugs to the package provider." Report bugs to the package provider."
ac_cs_config='--with-sdl3 --with-sdl3-image --with-sdl3-ttf' ac_cs_config=''
ac_cs_version="\ ac_cs_version="\
config.status config.status
configured by ./configure, generated by GNU Autoconf 2.72, configured by ./configure, generated by GNU Autoconf 2.72,
@ -494,7 +494,7 @@ if $ac_cs_silent; then
fi fi
if $ac_cs_recheck; then if $ac_cs_recheck; then
set X /bin/bash './configure' '--with-sdl3' '--with-sdl3-image' '--with-sdl3-ttf' $ac_configure_extra_args --no-create --no-recursion set X /bin/bash './configure' $ac_configure_extra_args --no-create --no-recursion
shift shift
\printf "%s\n" "running CONFIG_SHELL=/bin/bash $*" >&6 \printf "%s\n" "running CONFIG_SHELL=/bin/bash $*" >&6
CONFIG_SHELL='/bin/bash' CONFIG_SHELL='/bin/bash'

37
php-sdl3/configure vendored
View File

@ -5247,6 +5247,7 @@ printf "%s\n" "#define HAVE_LIBNOTIFY 1" >>confdefs.h
fi fi
SDL_SOURCE_FILES="sdl3.c helper.c sdl3_image.c sdl3_ttf.c sdl3_events.c" SDL_SOURCE_FILES="sdl3.c helper.c sdl3_image.c sdl3_ttf.c sdl3_events.c"
@ -6102,7 +6103,7 @@ ia64-*-hpux*)
;; ;;
*-*-irix6*) *-*-irix6*)
# Find out which ABI we are using. # Find out which ABI we are using.
echo '#line 6105 "configure"' > conftest.$ac_ext echo '#line 6106 "configure"' > conftest.$ac_ext
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
(eval $ac_compile) 2>&5 (eval $ac_compile) 2>&5
ac_status=$? ac_status=$?
@ -7481,7 +7482,7 @@ else case e in #(
LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym"
cat > conftest.$ac_ext <<EOF cat > conftest.$ac_ext <<EOF
#line 7484 "configure" #line 7485 "configure"
#include "confdefs.h" #include "confdefs.h"
int main(void) { int main(void) {
; return 0; } ; return 0; }
@ -7643,11 +7644,11 @@ else case e in #(
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'` -e 's:$: $lt_compiler_flag:'`
(eval echo "\"configure:7646: $lt_compile\"" >&5) (eval echo "\"configure:7647: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err) (eval "$lt_compile" 2>conftest.err)
ac_status=$? ac_status=$?
cat conftest.err >&5 cat conftest.err >&5
echo "configure:7650: \$? = $ac_status" >&5 echo "configure:7651: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized # The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output. # So say no if there are warnings other than the usual output.
@ -7943,11 +7944,11 @@ else case e in #(
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'` -e 's:$: $lt_compiler_flag:'`
(eval echo "\"configure:7946: $lt_compile\"" >&5) (eval echo "\"configure:7947: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err) (eval "$lt_compile" 2>conftest.err)
ac_status=$? ac_status=$?
cat conftest.err >&5 cat conftest.err >&5
echo "configure:7950: \$? = $ac_status" >&5 echo "configure:7951: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized # The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output. # So say no if there are warnings other than the usual output.
@ -8051,11 +8052,11 @@ else case e in #(
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'` -e 's:$: $lt_compiler_flag:'`
(eval echo "\"configure:8054: $lt_compile\"" >&5) (eval echo "\"configure:8055: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err) (eval "$lt_compile" 2>out/conftest.err)
ac_status=$? ac_status=$?
cat out/conftest.err >&5 cat out/conftest.err >&5
echo "configure:8058: \$? = $ac_status" >&5 echo "configure:8059: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext if (exit $ac_status) && test -s out/conftest2.$ac_objext
then then
# The compiler can only warn and ignore the option if not recognized # The compiler can only warn and ignore the option if not recognized
@ -8516,7 +8517,7 @@ _LT_EOF
# Determine the default libpath from the value encoded in an empty executable. # Determine the default libpath from the value encoded in an empty executable.
cat > conftest.$ac_ext <<EOF cat > conftest.$ac_ext <<EOF
#line 8519 "configure" #line 8520 "configure"
#include "confdefs.h" #include "confdefs.h"
int main(void) { int main(void) {
; return 0; } ; return 0; }
@ -8558,7 +8559,7 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
# Determine the default libpath from the value encoded in an empty executable. # Determine the default libpath from the value encoded in an empty executable.
cat > conftest.$ac_ext <<EOF cat > conftest.$ac_ext <<EOF
#line 8561 "configure" #line 8562 "configure"
#include "confdefs.h" #include "confdefs.h"
int main(void) { int main(void) {
; return 0; } ; return 0; }
@ -10139,7 +10140,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<EOF cat > conftest.$ac_ext <<EOF
#line 10142 "configure" #line 10143 "configure"
#include "confdefs.h" #include "confdefs.h"
#if HAVE_DLFCN_H #if HAVE_DLFCN_H
@ -10238,7 +10239,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<EOF cat > conftest.$ac_ext <<EOF
#line 10241 "configure" #line 10242 "configure"
#include "confdefs.h" #include "confdefs.h"
#if HAVE_DLFCN_H #if HAVE_DLFCN_H
@ -11307,7 +11308,7 @@ case $host_os in
# Determine the default libpath from the value encoded in an empty executable. # Determine the default libpath from the value encoded in an empty executable.
cat > conftest.$ac_ext <<EOF cat > conftest.$ac_ext <<EOF
#line 11310 "configure" #line 11311 "configure"
#include "confdefs.h" #include "confdefs.h"
int main(void) { int main(void) {
; return 0; } ; return 0; }
@ -11350,7 +11351,7 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
# Determine the default libpath from the value encoded in an empty executable. # Determine the default libpath from the value encoded in an empty executable.
cat > conftest.$ac_ext <<EOF cat > conftest.$ac_ext <<EOF
#line 11353 "configure" #line 11354 "configure"
#include "confdefs.h" #include "confdefs.h"
int main(void) { int main(void) {
; return 0; } ; return 0; }
@ -12603,11 +12604,11 @@ else case e in #(
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'` -e 's:$: $lt_compiler_flag:'`
(eval echo "\"configure:12606: $lt_compile\"" >&5) (eval echo "\"configure:12607: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err) (eval "$lt_compile" 2>conftest.err)
ac_status=$? ac_status=$?
cat conftest.err >&5 cat conftest.err >&5
echo "configure:12610: \$? = $ac_status" >&5 echo "configure:12611: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized # The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output. # So say no if there are warnings other than the usual output.
@ -12711,11 +12712,11 @@ else case e in #(
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'` -e 's:$: $lt_compiler_flag:'`
(eval echo "\"configure:12714: $lt_compile\"" >&5) (eval echo "\"configure:12715: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err) (eval "$lt_compile" 2>out/conftest.err)
ac_status=$? ac_status=$?
cat out/conftest.err >&5 cat out/conftest.err >&5
echo "configure:12718: \$? = $ac_status" >&5 echo "configure:12719: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext if (exit $ac_status) && test -s out/conftest2.$ac_objext
then then
# The compiler can only warn and ignore the option if not recognized # The compiler can only warn and ignore the option if not recognized

Binary file not shown.

View File

@ -11,11 +11,15 @@
#include "sdl3_events.h" #include "sdl3_events.h"
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <math.h> #include <math.h>
#include <string.h>
#ifdef HAVE_LIBNOTIFY #ifdef HAVE_LIBNOTIFY
#include <libnotify/notify.h> #include <libnotify/notify.h>
#endif #endif
// SDL3 native tray support
#include <SDL3/SDL_tray.h>
// Resource handles (nicht static, damit sie in anderen Modulen verfügbar sind) // Resource handles (nicht static, damit sie in anderen Modulen verfügbar sind)
int le_sdl_window; int le_sdl_window;
int le_sdl_renderer; int le_sdl_renderer;
@ -45,6 +49,64 @@ static void sdl_texture_dtor(zend_resource *rsrc) {
} }
} }
// --- Tray integration state ---
// SDL3 Tray globals
static SDL_Tray *g_sdl_tray = NULL;
static SDL_TrayMenu *g_sdl_tray_menu = NULL;
static SDL_TrayEntry **g_sdl_tray_entries = NULL;
static int g_sdl_tray_entry_count = 0;
static zval *g_tray_callbacks = NULL; // Array of PHP callbacks for each tray entry
static void SDLCALL php_tray_callback(void *userdata, SDL_TrayEntry *entry) {
intptr_t idx = (intptr_t)userdata;
idx = idx-1;
// Log all callback invocations for debugging
php_error_docref(NULL, E_NOTICE, "Tray callback invoked: userdata=%p (idx=%d), g_tray_callbacks=%p",
userdata, (int)idx, (void*)g_tray_callbacks);
// userdata can be NULL for events without callbacks (e.g., clicking the tray icon itself)
// This is normal, so just return silently
if (!userdata || !g_tray_callbacks) {
php_error_docref(NULL, E_NOTICE, "Tray callback: skipping (userdata=%p, callbacks=%p)",
userdata, (void*)g_tray_callbacks);
return;
}
// Check if we have a callback for this index
if (idx < 0 || idx >= g_sdl_tray_entry_count) {
php_error_docref(NULL, E_WARNING, "Tray callback: invalid index %d (max %d)", (int)idx, g_sdl_tray_entry_count);
return;
}
zval *callback = &g_tray_callbacks[idx];
// Only call if callback is set and callable
if (Z_TYPE_P(callback) == IS_UNDEF) {
php_error_docref(NULL, E_WARNING, "Tray callback %d: callback is undefined", (int)idx);
return;
}
if (!zend_is_callable(callback, 0, NULL)) {
php_error_docref(NULL, E_WARNING, "Tray callback %d: callback is not callable", (int)idx);
return;
}
zval retval;
zval params[1];
// Pass the index as parameter to the callback
ZVAL_LONG(&params[0], idx);
// Call the PHP callback
int result = call_user_function(EG(function_table), NULL, callback, &retval, 1, params);
if (result == SUCCESS) {
zval_ptr_dtor(&retval);
} else {
php_error_docref(NULL, E_WARNING, "Tray callback %d: call_user_function failed with code %d", (int)idx, result);
}
}
PHP_MINIT_FUNCTION(sdl3) { PHP_MINIT_FUNCTION(sdl3) {
le_sdl_window = zend_register_list_destructors_ex(sdl_window_dtor, NULL, "SDL_Window", module_number); le_sdl_window = zend_register_list_destructors_ex(sdl_window_dtor, NULL, "SDL_Window", module_number);
le_sdl_renderer = zend_register_list_destructors_ex(sdl_renderer_dtor, NULL, "SDL_Renderer", module_number); le_sdl_renderer = zend_register_list_destructors_ex(sdl_renderer_dtor, NULL, "SDL_Renderer", module_number);
@ -168,8 +230,10 @@ PHP_FUNCTION(sdl_get_window_id) {
PHP_FUNCTION(sdl_create_renderer) { PHP_FUNCTION(sdl_create_renderer) {
zval *win_res; zval *win_res;
SDL_Window *win; SDL_Window *win;
char *renderer_name = NULL;
size_t renderer_name_len = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &win_res) == FAILURE) { if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|s", &win_res, &renderer_name, &renderer_name_len) == FAILURE) {
RETURN_THROWS(); RETURN_THROWS();
} }
@ -178,13 +242,32 @@ PHP_FUNCTION(sdl_create_renderer) {
RETURN_FALSE; RETURN_FALSE;
} }
SDL_Renderer *ren = SDL_CreateRenderer(win, NULL); SDL_Renderer *ren = SDL_CreateRenderer(win, renderer_name_len > 0 ? renderer_name : NULL);
if (!ren) { if (!ren) {
RETURN_FALSE; RETURN_FALSE;
} }
RETURN_RES(zend_register_resource(ren, le_sdl_renderer)); RETURN_RES(zend_register_resource(ren, le_sdl_renderer));
} }
PHP_FUNCTION(sdl_get_num_render_drivers) {
int num = SDL_GetNumRenderDrivers();
RETURN_LONG(num);
}
PHP_FUNCTION(sdl_get_render_driver) {
zend_long index;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &index) == FAILURE) {
RETURN_THROWS();
}
const char *name = SDL_GetRenderDriver((int)index);
if (!name) {
RETURN_FALSE;
}
RETURN_STRING(name);
}
PHP_FUNCTION(sdl_set_render_draw_color) { PHP_FUNCTION(sdl_set_render_draw_color) {
zval *ren_res; zval *ren_res;
SDL_Renderer *ren; SDL_Renderer *ren;
@ -529,6 +612,211 @@ PHP_FUNCTION(sdl_set_texture_alpha_mod) {
RETURN_TRUE; RETURN_TRUE;
} }
PHP_FUNCTION(tray_setup)
{
char *icon_path;
size_t icon_len;
zval *menu_arr = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|a", &icon_path, &icon_len, &menu_arr) == FAILURE) {
RETURN_THROWS();
}
// Clean up existing tray if any
if (g_sdl_tray) {
SDL_DestroyTray(g_sdl_tray);
g_sdl_tray = NULL;
g_sdl_tray_menu = NULL;
}
if (g_sdl_tray_entries) {
efree(g_sdl_tray_entries);
g_sdl_tray_entries = NULL;
g_sdl_tray_entry_count = 0;
}
if (g_tray_callbacks) {
// Free old callbacks
for (int i = 0; i < g_sdl_tray_entry_count; i++) {
zval_ptr_dtor(&g_tray_callbacks[i]);
}
efree(g_tray_callbacks);
g_tray_callbacks = NULL;
}
// Load icon if provided (optional)
SDL_Surface *icon_surface = NULL;
if (icon_len > 0) {
icon_surface = SDL_LoadBMP(icon_path);
// Icon can be NULL, SDL will handle it
}
// Initialize video subsystem if not already initialized (required for tray)
if (!SDL_WasInit(SDL_INIT_VIDEO)) {
if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) {
if (icon_surface) {
SDL_DestroySurface(icon_surface);
}
php_error_docref(NULL, E_WARNING, "Failed to init video subsystem for tray: %s", SDL_GetError());
RETURN_FALSE;
}
}
// Check if DISPLAY is set (required for GTK-based tray on Linux)
#ifdef __linux__
const char *display = getenv("DISPLAY");
const char *wayland = getenv("WAYLAND_DISPLAY");
if (!display && !wayland) {
if (icon_surface) {
SDL_DestroySurface(icon_surface);
}
php_error_docref(NULL, E_WARNING, "Cannot create tray: No DISPLAY or WAYLAND_DISPLAY environment variable set");
RETURN_FALSE;
}
#endif
// Create SDL3 tray
g_sdl_tray = SDL_CreateTray(icon_surface, "PHP SDL3 Tray");
if (icon_surface) {
SDL_DestroySurface(icon_surface);
}
if (!g_sdl_tray) {
php_error_docref(NULL, E_WARNING, "Failed to create tray: %s", SDL_GetError());
RETURN_FALSE;
}
// Create tray menu
g_sdl_tray_menu = SDL_CreateTrayMenu(g_sdl_tray);
if (!g_sdl_tray_menu) {
SDL_DestroyTray(g_sdl_tray);
g_sdl_tray = NULL;
php_error_docref(NULL, E_WARNING, "Failed to create tray menu: %s", SDL_GetError());
RETURN_FALSE;
}
// Add menu items if provided
if (menu_arr && Z_TYPE_P(menu_arr) == IS_ARRAY) {
HashTable *ht = Z_ARRVAL_P(menu_arr);
int count = zend_hash_num_elements(ht);
if (count > 0) {
g_sdl_tray_entries = ecalloc(count, sizeof(SDL_TrayEntry *));
g_tray_callbacks = ecalloc(count, sizeof(zval));
g_sdl_tray_entry_count = count;
int idx = 0;
zval *val;
ZEND_HASH_FOREACH_VAL(ht, val) {
if (idx >= count) {
break;
}
const char *label = NULL;
zval *callback = NULL;
// Handle array entries: ['label' => '...', 'callback' => function]
if (Z_TYPE_P(val) == IS_ARRAY) {
zval *label_val = zend_hash_str_find(Z_ARRVAL_P(val), "label", sizeof("label") - 1);
zval *callback_val = zend_hash_str_find(Z_ARRVAL_P(val), "callback", sizeof("callback") - 1);
if (label_val && Z_TYPE_P(label_val) == IS_STRING) {
label = Z_STRVAL_P(label_val);
}
if (callback_val && zend_is_callable(callback_val, 0, NULL)) {
callback = callback_val;
}
}
// Handle simple string entries (backward compatibility)
else if (Z_TYPE_P(val) == IS_STRING) {
label = Z_STRVAL_P(val);
}
// Create entry (NULL label creates separator)
SDL_TrayEntry *entry = SDL_InsertTrayEntryAt(
g_sdl_tray_menu,
-1,
label,
SDL_TRAYENTRY_BUTTON
);
if (!entry) {
php_error_docref(NULL, E_WARNING, "Failed to create tray entry %d ('%s'): %s", idx, label ? label : "(null)", SDL_GetError());
}
if (entry && label) {
// Set callback with index+1 as userdata (so index 0 doesn't become NULL)
SDL_SetTrayEntryCallback(entry, php_tray_callback, (void *)(intptr_t)(idx + 1));
php_error_docref(NULL, E_NOTICE, "Registered tray entry %d: '%s' with callback=%s", idx, label, callback ? "YES" : "NO");
}
// Store PHP callback if provided
if (callback) {
ZVAL_COPY(&g_tray_callbacks[idx], callback);
} else {
ZVAL_UNDEF(&g_tray_callbacks[idx]);
}
g_sdl_tray_entries[idx] = entry;
idx++;
} ZEND_HASH_FOREACH_END();
}
}
RETURN_TRUE;
}
PHP_FUNCTION(tray_poll)
{
zend_bool blocking = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &blocking) == FAILURE) {
RETURN_THROWS();
}
if (!g_sdl_tray) {
RETURN_LONG(-1);
}
// SDL_UpdateTrays() processes events that were already polled by sdl_poll_event()
// The event polling happens in the Application loop before this is called
// SDL_UpdateTrays() will trigger our C callbacks, which call the PHP callbacks
SDL_UpdateTrays();
// Always return -1 (events are handled via callbacks)
RETURN_LONG(-1);
}
PHP_FUNCTION(tray_exit)
{
if (zend_parse_parameters_none() == FAILURE) {
RETURN_THROWS();
}
if (g_sdl_tray) {
SDL_DestroyTray(g_sdl_tray);
g_sdl_tray = NULL;
g_sdl_tray_menu = NULL;
}
if (g_sdl_tray_entries) {
efree(g_sdl_tray_entries);
g_sdl_tray_entries = NULL;
}
if (g_tray_callbacks) {
// Free callbacks
for (int i = 0; i < g_sdl_tray_entry_count; i++) {
zval_ptr_dtor(&g_tray_callbacks[i]);
}
efree(g_tray_callbacks);
g_tray_callbacks = NULL;
}
g_sdl_tray_entry_count = 0;
RETURN_TRUE;
}
PHP_FUNCTION(desktop_notify) PHP_FUNCTION(desktop_notify)
{ {
char *title, *body; char *title, *body;
@ -1105,6 +1393,25 @@ PHP_FUNCTION(sdl_get_current_video_driver) {
RETURN_STRING(drv); RETURN_STRING(drv);
} }
PHP_FUNCTION(sdl_get_num_video_drivers) {
int num = SDL_GetNumVideoDrivers();
RETURN_LONG(num);
}
PHP_FUNCTION(sdl_get_video_driver) {
zend_long index;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &index) == FAILURE) {
RETURN_THROWS();
}
const char *driver = SDL_GetVideoDriver((int)index);
if (!driver) {
RETURN_FALSE;
}
RETURN_STRING(driver);
}
PHP_FUNCTION(sdl_start_text_input) { PHP_FUNCTION(sdl_start_text_input) {
zval *win_res; zval *win_res;
SDL_Window *win; SDL_Window *win;
@ -1167,6 +1474,14 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_create_renderer, 0, 0, 1) ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_create_renderer, 0, 0, 1)
ZEND_ARG_INFO(0, window) ZEND_ARG_INFO(0, window)
ZEND_ARG_INFO(0, renderer_name)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_get_num_render_drivers, 0, 0, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_get_render_driver, 0, 0, 1)
ZEND_ARG_INFO(0, index)
ZEND_END_ARG_INFO() ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_set_render_draw_color, 0, 0, 5) ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_set_render_draw_color, 0, 0, 5)
@ -1242,6 +1557,18 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_set_texture_alpha_mod, 0, 0, 2)
ZEND_ARG_INFO(0, alpha) ZEND_ARG_INFO(0, alpha)
ZEND_END_ARG_INFO() ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_tray_setup, 0, 0, 1)
ZEND_ARG_INFO(0, icon)
ZEND_ARG_ARRAY_INFO(0, menuItems, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_tray_poll, 0, 0, 0)
ZEND_ARG_INFO(0, blocking)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_tray_exit, 0, 0, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_desktop_notify, 0, 0, 2) ZEND_BEGIN_ARG_INFO_EX(arginfo_desktop_notify, 0, 0, 2)
ZEND_ARG_INFO(0, title) ZEND_ARG_INFO(0, title)
ZEND_ARG_INFO(0, body) ZEND_ARG_INFO(0, body)
@ -1329,6 +1656,14 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_get_current_video_driver, 0, 0, 0) ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_get_current_video_driver, 0, 0, 0)
ZEND_END_ARG_INFO() ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_get_num_video_drivers, 0, 0, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_get_video_driver, 0, 0, 1)
ZEND_ARG_INFO(0, index)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_get_renderer_output_size, 0, 0, 1) ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_get_renderer_output_size, 0, 0, 1)
ZEND_ARG_INFO(0, renderer) ZEND_ARG_INFO(0, renderer)
ZEND_END_ARG_INFO() ZEND_END_ARG_INFO()
@ -1350,6 +1685,8 @@ const zend_function_entry sdl3_functions[] = {
PHP_FE(sdl_destroy_renderer, arginfo_sdl_destroy_renderer) PHP_FE(sdl_destroy_renderer, arginfo_sdl_destroy_renderer)
PHP_FE(sdl_get_window_id, arginfo_sdl_get_window_id) PHP_FE(sdl_get_window_id, arginfo_sdl_get_window_id)
PHP_FE(sdl_create_renderer, arginfo_sdl_create_renderer) PHP_FE(sdl_create_renderer, arginfo_sdl_create_renderer)
PHP_FE(sdl_get_num_render_drivers, arginfo_sdl_get_num_render_drivers)
PHP_FE(sdl_get_render_driver, arginfo_sdl_get_render_driver)
PHP_FE(sdl_set_render_draw_color, arginfo_sdl_set_render_draw_color) PHP_FE(sdl_set_render_draw_color, arginfo_sdl_set_render_draw_color)
PHP_FE(sdl_render_clear, arginfo_sdl_render_clear) PHP_FE(sdl_render_clear, arginfo_sdl_render_clear)
PHP_FE(sdl_render_fill_rect, arginfo_sdl_render_fill_rect) PHP_FE(sdl_render_fill_rect, arginfo_sdl_render_fill_rect)
@ -1365,6 +1702,11 @@ const zend_function_entry sdl3_functions[] = {
PHP_FE(sdl_update_texture, arginfo_sdl_update_texture) PHP_FE(sdl_update_texture, arginfo_sdl_update_texture)
PHP_FE(sdl_set_texture_blend_mode, arginfo_sdl_set_texture_blend_mode) PHP_FE(sdl_set_texture_blend_mode, arginfo_sdl_set_texture_blend_mode)
PHP_FE(sdl_set_texture_alpha_mod, arginfo_sdl_set_texture_alpha_mod) PHP_FE(sdl_set_texture_alpha_mod, arginfo_sdl_set_texture_alpha_mod)
// Tray API
PHP_FE(tray_setup, arginfo_tray_setup)
PHP_FE(tray_poll, arginfo_tray_poll)
PHP_FE(tray_exit, arginfo_tray_exit)
// Desktop notifications
PHP_FE(desktop_notify, arginfo_desktop_notify) PHP_FE(desktop_notify, arginfo_desktop_notify)
PHP_FE(sdl_create_box_shadow_texture, arginfo_sdl_create_box_shadow_texture) PHP_FE(sdl_create_box_shadow_texture, arginfo_sdl_create_box_shadow_texture)
PHP_FE(sdl_get_render_target, arginfo_sdl_get_render_target) PHP_FE(sdl_get_render_target, arginfo_sdl_get_render_target)
@ -1378,6 +1720,8 @@ const zend_function_entry sdl3_functions[] = {
PHP_FE(sdl_get_window_display_scale, arginfo_sdl_get_window_display_scale) PHP_FE(sdl_get_window_display_scale, arginfo_sdl_get_window_display_scale)
PHP_FE(sdl_get_display_content_scale, arginfo_sdl_get_display_content_scale) PHP_FE(sdl_get_display_content_scale, arginfo_sdl_get_display_content_scale)
PHP_FE(sdl_get_current_video_driver, arginfo_sdl_get_current_video_driver) PHP_FE(sdl_get_current_video_driver, arginfo_sdl_get_current_video_driver)
PHP_FE(sdl_get_num_video_drivers, arginfo_sdl_get_num_video_drivers)
PHP_FE(sdl_get_video_driver, arginfo_sdl_get_video_driver)
PHP_FE(sdl_get_renderer_output_size, arginfo_sdl_get_renderer_output_size) PHP_FE(sdl_get_renderer_output_size, arginfo_sdl_get_renderer_output_size)
PHP_FE(sdl_start_text_input, arginfo_sdl_start_text_input) PHP_FE(sdl_start_text_input, arginfo_sdl_start_text_input)
PHP_FE(sdl_stop_text_input, arginfo_sdl_stop_text_input) PHP_FE(sdl_stop_text_input, arginfo_sdl_stop_text_input)

View File

@ -86,6 +86,7 @@ class Application
while ($this->running && count($this->windows) > 0) { while ($this->running && count($this->windows) > 0) {
$frameStart = microtime(true); $frameStart = microtime(true);
// Layout all windows FIRST (sets window references and calculates positions) // Layout all windows FIRST (sets window references and calculates positions)
foreach ($this->windows as $windowId => $window) { foreach ($this->windows as $windowId => $window) {
$window->layout(); $window->layout();
@ -100,6 +101,12 @@ class Application
} }
} }
// Process tray events AFTER polling SDL events
// This ensures tray callbacks are triggered
if (function_exists('tray_poll')) {
tray_poll(false);
}
// Coalesce mouse motion events: Only keep the last MouseMotion event per window // Coalesce mouse motion events: Only keep the last MouseMotion event per window
// This dramatically reduces the number of events to process // This dramatically reduces the number of events to process
$coalescedEvents = []; $coalescedEvents = [];

70
test_render_drivers.php Normal file
View File

@ -0,0 +1,70 @@
<?php
// Test script for SDL_GetNumRenderDrivers and SDL_GetRenderDriver
echo "SDL3 Render Drivers Test\n";
echo "========================\n\n";
// Initialize SDL
if (!sdl_init(SDL_INIT_VIDEO)) {
die('Failed to initialize SDL: ' . sdl_get_error() . "\n");
}
echo "SDL initialized successfully\n\n";
// Get number of render drivers
$numDrivers = \sdl_get_num_render_drivers();
echo "Number of available render drivers: {$numDrivers}\n\n";
// List all available render drivers
echo "Available render drivers:\n";
for ($i = 0; $i < $numDrivers; $i++) {
$driver = sdl_get_render_driver($i);
if ($driver !== false) {
echo " [{$i}] {$driver}\n";
} else {
echo " [{$i}] Error getting driver\n";
}
}
echo "\n";
// Try creating a window and renderer
echo "Creating a window...\n";
$window = sdl_create_window('Render Driver Test', 800, 600, SDL_WINDOW_HIDDEN);
if (!$window) {
die('Failed to create window: ' . sdl_get_error() . "\n");
}
echo "Window created successfully\n\n";
// Try creating a renderer with default (null) name
echo "Attempting to create renderer with default settings...\n";
$renderer = sdl_create_renderer($window);
if (!$renderer) {
echo 'Failed to create renderer: ' . sdl_get_error() . "\n\n";
// Try with explicit driver names
if ($numDrivers > 0) {
echo "Trying with explicit driver names:\n";
for ($i = 0; $i < $numDrivers; $i++) {
$driverName = sdl_get_render_driver($i);
echo " Trying '{$driverName}'...\n";
$renderer = sdl_create_renderer($window, $driverName);
if ($renderer) {
echo " SUCCESS with '{$driverName}'\n";
sdl_destroy_renderer($renderer);
break;
} else {
echo ' FAILED: ' . sdl_get_error() . "\n";
}
}
}
} else {
echo "Renderer created successfully!\n";
sdl_destroy_renderer($renderer);
}
// Cleanup
sdl_destroy_window($window);
sdl_quit();
echo "\nTest completed!\n";

67
test_renderer_simple.php Normal file
View File

@ -0,0 +1,67 @@
<?php
putenv('XLOCALEDIR=/usr/share/X11/locale');
echo "Simple SDL3 Renderer Test\n";
echo "=========================\n\n";
// Initialize SDL
if (!sdl_init(SDL_INIT_VIDEO)) {
die('Failed to initialize SDL: ' . sdl_get_error() . "\n");
}
echo "SDL initialized successfully\n";
// Get current video driver
$videoDriver = sdl_get_current_video_driver();
echo 'Current video driver: ' . ($videoDriver ?: 'none') . "\n\n";
// Create window with different flags to test
$flags = SDL_WINDOW_HIDDEN;
echo "Creating window with HIDDEN flag...\n";
$window = sdl_create_window('Renderer Test', 800, 600, $flags);
if (!$window) {
die('Failed to create window: ' . sdl_get_error() . "\n");
}
echo "Window created successfully\n\n";
// Get video driver after window creation
$videoDriver = sdl_get_current_video_driver();
echo 'Video driver after window: ' . ($videoDriver ?: 'none') . "\n\n";
// Try to create renderer
echo "Attempting to create renderer...\n";
$renderer = sdl_create_renderer($window);
if (!$renderer) {
$error = sdl_get_error();
echo "FAILED to create renderer: {$error}\n\n";
// Try with a visible window instead
echo "Destroying window and trying with visible window...\n";
sdl_destroy_window($window);
$window = sdl_create_window('Renderer Test', 800, 600, 0);
if (!$window) {
die('Failed to create visible window: ' . sdl_get_error() . "\n");
}
echo "Visible window created\n";
echo "Attempting to create renderer on visible window...\n";
$renderer = sdl_create_renderer($window);
if (!$renderer) {
echo 'FAILED again: ' . sdl_get_error() . "\n";
} else {
echo "SUCCESS on visible window!\n";
sdl_destroy_renderer($renderer);
}
sdl_destroy_window($window);
} else {
echo "SUCCESS! Renderer created\n";
sdl_destroy_renderer($renderer);
sdl_destroy_window($window);
}
sdl_quit();
echo "\nTest completed\n";

76
test_simple_rect.php Normal file
View File

@ -0,0 +1,76 @@
<?php
// SDL initialisieren
if (!sdl_init(SDL_INIT_VIDEO)) {
die("sdl_init failed: " . sdl_get_error() . "\n");
}
echo "SDL initialized successfully\n";
// Fenster erstellen
$window = sdl_create_window(
"Simple Rectangle Test",
800, 600,
SDL_WINDOW_RESIZABLE
);
if (!$window) {
die("sdl_create_window failed: " . sdl_get_error() . "\n");
}
echo "Window created successfully\n";
// Renderer erstellen
$renderer = sdl_create_renderer($window, null);
if (!$renderer) {
sdl_destroy_window($window);
die("sdl_create_renderer failed: " . sdl_get_error() . "\n");
}
echo "Renderer created successfully\n";
// Event Loop
$running = true;
while ($running) {
// Events verarbeiten
while ($event = sdl_poll_event()) {
if ($event['type'] === SDL_EVENT_QUIT) {
$running = false;
} elseif ($event['type'] === SDL_EVENT_KEY_DOWN) {
if ($event['keycode'] === SDLK_ESCAPE || $event['keycode'] === SDLK_Q) {
$running = false;
}
}
}
// Bildschirm löschen (schwarz)
sdl_set_render_draw_color($renderer, 0, 0, 0, 255);
sdl_render_clear($renderer);
// Rotes Rechteck zeichnen (gefüllt)
sdl_set_render_draw_color($renderer, 255, 0, 0, 255);
sdl_render_fill_rect($renderer, ['x' => 300, 'y' => 200, 'w' => 200, 'h' => 150]);
// Grünes Rechteck zeichnen (Umriss)
sdl_set_render_draw_color($renderer, 0, 255, 0, 255);
sdl_render_rect($renderer, ['x' => 350, 'y' => 250, 'w' => 100, 'h' => 100]);
// Blaues Rechteck zeichnen (klein, gefüllt)
sdl_set_render_draw_color($renderer, 0, 0, 255, 255);
sdl_render_fill_rect($renderer, ['x' => 100, 'y' => 100, 'w' => 50, 'h' => 50]);
// Renderer anzeigen
sdl_render_present($renderer);
// Kleine Pause um CPU zu schonen
sdl_delay(16); // ~60 FPS
}
// Aufräumen
sdl_destroy_renderer($renderer);
sdl_destroy_window($window);
sdl_quit();
echo "Cleanup complete\n";

63
test_video_drivers.php Normal file
View File

@ -0,0 +1,63 @@
<?php
// Test script for SDL_GetNumVideoDrivers and SDL_GetVideoDriver
echo "SDL3 Video Drivers Test\n";
echo "========================\n\n";
// Initialize SDL
if (!sdl_init(SDL_INIT_VIDEO)) {
die("Failed to initialize SDL: " . sdl_get_error() . "\n");
}
echo "SDL initialized successfully\n\n";
// Get number of video drivers
$numDrivers = sdl_get_num_video_drivers();
echo "Number of available video drivers: $numDrivers\n\n";
// List all available video drivers
echo "Available video drivers:\n";
for ($i = 0; $i < $numDrivers; $i++) {
$driver = sdl_get_video_driver($i);
if ($driver !== false) {
echo " [$i] $driver\n";
} else {
echo " [$i] Error getting driver\n";
}
}
echo "\n";
// Get current video driver
$currentDriver = sdl_get_current_video_driver();
if ($currentDriver !== false) {
echo "Current video driver: $currentDriver\n";
} else {
echo "No video driver initialized yet (window not created)\n";
}
echo "\n";
// Create a window to initialize video driver
echo "Creating a window to initialize video driver...\n";
$window = sdl_create_window("Video Driver Test", 800, 600, SDL_WINDOW_HIDDEN);
if (!$window) {
die("Failed to create window: " . sdl_get_error() . "\n");
}
echo "Window created successfully\n\n";
// Now get the current driver again
$currentDriver = sdl_get_current_video_driver();
if ($currentDriver !== false) {
echo "Current video driver (after window creation): $currentDriver\n";
} else {
echo "Failed to get current video driver\n";
}
// Cleanup
sdl_destroy_window($window);
sdl_quit();
echo "\nTest completed successfully!\n";

35
test_window.php Normal file
View File

@ -0,0 +1,35 @@
<?php
// Fenster und Renderer erstellen
// In SDL3 ist ein Fenster standardmäßig sichtbar, daher verwenden wir 0 oder andere Flags
$window = sdl_create_window('Server Manager', 800, 600, 0);
$renderer = sdl_create_renderer($window, null);
// Event-Loop
$running = true;
while ($running) {
// Events verarbeiten
while ($event = sdl_poll_event()) {
if ($event['type'] === SDL_EVENT_QUIT) {
$running = false;
}
// Weitere Events (Tastatur, Maus, etc.)
}
// Rendern
sdl_set_render_draw_color($renderer, 0, 0, 0, 255);
sdl_render_clear($renderer);
// Dein UI-Code hier
sdl_render_present($renderer);
// Kleine Pause
sdl_delay(16); // ~60 FPS
}
// Cleanup
sdl_destroy_renderer($renderer);
sdl_destroy_window($window);
sdl_quit();