This commit is contained in:
Thomas Peterson 2025-10-26 13:39:25 +01:00
parent 6e1b7c2b1b
commit a1fabd3623
72 changed files with 3317 additions and 1981 deletions

View File

@ -1,75 +0,0 @@
<?php
require_once __DIR__ . '/../vendor/autoload.php';
use PHPNative\Framework\Application;
use PHPNative\Ui\Widget\Button;
use PHPNative\Ui\Widget\Container;
use PHPNative\Ui\Widget\Label;
// Check PHP version
if (PHP_VERSION_ID < 80100) {
die("This demo requires PHP 8.1+ for Fiber support.\nYour version: " . PHP_VERSION . "\n");
}
$app = new Application('Button Example', 800, 600);
// Main container
$mainContainer = new Container(style: 'p-10 bg-gray-100');
// Title
$title = new Label(
text: 'Button Sizing Example',
style: 'text-xl text-black',
);
$mainContainer->addComponent($title);
// Button with m-10 p-10, should be: 12 (text) + 10 (padding-top) + 10 (padding-bottom) = 32px height
$button1 = new Button(
text: 'Click Me',
style: 'm-10 p-10 bg-blue-500 rounded-lg',
);
$mainContainer->addComponent($button1);
// Button with different padding
$button2 = new Button(
text: 'Another Button',
style: 'm-5 p-15 bg-green-500 hover:bg-green-200 rounded-lg',
onClick: function () {
echo 'test2';
},
);
$mainContainer->addComponent($button2);
// Button with no padding
$button3 = new Button(
text: 'No Padding',
style: 'm-10 bg-red-500 rounded-lg',
);
$mainContainer->addComponent($button3);
// Container with multiple labels (should stack)
$labelContainer = new Container(style: 'm-10 p-10 bg-white rounded-lg');
$labelContainer->addComponent(new Label(
text: 'Label 1',
style: 'text-black',
));
$labelContainer->addComponent(new Label(
text: 'Label 2',
style: 'text-black',
));
$labelContainer->addComponent(new Label(
text: 'Label 3',
style: 'text-black',
));
$mainContainer->addComponent($labelContainer);
// Info text
$info = new Label(
text: 'Containers should auto-size to their content',
style: 'text-black m-10',
);
$mainContainer->addComponent($info);
$app->setRoot($mainContainer);
$app->run();

View File

@ -1,72 +0,0 @@
<?php
require_once __DIR__ . '/../vendor/autoload.php';
use PHPNative\Framework\Application;
use PHPNative\Ui\Widget\Container;
use PHPNative\Ui\Widget\Label;
// Check PHP version
if (PHP_VERSION_ID < 80100) {
die("This demo requires PHP 8.1+ for Fiber support.\nYour version: " . PHP_VERSION . "\n");
}
$app = new Application('Flexbox Layout Demo', 800, 600);
// Main container with padding
$mainContainer = new Container(style: 'p-4 bg-gray-100');
// Example 1: Flex Row with basis
$row1 = new Container(style: 'flex flex-row bg-white m-2 p-2');
$box1 = new Container(style: 'basis-1/3 bg-blue-500 p-4');
$label1 = new Label(text: '33% Basis', style: 'text-white');
$box1->addComponent($label1);
$box2 = new Container(style: 'basis-1/3 bg-green-500 p-4');
$label2 = new Label(text: '33% Basis', style: 'text-white');
$box2->addComponent($label2);
$box3 = new Container(style: 'basis-1/3 bg-red-500 p-4');
$label3 = new Label(text: '33% Basis', style: 'text-white');
$box3->addComponent($label3);
$row1->addComponent($box1);
$row1->addComponent($box2);
$row1->addComponent($box3);
// Example 2: Flex Row with flex-grow
$row2 = new Container(style: 'flex flex-row bg-white m-2 p-2');
$fixedBox = new Container(style: 'w-200 bg-purple-500 p-4');
$fixedLabel = new Label(text: 'Fixed 200px', style: 'text-white');
$fixedBox->addComponent($fixedLabel);
$growBox = new Container(style: 'flex-1 bg-yellow-500 p-4');
$growLabel = new Label(text: 'Flex Grow (rest)', style: 'text-black');
$growBox->addComponent($growLabel);
$row2->addComponent($fixedBox);
$row2->addComponent($growBox);
// Example 3: Flex Column
$col1 = new Container(style: 'flex flex-col bg-white m-2 p-2 h-200');
$colBox1 = new Container(style: 'basis-1/2 bg-cyan-500 p-4');
$colLabel1 = new Label(text: '50% Height', style: 'text-white');
$colBox1->addComponent($colLabel1);
$colBox2 = new Container(style: 'basis-1/2 bg-orange-500 p-4');
$colLabel2 = new Label(text: '50% Height', style: 'text-white');
$colBox2->addComponent($colLabel2);
$col1->addComponent($colBox1);
$col1->addComponent($colBox2);
// Add all examples to main container
$mainContainer->addComponent($row1);
$mainContainer->addComponent($row2);
$mainContainer->addComponent($col1);
$app->setRoot($mainContainer);
$app->run();

View File

@ -1,64 +0,0 @@
<?php
require_once __DIR__ . '/../vendor/autoload.php';
use PHPNative\Framework\Application;
use PHPNative\Ui\Widget\Container;
use PHPNative\Ui\Widget\Label;
// Check PHP version
if (PHP_VERSION_ID < 80100) {
die("This demo requires PHP 8.1+ for Fiber support.\nYour version: " . PHP_VERSION . "\n");
}
$app = new Application('Overflow Scroll Demo', 800, 600);
// Main container
$mainContainer = new Container(style: 'p-4 bg-gray-100');
// Title
$title = new Label(text: 'Overflow Scroll Demo', style: 'text-xl text-black p-2');
$mainContainer->addComponent($title);
// Example 1: Vertical scroll with overflow-y-auto
$scrollContainer = new Container(style: 'overflow-y-auto bg-white m-4 p-4 h-200');
// Add many items to trigger overflow
for ($i = 1; $i <= 20; $i++) {
$item = new Container(style: 'bg-blue-500 m-2 p-3 rounded-lg');
$label = new Label(
text: "Item {$i} - Scroll vertically with mouse wheel or drag the scrollbar",
style: 'text-white'
);
$item->addComponent($label);
$scrollContainer->addComponent($item);
}
$mainContainer->addComponent($scrollContainer);
// Example 2: Horizontal scroll with overflow-x-auto
$label2 = new Label(text: 'Horizontal Scroll:', style: 'text-black p-2');
$mainContainer->addComponent($label2);
$horizontalScroll = new Container(style: 'flex flex-row overflow-x-auto bg-white m-4 p-4 h-100');
for ($i = 1; $i <= 10; $i++) {
$box = new Container(style: 'w-150 bg-green-500 m-2 p-3 rounded-lg');
$boxLabel = new Label(text: "Box {$i}", style: 'text-white');
$box->addComponent($boxLabel);
$horizontalScroll->addComponent($box);
}
$mainContainer->addComponent($horizontalScroll);
// Instructions
$instructions = new Container(style: 'bg-yellow-200 p-4 m-4 rounded-lg');
$instructionText = new Label(
text: 'Use mouse wheel to scroll. Click and drag scrollbars.',
style: 'text-black'
);
$instructions->addComponent($instructionText);
$mainContainer->addComponent($instructions);
$app->setRoot($mainContainer);
$app->run();

View File

@ -1,71 +0,0 @@
<?php
require_once __DIR__ . '/../vendor/autoload.php';
use PHPNative\Framework\Application;
use PHPNative\Ui\Widget\Button;
use PHPNative\Ui\Widget\Container;
use PHPNative\Ui\Widget\Label;
// Check PHP version
if (PHP_VERSION_ID < 80100) {
die("This demo requires PHP 8.1+ for Fiber support.\nYour version: " . PHP_VERSION . "\n");
}
$app = new Application('Button Example', 800, 600);
$container = new Container('p-4');
// Button with different padding
$button2 = new Button(
text: 'Another Button',
style: 'm-5 p-15 bg-lime-500 hover:bg-green-200',
);
$button2->setOnClickAsync(
onClickAsync: function () {
// Fetch data from API (example: JSON placeholder)
$url = 'https://jsonplaceholder.typicode.com/todos/1';
$context = stream_context_create([
'http' => [
'timeout' => 10,
'method' => 'GET',
],
]);
$response = file_get_contents($url, false, $context);
if ($response === false) {
throw new \Exception('Failed to fetch data');
}
return json_decode($response, true);
},
onComplete: function ($data) use ($container) {
$statusLabel = new Label(
text: 'Klicken Sie den Button, um Daten zu laden...',
style: 'text-base text-gray-700',
);
$resultLabel = new Label(
text: '',
style: 'text-sm text-blue-600',
);
$statusLabel->setText('✓ Daten erfolgreich geladen!');
$formatted = 'ID: ' . ($data['id'] ?? 'N/A') . "\n";
$formatted .= 'Titel: ' . ($data['title'] ?? 'N/A') . "\n";
$formatted .= 'Erledigt: ' . (($data['completed'] ?? false) ? 'Ja' : 'Nein');
$resultLabel->setText($formatted);
$container->addComponent($statusLabel);
$container->addComponent($resultLabel);
},
onError: function ($error) {},
);
$container->addComponent($button2);
$app->setRoot($container);
$app->run();

View File

@ -1,29 +0,0 @@
<?php
use PHPNative\Ui\Widget\Container;
use PHPNative\Ui\Widget\Label;
require_once __DIR__ . '/../vendor/autoload.php';
// Check PHP version
if (PHP_VERSION_ID < 80100) {
die("This demo requires PHP 8.1+ for Fiber support.\nYour version: " . PHP_VERSION . "\n");
}
$app = new \PHPNative\Framework\Application('ToDo Demo', 700, 500);
$container = new Container('m-10 p-10 bg-lime-500');
$scrollContainer = new Container(style: 'overflow-y-auto bg-red m-4 p-4');
// Add many items to trigger overflow
for ($i = 1; $i <= 200; $i++) {
$item = new Container(style: 'bg-blue-500 m-2 p-3');
$label = new Label(
text: "Item {$i}",
style: 'text-green-500',
);
$item->addComponent($label);
$scrollContainer->addComponent($item);
}
$container->addComponent($scrollContainer);
$app->setRoot($container);
$app->run();

View File

@ -1,43 +0,0 @@
<?php
use PHPNative\Ui\Widget\Container;
use PHPNative\Ui\Widget\Label;
require_once __DIR__ . '/../vendor/autoload.php';
// Check PHP version
if (PHP_VERSION_ID < 80100) {
die("This demo requires PHP 8.1+ for Fiber support.\nYour version: " . PHP_VERSION . "\n");
}
$app = new \PHPNative\Framework\Application('ToDo Demo', 700, 500);
$container = new \PHPNative\Ui\Widget\Container(style: 'm-10 p-10 flex-row rounded-xl bg-lime-200');
$containerContent = new \PHPNative\Ui\Widget\Container(style: 'flex-1 bg-lime-700');
$labelContent = new Label(
text: 'Todo App',
style: 'text-xl2 text-red-500',
);
$containerContent->addComponent($labelContent);
$container->addComponent($containerContent);
$containerMenu = new \PHPNative\Ui\Widget\Container(style: 'w-200 bg-lime-200');
$label = new Label(
text: 'Menu App',
style: 'text-red-500',
);
$containerMenu->addComponent($label);
$scrollContainer = new Container(style: 'overflow-y-auto bg-red m-4 p-4');
// Add many items to trigger overflow
for ($i = 1; $i <= 100; $i++) {
$item = new Container(style: 'bg-blue-500 m-2 p-3');
$label = new Label(
text: "Item {$i}",
style: 'text-green-500',
);
$item->addComponent($label);
$scrollContainer->addComponent($item);
}
$containerMenu->addComponent($scrollContainer);
$container->addComponent($containerMenu);
$app->setRoot($container);
$app->run();

View File

@ -1,185 +0,0 @@
<?php
require_once __DIR__ . '/../vendor/autoload.php';
define('DEBUG_EVENTS', false);
use PHPNative\Framework\Application;
use PHPNative\Ui\Widget\Button;
use PHPNative\Ui\Widget\Container;
use PHPNative\Ui\Widget\Label;
// Create application
$app = new Application();
// Create main window
$mainWindow = $app->createWindow('Advanced Multi-Window Demo', 700, 500, 100, 100);
$windowCounter = 1;
// Main UI
$mainContainer = new Container('flex flex-col p-6 gap-4 bg-gray-50');
$title = new Label(
text: 'Advanced Multi-Window Demo',
style: 'text-2xl text-gray-900',
);
$subtitle = new Label(
text: 'Windows mit asynchronen Operationen',
style: 'text-base text-gray-600',
);
// Button to create window with async data loading
$createAsyncWindowButton = new Button(
text: 'Window mit Async-Datenladung',
style: 'bg-purple-500 hover:bg-purple-700 text-white p-4 rounded-lg',
);
$createAsyncWindowButton->setOnClickAsync(
onClickAsync: function () use (&$windowCounter) {
// Simulate loading data in background
sleep(1);
return [
'windowId' => $windowCounter++,
'data' => 'Daten erfolgreich geladen!',
'timestamp' => date('H:i:s'),
'items' => ['Item 1', 'Item 2', 'Item 3'],
];
},
onComplete: function ($result) use ($app) {
// Create window with loaded data
$windowId = $result['windowId'];
$newWindow = $app->createWindow(
"Data Window #{$windowId}",
500,
350,
150 + ($windowId * 30),
150 + ($windowId * 30),
);
$container = new Container('flex flex-col p-6 gap-3 bg-green-50');
$titleLabel = new Label(
text: "Data Window #{$windowId}",
style: 'text-xl text-gray-900',
);
$dataLabel = new Label(
text: $result['data'],
style: 'text-base text-green-700 p-2 bg-white rounded',
);
$timeLabel = new Label(
text: 'Geladen um: ' . $result['timestamp'],
style: 'text-sm text-gray-600',
);
$itemsLabel = new Label(
text: "Items:\n" . implode("\n", $result['items']),
style: 'text-sm text-gray-700 p-2 bg-white rounded',
);
$closeButton = new Button(
text: 'Schließen',
style: 'bg-red-500 hover:bg-red-700 text-white p-3 rounded',
);
$closeButton->setOnClick(function () use ($newWindow) {
$newWindow->close();
});
$container->addComponent($titleLabel);
$container->addComponent($dataLabel);
$container->addComponent($timeLabel);
$container->addComponent($itemsLabel);
$container->addComponent($closeButton);
$newWindow->setRoot($container);
},
onError: function ($error) {
error_log('Error creating window: ' . $error->getMessage());
},
);
// Button to create multiple windows at once
$createMultipleButton = new Button(
text: '3 Windows auf einmal erstellen',
style: 'bg-orange-500 hover:bg-orange-700 text-white p-4 rounded-lg',
);
$createMultipleButton->setOnClick(function () use ($app, &$windowCounter) {
for ($i = 0; $i < 3; $i++) {
$currentId = $windowCounter++;
$offset = $currentId * 40;
$newWindow = $app->createWindow("Batch Window #{$currentId}", 400, 250, 200 + $offset, 200 + ($i * 50));
$container = new Container('flex flex-col p-4 gap-2 bg-yellow-50');
$label = new Label(
text: "Batch Window #{$currentId}",
style: 'text-lg text-gray-900',
);
$info = new Label(
text: "Teil einer Batch-Erstellung ({$i}/3)",
style: 'text-sm text-gray-600',
);
$closeButton = new Button(
text: 'X',
style: 'bg-red-500 hover:bg-red-700 text-white p-2 rounded',
);
$closeButton->setOnClick(function () use ($newWindow) {
$newWindow->close();
});
$container->addComponent($label);
$container->addComponent($info);
$container->addComponent($closeButton);
$newWindow->setRoot($container);
}
});
// Window count label
$windowCountLabel = new Label(
text: 'Offene Windows: 1',
style: 'text-sm text-blue-600 p-2 bg-white rounded',
);
// Update window count periodically
$mainContainer->addComponent($title);
$mainContainer->addComponent($subtitle);
$mainContainer->addComponent($windowCountLabel);
$mainContainer->addComponent($createAsyncWindowButton);
$mainContainer->addComponent($createMultipleButton);
// Info
$info = new Label(
text: 'Die UI bleibt responsive während des Ladens!',
style: 'text-xs text-gray-500 italic mt-4',
);
$mainContainer->addComponent($info);
// Quit button
$quitButton = new Button(
text: 'Beenden',
style: 'bg-red-600 hover:bg-red-800 text-white p-4 rounded-lg',
);
$quitButton->setOnClick(function () use ($app) {
$app->quit();
});
$mainContainer->addComponent($quitButton);
$mainWindow->setRoot($mainContainer);
// Run application
$app->run();

View File

@ -1,75 +0,0 @@
<?php
require_once __DIR__ . '/../vendor/autoload.php';
use PHPNative\Framework\Application;
use PHPNative\Ui\Widget\Button;
use PHPNative\Ui\Widget\Container;
use PHPNative\Ui\Widget\Label;
// Create application
$app = new Application('Async Button Example', 800, 600);
// Create UI components
$container = new Container('flex flex-col p-4 gap-4');
// Status label to show loading state
$statusLabel = new Label(
text: 'Klicken Sie den Button, um Daten zu laden...',
style: 'text-base text-gray-700'
);
// Result label to show fetched data
$resultLabel = new Label(
text: '',
style: 'text-sm text-blue-600'
);
// Button with async onClick handler
$button = new Button(
text: 'Daten aus dem Web laden',
style: 'bg-blue-500 hover:bg-blue-700 text-white p-4 rounded-lg'
);
// Set async click handler
$button->setOnClickAsync(
// Task that runs in background thread
onClickAsync: function() {
// Simulate web request (or use real HTTP client)
sleep(2); // Simulates network delay
// In production, you would use something like:
// $response = file_get_contents('https://api.example.com/data');
// return json_decode($response, true);
return [
'status' => 'success',
'data' => 'Daten erfolgreich geladen!',
'timestamp' => date('H:i:s')
];
},
// Callback when task completes successfully
onComplete: function($result) use ($statusLabel, $resultLabel) {
$statusLabel->setText('✓ Laden abgeschlossen!');
$resultLabel->setText(
'Status: ' . $result['status'] . "\n" .
'Daten: ' . $result['data'] . "\n" .
'Zeit: ' . $result['timestamp']
);
},
// Callback when task fails
onError: function($error) use ($statusLabel, $resultLabel) {
$statusLabel->setText('✗ Fehler beim Laden!');
$resultLabel->setText('Error: ' . $error->getMessage());
}
);
// Add components to container
$container->addComponent($statusLabel);
$container->addComponent($button);
$container->addComponent($resultLabel);
// Set root component and run
$app->setRoot($container)->run();

View File

@ -1,126 +0,0 @@
<?php
require_once __DIR__ . '/../vendor/autoload.php';
use PHPNative\Framework\Application;
use PHPNative\Ui\Widget\Button;
use PHPNative\Ui\Widget\Container;
use PHPNative\Ui\Widget\Label;
// Create application
$app = new Application('Async HTTP Request Example', 900, 700);
// Create UI
$container = new Container('flex flex-col p-6 gap-3 bg-gray-100');
$title = new Label(
text: 'Asynchrone HTTP Requests Demo',
style: 'text-2xl text-gray-900 mb-4'
);
$statusLabel = new Label(
text: 'Bereit zum Laden...',
style: 'text-base text-gray-700 p-2 bg-white rounded'
);
// Button for fetching JSON data
$fetchButton = new Button(
text: 'JSON API abrufen',
style: 'bg-green-500 hover:bg-green-700 text-white p-3 rounded-lg'
);
$resultLabel = new Label(
text: '',
style: 'text-sm text-gray-800 p-3 bg-white rounded whitespace-pre-wrap'
);
// Set async handler for JSON fetch
$fetchButton->setOnClickAsync(
onClickAsync: function() {
// Fetch data from API (example: JSON placeholder)
$url = 'https://jsonplaceholder.typicode.com/todos/1';
$context = stream_context_create([
'http' => [
'timeout' => 10,
'method' => 'GET',
]
]);
$response = @file_get_contents($url, false, $context);
if ($response === false) {
throw new \Exception('Failed to fetch data');
}
return json_decode($response, true);
},
onComplete: function($data) use ($statusLabel, $resultLabel) {
$statusLabel->setText('✓ Daten erfolgreich geladen!');
$formatted = "ID: " . ($data['id'] ?? 'N/A') . "\n";
$formatted .= "Titel: " . ($data['title'] ?? 'N/A') . "\n";
$formatted .= "Erledigt: " . (($data['completed'] ?? false) ? 'Ja' : 'Nein');
$resultLabel->setText($formatted);
},
onError: function($error) use ($statusLabel, $resultLabel) {
$statusLabel->setText('✗ Fehler aufgetreten');
$resultLabel->setText('Error: ' . $error->getMessage());
}
);
// Button for simulating slow operation
$slowButton = new Button(
text: 'Langsame Operation (5s)',
style: 'bg-orange-500 hover:bg-orange-700 text-white p-3 rounded-lg'
);
$slowButton->setOnClickAsync(
onClickAsync: function() {
$startTime = microtime(true);
// Simulate heavy computation
sleep(5);
$endTime = microtime(true);
$duration = round($endTime - $startTime, 2);
return [
'message' => 'Operation abgeschlossen',
'duration' => $duration . ' Sekunden'
];
},
onComplete: function($result) use ($statusLabel, $resultLabel) {
$statusLabel->setText('✓ Operation abgeschlossen!');
$resultLabel->setText(
$result['message'] . "\n" .
'Dauer: ' . $result['duration']
);
},
onError: function($error) use ($statusLabel, $resultLabel) {
$statusLabel->setText('✗ Fehler bei Operation');
$resultLabel->setText('Error: ' . $error->getMessage());
}
);
// Add all components
$container->addComponent($title);
$container->addComponent($statusLabel);
$container->addComponent($fetchButton);
$container->addComponent($slowButton);
$container->addComponent($resultLabel);
// Info label
$infoLabel = new Label(
text: 'Die UI bleibt während der Requests responsive!',
style: 'text-xs text-gray-500 mt-4 italic'
);
$container->addComponent($infoLabel);
// Run application
$app->setRoot($container)->run();

View File

@ -1,160 +0,0 @@
<?php
/**
* Multi-Process Window Example
*
* Each window runs in a separate process (not thread!)
* This works because each process has its own main thread for OpenGL
*/
require_once __DIR__ . '/../vendor/autoload.php';
use PHPNative\Framework\Application;
use PHPNative\Ui\Widget\Button;
use PHPNative\Ui\Widget\Container;
use PHPNative\Ui\Widget\Label;
// Check if we're the main process or a child
$windowId = $argv[1] ?? null;
if ($windowId === null) {
// MAIN PROCESS - Launch child processes
echo "Main Process: Launching 2 window processes...\n\n";
$pids = [];
// Launch Window 1 process
$pid1 = pcntl_fork();
if ($pid1 == -1) {
die("Could not fork for Window 1\n");
} elseif ($pid1 == 0) {
// Child process 1
runWindow('1', 'Window 1 - Blue', 100, 100, 'blue');
exit(0);
} else {
$pids[] = $pid1;
echo "Main Process: Window 1 started with PID $pid1\n";
}
// Small delay to avoid race conditions
usleep(100000);
// Launch Window 2 process
$pid2 = pcntl_fork();
if ($pid2 == -1) {
die("Could not fork for Window 2\n");
} elseif ($pid2 == 0) {
// Child process 2
runWindow('2', 'Window 2 - Green', 550, 100, 'green');
exit(0);
} else {
$pids[] = $pid2;
echo "Main Process: Window 2 started with PID $pid2\n";
}
echo "\nMain Process: Both windows launched!\n";
echo "Main Process: Waiting for child processes to exit...\n\n";
// Wait for all child processes
foreach ($pids as $pid) {
$status = 0;
pcntl_waitpid($pid, $status);
echo "Main Process: Child process $pid exited\n";
}
echo "\nMain Process: All windows closed. Exiting.\n";
} else {
// This shouldn't happen in fork mode
die("Error: Unexpected command line argument\n");
}
/**
* Run a single window in this process
*/
function runWindow(string $id, string $title, int $x, int $y, string $bgColor): void
{
echo "Process $id: Creating window '$title'\n";
// Create application
$app = new Application();
// Create window
$window = $app->createWindow($title, 400, 350, $x, $y);
// Create UI
$container = new Container("flex flex-col p-6 gap-4 bg-$bgColor-100");
$titleLabel = new Label(
text: $title,
style: "text-2xl text-$bgColor-900"
);
$processLabel = new Label(
text: 'Process ID: ' . getmypid(),
style: "text-sm text-$bgColor-700"
);
$frameLabel = new Label(
text: 'Frame: 0',
style: "text-base text-$bgColor-600"
);
$button = new Button(
text: 'Click Me!',
style: "bg-$bgColor-500 hover:bg-$bgColor-700 text-white p-4 rounded-lg"
);
$clickCount = 0;
$button->setOnClick(function() use (&$clickCount, $button, $title) {
$clickCount++;
$button->setText("Clicked $clickCount times");
echo "$title - Button clicked! Count: $clickCount\n";
});
$closeButton = new Button(
text: 'Close Window',
style: "bg-red-500 hover:bg-red-700 text-white p-3 rounded"
);
$closeButton->setOnClick(function() use ($window) {
echo "Close button clicked!\n";
$window->close();
});
$container->addComponent($titleLabel);
$container->addComponent($processLabel);
$container->addComponent($frameLabel);
$container->addComponent($button);
$container->addComponent($closeButton);
$window->setRoot($container);
echo "Process $id: Starting event loop\n";
// Run the window
$frameCount = 0;
while (!$window->shouldClose()) {
$frameCount++;
$frameLabel->setText("Frame: $frameCount");
$window->layout();
// Poll events
rgfw_pollEvents();
$window->handleEvents();
// Update
\PHPNative\Async\TaskManager::getInstance()->update();
$window->update();
// Render
$window->render();
// ~60 FPS
usleep(16666);
}
// Cleanup
echo "Process $id: Window closed after $frameCount frames\n";
$window->cleanup();
}

View File

@ -1,161 +0,0 @@
<?php
require_once __DIR__ . '/../vendor/autoload.php';
use PHPNative\Framework\Application;
use PHPNative\Ui\Widget\Button;
use PHPNative\Ui\Widget\Container;
use PHPNative\Ui\Widget\Label;
// Create application
$app = new Application();
// Create main window
$mainWindow = $app->createWindow('Main Window - Multi-Window Demo', 600, 400, 100, 100);
// Counter for new windows
$windowCounter = 1;
// Create main window UI
$mainContainer = new Container('flex flex-col p-6 gap-4 bg-gray-100');
$title = new Label(
text: 'Multi-Window Demo',
style: 'text-2xl text-gray-900'
);
$description = new Label(
text: 'Klicken Sie auf die Buttons, um neue Windows zu erstellen!',
style: 'text-base text-gray-700'
);
$windowCountLabel = new Label(
text: 'Offene Windows: 1',
style: 'text-sm text-blue-600 p-2 bg-white rounded'
);
// Button to create a simple new window
$createSimpleButton = new Button(
text: 'Neues Fenster erstellen',
style: 'bg-blue-500 hover:bg-blue-700 text-white p-4 rounded-lg'
);
$createSimpleButton->setOnClick(function() use ($app, &$windowCounter, $windowCountLabel) {
// Create new window with offset position
$offset = $windowCounter * 30;
$newWindow = $app->createWindow(
"Window #$windowCounter",
500,
300,
150 + $offset,
150 + $offset
);
// Create UI for new window
$container = new Container('flex flex-col p-6 gap-3 bg-gradient-to-br from-purple-400 to-pink-400');
$label = new Label(
text: "Dies ist Window #$windowCounter",
style: 'text-xl text-white'
);
$closeButton = new Button(
text: 'Fenster schließen',
style: 'bg-red-500 hover:bg-red-700 text-white p-3 rounded'
);
$closeButton->setOnClick(function() use ($newWindow) {
$newWindow->close();
});
$container->addComponent($label);
$container->addComponent($closeButton);
$newWindow->setRoot($container);
$windowCounter++;
// Update counter
$windowCountLabel->setText('Offene Windows: ' . $app->getWindowCount());
});
// Button to create a window with interactive content
$createInteractiveButton = new Button(
text: 'Interaktives Fenster erstellen',
style: 'bg-green-500 hover:bg-green-700 text-white p-4 rounded-lg'
);
$createInteractiveButton->setOnClick(function() use ($app, &$windowCounter, $windowCountLabel) {
$offset = $windowCounter * 30;
$newWindow = $app->createWindow(
"Interactive Window #$windowCounter",
600,
400,
150 + $offset,
150 + $offset
);
// Create interactive UI
$container = new Container('flex flex-col p-6 gap-3 bg-blue-50');
$title = new Label(
text: "Interaktives Window #$windowCounter",
style: 'text-xl text-gray-900'
);
$counter = 0;
$counterLabel = new Label(
text: "Zähler: $counter",
style: 'text-lg text-blue-600 p-2 bg-white rounded'
);
$incrementButton = new Button(
text: 'Zähler erhöhen',
style: 'bg-blue-500 hover:bg-blue-700 text-white p-3 rounded'
);
$incrementButton->setOnClick(function() use (&$counter, $counterLabel) {
$counter++;
$counterLabel->setText("Zähler: $counter");
});
$closeButton = new Button(
text: 'Fenster schließen',
style: 'bg-red-500 hover:bg-red-700 text-white p-3 rounded'
);
$closeButton->setOnClick(function() use ($newWindow) {
$newWindow->close();
});
$container->addComponent($title);
$container->addComponent($counterLabel);
$container->addComponent($incrementButton);
$container->addComponent($closeButton);
$newWindow->setRoot($container);
$windowCounter++;
$windowCountLabel->setText('Offene Windows: ' . $app->getWindowCount());
});
// Button to quit application
$quitButton = new Button(
text: 'Alle Fenster schließen und beenden',
style: 'bg-red-600 hover:bg-red-800 text-white p-4 rounded-lg mt-4'
);
$quitButton->setOnClick(function() use ($app) {
$app->quit();
});
// Add all components to main container
$mainContainer->addComponent($title);
$mainContainer->addComponent($description);
$mainContainer->addComponent($windowCountLabel);
$mainContainer->addComponent($createSimpleButton);
$mainContainer->addComponent($createInteractiveButton);
$mainContainer->addComponent($quitButton);
$mainWindow->setRoot($mainContainer);
// Run application
$app->run();

View File

@ -1,135 +0,0 @@
<?php
// Enable event debugging (optional - set to false to disable)
define('DEBUG_EVENTS', true);
require_once __DIR__ . '/../vendor/autoload.php';
use PHPNative\Framework\Application;
use PHPNative\Ui\Widget\Button;
use PHPNative\Ui\Widget\Container;
use PHPNative\Ui\Widget\Label;
echo "Starting two-window test application...\n";
echo 'DEBUG_EVENTS: ' . (DEBUG_EVENTS ? 'enabled' : 'disabled') . "\n";
// Create application
$app = new Application();
// Frame counter for window 1
$window1FrameCount = 0;
echo "Creating Window 1...\n";
$window1 = $app->createWindow('Window 1', 400, 300, 100, 100);
$container1 = new Container('flex flex-col p-6 gap-4 bg-blue-100');
$title1 = new Label(
text: 'Window 1',
style: 'text-2xl text-blue-900',
);
$frameLabel1 = new Label(
text: 'Frame: 0',
style: 'text-base text-blue-700',
);
$button1 = new Button(
text: 'Click Me (Window 1)',
style: 'bg-blue-500 hover:bg-blue-700 text-white p-4 rounded',
);
$clickCount1 = 0;
$button1->setOnClick(function () use (&$clickCount1, $button1) {
$clickCount1++;
$button1->setText("Clicked {$clickCount1} times");
echo "Window 1 button clicked! Count: {$clickCount1}\n";
});
$container1->addComponent($title1);
$container1->addComponent($frameLabel1);
$container1->addComponent($button1);
$window1->setRoot($container1);
// Frame counter for window 2
$window2FrameCount = 0;
echo "Creating Window 2...\n";
$window2 = $app->createWindow('Window 2', 400, 300, 520, 100);
$container2 = new Container('flex flex-col p-6 gap-4 bg-green-100');
$title2 = new Label(
text: 'Window 2',
style: 'text-2xl text-green-900',
);
$frameLabel2 = new Label(
text: 'Frame: 0',
style: 'text-base text-green-700',
);
$button2 = new Button(
text: 'Click Me (Window 2)',
style: 'bg-green-500 hover:bg-green-700 text-white p-4 rounded',
);
$clickCount2 = 0;
$button2->setOnClick(function () use (&$clickCount2, $button2) {
$clickCount2++;
$button2->setText("Clicked {$clickCount2} times");
echo "Window 2 button clicked! Count: {$clickCount2}\n";
});
$container2->addComponent($title2);
$container2->addComponent($frameLabel2);
$container2->addComponent($button2);
$window2->setRoot($container2);
echo "Starting main loop...\n";
echo "Click the buttons to test interaction!\n";
echo "Close all windows to exit.\n\n";
// Custom run loop with frame counter updates
$running = true;
while ($running && count($app->getWindows()) > 0) {
// Update frame counters
$window1FrameCount++;
$window2FrameCount++;
$frameLabel1->setText("Frame: {$window1FrameCount}");
$frameLabel2->setText("Frame: {$window2FrameCount}");
// Layout all windows FIRST (sets window references and calculates positions)
foreach ($app->getWindows() as $windowId => $window) {
$window->layout();
}
// Handle events for all windows (now that layout is done)
foreach ($app->getWindows() as $windowId => $window) {
$window->handleEvents();
}
// Update async tasks (global)
PHPNative\Async\TaskManager::getInstance()->update();
// Update all windows
foreach ($app->getWindows() as $windowId => $window) {
$window->update();
}
// Render all windows
foreach ($app->getWindows() as $windowId => $window) {
$window->render();
}
// Remove closed windows
$windowsCopy = $app->getWindows();
foreach ($windowsCopy as $windowId => $window) {
if ($window->shouldClose()) {
echo "Window {$windowId} closing...\n";
}
}
// Limit frame rate to ~60 FPS
usleep(16666);
}
echo "Application exited.\n";

View File

@ -1,65 +0,0 @@
<?php
require_once __DIR__ . '/../vendor/autoload.php';
use PHPNative\Framework\Application;
use PHPNative\Ui\Widget\Button;
use PHPNative\Ui\Widget\Container;
use PHPNative\Ui\Widget\Label;
// Create application
$app = new Application();
// Create a single window (simple usage)
$window = $app->createWindow('Simple Window Example', 800, 600);
// Create UI
$container = new Container('flex flex-col p-6 gap-4 bg-gradient-to-br from-blue-400 to-purple-500');
$title = new Label(
text: 'Willkommen zu PHPNative!',
style: 'text-3xl text-white'
);
$description = new Label(
text: 'Dies ist ein einfaches Beispiel mit einem Window.',
style: 'text-lg text-white'
);
$button = new Button(
text: 'Klick mich!',
style: 'bg-white hover:bg-gray-100 text-blue-600 p-4 rounded-lg'
);
$statusLabel = new Label(
text: '',
style: 'text-base text-white'
);
$clickCount = 0;
$button->setOnClick(function() use ($statusLabel, &$clickCount) {
$clickCount++;
$statusLabel->setText("Button wurde $clickCount mal geklickt!");
});
$quitButton = new Button(
text: 'Beenden',
style: 'bg-red-500 hover:bg-red-700 text-white p-4 rounded-lg mt-4'
);
$quitButton->setOnClick(function() use ($app) {
$app->quit();
});
// Add components
$container->addComponent($title);
$container->addComponent($description);
$container->addComponent($button);
$container->addComponent($statusLabel);
$container->addComponent($quitButton);
// Set window root
$window->setRoot($container);
// Run application
$app->run();

View File

@ -0,0 +1,42 @@
<?php
require_once __DIR__ . '/../vendor/autoload.php';
use PHPNative\Framework\Application;
use PHPNative\Ui\Window;
use PHPNative\Ui\Widget\Container;
use PHPNative\Ui\Widget\Label;
$app = new Application();
$window = new Window('Flex-Col Test', 800, 600);
// Main container mit flex-col
$mainContainer = new Container('flex flex-col p-4 bg-gray-100');
// Container 1 - Feste Höhe
$container1 = new Container('bg-red-500 h-20 p-4');
$label1 = new Label('Container 1 - Fixed Height (h-20)', 'text-white text-xl');
$container1->addComponent($label1);
$mainContainer->addComponent($container1);
// Container 2 - Mit flex-grow (sollte verfügbaren Platz einnehmen)
$container2 = new Container('flex-grow bg-blue-500 p-4');
$label2 = new Label('Container 2 - Flex Grow (flex-grow)', 'text-white text-xl');
$container2->addComponent($label2);
$mainContainer->addComponent($container2);
// Container 3 - Natürliche Höhe (basierend auf Inhalt)
$container3 = new Container('bg-green-500 p-4');
$label3 = new Label('Container 3 - Natural Height', 'text-white text-xl');
$container3->addComponent($label3);
$mainContainer->addComponent($container3);
$window->setRoot($mainContainer);
$app->addWindow($window);
echo "Flex-Col Test started!\n";
echo "- Red: Fixed height (80px)\n";
echo "- Blue: Should grow to fill available space\n";
echo "- Green: Natural height based on content\n\n";
$app->run();

View File

@ -0,0 +1,47 @@
<?php
require_once __DIR__ . '/../vendor/autoload.php';
use PHPNative\Framework\Application;
use PHPNative\Ui\Widget\Button;
use PHPNative\Ui\Widget\Container;
use PHPNative\Ui\Widget\TextInput;
use PHPNative\Ui\Window;
$app = new Application();
$window = new Window('TextInput Test', 600, 400);
// Main container
$mainContainer = new Container('flex flex-col p-4 bg-gray-100');
// Container 1 - Red background mit fester Höhe
$container1 = new Container('bg-red-500 h-20 mb-4 p-2');
$mainContainer->addComponent($container1);
// Container 2 - Mit TextInput und Button (flex-row)
$inputContainer = new Container('flex flex-row mb-4 bg-blue-200');
$input = new TextInput(
placeholder: 'Type something...',
style: 'flex-1 p-2 border border-gray-300 rounded mr-2 h-10',
);
$button = new Button('Submit', 'bg-green-500 text-white p-2 rounded');
$inputContainer->addComponent($input);
$inputContainer->addComponent($button);
$mainContainer->addComponent($inputContainer);
// Container 3 - Green background mit fester Höhe
$container3 = new Container('bg-green-500 h-20 p-2');
$mainContainer->addComponent($container3);
$window->setRoot($mainContainer);
$app->addWindow($window);
echo "TextInput Test started!\n";
echo "- Red container (80px)\n";
echo "- Blue container with TextInput and Button (40px)\n";
echo "- Green container (80px)\n\n";
$app->run();

View File

@ -0,0 +1,39 @@
<?php
require_once __DIR__ . '/../vendor/autoload.php';
use PHPNative\Framework\Application;
use PHPNative\Ui\Widget\Button;
use PHPNative\Ui\Widget\Container;
use PHPNative\Ui\Widget\TextInput;
use PHPNative\Ui\Window;
$app = new Application();
$window = new Window('TextInput Test', 600, 400);
// Main container
$mainContainer = new Container('flex flex-col p-4 bg-gray-100');
// Container 2 - Mit TextInput und Button (flex-row)
$inputContainer = new Container('flex flex-row p-4 g-blue-200');
$input = new TextInput(
placeholder: 'Type something...',
style: 'flex-1 p-2 border border-gray-300 rounded mr-2',
);
$button = new Button('Submit', 'bg-green-500 text-white p-2 rounded-xl');
$inputContainer->addComponent($input);
$inputContainer->addComponent($button);
$mainContainer->addComponent($inputContainer);
$window->setRoot($mainContainer);
$app->addWindow($window);
echo "TextInput Test started!\n";
echo "- Red container (80px)\n";
echo "- Blue container with TextInput and Button (40px)\n";
echo "- Green container (80px)\n\n";
$app->run();

View File

@ -1,153 +0,0 @@
<?php
/**
* Example with 2 framework windows running in parallel threads
* Requires: parallel extension
*
* Note: Each window runs in its own thread with its own event loop
*/
require_once __DIR__ . '/../vendor/autoload.php';
use parallel\Runtime;
use PHPNative\Framework\Application;
use PHPNative\Ui\Widget\Button;
use PHPNative\Ui\Widget\Container;
use PHPNative\Ui\Widget\Label;
// Check if parallel extension is loaded
if (!extension_loaded('parallel')) {
die("Error: parallel extension is not loaded. Install with: pecl install parallel\n");
}
echo "Starting 2 framework windows in parallel threads...\n\n";
/**
* Function to run a window with the framework in a thread
*/
$windowRunner = function(string $title, int $x, int $y, string $bgColor) {
require_once __DIR__ . '/../vendor/autoload.php';
// Create application for this thread
$app = new \PHPNative\Framework\Application();
// Create window
$window = $app->createWindow($title, 400, 350, $x, $y);
// Create UI
$container = new \PHPNative\Ui\Widget\Container("flex flex-col p-6 gap-4 bg-$bgColor-100");
$titleLabel = new \PHPNative\Ui\Widget\Label(
text: $title,
style: "text-2xl text-$bgColor-900"
);
$infoLabel = new \PHPNative\Ui\Widget\Label(
text: 'Running in separate thread',
style: "text-sm text-$bgColor-700"
);
$frameLabel = new \PHPNative\Ui\Widget\Label(
text: 'Frame: 0',
style: "text-base text-$bgColor-600"
);
$button = new \PHPNative\Ui\Widget\Button(
text: 'Click Me!',
style: "bg-$bgColor-500 hover:bg-$bgColor-700 text-white p-4 rounded-lg"
);
$clickCount = 0;
$button->setOnClick(function() use (&$clickCount, $button, $title) {
$clickCount++;
$button->setText("Clicked $clickCount times");
echo "$title - Button clicked! Count: $clickCount\n";
});
$closeButton = new \PHPNative\Ui\Widget\Button(
text: 'Close Window',
style: "bg-red-500 hover:bg-red-700 text-white p-3 rounded"
);
$closeButton->setOnClick(function() use ($window) {
$window->close();
});
$container->addComponent($titleLabel);
$container->addComponent($infoLabel);
$container->addComponent($frameLabel);
$container->addComponent($button);
$container->addComponent($closeButton);
$window->setRoot($container);
// Custom run loop with frame counter
$frameCount = 0;
while (!$window->shouldClose()) {
$frameCount++;
$frameLabel->setText("Frame: $frameCount");
$window->layout();
// Poll events
rgfw_pollEvents();
$window->handleEvents();
// Update
\PHPNative\Async\TaskManager::getInstance()->update();
$window->update();
// Render
$window->render();
// ~60 FPS
usleep(16666);
}
// Cleanup
$window->cleanup();
return "$title closed after $frameCount frames";
};
try {
// Launch Window 1 (Blue) in thread
echo "Launching Window 1 (Blue) in thread...\n";
$runtime1 = new Runtime();
$future1 = $runtime1->run($windowRunner, [
'Window 1 - Blue',
100,
100,
'blue'
]);
// Launch Window 2 (Green) in thread
echo "Launching Window 2 (Green) in thread...\n";
$runtime2 = new Runtime();
$future2 = $runtime2->run($windowRunner, [
'Window 2 - Green',
550,
100,
'green'
]);
echo "\nBoth windows launched in parallel threads!\n";
echo "Each window has its own event loop\n";
echo "Try clicking the buttons and closing windows\n";
echo "Main thread waiting for windows to close...\n\n";
// Wait for both windows to complete
echo "Waiting for Window 1...\n";
$result1 = $future1->value();
echo $result1 . "\n";
echo "Waiting for Window 2...\n";
$result2 = $future2->value();
echo $result2 . "\n";
echo "\nAll windows closed. Application exiting.\n";
} catch (\Throwable $e) {
echo "Error: " . $e->getMessage() . "\n";
echo "Stack trace:\n" . $e->getTraceAsString() . "\n";
}

View File

@ -1,170 +0,0 @@
<?php
/**
* Simple example with 2 windows running in parallel threads
* Requires: parallel extension
*/
require_once __DIR__ . '/../vendor/autoload.php';
use parallel\Runtime;
use parallel\Channel;
// Check if parallel extension is loaded
if (!extension_loaded('parallel')) {
die("Error: parallel extension is not loaded. Install with: pecl install parallel\n");
}
echo "Starting 2 windows in parallel threads...\n";
echo "Each window runs independently in its own thread\n";
echo "Press ESC or close button to exit each window\n\n";
/**
* Window function that runs in a thread
*/
$windowFunction = function(string $title, int $x, int $y, int $width, int $height, array $bgColor) {
// Enable event queue mode
rgfw_setQueueEvents(true);
// Create window
$window = rgfw_createWindow($title, $x, $y, $width, $height, 0);
if (!$window) {
throw new \Exception("Failed to create window: $title");
}
// Initialize RSGL renderer
if (!rsgl_init($window)) {
throw new \Exception("Failed to initialize RSGL for: $title");
}
[$r, $g, $b] = $bgColor;
$running = true;
$frameCount = 0;
$mouseX = 0;
$mouseY = 0;
$isHovering = false;
while ($running) {
// Poll events for this window
rgfw_pollEvents();
// Process queued events
while ($event = rgfw_window_checkQueuedEvent($window)) {
switch ($event['type']) {
case RGFW_quit:
$running = false;
break;
case RGFW_keyPressed:
$keyCode = $event['keyCode'] ?? 0;
if ($keyCode == RGFW_Escape) {
$running = false;
}
break;
case RGFW_mousePosChanged:
$mouseX = $event[0] ?? 0;
$mouseY = $event[1] ?? 0;
// Check if hovering over center rectangle
$rectSize = 100;
$rectX = ($width / 2) - ($rectSize / 2);
$rectY = ($height / 2) - ($rectSize / 2);
$isHovering = $mouseX >= $rectX && $mouseX <= ($rectX + $rectSize) &&
$mouseY >= $rectY && $mouseY <= ($rectY + $rectSize);
break;
case RGFW_mouseButtonPressed:
if ($isHovering) {
echo "$title - Rectangle clicked!\n";
}
break;
}
}
// Check if window should close
if (rgfw_window_shouldClose($window)) {
$running = false;
}
// Clear with background color
rsgl_clear($window, $r, $g, $b, 255);
// Draw center rectangle
$rectSize = 100;
$rectX = ($width / 2) - ($rectSize / 2);
$rectY = ($height / 2) - ($rectSize / 2);
// White or yellow depending on hover state
if ($isHovering) {
rsgl_setColor($window, 255, 255, 100, 255); // Yellow on hover
} else {
rsgl_setColor($window, 255, 255, 255, 255); // White
}
rsgl_drawRectF($window, (int) $rectX, (int) $rectY, $rectSize, $rectSize);
// Draw text showing frame count (if possible)
$frameCount++;
// Render and swap buffers
rsgl_render($window);
rgfw_window_swapBuffers($window);
// ~60 FPS
usleep(16666);
}
// Cleanup
rsgl_close($window);
rgfw_window_close($window);
return "$title closed after $frameCount frames";
};
try {
// Create first window in a thread (Red background)
echo "Launching Window 1 (Red) in thread...\n";
$runtime1 = new Runtime();
$future1 = $runtime1->run($windowFunction, [
'Window 1 - Red',
100,
100,
400,
300,
[255, 100, 100]
]);
// Create second window in a thread (Blue background)
echo "Launching Window 2 (Blue) in thread...\n";
$runtime2 = new Runtime();
$future2 = $runtime2->run($windowFunction, [
'Window 2 - Blue',
550,
100,
400,
300,
[100, 100, 255]
]);
echo "\nBoth windows launched!\n";
echo "Hover over the center rectangles to see them change color\n";
echo "Click on them to see messages in the console\n";
echo "Main thread waiting for windows to close...\n\n";
// Wait for first window
echo "Waiting for Window 1...\n";
$result1 = $future1->value();
echo $result1 . "\n";
// Wait for second window
echo "Waiting for Window 2...\n";
$result2 = $future2->value();
echo $result2 . "\n";
echo "\nAll windows closed. Application exiting.\n";
} catch (\Throwable $e) {
echo "Error: " . $e->getMessage() . "\n";
echo "Stack trace:\n" . $e->getTraceAsString() . "\n";
}

129
examples/todo_app.php Normal file
View File

@ -0,0 +1,129 @@
<?php
require_once __DIR__ . '/../vendor/autoload.php';
use PHPNative\Framework\Application;
use PHPNative\Ui\Widget\Button;
use PHPNative\Ui\Widget\Checkbox;
use PHPNative\Ui\Widget\Container;
use PHPNative\Ui\Widget\Label;
use PHPNative\Ui\Widget\TextInput;
use PHPNative\Ui\Window;
$app = new Application();
$window = new Window('Todo App', 600, 500);
// Main container
$mainContainer = new Container('flex flex-col p-4 bg-gray-50');
// Title
$title = new Label('My Todo List', 'text-2xl font-bold mb-4 text-gray-800');
$mainContainer->addComponent($title);
// Input section
$inputContainer = new Container('flex flex-row mb-4');
$input = new TextInput(
placeholder: 'What needs to be done?',
style: 'flex-1 p-2 border border-gray-300 rounded mr-2',
);
$addButton = new Button('Add', 'bg-blue-500 text-white p-2 rounded-lg hover:bg-blue-600');
$inputContainer->addComponent($input);
$inputContainer->addComponent($addButton);
$mainContainer->addComponent($inputContainer);
// Todo list container
$todoListContainer = new Container('flex flex-col');
$mainContainer->addComponent($todoListContainer);
// Stats
$statsLabel = new Label('0 items', 'mt-4 text-gray-600 text-sm h-10');
$mainContainer->addComponent($statsLabel);
// Todos array
$todos = [];
// Function to update stats
$updateStats = function () use (&$todos, $statsLabel) {
$total = count($todos);
$completed = count(array_filter($todos, fn($t) => $t['completed']));
$statsLabel->setText("{$total} items ({$completed} completed)");
};
// Function to render todos - use a reference so it can be used in closures
$renderTodos = null;
$renderTodos = function () use (&$todos, $todoListContainer, $updateStats, &$renderTodos, $window) {
// Clear existing todos
$todoListContainer->clearChildren();
foreach ($todos as $index => $todo) {
$todoItem = new Container('flex flex-row items-center p-2 mb-2 bg-white border border-gray-200 rounded h-12');
// Checkbox
$checkbox = new Checkbox(
label: '',
checked: $todo['completed'],
style: 'mr-2 w-6 h-6',
);
$checkbox->setOnChange(function ($checked) use (&$todos, $index, &$renderTodos, $updateStats) {
$todos[$index]['completed'] = $checked;
$updateStats();
});
// Text
$textStyle = $todo['completed'] ? 'flex-1 text-gray-400 line-through' : 'flex-1 text-gray-800';
$todoText = new Label($todo['text'], $textStyle);
// Delete button
$deleteButton = new Button('Delete', 'bg-red-500 text-white px-2 py-1 rounded text-sm hover:bg-red-600');
$deleteButton->setOnClick(function () use (&$todos, $index, &$renderTodos, $updateStats) {
array_splice($todos, $index, 1);
$renderTodos();
$updateStats();
});
$todoItem->addComponent($checkbox);
$todoItem->addComponent($todoText);
$todoItem->addComponent($deleteButton);
$todoListContainer->addComponent($todoItem);
}
$updateStats();
// Trigger re-layout after adding new components
$window->setShouldBeReLayouted(true);
};
// Add button click handler
$addButton->setOnClick(function () use ($input, &$todos, &$renderTodos) {
$text = trim($input->getValue());
if (!empty($text)) {
$todos[] = [
'text' => $text,
'completed' => false,
];
$input->setValue(''); // Clear input
$renderTodos();
}
});
// Handle Enter key in input
$input->setOnChange(function ($value) {
// Could add live validation here
});
$window->setRoot($mainContainer);
$app->addWindow($window);
echo "Todo App started!\n";
echo "- Type in the input field and click 'Add' or press Enter\n";
echo "- Click checkboxes to mark todos as complete\n";
echo "- Click 'Delete' to remove a todo\n\n";
$app->run();

View File

@ -14,7 +14,7 @@ library_names='sdl3.so sdl3.so sdl3.so'
old_library=''
# Libraries that this one depends upon.
dependency_libs=' -L/usr/local/lib -lSDL3_gfx -lSDL3'
dependency_libs=' -L/usr/local/lib -lSDL3_gfx -lSDL3_image -lSDL3_ttf -lSDL3'
# Version information for sdl3.
current=0

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
php-sdl3/.libs/sdl3_image.o Normal file

Binary file not shown.

BIN
php-sdl3/.libs/sdl3_ttf.o Normal file

Binary file not shown.

View File

@ -7,7 +7,7 @@ SED = /usr/bin/sed
AWK = nawk
SHLIB_SUFFIX_NAME = so
SHLIB_DL_SUFFIX_NAME = so
shared_objects_sdl3 = sdl3.lo helper.lo
shared_objects_sdl3 = sdl3.lo helper.lo sdl3_image.lo sdl3_ttf.lo sdl3_events.lo
PHP_PECL_EXTENSION = sdl3
PHP_MODULES = $(phplibdir)/sdl3.la
PHP_ZEND_EX =
@ -18,7 +18,7 @@ exec_prefix = $(prefix)
libdir = ${exec_prefix}/lib
phpincludedir = /usr/local/include/php
CC = cc
CFLAGS = -g -O2 -I/usr/include/pipewire-0.3 -I/usr/include/spa-0.2 -D_REENTRANT -I/usr/include/libdrm -I/usr/include/libdecor-0 -I/usr/local/include -I/usr/include/pipewire-0.3 -I/usr/include/spa-0.2 -D_REENTRANT -I/usr/include/libdrm -I/usr/include/libdecor-0
CFLAGS = -g -O2 -I/usr/include/pipewire-0.3 -I/usr/include/spa-0.2 -D_REENTRANT -I/usr/include/libdrm -I/usr/include/libdecor-0 -I/usr/local/include -I/usr/include/pipewire-0.3 -I/usr/include/spa-0.2 -D_REENTRANT -I/usr/include/libdrm -I/usr/include/libdecor-0 -I/usr/include/pipewire-0.3 -I/usr/include/spa-0.2 -D_REENTRANT -I/usr/include/libdrm -I/usr/include/libdecor-0 -I/usr/include/libpng16 -I/usr/include/x86_64-linux-gnu -I/usr/include/webp -I/usr/include/pipewire-0.3 -I/usr/include/spa-0.2 -D_REENTRANT -I/usr/include/libdrm -I/usr/include/libdecor-0 -I/usr/include/harfbuzz -I/usr/include/freetype2 -I/usr/include/libpng16 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/sysprof-6 -pthread
CFLAGS_CLEAN = $(CFLAGS) -D_GNU_SOURCE
CPP = cc -E
CPPFLAGS = -DHAVE_CONFIG_H
@ -30,7 +30,7 @@ PHP_EXECUTABLE = /usr/local/bin/php
EXTRA_LDFLAGS =
EXTRA_LIBS =
INCLUDES = -I/usr/local/include/php -I/usr/local/include/php/main -I/usr/local/include/php/TSRM -I/usr/local/include/php/Zend -I/usr/local/include/php/ext -I/usr/local/include/php/ext/date/lib
LDFLAGS = -lSDL3 -L/usr/local/lib -lSDL3_gfx -lSDL3
LDFLAGS = -lSDL3 -L/usr/local/lib -lSDL3_gfx -lSDL3 -lSDL3_image -lSDL3 -lSDL3_ttf -lSDL3
LIBTOOL = $(SHELL) $(top_builddir)/libtool
SHELL = /bin/bash
INSTALL_HEADERS =
@ -208,6 +208,15 @@ sdl3.lo: /home/thomas/projekte/phpnative/framework/php-sdl3/sdl3.c
-include helper.dep
helper.lo: /home/thomas/projekte/phpnative/framework/php-sdl3/helper.c
$(LIBTOOL) --tag=CC --mode=compile $(CC) -I. -I/home/thomas/projekte/phpnative/framework/php-sdl3 $(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) -DZEND_COMPILE_DL_EXT=1 -c /home/thomas/projekte/phpnative/framework/php-sdl3/helper.c -o helper.lo -MMD -MF helper.dep -MT helper.lo
-include sdl3_image.dep
sdl3_image.lo: /home/thomas/projekte/phpnative/framework/php-sdl3/sdl3_image.c
$(LIBTOOL) --tag=CC --mode=compile $(CC) -I. -I/home/thomas/projekte/phpnative/framework/php-sdl3 $(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) -DZEND_COMPILE_DL_EXT=1 -c /home/thomas/projekte/phpnative/framework/php-sdl3/sdl3_image.c -o sdl3_image.lo -MMD -MF sdl3_image.dep -MT sdl3_image.lo
-include sdl3_ttf.dep
sdl3_ttf.lo: /home/thomas/projekte/phpnative/framework/php-sdl3/sdl3_ttf.c
$(LIBTOOL) --tag=CC --mode=compile $(CC) -I. -I/home/thomas/projekte/phpnative/framework/php-sdl3 $(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) -DZEND_COMPILE_DL_EXT=1 -c /home/thomas/projekte/phpnative/framework/php-sdl3/sdl3_ttf.c -o sdl3_ttf.lo -MMD -MF sdl3_ttf.dep -MT sdl3_ttf.lo
-include sdl3_events.dep
sdl3_events.lo: /home/thomas/projekte/phpnative/framework/php-sdl3/sdl3_events.c
$(LIBTOOL) --tag=CC --mode=compile $(CC) -I. -I/home/thomas/projekte/phpnative/framework/php-sdl3 $(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) -DZEND_COMPILE_DL_EXT=1 -c /home/thomas/projekte/phpnative/framework/php-sdl3/sdl3_events.c -o sdl3_events.lo -MMD -MF sdl3_events.dep -MT sdl3_events.lo
$(phplibdir)/sdl3.la: ./sdl3.la
$(LIBTOOL) --tag=CC --mode=install cp ./sdl3.la $(phplibdir)

View File

@ -4,6 +4,15 @@ sdl3.lo: /home/thomas/projekte/phpnative/framework/php-sdl3/sdl3.c
-include helper.dep
helper.lo: /home/thomas/projekte/phpnative/framework/php-sdl3/helper.c
$(LIBTOOL) --tag=CC --mode=compile $(CC) -I. -I/home/thomas/projekte/phpnative/framework/php-sdl3 $(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) -DZEND_COMPILE_DL_EXT=1 -c /home/thomas/projekte/phpnative/framework/php-sdl3/helper.c -o helper.lo -MMD -MF helper.dep -MT helper.lo
-include sdl3_image.dep
sdl3_image.lo: /home/thomas/projekte/phpnative/framework/php-sdl3/sdl3_image.c
$(LIBTOOL) --tag=CC --mode=compile $(CC) -I. -I/home/thomas/projekte/phpnative/framework/php-sdl3 $(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) -DZEND_COMPILE_DL_EXT=1 -c /home/thomas/projekte/phpnative/framework/php-sdl3/sdl3_image.c -o sdl3_image.lo -MMD -MF sdl3_image.dep -MT sdl3_image.lo
-include sdl3_ttf.dep
sdl3_ttf.lo: /home/thomas/projekte/phpnative/framework/php-sdl3/sdl3_ttf.c
$(LIBTOOL) --tag=CC --mode=compile $(CC) -I. -I/home/thomas/projekte/phpnative/framework/php-sdl3 $(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) -DZEND_COMPILE_DL_EXT=1 -c /home/thomas/projekte/phpnative/framework/php-sdl3/sdl3_ttf.c -o sdl3_ttf.lo -MMD -MF sdl3_ttf.dep -MT sdl3_ttf.lo
-include sdl3_events.dep
sdl3_events.lo: /home/thomas/projekte/phpnative/framework/php-sdl3/sdl3_events.c
$(LIBTOOL) --tag=CC --mode=compile $(CC) -I. -I/home/thomas/projekte/phpnative/framework/php-sdl3 $(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) -DZEND_COMPILE_DL_EXT=1 -c /home/thomas/projekte/phpnative/framework/php-sdl3/sdl3_events.c -o sdl3_events.lo -MMD -MF sdl3_events.dep -MT sdl3_events.lo
$(phplibdir)/sdl3.la: ./sdl3.la
$(LIBTOOL) --tag=CC --mode=install cp ./sdl3.la $(phplibdir)

148
php-sdl3/button_example.php Normal file
View File

@ -0,0 +1,148 @@
<?php
if (!extension_loaded('sdl3')) {
echo "Die Erweiterung 'sdl3' ist nicht geladen.\n";
exit(1);
}
// SDL_INIT_VIDEO
const SDL_INIT_VIDEO = 0x00000020;
// Initialisierung
if (!sdl_init(SDL_INIT_VIDEO)) {
echo 'Fehler bei der Initialisierung von SDL: ' . sdl_get_error() . "\n";
exit(1);
}
// Fenster erstellen
$window = sdl_create_window('SDL3 Button Beispiel', 640, 480);
if (!$window) {
echo 'Fehler beim Erstellen des Fensters: ' . sdl_get_error() . "\n";
sdl_quit();
exit(1);
}
// Renderer erstellen
$renderer = sdl_create_renderer($window);
if (!$renderer) {
echo 'Fehler beim Erstellen des Renderers: ' . sdl_get_error() . "\n";
sdl_quit();
exit(1);
}
// Button-Eigenschaften
$button = [
'x' => 220,
'y' => 200,
'w' => 200,
'h' => 80,
'hovered' => false
];
// Hilfsfunktion: Prüft ob Punkt in Rechteck ist
function point_in_rect($x, $y, $rect) {
return $x >= $rect['x'] &&
$x <= $rect['x'] + $rect['w'] &&
$y >= $rect['y'] &&
$y <= $rect['y'] + $rect['h'];
}
// Zeichnet den Button
function draw_button($renderer, $button) {
// Button-Hintergrund (grün wenn hovered, blau sonst)
if ($button['hovered']) {
sdl_set_render_draw_color($renderer, 50, 150, 50, 255);
} else {
sdl_set_render_draw_color($renderer, 70, 130, 180, 255);
}
// Abgerundeter Button
sdl_rounded_box($renderer,
$button['x'], $button['y'],
$button['x'] + $button['w'], $button['y'] + $button['h'],
15,
$button['hovered'] ? 50 : 70,
$button['hovered'] ? 150 : 130,
$button['hovered'] ? 50 : 180,
255
);
// Button-Rahmen (dunkler)
sdl_set_render_draw_color($renderer, 40, 80, 120, 255);
sdl_render_fill_rect($renderer, [
'x' => $button['x'],
'y' => $button['y'],
'w' => $button['w'],
'h' => 3
]);
sdl_render_fill_rect($renderer, [
'x' => $button['x'],
'y' => $button['y'] + $button['h'] - 3,
'w' => $button['w'],
'h' => 3
]);
}
echo "Button-Beispiel läuft!\n";
echo "Bewege die Maus über den blauen Button.\n";
echo "Klicke auf den Button um das Fenster zu schließen.\n";
// Hauptschleife
$running = true;
while ($running) {
// Events verarbeiten
while ($event = sdl_poll_event()) {
// Quit-Event
if ($event['type'] === SDL_EVENT_QUIT) {
$running = false;
}
// Window Close Button
if ($event['type'] === SDL_EVENT_WINDOW_CLOSE_REQUESTED) {
$running = false;
}
// Maus-Bewegung
if ($event['type'] === SDL_EVENT_MOUSE_MOTION) {
$button['hovered'] = point_in_rect($event['x'], $event['y'], $button);
}
// Maus-Klick
if ($event['type'] === SDL_EVENT_MOUSE_BUTTON_DOWN) {
if ($event['button'] === SDL_BUTTON_LEFT) {
// Prüfe ob auf Button geklickt wurde
if (point_in_rect($event['x'], $event['y'], $button)) {
echo "Button wurde geklickt! Fenster wird geschlossen...\n";
$running = false;
}
}
}
}
// Hintergrund zeichnen (hellgrau)
sdl_set_render_draw_color($renderer, 220, 220, 220, 255);
sdl_render_clear($renderer);
// Button zeichnen
draw_button($renderer, $button);
// Text-Bereich für "X Schließen" simulieren (schwarzes Rechteck in der Mitte)
$text_x = $button['x'] + ($button['w'] - 100) / 2;
$text_y = $button['y'] + ($button['h'] - 20) / 2;
sdl_set_render_draw_color($renderer, 255, 255, 255, 255);
sdl_render_fill_rect($renderer, [
'x' => $text_x,
'y' => $text_y,
'w' => 100,
'h' => 20
]);
// Anzeigen
sdl_render_present($renderer);
// Kurze Pause um CPU zu schonen
sdl_delay(16); // ~60 FPS
}
echo "Fenster geschlossen.\n";
sdl_quit();

View File

@ -0,0 +1,166 @@
<?php
if (!extension_loaded('sdl3')) {
echo "Die Erweiterung 'sdl3' ist nicht geladen.\n";
exit(1);
}
// SDL_INIT_VIDEO
const SDL_INIT_VIDEO = 0x00000020;
// Initialisierung
if (!sdl_init(SDL_INIT_VIDEO)) {
echo 'Fehler bei der Initialisierung von SDL: ' . sdl_get_error() . "\n";
exit(1);
}
// TTF initialisieren
if (!ttf_init()) {
echo 'Fehler bei der Initialisierung von TTF: ' . sdl_get_error() . "\n";
sdl_quit();
exit(1);
}
// Fenster erstellen
$window = sdl_create_window('SDL3 Button mit Text', 640, 480);
if (!$window) {
echo 'Fehler beim Erstellen des Fensters: ' . sdl_get_error() . "\n";
sdl_quit();
exit(1);
}
// Renderer erstellen
$renderer = sdl_create_renderer($window);
if (!$renderer) {
echo 'Fehler beim Erstellen des Renderers: ' . sdl_get_error() . "\n";
sdl_quit();
exit(1);
}
// Font laden
$font = ttf_open_font('/usr/share/fonts/truetype/ubuntu/Ubuntu[wdth,wght].ttf', 24);
if (!$font) {
echo 'Fehler beim Laden der Schriftart: ' . sdl_get_error() . "\n";
sdl_quit();
exit(1);
}
// Text-Surface erstellen
$text_surface = ttf_render_text_solid($font, 'Schließen', 255, 255, 255);
if (!$text_surface) {
echo 'Fehler beim Rendern des Textes: ' . sdl_get_error() . "\n";
sdl_quit();
exit(1);
}
// Text-Texture erstellen
$text_texture = sdl_create_texture_from_surface($renderer, $text_surface);
if (!$text_texture) {
echo 'Fehler beim Erstellen der Text-Texture: ' . sdl_get_error() . "\n";
sdl_quit();
exit(1);
}
// Textgröße ermitteln
$text_size = ttf_size_text($font, 'Schließen');
// Button-Eigenschaften
$button = [
'x' => 220,
'y' => 200,
'w' => 200,
'h' => 80,
'hovered' => false
];
// Hilfsfunktion: Prüft ob Punkt in Rechteck ist
function point_in_rect($x, $y, $rect) {
return $x >= $rect['x'] &&
$x <= $rect['x'] + $rect['w'] &&
$y >= $rect['y'] &&
$y <= $rect['y'] + $rect['h'];
}
// Zeichnet den Button
function draw_button($renderer, $button, $text_texture, $text_size) {
// Button-Hintergrund (grün wenn hovered, blau sonst)
if ($button['hovered']) {
sdl_rounded_box($renderer,
$button['x'], $button['y'],
$button['x'] + $button['w'], $button['y'] + $button['h'],
15,
60, 180, 60, 255
);
} else {
sdl_rounded_box($renderer,
$button['x'], $button['y'],
$button['x'] + $button['w'], $button['y'] + $button['h'],
15,
70, 130, 180, 255
);
}
// Text zentriert auf Button rendern
$text_x = $button['x'] + ($button['w'] - $text_size['w']) / 2;
$text_y = $button['y'] + ($button['h'] - $text_size['h']) / 2;
sdl_render_texture($renderer, $text_texture, [
'x' => (int)$text_x,
'y' => (int)$text_y,
'w' => $text_size['w'],
'h' => $text_size['h']
]);
}
echo "Button-Beispiel mit Text läuft!\n";
echo "Bewege die Maus über den Button.\n";
echo "Klicke auf den 'Schließen'-Button um das Fenster zu schließen.\n";
// Hauptschleife
$running = true;
while ($running) {
// Events verarbeiten
while ($event = sdl_poll_event()) {
// Quit-Event
if ($event['type'] === SDL_EVENT_QUIT) {
$running = false;
}
// Window Close Button
if ($event['type'] === SDL_EVENT_WINDOW_CLOSE_REQUESTED) {
$running = false;
}
// Maus-Bewegung
if ($event['type'] === SDL_EVENT_MOUSE_MOTION) {
$button['hovered'] = point_in_rect($event['x'], $event['y'], $button);
}
// Maus-Klick
if ($event['type'] === SDL_EVENT_MOUSE_BUTTON_DOWN) {
if ($event['button'] === SDL_BUTTON_LEFT) {
// Prüfe ob auf Button geklickt wurde
if (point_in_rect($event['x'], $event['y'], $button)) {
echo "Button wurde geklickt! Fenster wird geschlossen...\n";
$running = false;
}
}
}
}
// Hintergrund zeichnen (dunkelgrau)
sdl_set_render_draw_color($renderer, 45, 45, 48, 255);
sdl_render_clear($renderer);
// Button zeichnen
draw_button($renderer, $button, $text_texture, $text_size);
// Anzeigen
sdl_render_present($renderer);
// Kurze Pause um CPU zu schonen
sdl_delay(16); // ~60 FPS
}
echo "Fenster geschlossen.\n";
sdl_quit();

View File

@ -6,6 +6,14 @@ PHP_ARG_WITH(sdl3_gfx, [for sdl3_gfx support], [
AS_HELP_STRING([--with-sdl3-gfx[=DIR]], [Enable sdl3_gfx support. DIR is the prefix for SDL3_gfx installation.])
])
PHP_ARG_WITH(sdl3_image, [for sdl3_image support], [
AS_HELP_STRING([--with-sdl3-image[=DIR]], [Enable sdl3_image support. DIR is the prefix for SDL3_image installation.])
])
PHP_ARG_WITH(sdl3_ttf, [for sdl3_ttf support], [
AS_HELP_STRING([--with-sdl3-ttf[=DIR]], [Enable sdl3_ttf support. DIR is the prefix for SDL3_ttf installation.])
])
if test "$PHP_SDL3" != "no"; then
if test -d "$PHP_SDL3"; then
PKG_CONFIG_PATH="$PHP_SDL3/lib/pkgconfig:$PHP_SDL3/share/pkgconfig:$PKG_CONFIG_PATH"
@ -31,7 +39,33 @@ if test "$PHP_SDL3" != "no"; then
])
fi
SDL_SOURCE_FILES="sdl3.c helper.c"
if test "$PHP_SDL3_IMAGE" != "no"; then
if test -d "$PHP_SDL3_IMAGE"; then
PKG_CONFIG_PATH="$PHP_SDL3_IMAGE/lib/pkgconfig:$PHP_SDL3_IMAGE/share/pkgconfig:$PKG_CONFIG_PATH"
fi
PKG_CHECK_MODULES([SDL3_IMAGE], [sdl3-image >= 3.0.0], [
CFLAGS="$CFLAGS $SDL3_IMAGE_CFLAGS"
LDFLAGS="$LDFLAGS $SDL3_IMAGE_LIBS"
],[
AC_MSG_ERROR([SDL3_image not found. Please check your installation or use --with-sdl3-image=/path/to/sdl3_image])
])
fi
if test "$PHP_SDL3_TTF" != "no"; then
if test -d "$PHP_SDL3_TTF"; then
PKG_CONFIG_PATH="$PHP_SDL3_TTF/lib/pkgconfig:$PHP_SDL3_TTF/share/pkgconfig:$PKG_CONFIG_PATH"
fi
PKG_CHECK_MODULES([SDL3_TTF], [sdl3-ttf >= 3.0.0], [
CFLAGS="$CFLAGS $SDL3_TTF_CFLAGS"
LDFLAGS="$LDFLAGS $SDL3_TTF_LIBS"
],[
AC_MSG_ERROR([SDL3_ttf not found. Please check your installation or use --with-sdl3-ttf=/path/to/sdl3_ttf])
])
fi
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)
fi

View File

@ -3,5 +3,8 @@
# Created by configure
'./configure' \
'--with-sdl3=/usr/local' \
'--with-sdl3-gfx=/usr/local' \
'--with-sdl3-image=/usr/local' \
'--with-sdl3-ttf=/usr/local' \
"$@"

View File

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

338
php-sdl3/configure vendored
View File

@ -802,6 +802,10 @@ RANLIB
AR
ECHO
LN_S
SDL3_TTF_LIBS
SDL3_TTF_CFLAGS
SDL3_IMAGE_LIBS
SDL3_IMAGE_CFLAGS
SDL3_GFX_LIBS
SDL3_GFX_CFLAGS
SDL3_LIBS
@ -884,6 +888,8 @@ with_php_config
enable_
with_sdl3
with_sdl3_gfx
with_sdl3_image
with_sdl3_ttf
enable_shared
enable_static
enable_fast_install
@ -907,7 +913,11 @@ CPP
SDL3_CFLAGS
SDL3_LIBS
SDL3_GFX_CFLAGS
SDL3_GFX_LIBS'
SDL3_GFX_LIBS
SDL3_IMAGE_CFLAGS
SDL3_IMAGE_LIBS
SDL3_TTF_CFLAGS
SDL3_TTF_LIBS'
# Initialize some variables set by options.
@ -1544,6 +1554,14 @@ Extension:
SDL3_gfx installation.
--with-sdl3-image=DIR Enable sdl3_image support. DIR is the prefix for
SDL3_image installation.
--with-sdl3-ttf=DIR Enable sdl3_ttf support. DIR is the prefix for
SDL3_ttf installation.
Libtool:
--enable-shared=PKGS Build shared libraries default=yes
--enable-static=PKGS Build static libraries default=yes
@ -1575,6 +1593,14 @@ Some influential environment variables:
C compiler flags for SDL3_GFX, overriding pkg-config
SDL3_GFX_LIBS
linker flags for SDL3_GFX, overriding pkg-config
SDL3_IMAGE_CFLAGS
C compiler flags for SDL3_IMAGE, overriding pkg-config
SDL3_IMAGE_LIBS
linker flags for SDL3_IMAGE, overriding pkg-config
SDL3_TTF_CFLAGS
C compiler flags for SDL3_TTF, overriding pkg-config
SDL3_TTF_LIBS
linker flags for SDL3_TTF, overriding pkg-config
Use these variables to override the choices made by 'configure' or to help
it to find libraries and programs with nonstandard names/locations.
@ -4801,6 +4827,108 @@ printf "%s\n" "$ext_output" >&6; }
php_with_sdl3_image=no
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for sdl3_image support" >&5
printf %s "checking for sdl3_image support... " >&6; }
# Check whether --with-sdl3_image was given.
if test ${with_sdl3_image+y}
then :
withval=$with_sdl3_image; PHP_SDL3_IMAGE=$withval
else case e in #(
e)
PHP_SDL3_IMAGE=no
test "$PHP_ENABLE_ALL" && PHP_SDL3_IMAGE=$PHP_ENABLE_ALL
;;
esac
fi
ext_output="yes, shared"
ext_shared=yes
case $PHP_SDL3_IMAGE in
shared,*)
PHP_SDL3_IMAGE=$(echo "$PHP_SDL3_IMAGE"|$SED 's/^shared,//')
;;
shared)
PHP_SDL3_IMAGE=yes
;;
no)
ext_output=no
ext_shared=no
;;
*)
ext_output=yes
ext_shared=no
;;
esac
ext_output="yes, shared"
ext_shared=yes
test "$PHP_SDL3_IMAGE" = "no" && PHP_SDL3_IMAGE=yes
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ext_output" >&5
printf "%s\n" "$ext_output" >&6; }
php_with_sdl3_ttf=no
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for sdl3_ttf support" >&5
printf %s "checking for sdl3_ttf support... " >&6; }
# Check whether --with-sdl3_ttf was given.
if test ${with_sdl3_ttf+y}
then :
withval=$with_sdl3_ttf; PHP_SDL3_TTF=$withval
else case e in #(
e)
PHP_SDL3_TTF=no
test "$PHP_ENABLE_ALL" && PHP_SDL3_TTF=$PHP_ENABLE_ALL
;;
esac
fi
ext_output="yes, shared"
ext_shared=yes
case $PHP_SDL3_TTF in
shared,*)
PHP_SDL3_TTF=$(echo "$PHP_SDL3_TTF"|$SED 's/^shared,//')
;;
shared)
PHP_SDL3_TTF=yes
;;
no)
ext_output=no
ext_shared=no
;;
*)
ext_output=yes
ext_shared=no
;;
esac
ext_output="yes, shared"
ext_shared=yes
test "$PHP_SDL3_TTF" = "no" && PHP_SDL3_TTF=yes
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ext_output" >&5
printf "%s\n" "$ext_output" >&6; }
if test "$PHP_SDL3" != "no"; then
if test -d "$PHP_SDL3"; then
PKG_CONFIG_PATH="$PHP_SDL3/lib/pkgconfig:$PHP_SDL3/share/pkgconfig:$PKG_CONFIG_PATH"
@ -4970,7 +5098,177 @@ printf "%s\n" "yes" >&6; }
fi
fi
SDL_SOURCE_FILES="sdl3.c helper.c"
if test "$PHP_SDL3_IMAGE" != "no"; then
if test -d "$PHP_SDL3_IMAGE"; then
PKG_CONFIG_PATH="$PHP_SDL3_IMAGE/lib/pkgconfig:$PHP_SDL3_IMAGE/share/pkgconfig:$PKG_CONFIG_PATH"
fi
pkg_failed=no
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for sdl3-image >= 3.0.0" >&5
printf %s "checking for sdl3-image >= 3.0.0... " >&6; }
if test -n "$SDL3_IMAGE_CFLAGS"; then
pkg_cv_SDL3_IMAGE_CFLAGS="$SDL3_IMAGE_CFLAGS"
elif test -n "$PKG_CONFIG"; then
if test -n "$PKG_CONFIG" && \
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"sdl3-image >= 3.0.0\""; } >&5
($PKG_CONFIG --exists --print-errors "sdl3-image >= 3.0.0") 2>&5
ac_status=$?
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
pkg_cv_SDL3_IMAGE_CFLAGS=`$PKG_CONFIG --cflags "sdl3-image >= 3.0.0" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
fi
else
pkg_failed=untried
fi
if test -n "$SDL3_IMAGE_LIBS"; then
pkg_cv_SDL3_IMAGE_LIBS="$SDL3_IMAGE_LIBS"
elif test -n "$PKG_CONFIG"; then
if test -n "$PKG_CONFIG" && \
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"sdl3-image >= 3.0.0\""; } >&5
($PKG_CONFIG --exists --print-errors "sdl3-image >= 3.0.0") 2>&5
ac_status=$?
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
pkg_cv_SDL3_IMAGE_LIBS=`$PKG_CONFIG --libs "sdl3-image >= 3.0.0" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
fi
else
pkg_failed=untried
fi
if test $pkg_failed = yes; then
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
_pkg_short_errors_supported=yes
else
_pkg_short_errors_supported=no
fi
if test $_pkg_short_errors_supported = yes; then
SDL3_IMAGE_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "sdl3-image >= 3.0.0" 2>&1`
else
SDL3_IMAGE_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "sdl3-image >= 3.0.0" 2>&1`
fi
# Put the nasty error message in config.log where it belongs
echo "$SDL3_IMAGE_PKG_ERRORS" >&5
as_fn_error $? "SDL3_image not found. Please check your installation or use --with-sdl3-image=/path/to/sdl3_image" "$LINENO" 5
elif test $pkg_failed = untried; then
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
as_fn_error $? "SDL3_image not found. Please check your installation or use --with-sdl3-image=/path/to/sdl3_image" "$LINENO" 5
else
SDL3_IMAGE_CFLAGS=$pkg_cv_SDL3_IMAGE_CFLAGS
SDL3_IMAGE_LIBS=$pkg_cv_SDL3_IMAGE_LIBS
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; }
CFLAGS="$CFLAGS $SDL3_IMAGE_CFLAGS"
LDFLAGS="$LDFLAGS $SDL3_IMAGE_LIBS"
fi
fi
if test "$PHP_SDL3_TTF" != "no"; then
if test -d "$PHP_SDL3_TTF"; then
PKG_CONFIG_PATH="$PHP_SDL3_TTF/lib/pkgconfig:$PHP_SDL3_TTF/share/pkgconfig:$PKG_CONFIG_PATH"
fi
pkg_failed=no
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for sdl3-ttf >= 3.0.0" >&5
printf %s "checking for sdl3-ttf >= 3.0.0... " >&6; }
if test -n "$SDL3_TTF_CFLAGS"; then
pkg_cv_SDL3_TTF_CFLAGS="$SDL3_TTF_CFLAGS"
elif test -n "$PKG_CONFIG"; then
if test -n "$PKG_CONFIG" && \
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"sdl3-ttf >= 3.0.0\""; } >&5
($PKG_CONFIG --exists --print-errors "sdl3-ttf >= 3.0.0") 2>&5
ac_status=$?
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
pkg_cv_SDL3_TTF_CFLAGS=`$PKG_CONFIG --cflags "sdl3-ttf >= 3.0.0" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
fi
else
pkg_failed=untried
fi
if test -n "$SDL3_TTF_LIBS"; then
pkg_cv_SDL3_TTF_LIBS="$SDL3_TTF_LIBS"
elif test -n "$PKG_CONFIG"; then
if test -n "$PKG_CONFIG" && \
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"sdl3-ttf >= 3.0.0\""; } >&5
($PKG_CONFIG --exists --print-errors "sdl3-ttf >= 3.0.0") 2>&5
ac_status=$?
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
pkg_cv_SDL3_TTF_LIBS=`$PKG_CONFIG --libs "sdl3-ttf >= 3.0.0" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
fi
else
pkg_failed=untried
fi
if test $pkg_failed = yes; then
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
_pkg_short_errors_supported=yes
else
_pkg_short_errors_supported=no
fi
if test $_pkg_short_errors_supported = yes; then
SDL3_TTF_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "sdl3-ttf >= 3.0.0" 2>&1`
else
SDL3_TTF_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "sdl3-ttf >= 3.0.0" 2>&1`
fi
# Put the nasty error message in config.log where it belongs
echo "$SDL3_TTF_PKG_ERRORS" >&5
as_fn_error $? "SDL3_ttf not found. Please check your installation or use --with-sdl3-ttf=/path/to/sdl3_ttf" "$LINENO" 5
elif test $pkg_failed = untried; then
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
as_fn_error $? "SDL3_ttf not found. Please check your installation or use --with-sdl3-ttf=/path/to/sdl3_ttf" "$LINENO" 5
else
SDL3_TTF_CFLAGS=$pkg_cv_SDL3_TTF_CFLAGS
SDL3_TTF_LIBS=$pkg_cv_SDL3_TTF_LIBS
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; }
CFLAGS="$CFLAGS $SDL3_TTF_CFLAGS"
LDFLAGS="$LDFLAGS $SDL3_TTF_LIBS"
fi
fi
SDL_SOURCE_FILES="sdl3.c helper.c sdl3_image.c sdl3_ttf.c sdl3_events.c"
ext_builddir=.
@ -5825,7 +6123,7 @@ ia64-*-hpux*)
;;
*-*-irix6*)
# Find out which ABI we are using.
echo '#line 5828 "configure"' > conftest.$ac_ext
echo '#line 6126 "configure"' > conftest.$ac_ext
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
(eval $ac_compile) 2>&5
ac_status=$?
@ -7204,7 +7502,7 @@ else case e in #(
LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym"
cat > conftest.$ac_ext <<EOF
#line 7207 "configure"
#line 7505 "configure"
#include "confdefs.h"
int main(void) {
; return 0; }
@ -7366,11 +7664,11 @@ else case e in #(
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"configure:7369: $lt_compile\"" >&5)
(eval echo "\"configure:7667: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
echo "configure:7373: \$? = $ac_status" >&5
echo "configure:7671: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
@ -7666,11 +7964,11 @@ else case e in #(
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"configure:7669: $lt_compile\"" >&5)
(eval echo "\"configure:7967: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
echo "configure:7673: \$? = $ac_status" >&5
echo "configure:7971: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
@ -7774,11 +8072,11 @@ else case e in #(
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"configure:7777: $lt_compile\"" >&5)
(eval echo "\"configure:8075: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
echo "configure:7781: \$? = $ac_status" >&5
echo "configure:8079: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
@ -8239,7 +8537,7 @@ _LT_EOF
# Determine the default libpath from the value encoded in an empty executable.
cat > conftest.$ac_ext <<EOF
#line 8242 "configure"
#line 8540 "configure"
#include "confdefs.h"
int main(void) {
; return 0; }
@ -8281,7 +8579,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.
cat > conftest.$ac_ext <<EOF
#line 8284 "configure"
#line 8582 "configure"
#include "confdefs.h"
int main(void) {
; return 0; }
@ -9862,7 +10160,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<EOF
#line 9865 "configure"
#line 10163 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@ -9961,7 +10259,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<EOF
#line 9964 "configure"
#line 10262 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@ -11030,7 +11328,7 @@ case $host_os in
# Determine the default libpath from the value encoded in an empty executable.
cat > conftest.$ac_ext <<EOF
#line 11033 "configure"
#line 11331 "configure"
#include "confdefs.h"
int main(void) {
; return 0; }
@ -11073,7 +11371,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.
cat > conftest.$ac_ext <<EOF
#line 11076 "configure"
#line 11374 "configure"
#include "confdefs.h"
int main(void) {
; return 0; }
@ -12326,11 +12624,11 @@ else case e in #(
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"configure:12329: $lt_compile\"" >&5)
(eval echo "\"configure:12627: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
echo "configure:12333: \$? = $ac_status" >&5
echo "configure:12631: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
@ -12434,11 +12732,11 @@ else case e in #(
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"configure:12437: $lt_compile\"" >&5)
(eval echo "\"configure:12735: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
echo "configure:12441: \$? = $ac_status" >&5
echo "configure:12739: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized

View File

@ -33,10 +33,10 @@ sdl_render_clear($renderer);
// Ein grünes, abgerundetes Rechteck mit verschiedenen Radien zeichnen
// Radien: Oben-Links, Oben-Rechts, Unten-Rechts, Unten-Links
$rad_tl = 20;
$rad_tr = 20;
$rad_br = 20;
$rad_bl = 20;
$rad_tl = 40;
$rad_tr = 10;
$rad_br = 40;
$rad_bl = 10;
sdl_rounded_box_ex($renderer, 100, 100, 200, 200, $rad_tl, $rad_tr, $rad_br, $rad_bl, 30, 200, 70, 255);
// Alles auf dem Bildschirm anzeigen

View File

@ -2,35 +2,34 @@
#define PHP_SDL3_HELPER
#include <SDL3/SDL.h>
#include <SDL3_gfx/SDL3_gfxPrimitives.h>
#include "math.h"
static void draw_hline(SDL_Renderer *renderer, int x1, int x2, int y) {
if (x2 < x1) return;
SDL_RenderLine(renderer, x1, y, x2, y);
}
// Zeichnet einen gefüllten Viertel-Kreis (filled quarter circle).
// Zeichnet einen gefüllten Viertel-Kreis mit Anti-Aliasing (filled quarter circle).
// quadrant: 0 = top-left, 1 = top-right, 2 = bottom-right, 3 = bottom-left
static void filled_quarter_circle(SDL_Renderer *renderer, int cx, int cy, int r, int quadrant) {
if (r <= 0) return;
int r2 = r * r;
for (int dy = 0; dy <= r; ++dy) {
// dy ist vertikale Distanz vom Kreiszentrum (0..r)
int dx = (int)floor(sqrt((double)r2 - (double)dy * dy));
// Get current draw color
Uint8 r_col, g_col, b_col, a_col;
SDL_GetRenderDrawColor(renderer, &r_col, &g_col, &b_col, &a_col);
// Use SDL_gfx filledCircle with proper quadrant rendering
// We'll draw the arc using the primitives library
switch (quadrant) {
case 0: // top-left: y = cy - dy, x in [cx - dx, cx]
draw_hline(renderer, cx - dx, cx, cy - dy);
case 0: // top-left
filledPieRGBA(renderer, cx, cy, r, 180, 270, r_col, g_col, b_col, a_col);
break;
case 1: // top-right: y = cy - dy, x in [cx, cx + dx]
draw_hline(renderer, cx, cx + dx, cy - dy);
case 1: // top-right
filledPieRGBA(renderer, cx, cy, r, 270, 360, r_col, g_col, b_col, a_col);
break;
case 2: // bottom-right: y = cy + dy, x in [cx, cx + dx]
draw_hline(renderer, cx, cx + dx, cy + dy);
case 2: // bottom-right
filledPieRGBA(renderer, cx, cy, r, 0, 90, r_col, g_col, b_col, a_col);
break;
case 3: // bottom-left: y = cy + dy, x in [cx - dx, cx]
draw_hline(renderer, cx - dx, cx, cy + dy);
case 3: // bottom-left
filledPieRGBA(renderer, cx, cy, r, 90, 180, r_col, g_col, b_col, a_col);
break;
}
}
}
#endif

View File

@ -85,7 +85,7 @@ AR_FLAGS="cru"
LTCC="cc"
# LTCC compiler flags.
LTCFLAGS="-g -O2 -I/usr/include/pipewire-0.3 -I/usr/include/spa-0.2 -D_REENTRANT -I/usr/include/libdrm -I/usr/include/libdecor-0 -I/usr/local/include -I/usr/include/pipewire-0.3 -I/usr/include/spa-0.2 -D_REENTRANT -I/usr/include/libdrm -I/usr/include/libdecor-0 "
LTCFLAGS="-g -O2 -I/usr/include/pipewire-0.3 -I/usr/include/spa-0.2 -D_REENTRANT -I/usr/include/libdrm -I/usr/include/libdecor-0 -I/usr/local/include -I/usr/include/pipewire-0.3 -I/usr/include/spa-0.2 -D_REENTRANT -I/usr/include/libdrm -I/usr/include/libdecor-0 -I/usr/include/pipewire-0.3 -I/usr/include/spa-0.2 -D_REENTRANT -I/usr/include/libdrm -I/usr/include/libdecor-0 -I/usr/include/libpng16 -I/usr/include/x86_64-linux-gnu -I/usr/include/webp -I/usr/include/pipewire-0.3 -I/usr/include/spa-0.2 -D_REENTRANT -I/usr/include/libdrm -I/usr/include/libdecor-0 -I/usr/include/harfbuzz -I/usr/include/freetype2 -I/usr/include/libpng16 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/sysprof-6 -pthread "
# A language-specific compiler.
CC="cc"

View File

@ -14,7 +14,7 @@ library_names='sdl3.so sdl3.so sdl3.so'
old_library=''
# Libraries that this one depends upon.
dependency_libs=' -L/usr/local/lib -lSDL3_gfx -lSDL3'
dependency_libs=' -L/usr/local/lib -lSDL3_gfx -lSDL3_image -lSDL3_ttf -lSDL3'
# Version information for sdl3.
current=0

Binary file not shown.

View File

@ -0,0 +1,243 @@
<?php
if (!extension_loaded('sdl3')) {
echo "Die Erweiterung 'sdl3' ist nicht geladen.\n";
exit(1);
}
// SDL_INIT_VIDEO
const SDL_INIT_VIDEO = 0x00000020;
// Initialisierung
if (!sdl_init(SDL_INIT_VIDEO)) {
echo 'Fehler bei der Initialisierung von SDL: ' . sdl_get_error() . "\n";
exit(1);
}
// TTF initialisieren
if (!ttf_init()) {
echo 'Fehler bei der Initialisierung von TTF: ' . sdl_get_error() . "\n";
sdl_quit();
exit(1);
}
// Font laden
$font = ttf_open_font('/usr/share/fonts/truetype/ubuntu/Ubuntu[wdth,wght].ttf', 24);
if (!$font) {
echo 'Fehler beim Laden der Schriftart: ' . sdl_get_error() . "\n";
sdl_quit();
exit(1);
}
// Hauptfenster erstellen
$main_window = sdl_create_window('Hauptfenster', 640, 480);
if (!$main_window) {
echo 'Fehler beim Erstellen des Hauptfensters: ' . sdl_get_error() . "\n";
sdl_quit();
exit(1);
}
$main_renderer = sdl_create_renderer($main_window);
if (!$main_renderer) {
echo 'Fehler beim Erstellen des Hauptrenderers: ' . sdl_get_error() . "\n";
sdl_quit();
exit(1);
}
// Window IDs speichern
$main_window_id = sdl_get_window_id($main_window);
// Text-Texturen erstellen
function create_text_texture($renderer, $font, $text) {
$surface = ttf_render_text_solid($font, $text, 255, 255, 255);
if (!$surface) return false;
$texture = sdl_create_texture_from_surface($renderer, $surface);
return $texture;
}
// Hauptfenster Text-Texture
$main_text_texture = create_text_texture($main_renderer, $font, 'Fenster öffnen');
$main_text_size = ttf_size_text($font, 'Fenster öffnen');
// Sekundäres Fenster (initial NULL)
$second_window = null;
$second_window_id = null;
$second_renderer = null;
$second_text_texture = null;
$second_text_size = null;
// Hilfsfunktion: Prüft ob Punkt in Rechteck ist
function point_in_rect($x, $y, $rect) {
return $x >= $rect['x'] &&
$x <= $rect['x'] + $rect['w'] &&
$y >= $rect['y'] &&
$y <= $rect['y'] + $rect['h'];
}
// Zeichnet einen Button
function draw_button($renderer, $button, $text_texture, $text_size) {
// Button-Hintergrund (grün wenn hovered, blau sonst)
if ($button['hovered']) {
sdl_rounded_box($renderer,
$button['x'], $button['y'],
$button['x'] + $button['w'], $button['y'] + $button['h'],
15,
60, 180, 60, 255
);
} else {
sdl_rounded_box($renderer,
$button['x'], $button['y'],
$button['x'] + $button['w'], $button['y'] + $button['h'],
15,
70, 130, 180, 255
);
}
// Text zentriert auf Button rendern
$text_x = $button['x'] + ($button['w'] - $text_size['w']) / 2;
$text_y = $button['y'] + ($button['h'] - $text_size['h']) / 2;
sdl_render_texture($renderer, $text_texture, [
'x' => (int)$text_x,
'y' => (int)$text_y,
'w' => $text_size['w'],
'h' => $text_size['h']
]);
}
// Button-Eigenschaften Hauptfenster
$main_button = [
'x' => 200,
'y' => 200,
'w' => 240,
'h' => 80,
'hovered' => false
];
// Button-Eigenschaften zweites Fenster
$second_button = [
'x' => 100,
'y' => 150,
'w' => 200,
'h' => 80,
'hovered' => false
];
echo "Multi-Window Beispiel läuft!\n";
echo "Hauptfenster: Klicke auf 'Fenster öffnen' um ein zweites Fenster zu öffnen.\n";
echo "Zweites Fenster: Klicke auf 'Schließen' um es zu schließen.\n";
// Hauptschleife
$main_running = true;
$second_running = false;
while ($main_running) {
// Events verarbeiten
while ($event = sdl_poll_event()) {
// Quit-Event schließt alle Fenster
if ($event['type'] === SDL_EVENT_QUIT) {
$main_running = false;
$second_running = false;
}
// Window Close Event - prüfe welches Fenster
if ($event['type'] === SDL_EVENT_WINDOW_CLOSE_REQUESTED) {
if (isset($event['window_id'])) {
if ($event['window_id'] === $main_window_id) {
echo "Hauptfenster schließen-Anfrage\n";
$main_running = false;
$second_running = false;
} elseif ($second_running && $event['window_id'] === $second_window_id) {
echo "Zweites Fenster schließen-Anfrage\n";
$second_running = false;
$second_window = null;
$second_window_id = null;
$second_renderer = null;
$second_text_texture = null;
$second_button['hovered'] = false;
}
}
}
// Maus-Bewegung
if ($event['type'] === SDL_EVENT_MOUSE_MOTION) {
if (isset($event['window_id'])) {
// Hauptfenster
if ($event['window_id'] === $main_window_id) {
$main_button['hovered'] = point_in_rect($event['x'], $event['y'], $main_button);
}
// Zweites Fenster
elseif ($second_running && $event['window_id'] === $second_window_id) {
$second_button['hovered'] = point_in_rect($event['x'], $event['y'], $second_button);
}
}
}
// Maus-Klick
if ($event['type'] === SDL_EVENT_MOUSE_BUTTON_DOWN) {
if ($event['button'] === SDL_BUTTON_LEFT && isset($event['window_id'])) {
// Hauptfenster-Button: Öffne zweites Fenster
if ($event['window_id'] === $main_window_id &&
point_in_rect($event['x'], $event['y'], $main_button)) {
if (!$second_running) {
echo "Öffne zweites Fenster...\n";
$second_window = sdl_create_window('Zweites Fenster', 400, 300);
if ($second_window) {
$second_window_id = sdl_get_window_id($second_window);
$second_renderer = sdl_create_renderer($second_window);
if ($second_renderer) {
$second_text_texture = create_text_texture($second_renderer, $font, 'Schließen');
$second_text_size = ttf_size_text($font, 'Schließen');
$second_running = true;
}
}
}
}
// Zweites Fenster-Button: Schließe zweites Fenster
if ($second_running && $event['window_id'] === $second_window_id &&
point_in_rect($event['x'], $event['y'], $second_button)) {
echo "Schließe zweites Fenster...\n";
$second_running = false;
$second_window = null;
$second_window_id = null;
$second_renderer = null;
$second_text_texture = null;
$second_button['hovered'] = false;
}
}
}
}
// Hauptfenster rendern
sdl_set_render_draw_color($main_renderer, 45, 45, 48, 255);
sdl_render_clear($main_renderer);
draw_button($main_renderer, $main_button, $main_text_texture, $main_text_size);
// Info-Text (weißes Rechteck als Platzhalter)
sdl_set_render_draw_color($main_renderer, 80, 80, 80, 255);
sdl_render_fill_rect($main_renderer, ['x' => 50, 'y' => 50, 'w' => 540, 'h' => 100]);
sdl_render_present($main_renderer);
// Zweites Fenster rendern (wenn offen)
if ($second_running && $second_renderer) {
sdl_set_render_draw_color($second_renderer, 60, 60, 65, 255);
sdl_render_clear($second_renderer);
draw_button($second_renderer, $second_button, $second_text_texture, $second_text_size);
// Info-Text
sdl_set_render_draw_color($second_renderer, 100, 100, 100, 255);
sdl_render_fill_rect($second_renderer, ['x' => 50, 'y' => 50, 'w' => 300, 'h' => 60]);
sdl_render_present($second_renderer);
}
// CPU schonen
sdl_delay(16); // ~60 FPS
}
echo "Programm beendet.\n";
sdl_quit();

View File

@ -6,4 +6,10 @@ extern zend_module_entry sdl3_module_entry;
#define PHP_SDL3_VERSION "0.1.0"
// Resource handles exportieren, damit sie in anderen Modulen verfügbar sind
extern int le_sdl_window;
extern int le_sdl_renderer;
extern int le_sdl_texture;
extern int le_sdl_surface;
#endif

View File

@ -6,13 +6,17 @@
#include "php.h"
#include "php_sdl3.h"
#include "helper.h"
#include "sdl3_image.h"
#include "sdl3_ttf.h"
#include "sdl3_events.h"
#include <SDL3/SDL.h>
#include <SDL3_gfx/SDL3_gfxPrimitives.h>
#include <math.h>
// Resource handles
static int le_sdl_window;
static int le_sdl_renderer;
// Resource handles (nicht static, damit sie in anderen Modulen verfügbar sind)
int le_sdl_window;
int le_sdl_renderer;
int le_sdl_texture;
// Destructor for window resource
static void sdl_window_dtor(zend_resource *rsrc) {
@ -30,9 +34,41 @@ static void sdl_renderer_dtor(zend_resource *rsrc) {
}
}
// Destructor for texture resource
static void sdl_texture_dtor(zend_resource *rsrc) {
SDL_Texture *tex = (SDL_Texture *)rsrc->ptr;
if (tex) {
SDL_DestroyTexture(tex);
}
}
PHP_MINIT_FUNCTION(sdl3) {
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_texture = zend_register_list_destructors_ex(sdl_texture_dtor, NULL, "SDL_Texture", module_number);
// Registriere SDL3_image und SDL3_ttf Resources
sdl3_image_register_resources(module_number);
sdl3_ttf_register_resources(module_number);
// Registriere Event-Konstanten
sdl3_events_register_constants(module_number);
// SDL Init Flags
REGISTER_LONG_CONSTANT("SDL_INIT_VIDEO", SDL_INIT_VIDEO, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDL_INIT_AUDIO", SDL_INIT_AUDIO, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDL_INIT_EVENTS", SDL_INIT_EVENTS, CONST_CS | CONST_PERSISTENT);
// SDL Window Flags
REGISTER_LONG_CONSTANT("SDL_WINDOW_FULLSCREEN", SDL_WINDOW_FULLSCREEN, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDL_WINDOW_OPENGL", SDL_WINDOW_OPENGL, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDL_WINDOW_HIDDEN", SDL_WINDOW_HIDDEN, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDL_WINDOW_BORDERLESS", SDL_WINDOW_BORDERLESS, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDL_WINDOW_RESIZABLE", SDL_WINDOW_RESIZABLE, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDL_WINDOW_MINIMIZED", SDL_WINDOW_MINIMIZED, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDL_WINDOW_MAXIMIZED", SDL_WINDOW_MAXIMIZED, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDL_WINDOW_HIGH_PIXEL_DENSITY", SDL_WINDOW_HIGH_PIXEL_DENSITY, CONST_CS | CONST_PERSISTENT);
return SUCCESS;
}
@ -54,19 +90,60 @@ PHP_FUNCTION(sdl_quit) {
PHP_FUNCTION(sdl_create_window) {
char *title;
size_t title_len;
zend_long w, h;
zend_long w, h, flags = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "sll", &title, &title_len, &w, &h) == FAILURE) {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "sll|l", &title, &title_len, &w, &h, &flags) == FAILURE) {
RETURN_THROWS();
}
SDL_Window *win = SDL_CreateWindow(title, (int)w, (int)h, 0);
SDL_Window *win = SDL_CreateWindow(title, (int)w, (int)h, (SDL_WindowFlags)flags);
if (!win) {
RETURN_FALSE;
}
RETURN_RES(zend_register_resource(win, le_sdl_window));
}
PHP_FUNCTION(sdl_destroy_window) {
zval *win_res;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &win_res) == FAILURE) {
RETURN_THROWS();
}
// This will call the destructor and free the resource
zend_list_close(Z_RES_P(win_res));
RETURN_TRUE;
}
PHP_FUNCTION(sdl_destroy_renderer) {
zval *ren_res;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &ren_res) == FAILURE) {
RETURN_THROWS();
}
// This will call the destructor and free the resource
zend_list_close(Z_RES_P(ren_res));
RETURN_TRUE;
}
PHP_FUNCTION(sdl_get_window_id) {
zval *win_res;
SDL_Window *win;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &win_res) == FAILURE) {
RETURN_THROWS();
}
win = (SDL_Window *)zend_fetch_resource(Z_RES_P(win_res), "SDL_Window", le_sdl_window);
if (!win) {
RETURN_FALSE;
}
SDL_WindowID window_id = SDL_GetWindowID(win);
RETURN_LONG(window_id);
}
PHP_FUNCTION(sdl_create_renderer) {
zval *win_res;
SDL_Window *win;
@ -185,6 +262,79 @@ PHP_FUNCTION(sdl_get_error) {
RETURN_STRING(error);
}
PHP_FUNCTION(sdl_create_texture_from_surface) {
zval *ren_res, *surf_res;
SDL_Renderer *renderer;
SDL_Surface *surface;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr", &ren_res, &surf_res) == FAILURE) {
RETURN_THROWS();
}
renderer = (SDL_Renderer *)zend_fetch_resource(Z_RES_P(ren_res), "SDL_Renderer", le_sdl_renderer);
if (!renderer) {
RETURN_FALSE;
}
surface = (SDL_Surface *)zend_fetch_resource(Z_RES_P(surf_res), "SDL_Surface", le_sdl_surface);
if (!surface) {
RETURN_FALSE;
}
SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, surface);
if (!texture) {
php_error_docref(NULL, E_WARNING, "Failed to create texture: %s", SDL_GetError());
RETURN_FALSE;
}
RETURN_RES(zend_register_resource(texture, le_sdl_texture));
}
PHP_FUNCTION(sdl_render_texture) {
zval *ren_res, *tex_res;
SDL_Renderer *renderer;
SDL_Texture *texture;
zval *dst_arr = NULL;
HashTable *dst_ht;
zval *data;
zend_long x, y, w, h;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr|a", &ren_res, &tex_res, &dst_arr) == FAILURE) {
RETURN_THROWS();
}
renderer = (SDL_Renderer *)zend_fetch_resource(Z_RES_P(ren_res), "SDL_Renderer", le_sdl_renderer);
if (!renderer) {
RETURN_FALSE;
}
texture = (SDL_Texture *)zend_fetch_resource(Z_RES_P(tex_res), "SDL_Texture", le_sdl_texture);
if (!texture) {
RETURN_FALSE;
}
if (dst_arr != NULL) {
// Mit Zielrechteck
dst_ht = Z_ARRVAL_P(dst_arr);
if (((data = zend_hash_str_find(dst_ht, "x", 1)) != NULL && (x = zval_get_long(data), true)) &&
((data = zend_hash_str_find(dst_ht, "y", 1)) != NULL && (y = zval_get_long(data), true)) &&
((data = zend_hash_str_find(dst_ht, "w", 1)) != NULL && (w = zval_get_long(data), true)) &&
((data = zend_hash_str_find(dst_ht, "h", 1)) != NULL && (h = zval_get_long(data), true))) {
SDL_FRect dstrect = {(float)x, (float)y, (float)w, (float)h};
SDL_RenderTexture(renderer, texture, NULL, &dstrect);
RETURN_TRUE;
}
zend_throw_error(NULL, "Invalid destination rectangle. Expected ['x'=>int, 'y'=>int, 'w'=>int, 'h'=>int]");
RETURN_THROWS();
} else {
// Ohne Zielrechteck - volle Größe
SDL_RenderTexture(renderer, texture, NULL, NULL);
RETURN_TRUE;
}
}
PHP_FUNCTION(sdl_rounded_box)
{
zval *ren_res;
@ -237,18 +387,40 @@ PHP_FUNCTION(sdl_rounded_box_ex)
int r_right = (rad_tr > rad_br) ? rad_tr : rad_br;
// 1) center vertical band (zwischen links und rechts Radien), ganze Höhe
SDL_FRect center = { x1 + rad_tl, y1, x2 - x1-rad_tl-rad_br, y2-y1 };
SDL_FRect center = { x1 + r_left, y1, x2 - x1-r_left-r_right, y2-y1 };
if (center.w > 0 && center.h > 0) SDL_RenderFillRect(ren, &center);
// 2) left vertical rectangle (zwischen oberen und unteren Ecken links)
SDL_FRect leftRect = { x1, y1 + rad_tl, rad_bl, y2-rad_tl-rad_bl-y1 };
SDL_FRect leftRect = { x1, y1 + rad_tl, r_left, y2 - y1 - rad_tl - rad_bl };
if (leftRect.w > 0 && leftRect.h > 0) SDL_RenderFillRect(ren, &leftRect);
// 3) right vertical rectangle (zwischen oberen und unteren Ecken rechts)
SDL_FRect rightRect = { x2 - r_right, y1 + rad_tr, r_right, y2 - y1 - rad_tr - rad_br };
if (rightRect.w > 0 && rightRect.h > 0) SDL_RenderFillRect(ren, &rightRect);
// 4) vier gefüllte Viertel-Kreise in den Ecken
// 4) Horizontale Füllrechtecke für Lücken zwischen Ecken und vertikalen Rechtecken
// Oben links: wenn rad_tl < r_left
if (rad_tl < r_left) {
SDL_FRect topLeft = { x1 + rad_tl, y1, r_left - rad_tl, rad_tl };
if (topLeft.w > 0 && topLeft.h > 0) SDL_RenderFillRect(ren, &topLeft);
}
// Oben rechts: wenn rad_tr < r_right
if (rad_tr < r_right) {
SDL_FRect topRight = { x2 - r_right, y1, r_right - rad_tr, rad_tr };
if (topRight.w > 0 && topRight.h > 0) SDL_RenderFillRect(ren, &topRight);
}
// Unten rechts: wenn rad_br < r_right
if (rad_br < r_right) {
SDL_FRect bottomRight = { x2 - r_right, y2 - rad_br, r_right - rad_br, rad_br };
if (bottomRight.w > 0 && bottomRight.h > 0) SDL_RenderFillRect(ren, &bottomRight);
}
// Unten links: wenn rad_bl < r_left
if (rad_bl < r_left) {
SDL_FRect bottomLeft = { x1 + rad_bl, y2 - rad_bl, r_left - rad_bl, rad_bl };
if (bottomLeft.w > 0 && bottomLeft.h > 0) SDL_RenderFillRect(ren, &bottomLeft);
}
// 5) vier gefüllte Viertel-Kreise in den Ecken
if (rad_tl > 0) filled_quarter_circle(ren, x1 + rad_tl, y1 + rad_tl, rad_tl, 0);
if (rad_tr > 0) filled_quarter_circle(ren, x2 - rad_tr, y1 + rad_tr, rad_tr, 1);
if (rad_br > 0) filled_quarter_circle(ren, x2 - rad_br, y2- rad_br, rad_br, 2);
@ -257,6 +429,99 @@ PHP_FUNCTION(sdl_rounded_box_ex)
RETURN_TRUE;
}
PHP_FUNCTION(sdl_set_render_clip_rect) {
zval *ren_res;
SDL_Renderer *ren;
zval *rect_arr = NULL;
HashTable *rect_ht;
zval *data;
zend_long x, y, w, h;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|a!", &ren_res, &rect_arr) == FAILURE) {
RETURN_THROWS();
}
ren = (SDL_Renderer *)zend_fetch_resource(Z_RES_P(ren_res), "SDL_Renderer", le_sdl_renderer);
if (!ren) {
RETURN_FALSE;
}
if (rect_arr == NULL || Z_TYPE_P(rect_arr) == IS_NULL) {
// Disable clipping
SDL_SetRenderClipRect(ren, NULL);
RETURN_TRUE;
}
rect_ht = Z_ARRVAL_P(rect_arr);
if (((data = zend_hash_str_find(rect_ht, "x", 1)) != NULL && (x = zval_get_long(data), true)) &&
((data = zend_hash_str_find(rect_ht, "y", 1)) != NULL && (y = zval_get_long(data), true)) &&
((data = zend_hash_str_find(rect_ht, "w", 1)) != NULL && (w = zval_get_long(data), true)) &&
((data = zend_hash_str_find(rect_ht, "h", 1)) != NULL && (h = zval_get_long(data), true))) {
SDL_Rect rect = {(int)x, (int)y, (int)w, (int)h};
SDL_SetRenderClipRect(ren, &rect);
RETURN_TRUE;
}
zend_throw_error(NULL, "Invalid rectangle array passed to sdl_set_render_clip_rect. Expected ['x'=>int, 'y'=>int, 'w'=>int, 'h'=>int] or null");
RETURN_THROWS();
}
PHP_FUNCTION(sdl_get_window_size) {
zval *win_res;
SDL_Window *win;
int w, h;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &win_res) == FAILURE) {
RETURN_THROWS();
}
win = (SDL_Window *)zend_fetch_resource(Z_RES_P(win_res), "SDL_Window", le_sdl_window);
if (!win) {
RETURN_FALSE;
}
SDL_GetWindowSize(win, &w, &h);
array_init(return_value);
add_index_long(return_value, 0, w);
add_index_long(return_value, 1, h);
}
PHP_FUNCTION(sdl_start_text_input) {
zval *win_res;
SDL_Window *win;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &win_res) == FAILURE) {
RETURN_THROWS();
}
win = (SDL_Window *)zend_fetch_resource(Z_RES_P(win_res), "SDL_Window", le_sdl_window);
if (!win) {
RETURN_FALSE;
}
SDL_StartTextInput(win);
RETURN_TRUE;
}
PHP_FUNCTION(sdl_stop_text_input) {
zval *win_res;
SDL_Window *win;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &win_res) == FAILURE) {
RETURN_THROWS();
}
win = (SDL_Window *)zend_fetch_resource(Z_RES_P(win_res), "SDL_Window", le_sdl_window);
if (!win) {
RETURN_FALSE;
}
SDL_StopTextInput(win);
RETURN_TRUE;
}
ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_init, 0, 0, 1)
ZEND_ARG_INFO(0, flags)
ZEND_END_ARG_INFO()
@ -268,6 +533,19 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_create_window, 0, 0, 3)
ZEND_ARG_INFO(0, title)
ZEND_ARG_INFO(0, w)
ZEND_ARG_INFO(0, h)
ZEND_ARG_INFO(0, flags)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_destroy_window, 0, 0, 1)
ZEND_ARG_INFO(0, window)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_destroy_renderer, 0, 0, 1)
ZEND_ARG_INFO(0, renderer)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_get_window_id, 0, 0, 1)
ZEND_ARG_INFO(0, window)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_create_renderer, 0, 0, 1)
@ -302,6 +580,17 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_get_error, 0, 0, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_create_texture_from_surface, 0, 0, 2)
ZEND_ARG_INFO(0, renderer)
ZEND_ARG_INFO(0, surface)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_render_texture, 0, 0, 2)
ZEND_ARG_INFO(0, renderer)
ZEND_ARG_INFO(0, texture)
ZEND_ARG_INFO(0, dstrect)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_rounded_box, 0, 0, 10)
ZEND_ARG_INFO(0, renderer)
ZEND_ARG_INFO(0, x1)
@ -331,10 +620,31 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_rounded_box_ex, 0, 0, 13)
ZEND_ARG_INFO(0, a)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_set_render_clip_rect, 0, 0, 1)
ZEND_ARG_INFO(0, renderer)
ZEND_ARG_INFO(0, rect)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_get_window_size, 0, 0, 1)
ZEND_ARG_INFO(0, window)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_start_text_input, 0, 0, 1)
ZEND_ARG_INFO(0, window)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_stop_text_input, 0, 0, 1)
ZEND_ARG_INFO(0, window)
ZEND_END_ARG_INFO()
const zend_function_entry sdl3_functions[] = {
// SDL3 Core
PHP_FE(sdl_init, arginfo_sdl_init)
PHP_FE(sdl_quit, arginfo_sdl_quit)
PHP_FE(sdl_create_window, arginfo_sdl_create_window)
PHP_FE(sdl_destroy_window, arginfo_sdl_destroy_window)
PHP_FE(sdl_destroy_renderer, arginfo_sdl_destroy_renderer)
PHP_FE(sdl_get_window_id, arginfo_sdl_get_window_id)
PHP_FE(sdl_create_renderer, arginfo_sdl_create_renderer)
PHP_FE(sdl_set_render_draw_color, arginfo_sdl_set_render_draw_color)
PHP_FE(sdl_render_clear, arginfo_sdl_render_clear)
@ -342,8 +652,33 @@ const zend_function_entry sdl3_functions[] = {
PHP_FE(sdl_render_present, arginfo_sdl_render_present)
PHP_FE(sdl_delay, arginfo_sdl_delay)
PHP_FE(sdl_get_error, arginfo_sdl_get_error)
PHP_FE(sdl_create_texture_from_surface, arginfo_sdl_create_texture_from_surface)
PHP_FE(sdl_render_texture, arginfo_sdl_render_texture)
PHP_FE(sdl_rounded_box, arginfo_sdl_rounded_box)
PHP_FE(sdl_rounded_box_ex, arginfo_sdl_rounded_box_ex)
PHP_FE(sdl_set_render_clip_rect, arginfo_sdl_set_render_clip_rect)
PHP_FE(sdl_get_window_size, arginfo_sdl_get_window_size)
PHP_FE(sdl_start_text_input, arginfo_sdl_start_text_input)
PHP_FE(sdl_stop_text_input, arginfo_sdl_stop_text_input)
// SDL3_image
PHP_FE(img_load, arginfo_img_load)
PHP_FE(img_load_texture, arginfo_img_load_texture)
// SDL3_ttf
PHP_FE(ttf_init, arginfo_ttf_init)
PHP_FE(ttf_open_font, arginfo_ttf_open_font)
PHP_FE(ttf_close_font, arginfo_ttf_close_font)
PHP_FE(ttf_render_text_solid, arginfo_ttf_render_text_solid)
PHP_FE(ttf_render_text_blended, arginfo_ttf_render_text_blended)
PHP_FE(ttf_render_text_shaded, arginfo_ttf_render_text_shaded)
PHP_FE(ttf_size_text, arginfo_ttf_size_text)
// SDL3 Events
PHP_FE(sdl_poll_event, arginfo_sdl_poll_event)
PHP_FE(sdl_wait_event, arginfo_sdl_wait_event)
PHP_FE(sdl_wait_event_timeout, arginfo_sdl_wait_event_timeout)
PHP_FE_END
};

View File

@ -91,4 +91,7 @@ sdl3.lo: /home/thomas/projekte/phpnative/framework/php-sdl3/sdl3.c \
/usr/local/include/php/Zend/zend_constants.h \
/usr/local/include/php/main/php_reentrancy.h \
/home/thomas/projekte/phpnative/framework/php-sdl3/php_sdl3.h \
/home/thomas/projekte/phpnative/framework/php-sdl3/helper.h
/home/thomas/projekte/phpnative/framework/php-sdl3/helper.h \
/home/thomas/projekte/phpnative/framework/php-sdl3/sdl3_image.h \
/home/thomas/projekte/phpnative/framework/php-sdl3/sdl3_ttf.h \
/home/thomas/projekte/phpnative/framework/php-sdl3/sdl3_events.h

View File

@ -14,7 +14,7 @@ library_names='sdl3.so sdl3.so sdl3.so'
old_library=''
# Libraries that this one depends upon.
dependency_libs=' -L/usr/local/lib -lSDL3_gfx -lSDL3'
dependency_libs=' -L/usr/local/lib -lSDL3_gfx -lSDL3_image -lSDL3_ttf -lSDL3'
# Version information for sdl3.
current=0

175
php-sdl3/sdl3_events.c Normal file
View File

@ -0,0 +1,175 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#include "php_sdl3.h"
#include "sdl3_events.h"
#include <SDL3/SDL.h>
// Registriert Event-Konstanten
void sdl3_events_register_constants(int module_number) {
// Event Types
REGISTER_LONG_CONSTANT("SDL_EVENT_QUIT", SDL_EVENT_QUIT, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDL_EVENT_WINDOW_CLOSE_REQUESTED", SDL_EVENT_WINDOW_CLOSE_REQUESTED, CONST_CS | CONST_PERSISTENT);
// Window Events
REGISTER_LONG_CONSTANT("SDL_EVENT_WINDOW_SHOWN", SDL_EVENT_WINDOW_SHOWN, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDL_EVENT_WINDOW_HIDDEN", SDL_EVENT_WINDOW_HIDDEN, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDL_EVENT_WINDOW_EXPOSED", SDL_EVENT_WINDOW_EXPOSED, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDL_EVENT_WINDOW_MOVED", SDL_EVENT_WINDOW_MOVED, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDL_EVENT_WINDOW_RESIZED", SDL_EVENT_WINDOW_RESIZED, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDL_EVENT_WINDOW_MINIMIZED", SDL_EVENT_WINDOW_MINIMIZED, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDL_EVENT_WINDOW_MAXIMIZED", SDL_EVENT_WINDOW_MAXIMIZED, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDL_EVENT_WINDOW_RESTORED", SDL_EVENT_WINDOW_RESTORED, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDL_EVENT_WINDOW_MOUSE_ENTER", SDL_EVENT_WINDOW_MOUSE_ENTER, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDL_EVENT_WINDOW_MOUSE_LEAVE", SDL_EVENT_WINDOW_MOUSE_LEAVE, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDL_EVENT_WINDOW_FOCUS_GAINED", SDL_EVENT_WINDOW_FOCUS_GAINED, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDL_EVENT_WINDOW_FOCUS_LOST", SDL_EVENT_WINDOW_FOCUS_LOST, CONST_CS | CONST_PERSISTENT);
// Keyboard Events
REGISTER_LONG_CONSTANT("SDL_EVENT_KEY_DOWN", SDL_EVENT_KEY_DOWN, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDL_EVENT_KEY_UP", SDL_EVENT_KEY_UP, CONST_CS | CONST_PERSISTENT);
// Mouse Events
REGISTER_LONG_CONSTANT("SDL_EVENT_MOUSE_MOTION", SDL_EVENT_MOUSE_MOTION, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDL_EVENT_MOUSE_BUTTON_DOWN", SDL_EVENT_MOUSE_BUTTON_DOWN, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDL_EVENT_MOUSE_BUTTON_UP", SDL_EVENT_MOUSE_BUTTON_UP, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDL_EVENT_MOUSE_WHEEL", SDL_EVENT_MOUSE_WHEEL, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDL_EVENT_TEXT_INPUT", SDL_EVENT_TEXT_INPUT, CONST_CS | CONST_PERSISTENT);
// Mouse Buttons
REGISTER_LONG_CONSTANT("SDL_BUTTON_LEFT", SDL_BUTTON_LEFT, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDL_BUTTON_MIDDLE", SDL_BUTTON_MIDDLE, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDL_BUTTON_RIGHT", SDL_BUTTON_RIGHT, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDL_BUTTON_X1", SDL_BUTTON_X1, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDL_BUTTON_X2", SDL_BUTTON_X2, CONST_CS | CONST_PERSISTENT);
// Common Keycodes
REGISTER_LONG_CONSTANT("SDLK_RETURN", SDLK_RETURN, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDLK_ESCAPE", SDLK_ESCAPE, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDLK_BACKSPACE", SDLK_BACKSPACE, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDLK_TAB", SDLK_TAB, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDLK_DELETE", SDLK_DELETE, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDLK_LEFT", SDLK_LEFT, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDLK_RIGHT", SDLK_RIGHT, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDLK_UP", SDLK_UP, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SDLK_DOWN", SDLK_DOWN, CONST_CS | CONST_PERSISTENT);
}
PHP_FUNCTION(sdl_poll_event) {
SDL_Event event;
if (!SDL_PollEvent(&event)) {
RETURN_FALSE;
}
// Event als Array zurückgeben
array_init(return_value);
add_assoc_long(return_value, "type", event.type);
add_assoc_long(return_value, "timestamp", event.common.timestamp);
switch (event.type) {
case SDL_EVENT_QUIT:
// Keine zusätzlichen Daten
break;
case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
case SDL_EVENT_WINDOW_SHOWN:
case SDL_EVENT_WINDOW_HIDDEN:
case SDL_EVENT_WINDOW_EXPOSED:
case SDL_EVENT_WINDOW_MOVED:
case SDL_EVENT_WINDOW_RESIZED:
case SDL_EVENT_WINDOW_MINIMIZED:
case SDL_EVENT_WINDOW_MAXIMIZED:
case SDL_EVENT_WINDOW_RESTORED:
case SDL_EVENT_WINDOW_MOUSE_ENTER:
case SDL_EVENT_WINDOW_MOUSE_LEAVE:
case SDL_EVENT_WINDOW_FOCUS_GAINED:
case SDL_EVENT_WINDOW_FOCUS_LOST:
add_assoc_long(return_value, "window_id", event.window.windowID);
if (event.type == SDL_EVENT_WINDOW_MOVED || event.type == SDL_EVENT_WINDOW_RESIZED) {
add_assoc_long(return_value, "data1", event.window.data1);
add_assoc_long(return_value, "data2", event.window.data2);
}
break;
case SDL_EVENT_KEY_DOWN:
case SDL_EVENT_KEY_UP:
add_assoc_long(return_value, "window_id", event.key.windowID);
add_assoc_long(return_value, "scancode", event.key.scancode);
add_assoc_long(return_value, "keycode", event.key.key);
add_assoc_long(return_value, "mod", event.key.mod);
add_assoc_bool(return_value, "repeat", event.key.repeat);
add_assoc_bool(return_value, "down", event.key.down);
break;
case SDL_EVENT_MOUSE_MOTION:
add_assoc_long(return_value, "window_id", event.motion.windowID);
add_assoc_long(return_value, "which", event.motion.which);
add_assoc_double(return_value, "x", event.motion.x);
add_assoc_double(return_value, "y", event.motion.y);
add_assoc_double(return_value, "xrel", event.motion.xrel);
add_assoc_double(return_value, "yrel", event.motion.yrel);
add_assoc_long(return_value, "state", event.motion.state);
break;
case SDL_EVENT_MOUSE_BUTTON_DOWN:
case SDL_EVENT_MOUSE_BUTTON_UP:
add_assoc_long(return_value, "window_id", event.button.windowID);
add_assoc_long(return_value, "which", event.button.which);
add_assoc_long(return_value, "button", event.button.button);
add_assoc_bool(return_value, "down", event.button.down);
add_assoc_long(return_value, "clicks", event.button.clicks);
add_assoc_double(return_value, "x", event.button.x);
add_assoc_double(return_value, "y", event.button.y);
break;
case SDL_EVENT_MOUSE_WHEEL:
add_assoc_long(return_value, "window_id", event.wheel.windowID);
add_assoc_long(return_value, "which", event.wheel.which);
add_assoc_double(return_value, "x", event.wheel.x);
add_assoc_double(return_value, "y", event.wheel.y);
add_assoc_long(return_value, "direction", event.wheel.direction);
add_assoc_double(return_value, "mouse_x", event.wheel.mouse_x);
add_assoc_double(return_value, "mouse_y", event.wheel.mouse_y);
break;
case SDL_EVENT_TEXT_INPUT:
add_assoc_long(return_value, "window_id", event.text.windowID);
add_assoc_string(return_value, "text", event.text.text);
break;
}
}
PHP_FUNCTION(sdl_wait_event) {
SDL_Event event;
if (!SDL_WaitEvent(&event)) {
php_error_docref(NULL, E_WARNING, "SDL_WaitEvent failed: %s", SDL_GetError());
RETURN_FALSE;
}
// Event als Array zurückgeben (gleiche Logik wie poll_event)
array_init(return_value);
add_assoc_long(return_value, "type", event.type);
add_assoc_long(return_value, "timestamp", event.common.timestamp);
}
PHP_FUNCTION(sdl_wait_event_timeout) {
zend_long timeout_ms;
SDL_Event event;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &timeout_ms) == FAILURE) {
RETURN_THROWS();
}
if (!SDL_WaitEventTimeout(&event, (int)timeout_ms)) {
RETURN_FALSE; // Timeout oder Fehler
}
// Event als Array zurückgeben
array_init(return_value);
add_assoc_long(return_value, "type", event.type);
add_assoc_long(return_value, "timestamp", event.common.timestamp);
}

95
php-sdl3/sdl3_events.dep Normal file
View File

@ -0,0 +1,95 @@
sdl3_events.lo: \
/home/thomas/projekte/phpnative/framework/php-sdl3/sdl3_events.c \
/home/thomas/projekte/phpnative/framework/php-sdl3/config.h \
/usr/local/include/php/main/php.h \
/usr/local/include/php/main/php_version.h \
/usr/local/include/php/Zend/zend.h \
/usr/local/include/php/Zend/zend_types.h \
/usr/local/include/php/Zend/zend_portability.h \
/usr/local/include/php/Zend/zend_config.h \
/usr/local/include/php/main/../main/php_config.h \
/usr/local/include/php/Zend/../TSRM/TSRM.h \
/usr/local/include/php/main/php_config.h \
/usr/local/include/php/Zend/zend_range_check.h \
/usr/local/include/php/Zend/zend_long.h \
/usr/local/include/php/Zend/zend_map_ptr.h \
/usr/local/include/php/Zend/zend_errors.h \
/usr/local/include/php/Zend/zend_alloc.h \
/usr/local/include/php/Zend/zend_alloc_sizes.h \
/usr/local/include/php/Zend/zend_llist.h \
/usr/local/include/php/Zend/zend_string.h \
/usr/local/include/php/Zend/zend_gc.h \
/usr/local/include/php/Zend/zend_hrtime.h \
/usr/local/include/php/Zend/zend_hash.h \
/usr/local/include/php/Zend/zend_sort.h \
/usr/local/include/php/Zend/zend_ast.h \
/usr/local/include/php/Zend/zend_variables.h \
/usr/local/include/php/Zend/zend_iterators.h \
/usr/local/include/php/Zend/zend_stream.h \
/usr/local/include/php/Zend/zend_smart_str_public.h \
/usr/local/include/php/Zend/zend_smart_string_public.h \
/usr/local/include/php/Zend/zend_signal.h \
/usr/local/include/php/Zend/zend_max_execution_timer.h \
/usr/local/include/php/Zend/zend_object_handlers.h \
/usr/local/include/php/Zend/zend_property_hooks.h \
/usr/local/include/php/Zend/zend_lazy_objects.h \
/usr/local/include/php/Zend/zend_types.h \
/usr/local/include/php/Zend/zend_operators.h \
/usr/local/include/php/Zend/zend_strtod.h \
/usr/local/include/php/Zend/zend_multiply.h \
/usr/local/include/php/Zend/zend_sort.h \
/usr/local/include/php/main/php_compat.h \
/usr/local/include/php/main/php_config.h \
/usr/local/include/php/Zend/zend_API.h \
/usr/local/include/php/Zend/zend_modules.h \
/usr/local/include/php/Zend/zend.h \
/usr/local/include/php/Zend/zend_compile.h \
/usr/local/include/php/Zend/zend_frameless_function.h \
/usr/local/include/php/Zend/zend_globals.h \
/usr/local/include/php/Zend/zend_globals_macros.h \
/usr/local/include/php/Zend/zend_atomic.h \
/usr/local/include/php/Zend/zend_stack.h \
/usr/local/include/php/Zend/zend_ptr_stack.h \
/usr/local/include/php/Zend/zend_objects.h \
/usr/local/include/php/Zend/zend_objects_API.h \
/usr/local/include/php/Zend/zend_float.h \
/usr/local/include/php/Zend/zend_multibyte.h \
/usr/local/include/php/Zend/zend_arena.h \
/usr/local/include/php/Zend/zend_call_stack.h \
/usr/local/include/php/Zend/zend_vm_opcodes.h \
/usr/local/include/php/Zend/zend_build.h \
/usr/local/include/php/Zend/zend_list.h \
/usr/local/include/php/Zend/zend_execute.h \
/usr/local/include/php/Zend/zend_type_info.h \
/usr/local/include/php/main/build-defs.h \
/usr/local/include/php/Zend/zend_hash.h \
/usr/local/include/php/Zend/zend_alloc.h \
/usr/local/include/php/Zend/zend_stack.h \
/usr/local/include/php/main/snprintf.h \
/usr/local/include/php/main/spprintf.h \
/usr/local/include/php/Zend/zend_smart_str_public.h \
/usr/local/include/php/Zend/zend_smart_string_public.h \
/usr/local/include/php/main/php_syslog.h \
/usr/local/include/php/main/php.h \
/usr/local/include/php/main/php_output.h \
/usr/local/include/php/main/php_streams.h \
/usr/local/include/php/Zend/zend_stream.h \
/usr/local/include/php/main/streams/php_stream_context.h \
/usr/local/include/php/main/streams/php_stream_filter_api.h \
/usr/local/include/php/main/streams/php_stream_transport.h \
/usr/local/include/php/main/streams/php_stream_plain_wrapper.h \
/usr/local/include/php/main/streams/php_stream_glob_wrapper.h \
/usr/local/include/php/main/streams/php_stream_userspace.h \
/usr/local/include/php/main/streams/php_stream_mmap.h \
/usr/local/include/php/main/php_memory_streams.h \
/usr/local/include/php/main/fopen_wrappers.h \
/usr/local/include/php/main/php_globals.h \
/usr/local/include/php/Zend/zend_globals.h \
/usr/local/include/php/main/php_ini.h \
/usr/local/include/php/Zend/zend_ini.h \
/usr/local/include/php/Zend/zend_virtual_cwd.h \
/usr/local/include/php/TSRM/TSRM.h \
/usr/local/include/php/Zend/zend_constants.h \
/usr/local/include/php/main/php_reentrancy.h \
/home/thomas/projekte/phpnative/framework/php-sdl3/php_sdl3.h \
/home/thomas/projekte/phpnative/framework/php-sdl3/sdl3_events.h

26
php-sdl3/sdl3_events.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef PHP_SDL3_EVENTS_H
#define PHP_SDL3_EVENTS_H
#include "php.h"
#include <SDL3/SDL.h>
// PHP Funktionen für SDL3 Events
PHP_FUNCTION(sdl_poll_event);
PHP_FUNCTION(sdl_wait_event);
PHP_FUNCTION(sdl_wait_event_timeout);
// Argument Info
ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_poll_event, 0, 0, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_wait_event, 0, 0, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_wait_event_timeout, 0, 0, 1)
ZEND_ARG_INFO(0, timeout_ms)
ZEND_END_ARG_INFO()
// Funktion zum Registrieren von Event-Konstanten
void sdl3_events_register_constants(int module_number);
#endif

12
php-sdl3/sdl3_events.lo Normal file
View File

@ -0,0 +1,12 @@
# sdl3_events.lo - a libtool object file
# Generated by ltmain.sh - GNU libtool 1.5.26 (1.1220.2.492 2008/01/30 06:40:56)
#
# Please DO NOT delete this file!
# It is necessary for linking the library.
# Name of the PIC object.
pic_object='.libs/sdl3_events.o'
# Name of the non-PIC object.
non_pic_object=none

66
php-sdl3/sdl3_image.c Normal file
View File

@ -0,0 +1,66 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#include "php_sdl3.h"
#include "sdl3_image.h"
#include <SDL3/SDL.h>
#include <SDL3_image/SDL_image.h>
// Resource handles
int le_sdl_surface;
// Destructor für Surface
void sdl_surface_dtor(zend_resource *rsrc) {
SDL_Surface *surface = (SDL_Surface *)rsrc->ptr;
if (surface) {
SDL_DestroySurface(surface);
}
}
// Registriert die Resource-Typen
void sdl3_image_register_resources(int module_number) {
le_sdl_surface = zend_register_list_destructors_ex(sdl_surface_dtor, NULL, "SDL_Surface", module_number);
}
PHP_FUNCTION(img_load) {
char *file;
size_t file_len;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &file, &file_len) == FAILURE) {
RETURN_THROWS();
}
SDL_Surface *surface = IMG_Load(file);
if (!surface) {
php_error_docref(NULL, E_WARNING, "Failed to load image: %s", SDL_GetError());
RETURN_FALSE;
}
RETURN_RES(zend_register_resource(surface, le_sdl_surface));
}
PHP_FUNCTION(img_load_texture) {
zval *ren_res;
SDL_Renderer *renderer;
char *file;
size_t file_len;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs", &ren_res, &file, &file_len) == FAILURE) {
RETURN_THROWS();
}
renderer = (SDL_Renderer *)zend_fetch_resource(Z_RES_P(ren_res), "SDL_Renderer", le_sdl_renderer);
if (!renderer) {
RETURN_FALSE;
}
SDL_Texture *texture = IMG_LoadTexture(renderer, file);
if (!texture) {
php_error_docref(NULL, E_WARNING, "Failed to load texture: %s", SDL_GetError());
RETURN_FALSE;
}
RETURN_RES(zend_register_resource(texture, le_sdl_texture));
}

95
php-sdl3/sdl3_image.dep Normal file
View File

@ -0,0 +1,95 @@
sdl3_image.lo: \
/home/thomas/projekte/phpnative/framework/php-sdl3/sdl3_image.c \
/home/thomas/projekte/phpnative/framework/php-sdl3/config.h \
/usr/local/include/php/main/php.h \
/usr/local/include/php/main/php_version.h \
/usr/local/include/php/Zend/zend.h \
/usr/local/include/php/Zend/zend_types.h \
/usr/local/include/php/Zend/zend_portability.h \
/usr/local/include/php/Zend/zend_config.h \
/usr/local/include/php/main/../main/php_config.h \
/usr/local/include/php/Zend/../TSRM/TSRM.h \
/usr/local/include/php/main/php_config.h \
/usr/local/include/php/Zend/zend_range_check.h \
/usr/local/include/php/Zend/zend_long.h \
/usr/local/include/php/Zend/zend_map_ptr.h \
/usr/local/include/php/Zend/zend_errors.h \
/usr/local/include/php/Zend/zend_alloc.h \
/usr/local/include/php/Zend/zend_alloc_sizes.h \
/usr/local/include/php/Zend/zend_llist.h \
/usr/local/include/php/Zend/zend_string.h \
/usr/local/include/php/Zend/zend_gc.h \
/usr/local/include/php/Zend/zend_hrtime.h \
/usr/local/include/php/Zend/zend_hash.h \
/usr/local/include/php/Zend/zend_sort.h \
/usr/local/include/php/Zend/zend_ast.h \
/usr/local/include/php/Zend/zend_variables.h \
/usr/local/include/php/Zend/zend_iterators.h \
/usr/local/include/php/Zend/zend_stream.h \
/usr/local/include/php/Zend/zend_smart_str_public.h \
/usr/local/include/php/Zend/zend_smart_string_public.h \
/usr/local/include/php/Zend/zend_signal.h \
/usr/local/include/php/Zend/zend_max_execution_timer.h \
/usr/local/include/php/Zend/zend_object_handlers.h \
/usr/local/include/php/Zend/zend_property_hooks.h \
/usr/local/include/php/Zend/zend_lazy_objects.h \
/usr/local/include/php/Zend/zend_types.h \
/usr/local/include/php/Zend/zend_operators.h \
/usr/local/include/php/Zend/zend_strtod.h \
/usr/local/include/php/Zend/zend_multiply.h \
/usr/local/include/php/Zend/zend_sort.h \
/usr/local/include/php/main/php_compat.h \
/usr/local/include/php/main/php_config.h \
/usr/local/include/php/Zend/zend_API.h \
/usr/local/include/php/Zend/zend_modules.h \
/usr/local/include/php/Zend/zend.h \
/usr/local/include/php/Zend/zend_compile.h \
/usr/local/include/php/Zend/zend_frameless_function.h \
/usr/local/include/php/Zend/zend_globals.h \
/usr/local/include/php/Zend/zend_globals_macros.h \
/usr/local/include/php/Zend/zend_atomic.h \
/usr/local/include/php/Zend/zend_stack.h \
/usr/local/include/php/Zend/zend_ptr_stack.h \
/usr/local/include/php/Zend/zend_objects.h \
/usr/local/include/php/Zend/zend_objects_API.h \
/usr/local/include/php/Zend/zend_float.h \
/usr/local/include/php/Zend/zend_multibyte.h \
/usr/local/include/php/Zend/zend_arena.h \
/usr/local/include/php/Zend/zend_call_stack.h \
/usr/local/include/php/Zend/zend_vm_opcodes.h \
/usr/local/include/php/Zend/zend_build.h \
/usr/local/include/php/Zend/zend_list.h \
/usr/local/include/php/Zend/zend_execute.h \
/usr/local/include/php/Zend/zend_type_info.h \
/usr/local/include/php/main/build-defs.h \
/usr/local/include/php/Zend/zend_hash.h \
/usr/local/include/php/Zend/zend_alloc.h \
/usr/local/include/php/Zend/zend_stack.h \
/usr/local/include/php/main/snprintf.h \
/usr/local/include/php/main/spprintf.h \
/usr/local/include/php/Zend/zend_smart_str_public.h \
/usr/local/include/php/Zend/zend_smart_string_public.h \
/usr/local/include/php/main/php_syslog.h \
/usr/local/include/php/main/php.h \
/usr/local/include/php/main/php_output.h \
/usr/local/include/php/main/php_streams.h \
/usr/local/include/php/Zend/zend_stream.h \
/usr/local/include/php/main/streams/php_stream_context.h \
/usr/local/include/php/main/streams/php_stream_filter_api.h \
/usr/local/include/php/main/streams/php_stream_transport.h \
/usr/local/include/php/main/streams/php_stream_plain_wrapper.h \
/usr/local/include/php/main/streams/php_stream_glob_wrapper.h \
/usr/local/include/php/main/streams/php_stream_userspace.h \
/usr/local/include/php/main/streams/php_stream_mmap.h \
/usr/local/include/php/main/php_memory_streams.h \
/usr/local/include/php/main/fopen_wrappers.h \
/usr/local/include/php/main/php_globals.h \
/usr/local/include/php/Zend/zend_globals.h \
/usr/local/include/php/main/php_ini.h \
/usr/local/include/php/Zend/zend_ini.h \
/usr/local/include/php/Zend/zend_virtual_cwd.h \
/usr/local/include/php/TSRM/TSRM.h \
/usr/local/include/php/Zend/zend_constants.h \
/usr/local/include/php/main/php_reentrancy.h \
/home/thomas/projekte/phpnative/framework/php-sdl3/php_sdl3.h \
/home/thomas/projekte/phpnative/framework/php-sdl3/sdl3_image.h

31
php-sdl3/sdl3_image.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef PHP_SDL3_IMAGE_H
#define PHP_SDL3_IMAGE_H
#include "php.h"
#include <SDL3/SDL.h>
#include <SDL3_image/SDL_image.h>
// Resource handle für SDL_Surface
extern int le_sdl_surface;
// Destructor
void sdl_surface_dtor(zend_resource *rsrc);
// PHP Funktionen für SDL3_image
PHP_FUNCTION(img_load);
PHP_FUNCTION(img_load_texture);
// Argument Info
ZEND_BEGIN_ARG_INFO_EX(arginfo_img_load, 0, 0, 1)
ZEND_ARG_INFO(0, file)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_img_load_texture, 0, 0, 2)
ZEND_ARG_INFO(0, renderer)
ZEND_ARG_INFO(0, file)
ZEND_END_ARG_INFO()
// Funktion zum Registrieren der Resource-Typen
void sdl3_image_register_resources(int module_number);
#endif

12
php-sdl3/sdl3_image.lo Normal file
View File

@ -0,0 +1,12 @@
# sdl3_image.lo - a libtool object file
# Generated by ltmain.sh - GNU libtool 1.5.26 (1.1220.2.492 2008/01/30 06:40:56)
#
# Please DO NOT delete this file!
# It is necessary for linking the library.
# Name of the PIC object.
pic_object='.libs/sdl3_image.o'
# Name of the non-PIC object.
non_pic_object=none

170
php-sdl3/sdl3_ttf.c Normal file
View File

@ -0,0 +1,170 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#include "php_sdl3.h"
#include "sdl3_ttf.h"
#include "sdl3_image.h"
#include <SDL3/SDL.h>
#include <SDL3_ttf/SDL_ttf.h>
// Resource handles
int le_ttf_font;
// Destructor für Font
void ttf_font_dtor(zend_resource *rsrc) {
TTF_Font *font = (TTF_Font *)rsrc->ptr;
if (font) {
TTF_CloseFont(font);
}
}
// Registriert die Resource-Typen
void sdl3_ttf_register_resources(int module_number) {
le_ttf_font = zend_register_list_destructors_ex(ttf_font_dtor, NULL, "TTF_Font", module_number);
}
PHP_FUNCTION(ttf_init) {
if (!TTF_Init()) {
php_error_docref(NULL, E_WARNING, "TTF_Init failed: %s", SDL_GetError());
RETURN_FALSE;
}
RETURN_TRUE;
}
PHP_FUNCTION(ttf_open_font) {
char *file;
size_t file_len;
zend_long ptsize;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "sl", &file, &file_len, &ptsize) == FAILURE) {
RETURN_THROWS();
}
TTF_Font *font = TTF_OpenFont(file, (float)ptsize);
if (!font) {
php_error_docref(NULL, E_WARNING, "Failed to open font: %s", SDL_GetError());
RETURN_FALSE;
}
RETURN_RES(zend_register_resource(font, le_ttf_font));
}
PHP_FUNCTION(ttf_close_font) {
zval *font_res;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &font_res) == FAILURE) {
RETURN_THROWS();
}
// Resource wird automatisch durch den Destructor freigegeben
zend_list_close(Z_RES_P(font_res));
RETURN_TRUE;
}
PHP_FUNCTION(ttf_render_text_solid) {
zval *font_res;
TTF_Font *font;
char *text;
size_t text_len;
zend_long r, g, b;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "rslll", &font_res, &text, &text_len, &r, &g, &b) == FAILURE) {
RETURN_THROWS();
}
font = (TTF_Font *)zend_fetch_resource(Z_RES_P(font_res), "TTF_Font", le_ttf_font);
if (!font) {
RETURN_FALSE;
}
SDL_Color color = {(Uint8)r, (Uint8)g, (Uint8)b, 255};
SDL_Surface *surface = TTF_RenderText_Solid(font, text, text_len, color);
if (!surface) {
php_error_docref(NULL, E_WARNING, "Failed to render text: %s", SDL_GetError());
RETURN_FALSE;
}
RETURN_RES(zend_register_resource(surface, le_sdl_surface));
}
PHP_FUNCTION(ttf_render_text_blended) {
zval *font_res;
TTF_Font *font;
char *text;
size_t text_len;
zend_long r, g, b;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "rslll", &font_res, &text, &text_len, &r, &g, &b) == FAILURE) {
RETURN_THROWS();
}
font = (TTF_Font *)zend_fetch_resource(Z_RES_P(font_res), "TTF_Font", le_ttf_font);
if (!font) {
RETURN_FALSE;
}
SDL_Color color = {(Uint8)r, (Uint8)g, (Uint8)b, 255};
SDL_Surface *surface = TTF_RenderText_Blended(font, text, text_len, color);
if (!surface) {
php_error_docref(NULL, E_WARNING, "Failed to render text: %s", SDL_GetError());
RETURN_FALSE;
}
RETURN_RES(zend_register_resource(surface, le_sdl_surface));
}
PHP_FUNCTION(ttf_render_text_shaded) {
zval *font_res;
TTF_Font *font;
char *text;
size_t text_len;
zend_long fg_r, fg_g, fg_b, bg_r, bg_g, bg_b;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "rsllllll", &font_res, &text, &text_len,
&fg_r, &fg_g, &fg_b, &bg_r, &bg_g, &bg_b) == FAILURE) {
RETURN_THROWS();
}
font = (TTF_Font *)zend_fetch_resource(Z_RES_P(font_res), "TTF_Font", le_ttf_font);
if (!font) {
RETURN_FALSE;
}
SDL_Color fg_color = {(Uint8)fg_r, (Uint8)fg_g, (Uint8)fg_b, 255};
SDL_Color bg_color = {(Uint8)bg_r, (Uint8)bg_g, (Uint8)bg_b, 255};
SDL_Surface *surface = TTF_RenderText_Shaded(font, text, text_len, fg_color, bg_color);
if (!surface) {
php_error_docref(NULL, E_WARNING, "Failed to render text: %s", SDL_GetError());
RETURN_FALSE;
}
RETURN_RES(zend_register_resource(surface, le_sdl_surface));
}
PHP_FUNCTION(ttf_size_text) {
zval *font_res;
TTF_Font *font;
char *text;
size_t text_len;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs", &font_res, &text, &text_len) == FAILURE) {
RETURN_THROWS();
}
font = (TTF_Font *)zend_fetch_resource(Z_RES_P(font_res), "TTF_Font", le_ttf_font);
if (!font) {
RETURN_FALSE;
}
int w, h;
if (!TTF_GetStringSize(font, text, text_len, &w, &h)) {
php_error_docref(NULL, E_WARNING, "Failed to get text size: %s", SDL_GetError());
RETURN_FALSE;
}
array_init(return_value);
add_assoc_long(return_value, "w", w);
add_assoc_long(return_value, "h", h);
}

96
php-sdl3/sdl3_ttf.dep Normal file
View File

@ -0,0 +1,96 @@
sdl3_ttf.lo: \
/home/thomas/projekte/phpnative/framework/php-sdl3/sdl3_ttf.c \
/home/thomas/projekte/phpnative/framework/php-sdl3/config.h \
/usr/local/include/php/main/php.h \
/usr/local/include/php/main/php_version.h \
/usr/local/include/php/Zend/zend.h \
/usr/local/include/php/Zend/zend_types.h \
/usr/local/include/php/Zend/zend_portability.h \
/usr/local/include/php/Zend/zend_config.h \
/usr/local/include/php/main/../main/php_config.h \
/usr/local/include/php/Zend/../TSRM/TSRM.h \
/usr/local/include/php/main/php_config.h \
/usr/local/include/php/Zend/zend_range_check.h \
/usr/local/include/php/Zend/zend_long.h \
/usr/local/include/php/Zend/zend_map_ptr.h \
/usr/local/include/php/Zend/zend_errors.h \
/usr/local/include/php/Zend/zend_alloc.h \
/usr/local/include/php/Zend/zend_alloc_sizes.h \
/usr/local/include/php/Zend/zend_llist.h \
/usr/local/include/php/Zend/zend_string.h \
/usr/local/include/php/Zend/zend_gc.h \
/usr/local/include/php/Zend/zend_hrtime.h \
/usr/local/include/php/Zend/zend_hash.h \
/usr/local/include/php/Zend/zend_sort.h \
/usr/local/include/php/Zend/zend_ast.h \
/usr/local/include/php/Zend/zend_variables.h \
/usr/local/include/php/Zend/zend_iterators.h \
/usr/local/include/php/Zend/zend_stream.h \
/usr/local/include/php/Zend/zend_smart_str_public.h \
/usr/local/include/php/Zend/zend_smart_string_public.h \
/usr/local/include/php/Zend/zend_signal.h \
/usr/local/include/php/Zend/zend_max_execution_timer.h \
/usr/local/include/php/Zend/zend_object_handlers.h \
/usr/local/include/php/Zend/zend_property_hooks.h \
/usr/local/include/php/Zend/zend_lazy_objects.h \
/usr/local/include/php/Zend/zend_types.h \
/usr/local/include/php/Zend/zend_operators.h \
/usr/local/include/php/Zend/zend_strtod.h \
/usr/local/include/php/Zend/zend_multiply.h \
/usr/local/include/php/Zend/zend_sort.h \
/usr/local/include/php/main/php_compat.h \
/usr/local/include/php/main/php_config.h \
/usr/local/include/php/Zend/zend_API.h \
/usr/local/include/php/Zend/zend_modules.h \
/usr/local/include/php/Zend/zend.h \
/usr/local/include/php/Zend/zend_compile.h \
/usr/local/include/php/Zend/zend_frameless_function.h \
/usr/local/include/php/Zend/zend_globals.h \
/usr/local/include/php/Zend/zend_globals_macros.h \
/usr/local/include/php/Zend/zend_atomic.h \
/usr/local/include/php/Zend/zend_stack.h \
/usr/local/include/php/Zend/zend_ptr_stack.h \
/usr/local/include/php/Zend/zend_objects.h \
/usr/local/include/php/Zend/zend_objects_API.h \
/usr/local/include/php/Zend/zend_float.h \
/usr/local/include/php/Zend/zend_multibyte.h \
/usr/local/include/php/Zend/zend_arena.h \
/usr/local/include/php/Zend/zend_call_stack.h \
/usr/local/include/php/Zend/zend_vm_opcodes.h \
/usr/local/include/php/Zend/zend_build.h \
/usr/local/include/php/Zend/zend_list.h \
/usr/local/include/php/Zend/zend_execute.h \
/usr/local/include/php/Zend/zend_type_info.h \
/usr/local/include/php/main/build-defs.h \
/usr/local/include/php/Zend/zend_hash.h \
/usr/local/include/php/Zend/zend_alloc.h \
/usr/local/include/php/Zend/zend_stack.h \
/usr/local/include/php/main/snprintf.h \
/usr/local/include/php/main/spprintf.h \
/usr/local/include/php/Zend/zend_smart_str_public.h \
/usr/local/include/php/Zend/zend_smart_string_public.h \
/usr/local/include/php/main/php_syslog.h \
/usr/local/include/php/main/php.h \
/usr/local/include/php/main/php_output.h \
/usr/local/include/php/main/php_streams.h \
/usr/local/include/php/Zend/zend_stream.h \
/usr/local/include/php/main/streams/php_stream_context.h \
/usr/local/include/php/main/streams/php_stream_filter_api.h \
/usr/local/include/php/main/streams/php_stream_transport.h \
/usr/local/include/php/main/streams/php_stream_plain_wrapper.h \
/usr/local/include/php/main/streams/php_stream_glob_wrapper.h \
/usr/local/include/php/main/streams/php_stream_userspace.h \
/usr/local/include/php/main/streams/php_stream_mmap.h \
/usr/local/include/php/main/php_memory_streams.h \
/usr/local/include/php/main/fopen_wrappers.h \
/usr/local/include/php/main/php_globals.h \
/usr/local/include/php/Zend/zend_globals.h \
/usr/local/include/php/main/php_ini.h \
/usr/local/include/php/Zend/zend_ini.h \
/usr/local/include/php/Zend/zend_virtual_cwd.h \
/usr/local/include/php/TSRM/TSRM.h \
/usr/local/include/php/Zend/zend_constants.h \
/usr/local/include/php/main/php_reentrancy.h \
/home/thomas/projekte/phpnative/framework/php-sdl3/php_sdl3.h \
/home/thomas/projekte/phpnative/framework/php-sdl3/sdl3_ttf.h \
/home/thomas/projekte/phpnative/framework/php-sdl3/sdl3_image.h

71
php-sdl3/sdl3_ttf.h Normal file
View File

@ -0,0 +1,71 @@
#ifndef PHP_SDL3_TTF_H
#define PHP_SDL3_TTF_H
#include "php.h"
#include <SDL3/SDL.h>
#include <SDL3_ttf/SDL_ttf.h>
// Resource handle für TTF_Font
extern int le_ttf_font;
// Destructor
void ttf_font_dtor(zend_resource *rsrc);
// PHP Funktionen für SDL3_ttf
PHP_FUNCTION(ttf_init);
PHP_FUNCTION(ttf_open_font);
PHP_FUNCTION(ttf_close_font);
PHP_FUNCTION(ttf_render_text_solid);
PHP_FUNCTION(ttf_render_text_blended);
PHP_FUNCTION(ttf_render_text_shaded);
PHP_FUNCTION(ttf_size_text);
// Argument Info
ZEND_BEGIN_ARG_INFO_EX(arginfo_ttf_init, 0, 0, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_ttf_open_font, 0, 0, 2)
ZEND_ARG_INFO(0, file)
ZEND_ARG_INFO(0, ptsize)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_ttf_close_font, 0, 0, 1)
ZEND_ARG_INFO(0, font)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_ttf_render_text_solid, 0, 0, 5)
ZEND_ARG_INFO(0, font)
ZEND_ARG_INFO(0, text)
ZEND_ARG_INFO(0, r)
ZEND_ARG_INFO(0, g)
ZEND_ARG_INFO(0, b)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_ttf_render_text_blended, 0, 0, 5)
ZEND_ARG_INFO(0, font)
ZEND_ARG_INFO(0, text)
ZEND_ARG_INFO(0, r)
ZEND_ARG_INFO(0, g)
ZEND_ARG_INFO(0, b)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_ttf_render_text_shaded, 0, 0, 8)
ZEND_ARG_INFO(0, font)
ZEND_ARG_INFO(0, text)
ZEND_ARG_INFO(0, fg_r)
ZEND_ARG_INFO(0, fg_g)
ZEND_ARG_INFO(0, fg_b)
ZEND_ARG_INFO(0, bg_r)
ZEND_ARG_INFO(0, bg_g)
ZEND_ARG_INFO(0, bg_b)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_ttf_size_text, 0, 0, 2)
ZEND_ARG_INFO(0, font)
ZEND_ARG_INFO(0, text)
ZEND_END_ARG_INFO()
// Funktion zum Registrieren der Resource-Typen
void sdl3_ttf_register_resources(int module_number);
#endif

12
php-sdl3/sdl3_ttf.lo Normal file
View File

@ -0,0 +1,12 @@
# sdl3_ttf.lo - a libtool object file
# Generated by ltmain.sh - GNU libtool 1.5.26 (1.1220.2.492 2008/01/30 06:40:56)
#
# Please DO NOT delete this file!
# It is necessary for linking the library.
# Name of the PIC object.
pic_object='.libs/sdl3_ttf.o'
# Name of the non-PIC object.
non_pic_object=none

View File

@ -0,0 +1,35 @@
<?php
if (!extension_loaded('sdl3')) {
echo "Die Erweiterung 'sdl3' ist nicht geladen.\n";
exit(1);
}
echo "SDL3 Extension erfolgreich geladen!\n\n";
echo "Verfügbare Funktionen:\n";
echo "======================\n\n";
echo "SDL3 Core:\n";
$core_functions = ['sdl_init', 'sdl_quit', 'sdl_create_window', 'sdl_create_renderer',
'sdl_set_render_draw_color', 'sdl_render_clear', 'sdl_render_fill_rect',
'sdl_render_present', 'sdl_delay', 'sdl_get_error', 'sdl_rounded_box',
'sdl_rounded_box_ex'];
foreach ($core_functions as $func) {
echo " - $func: " . (function_exists($func) ? "" : "") . "\n";
}
echo "\nSDL3_image:\n";
$image_functions = ['img_load', 'img_load_texture'];
foreach ($image_functions as $func) {
echo " - $func: " . (function_exists($func) ? "" : "") . "\n";
}
echo "\nSDL3_ttf:\n";
$ttf_functions = ['ttf_init', 'ttf_open_font', 'ttf_close_font', 'ttf_render_text_solid',
'ttf_render_text_blended', 'ttf_render_text_shaded', 'ttf_size_text'];
foreach ($ttf_functions as $func) {
echo " - $func: " . (function_exists($func) ? "" : "") . "\n";
}
echo "\nAlle Funktionen wurden erfolgreich registriert!\n";

View File

@ -12,12 +12,13 @@ class Application
protected array $windows = [];
protected int $nextWindowId = 0;
protected bool $running = true;
protected array $closedWindowIds = []; // Track recently closed window IDs
public function __construct()
{
self::$instance = $this;
rgfw_setQueueEvents(true);
// SDL3 uses event queue by default, no need to enable it
}
/**
@ -47,6 +48,18 @@ class Application
return $window;
}
/**
* Add an existing window to the application
*
* @param Window $window The window to add
* @return void
*/
public function addWindow(Window $window): void
{
$windowId = $this->nextWindowId++;
$this->windows[$windowId] = $window;
}
/**
* Get all windows
*/
@ -75,12 +88,52 @@ class Application
echo 'Layoutout: ' . PHP_EOL;
}
}
// Poll events globally for all windows (event queue mode, if available)
rgfw_pollEvents();
// SDL3: Poll all events globally and distribute to the correct windows
$events = [];
while ($event = sdl_poll_event()) {
$events[] = $event;
}
// Handle events for all windows (now that layout is done)
// Distribute events to windows based on window_id
foreach ($events as $event) {
$eventWindowId = $event['window_id'] ?? null;
// Debug event distribution
if (defined('DEBUG_EVENTS') && DEBUG_EVENTS && $eventWindowId !== null) {
$eventType = $event['type'];
error_log("[Application] Distributing event type={$eventType} to window_id={$eventWindowId}");
}
// Global events (like QUIT) go to all windows
if ($eventWindowId === null) {
foreach ($this->windows as $windowId => $window) {
$window->handleEvents();
$window->processEvent($event);
}
} else {
// Window-specific events go to the correct window
$delivered = false;
foreach ($this->windows as $windowId => $window) {
if ($window->getWindowId() === $eventWindowId) {
$window->processEvent($event);
$delivered = true;
break;
}
}
if (!$delivered) {
// Check if this is a recently closed window (expected)
if (!in_array($eventWindowId, $this->closedWindowIds)) {
if (defined('DEBUG_EVENTS') && DEBUG_EVENTS) {
error_log("[Application] WARNING: Event for window_id={$eventWindowId} could not be delivered (window not found)");
}
} else {
// This is a recently closed window, events are expected
if (defined('DEBUG_EVENTS') && DEBUG_EVENTS) {
error_log("[Application] Ignoring event for recently closed window_id={$eventWindowId}");
}
}
}
}
}
// Update async tasks (global)
@ -98,13 +151,25 @@ class Application
// Remove closed windows
foreach ($this->windows as $windowId => $window) {
if ($window->shouldClose()) {
$window->cleanup(); // Frees the TextRenderer to prevent "fat text" bug
rgfw_window_close($window->getWindowResource()); // Now, safely close the native window
// Track this window ID as recently closed
$sdlWindowId = $window->getWindowId();
if (!in_array($sdlWindowId, $this->closedWindowIds)) {
$this->closedWindowIds[] = $sdlWindowId;
}
// Explicitly destroy the window (closes SDL window, renderer, and text renderer)
$window->destroy();
unset($this->windows[$windowId]);
$oneWindowIsClosed = true;
}
}
// Clean up old closed window IDs after 10 frames (to prevent memory leak)
// This gives SDL enough time to stop sending events for closed windows
if (count($this->closedWindowIds) > 10) {
array_shift($this->closedWindowIds);
}
if ($oneWindowIsClosed) {
foreach ($this->windows as $windowId => $window) {
$window->setShouldBeReLayouted(true);

View File

@ -8,28 +8,33 @@ namespace PHPNative\Framework;
class TextRenderer
{
private $window;
private $renderer;
private $font = null;
private bool $initialized = false;
private string $fontPath;
private int $fontSize;
private float $colorR = 1.0;
private float $colorG = 1.0;
private float $colorB = 1.0;
private float $colorA = 1.0;
/**
* Constructor
*
* @param resource $window RGFW window resource
* @param resource $renderer SDL Renderer resource
*/
public function __construct($window)
public function __construct($renderer)
{
$this->window = $window;
$this->renderer = $renderer;
}
/**
* Initialize RFont with a font file
* Initialize TTF with a font file
*
* @param string|null $fontPath Path to TTF font file
* @param int $fontSize Default font size
* @param int $atlasWidth Font atlas texture width (default 512)
* @param int $atlasHeight Font atlas texture height (default 512)
* @param int $atlasWidth Font atlas texture width (ignored, for compatibility)
* @param int $atlasHeight Font atlas texture height (ignored, for compatibility)
* @return bool Success
*/
public function init(
@ -51,13 +56,16 @@ class TextRenderer
$this->fontPath = $fontPath;
$this->fontSize = $fontSize;
$this->initialized = rfont_init($this->window, $fontPath, $fontSize, $atlasWidth, $atlasHeight);
$this->font = ttf_open_font($fontPath, $fontSize);
if (!$this->initialized) {
error_log("Failed to initialize RFont with font: {$fontPath}");
if (!$this->font) {
error_log("Failed to open font: {$fontPath}");
$this->initialized = false;
return false;
}
$this->initialized = true;
// Set default color to white
$this->setColor(1.0, 1.0, 1.0, 1.0);
@ -103,15 +111,43 @@ class TextRenderer
*/
public function drawText(string $text, int $x, int $y, null|int $size = null): void
{
if (!$this->initialized) {
if (!$this->initialized || !$this->font) {
return;
}
if ($size === null) {
$size = $this->fontSize;
// SDL_ttf doesn't support variable font sizes easily
// We use the font size that was set when the font was opened
// For different sizes, you'd need to open multiple font instances
// Convert float color (0.0-1.0) to int (0-255)
$r = (int) ($this->colorR * 255);
$g = (int) ($this->colorG * 255);
$b = (int) ($this->colorB * 255);
// Render text to surface with anti-aliasing (blended mode for smooth text)
$surface = ttf_render_text_blended($this->font, $text, $r, $g, $b);
if (!$surface) {
return;
}
rfont_drawText($this->window, $text, $x, $y, $size);
// Create texture from surface
$texture = sdl_create_texture_from_surface($this->renderer, $surface);
if (!$texture) {
return;
}
// Get text size
$textSize = ttf_size_text($this->font, $text);
// Render texture
sdl_render_texture($this->renderer, $texture, [
'x' => $x,
'y' => $y,
'w' => $textSize['w'],
'h' => $textSize['h']
]);
// Note: Texture and surface are automatically cleaned up by PHP resource destructors
}
/**
@ -124,11 +160,10 @@ class TextRenderer
*/
public function setColor(float $r, float $g, float $b, float $a = 1.0): void
{
if (!$this->initialized) {
return;
}
rfont_setColor($this->window, $r, $g, $b, $a);
$this->colorR = $r;
$this->colorG = $g;
$this->colorB = $b;
$this->colorA = $a;
}
/**
@ -140,24 +175,13 @@ class TextRenderer
*/
public function measureText(string $text, null|int $size = null): array
{
if (!$this->initialized) {
if (!$this->initialized || !$this->font) {
return [0, 0];
}
if ($size === null) {
$size = $this->fontSize;
}
// Try to use rfont_getTextSize if available, otherwise estimate
if (function_exists('rfont_getTextSize')) {
$dimensions = rfont_getTextSize($this->window, $text, $size);
return [(int) $dimensions[0], (int) $dimensions[1]];
}
// Fallback: estimate based on font size
$width = strlen($text) * ($size * 0.6); // Rough estimate
$height = $size * 1.2; // Line height
return [(int) $width, (int) $height];
// Get text size using TTF
$dimensions = ttf_size_text($this->font, $text);
return [(int) $dimensions['w'], (int) $dimensions['h']];
}
/**
@ -168,20 +192,18 @@ class TextRenderer
*/
public function updateFramebuffer(int $width, int $height): void
{
if (!$this->initialized) {
return;
}
rfont_setFramebuffer($this->window, $width, $height);
// SDL3 handles this automatically through the renderer
// This method is kept for compatibility but does nothing
}
/**
* Free RFont resources
* Free TTF resources
*/
public function free(): void
{
if ($this->initialized) {
rfont_free($this->window);
if ($this->initialized && $this->font) {
ttf_close_font($this->font);
$this->font = null;
$this->initialized = false;
}
}

View File

@ -14,6 +14,10 @@ class Flex implements Parser
if (count($output_array[0]) > 0) {
return new \PHPNative\Tailwind\Style\Flex(type:FlexTypeEnum::none);
}
preg_match_all('/flex-grow/', $style, $output_array);
if (count($output_array[0]) > 0) {
return new \PHPNative\Tailwind\Style\Flex(type:FlexTypeEnum::grow);
}
preg_match_all('/flex-1/', $style, $output_array);
if (count($output_array[0]) > 0) {
return new \PHPNative\Tailwind\Style\Flex(type:FlexTypeEnum::one);

View File

@ -24,7 +24,8 @@ class Height implements Parser
preg_match_all('/h-(\d*)/', $style, $output_array);
if (!$found && count($output_array[0]) > 0) {
$value = (int)$output_array[1][0];
// Tailwind uses a 4px scale: h-1 = 4px, h-10 = 40px, etc.
$value = (int)$output_array[1][0] * 4;
}
preg_match_all('/(h-full|h-screen)/', $style, $output_array);

View File

@ -15,42 +15,42 @@ class Margin implements Parser
preg_match_all('/m-(\d*)/', $style, $output_array);
if (count($output_array[0]) > 0) {
$l = (int)$output_array[1][0];
$r = (int)$output_array[1][0];
$t = (int)$output_array[1][0];
$b = (int)$output_array[1][0];
$l = (int)$output_array[1][0] * 4;
$r = (int)$output_array[1][0] * 4;
$t = (int)$output_array[1][0] * 4;
$b = (int)$output_array[1][0] * 4;
}
preg_match_all('/mx-(\d*)/', $style, $output_array);
if (count($output_array[0]) > 0) {
$l = (int)$output_array[1][0];
$r = (int)$output_array[1][0];
$l = (int)$output_array[1][0] * 4;
$r = (int)$output_array[1][0] * 4;
}
preg_match_all('/my-(\d*)/', $style, $output_array);
if (count($output_array[0]) > 0) {
$t = (int)$output_array[1][0];
$b = (int)$output_array[1][0];
$t = (int)$output_array[1][0] * 4;
$b = (int)$output_array[1][0] * 4;
}
preg_match_all('/mt-(\d*)/', $style, $output_array);
if (count($output_array[0]) > 0) {
$t = (int)$output_array[1][0];
$t = (int)$output_array[1][0] * 4;
}
preg_match_all('/mb-(\d*)/', $style, $output_array);
if (count($output_array[0]) > 0) {
$b = (int)$output_array[1][0];
$b = (int)$output_array[1][0] * 4;
}
preg_match_all('/ml-(\d*)/', $style, $output_array);
if (count($output_array[0]) > 0) {
$l = (int)$output_array[1][0];
$l = (int)$output_array[1][0] * 4;
}
preg_match_all('/mr-(\d*)/', $style, $output_array);
if (count($output_array[0]) > 0) {
$r = (int)$output_array[1][0];
$r = (int)$output_array[1][0] * 4;
}
if($l != null || $r != null || $t != null || $b != null) {

View File

@ -15,42 +15,43 @@ class Padding implements Parser
preg_match_all('/p-(\d*)/', $style, $output_array);
if (count($output_array[0]) > 0) {
$l = (int)$output_array[1][0];
$r = (int)$output_array[1][0];
$t = (int)$output_array[1][0];
$b = (int)$output_array[1][0];
// Tailwind uses a 4px scale
$l = (int)$output_array[1][0] * 4;
$r = (int)$output_array[1][0] * 4;
$t = (int)$output_array[1][0] * 4;
$b = (int)$output_array[1][0] * 4;
}
preg_match_all('/px-(\d*)/', $style, $output_array);
if (count($output_array[0]) > 0) {
$l = (int)$output_array[1][0];
$r = (int)$output_array[1][0];
$l = (int)$output_array[1][0] * 4;
$r = (int)$output_array[1][0] * 4;
}
preg_match_all('/py-(\d*)/', $style, $output_array);
if (count($output_array[0]) > 0) {
$t = (int)$output_array[1][0];
$b = (int)$output_array[1][0];
$t = (int)$output_array[1][0] * 4;
$b = (int)$output_array[1][0] * 4;
}
preg_match_all('/pt-(\d*)/', $style, $output_array);
if (count($output_array[0]) > 0) {
$t = (int)$output_array[1][0];
$t = (int)$output_array[1][0] * 4;
}
preg_match_all('/pb-(\d*)/', $style, $output_array);
if (count($output_array[0]) > 0) {
$b = (int)$output_array[1][0];
$b = (int)$output_array[1][0] * 4;
}
preg_match_all('/pl-(\d*)/', $style, $output_array);
if (count($output_array[0]) > 0) {
$l = (int)$output_array[1][0];
$l = (int)$output_array[1][0] * 4;
}
preg_match_all('/pr-(\d*)/', $style, $output_array);
if (count($output_array[0]) > 0) {
$r = (int)$output_array[1][0];
$r = (int)$output_array[1][0] * 4;
}
if($l != null || $r != null || $t != null || $b != null) {

View File

@ -24,7 +24,8 @@ class Width implements Parser
preg_match_all('/w-(\d*)/', $style, $output_array);
if (!$found && count($output_array[0]) > 0) {
$value = (int)$output_array[1][0];
// Tailwind uses a 4px scale: w-1 = 4px, w-10 = 40px, etc.
$value = (int)$output_array[1][0] * 4;
}
preg_match_all('/(w-screen|w-full)/', $style, $output_array);

View File

@ -9,4 +9,5 @@ enum FlexTypeEnum
case auto;
case initial;
case one;
case grow;
}

View File

@ -27,7 +27,19 @@ abstract class Component
public function __construct(
protected string $style = '',
) {}
) {
// Initialize viewports with default values
// These will be properly set during layout()
$this->viewport = new Viewport(
x: 0,
y: 0,
width: 0,
height: 0,
windowWidth: 800,
windowHeight: 600
);
$this->contentViewport = clone $this->viewport;
}
public function setViewport(Viewport $viewport): void
{
@ -101,7 +113,7 @@ abstract class Component
}
}
public function render(&$window, null|TextRenderer $textRenderer = null): void
public function render(&$renderer, null|TextRenderer $textRenderer = null): void
{
if (!$this->visible) {
return;
@ -112,43 +124,49 @@ abstract class Component
($bg = $this->computedStyles[\PHPNative\Tailwind\Style\Background::class])
) {
if ($this->currentState == StateEnum::hover) {
rsgl_setColor($window, $bg->color->red, $bg->color->green, $bg->color->blue, 10);
sdl_set_render_draw_color($renderer, $bg->color->red, $bg->color->green, $bg->color->blue, 10);
} else {
rsgl_setColor($window, $bg->color->red, $bg->color->green, $bg->color->blue, $bg->color->alpha);
sdl_set_render_draw_color($renderer, $bg->color->red, $bg->color->green, $bg->color->blue, $bg->color->alpha);
}
if (
isset($this->computedStyles[\PHPNative\Tailwind\Style\Border::class]) &&
($border = $this->computedStyles[\PHPNative\Tailwind\Style\Border::class])
) {
rsgl_drawRoundRectExF(
$window,
// SDL3: sdl_rounded_box_ex uses (x1, y1, x2, y2) instead of (x, y, w, h)
$x2 = $this->viewport->x + $this->viewport->width;
$y2 = $this->viewport->y + $this->viewport->height;
sdl_rounded_box_ex(
$renderer,
$this->viewport->x,
$this->viewport->y,
$this->viewport->width,
$this->viewport->height,
$border->roundTopLeft ?? 0,
$x2,
$y2,
$border->roundTopLeft ?? 0,
$border->roundTopRight ?? 0,
$border->roundTopRight ?? 0,
$border->roundBottomLeft ?? 0,
$border->roundBottomLeft ?? 0,
$border->roundBottomRight ?? 0,
$border->roundBottomRight ?? 0,
$border->roundBottomLeft ?? 0,
$bg->color->red,
$bg->color->green,
$bg->color->blue,
$bg->color->alpha,
);
} else {
rsgl_drawRectF(
$window,
$this->viewport->x,
$this->viewport->y,
$this->viewport->width,
$this->viewport->height,
sdl_render_fill_rect(
$renderer,
[
'x' => $this->viewport->x,
'y' => $this->viewport->y,
'w' => $this->viewport->width,
'h' => $this->viewport->height,
]
);
}
}
}
public function renderContent(&$window, null|TextRenderer $textRenderer = null): void
public function renderContent(&$renderer, null|TextRenderer $textRenderer = null): void
{
if (!$this->visible) {
return;
@ -156,8 +174,8 @@ abstract class Component
// Render children
foreach ($this->children as $child) {
$child->render($window, $textRenderer);
$child->renderContent($window, $textRenderer);
$child->render($renderer, $textRenderer);
$child->renderContent($renderer, $textRenderer);
}
}
@ -234,4 +252,32 @@ abstract class Component
}
return false;
}
/**
* Handle text input event
* @param string $text The input text
*/
public function handleTextInput(string $text): void
{
// Default implementation: propagate to children
foreach ($this->children as $child) {
$child->handleTextInput($text);
}
}
/**
* Handle key down event
* @param int $keycode SDL keycode
* @return bool True if event was handled
*/
public function handleKeyDown(int $keycode): bool
{
// Default implementation: propagate to children
foreach ($this->children as $child) {
if ($child->handleKeyDown($keycode)) {
return true;
}
}
return false;
}
}

110
src/Ui/Widget/Checkbox.php Normal file
View File

@ -0,0 +1,110 @@
<?php
namespace PHPNative\Ui\Widget;
use PHPNative\Framework\TextRenderer;
use PHPNative\Ui\Component;
class Checkbox extends Component
{
private bool $checked = false;
private $onChange = null;
private string $labelText = '';
public function __construct(
string $label = '',
bool $checked = false,
string $style = '',
$onChange = null,
) {
parent::__construct($style);
$this->checked = $checked;
$this->onChange = $onChange;
$this->labelText = $label;
}
public function isChecked(): bool
{
return $this->checked;
}
public function setChecked(bool $checked): void
{
$this->checked = $checked;
if ($this->onChange !== null) {
($this->onChange)($checked);
}
}
public function setOnChange(callable $onChange): void
{
$this->onChange = $onChange;
}
public function handleMouseClick(float $mouseX, float $mouseY, int $button): bool
{
// Check if click is within checkbox bounds (not label)
$checkboxSize = 20;
if (
$mouseX >= $this->viewport->x &&
$mouseX <= ($this->viewport->x + $checkboxSize) &&
$mouseY >= $this->viewport->y &&
$mouseY <= ($this->viewport->y + $checkboxSize)
) {
$this->checked = !$this->checked;
if ($this->onChange !== null) {
($this->onChange)($this->checked);
}
return true;
}
return parent::handleMouseClick($mouseX, $mouseY, $button);
}
public function render(&$renderer, null|TextRenderer $textRenderer = null): void
{
$checkboxSize = 20;
// Draw checkbox border
sdl_set_render_draw_color($renderer, 156, 163, 175, 255); // Gray-400
sdl_render_fill_rect($renderer, [
'x' => $this->viewport->x,
'y' => $this->viewport->y,
'w' => $checkboxSize,
'h' => $checkboxSize,
]);
// Draw white background
sdl_set_render_draw_color($renderer, 255, 255, 255, 255);
sdl_render_fill_rect($renderer, [
'x' => $this->viewport->x + 2,
'y' => $this->viewport->y + 2,
'w' => $checkboxSize - 4,
'h' => $checkboxSize - 4,
]);
// Draw checkmark if checked
if ($this->checked) {
sdl_set_render_draw_color($renderer, 37, 99, 235, 255); // Blue-600
sdl_render_fill_rect($renderer, [
'x' => $this->viewport->x + 4,
'y' => $this->viewport->y + 4,
'w' => $checkboxSize - 8,
'h' => $checkboxSize - 8,
]);
}
// Render label text if present
if (!empty($this->labelText) && $textRenderer !== null && $textRenderer->isInitialized()) {
$textX = $this->viewport->x + $checkboxSize + 8; // Checkbox + margin
$textY = $this->viewport->y + 2;
$textRenderer->setColor(0, 0, 0, 1.0); // Black
$textRenderer->drawText($this->labelText, (int) $textX, (int) $textY);
}
}
}

View File

@ -38,6 +38,18 @@ class Container extends Component
parent::__construct($style);
}
/**
* Clear all child components
*/
public function clearChildren(): void
{
$this->children = [];
$this->contentWidth = 0;
$this->contentHeight = 0;
$this->scrollX = 0;
$this->scrollY = 0;
}
public function layout(null|TextRenderer $textRenderer = null): void
{
// Call parent to compute styles and setup viewports
@ -131,9 +143,10 @@ class Container extends Component
}
if (!$hasExplicitHeight) {
// Set viewport to min(contentSize + padding, availableSize)
// Set viewport to content height + padding (don't expand to fill available space unnecessarily)
$desiredHeight = $this->contentHeight + $paddingY;
$this->viewport->height = (int) min($desiredHeight, $availableHeight);
// Only limit to availableHeight if we're not trying to measure natural size
$this->viewport->height = (int) $desiredHeight;
$this->contentViewport->height = max(0, $this->viewport->height - ((int) $paddingY));
}
}
@ -186,18 +199,20 @@ class Container extends Component
if (!$hasExplicitSize) {
// Need to measure natural size - do a temporary layout
// For flex-col without explicit height, give minimal space to measure intrinsic size
// For flex-row without explicit width, give minimal space to measure intrinsic size
$tempViewport = new Viewport(
x: $this->contentViewport->x,
y: $this->contentViewport->y,
width: $isRow ? $availableSpace : $this->contentViewport->width,
height: $isRow ? $this->contentViewport->height : $availableSpace,
width: $isRow ? 9999 : $this->contentViewport->width,
height: $isRow ? $this->contentViewport->height : 9999,
windowWidth: $this->contentViewport->windowWidth,
windowHeight: $this->contentViewport->windowHeight,
);
$child->setViewport($tempViewport);
$child->layout($textRenderer);
// Get natural size
// Get natural size (the actual size the child computed after layout)
$size = $isRow ? $child->getViewport()->width : $child->getViewport()->height;
}
@ -309,7 +324,7 @@ class Container extends Component
];
}
public function renderContent(&$window, null|TextRenderer $textRenderer = null): void
public function renderContent(&$renderer, null|TextRenderer $textRenderer = null): void
{
if (!$this->visible) {
return;
@ -322,25 +337,21 @@ class Container extends Component
// Apply scroll offset to children
if ($overflow['x'] || $overflow['y']) {
// Enable scissor test for clipping
// Enable clipping
$scissorX = (int) $this->contentViewport->x;
$scissorY = (int) $this->contentViewport->y;
$scissorW = (int) $this->contentViewport->width;
$scissorH = (int) $this->contentViewport->height;
// IMPORTANT: RSGL uses batch rendering!
// We need to flush the batch before/after scissor to ensure correct clipping
// SDL3: Set clip rect for clipping
sdl_set_render_clip_rect($renderer, [
'x' => $scissorX,
'y' => $scissorY,
'w' => $scissorW,
'h' => $scissorH
]);
// Step 1: Flush any pending draw calls before scissor
rsgl_render($window);
// Step 2: Enable scissor test for clipping
$windowHeight = $this->contentViewport->windowHeight;
$invertedY = $windowHeight - ($scissorY + $scissorH);
rsgl_scissorStart($window, $scissorX, $invertedY, $scissorW, $scissorH);
// Step 3: Render children with scroll offset (batches draw calls)
// Render children with scroll offset
foreach ($this->children as $child) {
// Apply scroll offset recursively to child and all its descendants
$child->applyScrollOffset((int) $this->scrollX, (int) $this->scrollY);
@ -354,30 +365,26 @@ class Container extends Component
$childViewport->y < ($scissorY + $scissorH);
if ($isVisible) {
// Render - batches draw calls
$child->render($textRenderer);
$child->renderContent($textRenderer);
$child->render($renderer, $textRenderer);
$child->renderContent($renderer, $textRenderer);
}
// Restore by applying negative offset
$child->applyScrollOffset((int) -$this->scrollX, (int) -$this->scrollY);
}
// Step 4: Flush the batch while scissor is still active
rsgl_render($window);
// Step 5: Disable scissor test
rsgl_scissorEnd($window);
// Disable clipping
sdl_set_render_clip_rect($renderer, null);
// Render scrollbars
$this->renderScrollbars($overflow);
$this->renderScrollbars($renderer, $overflow);
} else {
// No overflow, render normally
parent::renderContent($window, $textRenderer);
parent::renderContent($renderer, $textRenderer);
}
}
private function renderScrollbars(array $overflow): void
private function renderScrollbars(&$renderer, array $overflow): void
{
$scrollbarColor = [100, 100, 100, 200]; // Gray with some transparency
@ -394,32 +401,32 @@ class Container extends Component
$scrollbarX = ($this->contentViewport->x + $this->contentViewport->width) - self::SCROLLBAR_WIDTH;
// Track
rsgl_setColor($this->window, 200, 200, 200, 100);
rsgl_drawRectF(
$this->window,
(int) $scrollbarX,
(int) $this->contentViewport->y,
(int) self::SCROLLBAR_WIDTH,
(int) $scrollbarHeight,
sdl_set_render_draw_color($renderer, 200, 200, 200, 100);
sdl_render_fill_rect(
$renderer,
[
'x' => (int) $scrollbarX,
'y' => (int) $this->contentViewport->y,
'w' => (int) self::SCROLLBAR_WIDTH,
'h' => (int) $scrollbarHeight,
]
);
// Thumb
rsgl_setColor(
$this->window,
// Thumb - using sdl_rounded_box for rounded rectangle
$thumbX = (int) ($scrollbarX + 2);
$thumbW = (int) (self::SCROLLBAR_WIDTH - 4);
sdl_rounded_box(
$renderer,
$thumbX,
(int) $thumbY,
$thumbX + $thumbW,
(int) ($thumbY + $thumbHeight),
4,
$scrollbarColor[0],
$scrollbarColor[1],
$scrollbarColor[2],
$scrollbarColor[3],
);
rsgl_drawRoundRectF(
$this->window,
(int) ($scrollbarX + 2),
(int) $thumbY,
(int) (self::SCROLLBAR_WIDTH - 4),
(int) $thumbHeight,
4,
4,
);
}
// Horizontal scrollbar
@ -435,32 +442,32 @@ class Container extends Component
$scrollbarY = ($this->contentViewport->y + $this->contentViewport->height) - self::SCROLLBAR_WIDTH;
// Track
rsgl_setColor($this->window, 200, 200, 200, 100);
rsgl_drawRectF(
$this->window,
(int) $this->contentViewport->x,
(int) $scrollbarY,
(int) $scrollbarWidth,
(int) self::SCROLLBAR_WIDTH,
sdl_set_render_draw_color($renderer, 200, 200, 200, 100);
sdl_render_fill_rect(
$renderer,
[
'x' => (int) $this->contentViewport->x,
'y' => (int) $scrollbarY,
'w' => (int) $scrollbarWidth,
'h' => (int) self::SCROLLBAR_WIDTH,
]
);
// Thumb
rsgl_setColor(
$this->window,
// Thumb - using sdl_rounded_box for rounded rectangle
$thumbY = (int) ($scrollbarY + 2);
$thumbH = (int) (self::SCROLLBAR_WIDTH - 4);
sdl_rounded_box(
$renderer,
(int) $thumbX,
$thumbY,
(int) ($thumbX + $thumbWidth),
$thumbY + $thumbH,
4,
$scrollbarColor[0],
$scrollbarColor[1],
$scrollbarColor[2],
$scrollbarColor[3],
);
rsgl_drawRoundRectF(
$this->window,
(int) $thumbX,
(int) ($scrollbarY + 2),
(int) $thumbWidth,
(int) (self::SCROLLBAR_WIDTH - 4),
4,
4,
);
}
}

View File

@ -14,7 +14,9 @@ class Label extends Component
public function __construct(
public string $text = '',
public string $style = '',
) {}
) {
parent::__construct($style);
}
public function setText(string $text): void
{

246
src/Ui/Widget/TextInput.php Normal file
View File

@ -0,0 +1,246 @@
<?php
namespace PHPNative\Ui\Widget;
use PHPNative\Framework\TextRenderer;
class TextInput extends Container
{
private string $value = '';
private string $placeholder = '';
private bool $focused = false;
private $onChange = null;
private int $cursorPosition = 0;
private float $cursorBlinkTime = 0;
private bool $cursorVisible = true;
public function __construct(
string $placeholder = '',
string $style = '',
$onChange = null,
) {
parent::__construct($style);
$this->placeholder = $placeholder;
$this->onChange = $onChange;
}
public function layout(null|TextRenderer $textRenderer = null): void
{
// Parse styles first to get computed styles
$this->computedStyles = \PHPNative\Tailwind\StyleParser::parse($this->style)->getValidStyles(
\PHPNative\Tailwind\Style\MediaQueryEnum::normal,
\PHPNative\Tailwind\Style\StateEnum::normal,
);
// Call parent layout
parent::layout($textRenderer);
// If no explicit height is set, calculate based on text size + padding
if (!isset($this->computedStyles[\PHPNative\Tailwind\Style\Height::class])) {
$padding = $this->computedStyles[\PHPNative\Tailwind\Style\Padding::class] ?? null;
$paddingY = $padding ? ($padding->top + $padding->bottom) : 0;
// Calculate text height
$textHeight = 16; // Default font size
if ($textRenderer !== null && $textRenderer->isInitialized()) {
$displayText = empty($this->value) ? ($this->placeholder ?: 'A') : 'A';
$size = $textRenderer->measureText($displayText);
$textHeight = $size[1] ?? 16;
}
// Set height to text height + padding + borders (2px * 2)
$this->viewport->height = (int) ($textHeight + $paddingY + 4);
$this->contentViewport->height = max(0, (int) ($textHeight + $paddingY));
}
}
public function getValue(): string
{
return $this->value;
}
public function setValue(string $value): void
{
$this->value = $value;
$this->cursorPosition = mb_strlen($value);
if ($this->onChange !== null) {
($this->onChange)($value);
}
}
public function setOnChange(callable $onChange): void
{
$this->onChange = $onChange;
}
public function handleMouseClick(float $mouseX, float $mouseY, int $button): bool
{
// Check if click is within bounds
if (
$mouseX >= $this->viewport->x &&
$mouseX <= ($this->viewport->x + $this->viewport->width) &&
$mouseY >= $this->viewport->y &&
$mouseY <= ($this->viewport->y + $this->viewport->height)
) {
$this->focused = true;
return true;
} else {
$this->focused = false;
return false;
}
}
public function handleTextInput(string $text): void
{
if (!$this->focused) {
return;
}
// Insert text at cursor position
$before = mb_substr($this->value, 0, $this->cursorPosition);
$after = mb_substr($this->value, $this->cursorPosition);
$this->value = $before . $text . $after;
$this->cursorPosition += mb_strlen($text);
if ($this->onChange !== null) {
($this->onChange)($this->value);
}
}
public function handleKeyDown(int $keycode): bool
{
if (!$this->focused) {
return false;
}
switch ($keycode) {
case SDLK_BACKSPACE:
if ($this->cursorPosition > 0) {
$before = mb_substr($this->value, 0, $this->cursorPosition - 1);
$after = mb_substr($this->value, $this->cursorPosition);
$this->value = $before . $after;
$this->cursorPosition--;
if ($this->onChange !== null) {
($this->onChange)($this->value);
}
}
return true;
case SDLK_DELETE:
if ($this->cursorPosition < mb_strlen($this->value)) {
$before = mb_substr($this->value, 0, $this->cursorPosition);
$after = mb_substr($this->value, $this->cursorPosition + 1);
$this->value = $before . $after;
if ($this->onChange !== null) {
($this->onChange)($this->value);
}
}
return true;
case SDLK_LEFT:
if ($this->cursorPosition > 0) {
$this->cursorPosition--;
}
return true;
case SDLK_RIGHT:
if ($this->cursorPosition < mb_strlen($this->value)) {
$this->cursorPosition++;
}
return true;
case SDLK_RETURN:
// Enter key - can be handled by parent
return false;
}
return false;
}
public function isFocused(): bool
{
return $this->focused;
}
public function focus(): void
{
$this->focused = true;
}
public function blur(): void
{
$this->focused = false;
}
public function render(&$renderer, null|TextRenderer $textRenderer = null): void
{
// Render background with focus indicator
if ($this->focused) {
// Focused: blue border
sdl_set_render_draw_color($renderer, 59, 130, 246, 255); // Blue-500
} else {
// Not focused: gray border
sdl_set_render_draw_color($renderer, 209, 213, 219, 255); // Gray-300
}
// Draw border
sdl_render_fill_rect($renderer, [
'x' => $this->viewport->x,
'y' => $this->viewport->y,
'w' => $this->viewport->width,
'h' => $this->viewport->height,
]);
// Draw white background inside
sdl_set_render_draw_color($renderer, 255, 255, 255, 255);
sdl_render_fill_rect($renderer, [
'x' => $this->viewport->x + 2,
'y' => $this->viewport->y + 2,
'w' => $this->viewport->width - 4,
'h' => $this->viewport->height - 4,
]);
// Render text or placeholder
if ($textRenderer !== null && $textRenderer->isInitialized()) {
$displayText = empty($this->value) ? $this->placeholder : $this->value;
$textX = $this->viewport->x + 6;
$textY = $this->viewport->y + 6;
// Set color: gray for placeholder, black for text
if (empty($this->value)) {
$textRenderer->setColor(156/255, 163/255, 175/255, 1.0); // Gray-400
} else {
$textRenderer->setColor(0, 0, 0, 1.0); // Black
}
$textRenderer->drawText($displayText, (int) $textX, (int) $textY);
}
// Render cursor if focused
if ($this->focused && $this->cursorVisible && $textRenderer) {
// Calculate cursor position
$textBeforeCursor = mb_substr($this->value, 0, $this->cursorPosition);
$cursorX = $this->viewport->x + 6; // Left padding
if (!empty($textBeforeCursor)) {
$size = $textRenderer->measureText($textBeforeCursor);
$cursorX += $size[0];
}
// Draw cursor
sdl_set_render_draw_color($renderer, 0, 0, 0, 255);
sdl_render_fill_rect($renderer, [
'x' => $cursorX,
'y' => $this->viewport->y + 4,
'w' => 2,
'h' => $this->viewport->height - 8,
]);
}
// Don't render children - we handle text rendering directly
}
}

View File

@ -7,6 +7,8 @@ use PHPNative\Framework\TextRenderer;
class Window
{
private mixed $window = null;
private mixed $renderer = null;
private int $windowId = 0;
private null|Component $rootComponent = null;
private ?TextRenderer $textRenderer;
private float $mouseX = 0;
@ -24,25 +26,50 @@ class Window
private int $x = 100,
private int $y = 100,
) {
// Create window
$this->window = rgfw_createWindow($title, $x, $y, $width, $height, 0);
if (!$this->window) {
throw new \Exception('Failed to create window');
// Initialize SDL if not already done
static $sdlInitialized = false;
if (!$sdlInitialized) {
if (!sdl_init(SDL_INIT_VIDEO)) {
throw new \Exception('Failed to initialize SDL: ' . sdl_get_error());
}
// Initialize RSGL renderer
if (!rsgl_init($this->window)) {
throw new \Exception('Failed to initialize RSGL renderer');
// Initialize TTF
if (!ttf_init()) {
throw new \Exception('Failed to initialize TTF: ' . sdl_get_error());
}
$sdlInitialized = true;
}
// Create window with resizable flag for normal window decorations
// SDL_WINDOW_RESIZABLE gives you the standard window controls (close, minimize, maximize)
$flags = SDL_WINDOW_RESIZABLE;
$this->window = sdl_create_window($title, $width, $height, $flags);
if (!$this->window) {
throw new \Exception('Failed to create window: ' . sdl_get_error());
}
// Get window ID for event routing
$this->windowId = sdl_get_window_id($this->window);
// Enable text input for this window
sdl_start_text_input($this->window);
// Create renderer
$this->renderer = sdl_create_renderer($this->window);
if (!$this->renderer) {
throw new \Exception('Failed to create renderer: ' . sdl_get_error());
}
// Initialize text renderer
$this->textRenderer = new TextRenderer($this->window);
$this->textRenderer = new TextRenderer($this->renderer);
if (!$this->textRenderer->init()) {
error_log('Warning: Failed to initialize text renderer. Text rendering will not be available.');
}
// Get actual window size
$size = rgfw_window_getSize($this->window);
$size = sdl_get_window_size($this->window);
$this->width = $size[0];
$this->height = $size[1];
$this->viewport = new Viewport(
@ -88,7 +115,7 @@ class Window
public function shouldClose(): bool
{
return $this->shouldClose || rgfw_window_shouldClose($this->window);
return $this->shouldClose;
}
public function close(): void
@ -101,48 +128,69 @@ class Window
return $this->window;
}
/**
* Handle window and input events
*/
public function handleEvents(): void
public function getRendererResource(): mixed
{
return $this->renderer;
}
public function getWindowId(): int
{
return $this->windowId;
}
/**
* Process a single event for this window
* Called by Application with pre-filtered events
*/
public function processEvent(array $event): void
{
while ($event = rgfw_window_checkQueuedEvent($this->window)) {
// Debug output - can be removed later
if (defined('DEBUG_EVENTS') && DEBUG_EVENTS) {
$eventTypes = [
RGFW_quit => 'QUIT',
RGFW_keyPressed => 'KEY_PRESSED',
RGFW_mouseButtonPressed => 'MOUSE_PRESSED',
RGFW_mouseButtonReleased => 'MOUSE_RELEASED',
RGFW_mousePosChanged => 'MOUSE_MOVE',
SDL_EVENT_QUIT => 'QUIT',
SDL_EVENT_WINDOW_CLOSE_REQUESTED => 'WINDOW_CLOSE_REQUESTED',
SDL_EVENT_KEY_DOWN => 'KEY_DOWN',
SDL_EVENT_MOUSE_BUTTON_DOWN => 'MOUSE_BUTTON_DOWN',
SDL_EVENT_MOUSE_BUTTON_UP => 'MOUSE_BUTTON_UP',
SDL_EVENT_MOUSE_MOTION => 'MOUSE_MOTION',
SDL_EVENT_WINDOW_RESIZED => 'WINDOW_RESIZED',
];
$typeName = $eventTypes[$event['type']] ?? ('UNKNOWN(' . $event['type'] . ')');
error_log("[{$this->title}] Event: {$typeName}");
error_log("[{$this->title}] (ID:{$this->windowId}) Event: {$typeName}");
}
switch ($event['type']) {
case RGFW_quit:
case SDL_EVENT_QUIT:
case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
$this->shouldClose = true;
break;
case RGFW_keyPressed:
$keyCode = $event['keyCode'] ?? 0;
if ($keyCode == RGFW_Escape) {
$this->shouldClose = true;
case SDL_EVENT_KEY_DOWN:
$keycode = $event['keycode'] ?? 0;
// Propagate key event to root component
if ($this->rootComponent) {
$this->rootComponent->handleKeyDown($keycode);
}
break;
case RGFW_windowResized:
case SDL_EVENT_TEXT_INPUT:
$text = $event['text'] ?? '';
// Propagate text input to root component
if ($this->rootComponent && !empty($text)) {
$this->rootComponent->handleTextInput($text);
}
break;
case SDL_EVENT_WINDOW_RESIZED:
// Update window dimensions (from event data)
$newWidth = $event[0] ?? $this->width;
$newHeight = $event[1] ?? $this->height;
$newWidth = $event['data1'] ?? $this->width;
$newHeight = $event['data2'] ?? $this->height;
$this->width = $newWidth;
$this->height = $newHeight;
// Update RSGL renderer size and viewport
rsgl_updateRendererSize($this->window);
// Update text renderer framebuffer
if ($this->textRenderer && $this->textRenderer->isInitialized()) {
$this->textRenderer->updateFramebuffer($newWidth, $newHeight);
@ -157,9 +205,9 @@ class Window
$this->shouldBeReLayouted = true;
break;
case RGFW_mousePosChanged:
$this->mouseX = $event[0] ?? 0;
$this->mouseY = $event[1] ?? 0;
case SDL_EVENT_MOUSE_MOTION:
$this->mouseX = $event['x'] ?? 0;
$this->mouseY = $event['y'] ?? 0;
// Propagate mouse move to root component
if ($this->rootComponent) {
@ -167,7 +215,7 @@ class Window
}
break;
case RGFW_mouseButtonPressed:
case SDL_EVENT_MOUSE_BUTTON_DOWN:
$button = $event['button'] ?? 0;
// Propagate click to root component
if ($this->rootComponent) {
@ -175,7 +223,7 @@ class Window
}
break;
case RGFW_mouseButtonReleased:
case SDL_EVENT_MOUSE_BUTTON_UP:
$button = $event['button'] ?? 0;
// Propagate release to root component
@ -184,8 +232,8 @@ class Window
}
break;
case RGFW_mouseScroll:
$deltaY = $event[1] ?? 0;
case SDL_EVENT_MOUSE_WHEEL:
$deltaY = $event['y'] ?? 0;
// Propagate wheel to root component
if ($this->rootComponent) {
@ -194,7 +242,6 @@ class Window
break;
}
}
}
/**
* Update mouse position and hover states
@ -202,36 +249,9 @@ class Window
*/
public function updateMousePosition(): void
{
if (!$this->rootComponent) {
return;
}
// Try to get current mouse position relative to this window
if (function_exists('rgfw_window_getMousePoint')) {
$mousePos = rgfw_window_getMousePoint($this->window);
if ($mousePos !== false && is_array($mousePos)) {
$newX = $mousePos[0] ?? $this->mouseX;
$newY = $mousePos[1] ?? $this->mouseY;
// Only update if position changed to avoid unnecessary updates
if ($newX !== $this->mouseX || $newY !== $this->mouseY) {
$this->mouseX = $newX;
$this->mouseY = $newY;
$this->rootComponent->handleMouseMove($this->mouseX, $this->mouseY);
}
}
} else {
// rgfw_window_getMousePoint() not available
// Hover will only work when window receives mouse events
// This is normal OS behavior - only focused windows get mouse events
static $warningShown = false;
if (!$warningShown && defined('DEBUG_EVENTS') && DEBUG_EVENTS) {
error_log(
'Note: rgfw_window_getMousePoint() not available. Hover states will only work for focused windows.',
);
$warningShown = true;
}
}
// With SDL3, mouse position is tracked through SDL_EVENT_MOUSE_MOTION events
// This method is kept for compatibility but does nothing
// Hover states are updated in handleEvents() via SDL_EVENT_MOUSE_MOTION
}
/**
@ -266,20 +286,19 @@ class Window
*/
public function render(): void
{
// Always clear the window to prevent black screens (white background, fully opaque)
// Clear the window with white background
sdl_set_render_draw_color($this->renderer, 255, 255, 255, 255);
sdl_render_clear($this->renderer);
// Only render content if window has been laid out
// This can happen when windows are created during async callbacks
if ($this->hasBeenLaidOut && $this->rootComponent) {
rgfw_window_makeCurrent($this->window);
rsgl_clear($this->window, 255, 255, 255, 255);
$this->rootComponent->render($this->window, $this->textRenderer);
$this->rootComponent->renderContent($this->window, $this->textRenderer);
rsgl_render($this->window);
rgfw_window_swapBuffers($this->window);
$this->rootComponent->render($this->renderer, $this->textRenderer);
$this->rootComponent->renderContent($this->renderer, $this->textRenderer);
}
// Always swap buffers to display the cleared window
// Present the rendered content
sdl_render_present($this->renderer);
}
/**
@ -293,6 +312,27 @@ class Window
}
}
/**
* Destroy the window and all resources
* This explicitly closes the SDL window
*/
public function destroy(): void
{
// First clean up text renderer
$this->cleanup();
// Explicitly destroy SDL renderer and window
if ($this->renderer) {
sdl_destroy_renderer($this->renderer);
$this->renderer = null;
}
if ($this->window) {
sdl_destroy_window($this->window);
$this->window = null;
}
}
public function setShouldBeReLayouted(bool $shouldBeReLayouted): void
{
$this->shouldBeReLayouted = $shouldBeReLayouted;

43
test_multi_window.php Normal file
View File

@ -0,0 +1,43 @@
<?php
require_once __DIR__ . '/vendor/autoload.php';
use PHPNative\Framework\Application;
use PHPNative\Ui\Window;
use PHPNative\Ui\Widget\Button;
use PHPNative\Ui\Widget\Container;
define('DEBUG_EVENTS', true); // Enable event debugging
$app = new Application();
// Main window
$mainWindow = new Window('Main Window', 400, 300);
$mainContainer = new Container('flex flex-col p-4 bg-gray-100');
$openButton = new Button('Open Second Window', 'bg-blue-500 text-white p-4 m-2 rounded hover:bg-blue-600');
$openButton->setOnClick(function() use ($app) {
echo "Creating second window...\n";
$secondWindow = new Window('Second Window', 400, 300);
$secondContainer = new Container('flex flex-col p-4 bg-green-100');
$closeButton = new Button('Close This Window', 'bg-red-500 text-white p-4 m-2 rounded hover:bg-red-600');
$closeButton->setOnClick(function() use ($secondWindow) {
echo "Close button clicked in second window (ID: {$secondWindow->getWindowId()})\n";
$secondWindow->close();
});
$secondContainer->addComponent($closeButton);
$secondWindow->setRoot($secondContainer);
$app->addWindow($secondWindow);
echo "Second window created with ID: {$secondWindow->getWindowId()}\n";
});
$mainContainer->addComponent($openButton);
$mainWindow->setRoot($mainContainer);
$app->addWindow($mainWindow);
echo "Main window ID: {$mainWindow->getWindowId()}\n";
$app->run();

View File

@ -1,75 +0,0 @@
<?php
/* Test multi-window with event queue mode */
// Enable event queue mode
rgfw_setQueueEvents(true);
$win1 = rgfw_createWindow("Window 1", 100, 100, 400, 300, 0);
$win2 = rgfw_createWindow("Window 2", 550, 100, 400, 300, 0);
rsgl_init($win1);
rsgl_init($win2);
echo "Event queue mode enabled. Testing with 2 windows...\n";
echo "Try clicking in each window to test mouse events\n";
$running = true;
$frameCount = 0;
while ($running) {
// Poll events for all windows
rgfw_pollEvents();
// Check events for window 1
while ($event = rgfw_window_checkQueuedEvent($win1)) {
echo "Window 1 - Event type: {$event['type']}\n";
if ($event['type'] == RGFW_quit) {
$running = false;
} elseif ($event['type'] == RGFW_mouseButtonPressed) {
echo " Window 1 - Mouse clicked! Button: {$event['button']}\n";
} elseif ($event['type'] == RGFW_mousePosChanged) {
echo " Window 1 - Mouse moved to: {$event[0]}, {$event[1]}\n";
}
}
// Check events for window 2
while ($event = rgfw_window_checkQueuedEvent($win2)) {
echo "Window 2 - Event type: {$event['type']}\n";
if ($event['type'] == RGFW_quit) {
$running = false;
} elseif ($event['type'] == RGFW_mouseButtonPressed) {
echo " Window 2 - Mouse clicked! Button: {$event['button']}\n";
} elseif ($event['type'] == RGFW_mousePosChanged) {
echo " Window 2 - Mouse moved to: {$event[0]}, {$event[1]}\n";
}
}
// Render window 1 (red background)
rsgl_clear($win1, 255, 0, 0, 255);
rsgl_drawRectF($win1, 150, 100, 100, 100);
rgfw_window_swapBuffers($win1);
// Render window 2 (blue background)
rsgl_clear($win2, 0, 0, 255, 255);
rsgl_drawRectF($win2, 150, 100, 100, 100);
rgfw_window_swapBuffers($win2);
$frameCount++;
if ($frameCount % 100 == 0) {
echo "Frame $frameCount - Both windows running\n";
}
// Check if windows should close
if (rgfw_window_shouldClose($win1) || rgfw_window_shouldClose($win2)) {
$running = false;
}
usleep(16000); // ~60 FPS
}
echo "Test finished after $frameCount frames\n";
rgfw_window_close($win1);
rgfw_window_close($win2);