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='' old_library=''
# Libraries that this one depends upon. # 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. # Version information for sdl3.
current=0 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 AWK = nawk
SHLIB_SUFFIX_NAME = so SHLIB_SUFFIX_NAME = so
SHLIB_DL_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_PECL_EXTENSION = sdl3
PHP_MODULES = $(phplibdir)/sdl3.la PHP_MODULES = $(phplibdir)/sdl3.la
PHP_ZEND_EX = PHP_ZEND_EX =
@ -18,7 +18,7 @@ exec_prefix = $(prefix)
libdir = ${exec_prefix}/lib libdir = ${exec_prefix}/lib
phpincludedir = /usr/local/include/php phpincludedir = /usr/local/include/php
CC = cc 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 CFLAGS_CLEAN = $(CFLAGS) -D_GNU_SOURCE
CPP = cc -E CPP = cc -E
CPPFLAGS = -DHAVE_CONFIG_H CPPFLAGS = -DHAVE_CONFIG_H
@ -30,7 +30,7 @@ PHP_EXECUTABLE = /usr/local/bin/php
EXTRA_LDFLAGS = EXTRA_LDFLAGS =
EXTRA_LIBS = 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 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 LIBTOOL = $(SHELL) $(top_builddir)/libtool
SHELL = /bin/bash SHELL = /bin/bash
INSTALL_HEADERS = INSTALL_HEADERS =
@ -208,6 +208,15 @@ sdl3.lo: /home/thomas/projekte/phpnative/framework/php-sdl3/sdl3.c
-include helper.dep -include helper.dep
helper.lo: /home/thomas/projekte/phpnative/framework/php-sdl3/helper.c 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 $(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 $(phplibdir)/sdl3.la: ./sdl3.la
$(LIBTOOL) --tag=CC --mode=install cp ./sdl3.la $(phplibdir) $(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 -include helper.dep
helper.lo: /home/thomas/projekte/phpnative/framework/php-sdl3/helper.c 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 $(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 $(phplibdir)/sdl3.la: ./sdl3.la
$(LIBTOOL) --tag=CC --mode=install cp ./sdl3.la $(phplibdir) $(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.]) 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 "$PHP_SDL3" != "no"; then
if test -d "$PHP_SDL3"; then if test -d "$PHP_SDL3"; then
PKG_CONFIG_PATH="$PHP_SDL3/lib/pkgconfig:$PHP_SDL3/share/pkgconfig:$PKG_CONFIG_PATH" 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 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) PHP_NEW_EXTENSION(sdl3, $SDL_SOURCE_FILES, $ext_shared)
fi fi

View File

@ -3,5 +3,8 @@
# Created by configure # Created by configure
'./configure' \ './configure' \
'--with-sdl3=/usr/local' \
'--with-sdl3-gfx=/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." 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="\ ac_cs_version="\
config.status config.status
configured by ./configure, generated by GNU Autoconf 2.72, configured by ./configure, generated by GNU Autoconf 2.72,
@ -494,7 +494,7 @@ if $ac_cs_silent; then
fi fi
if $ac_cs_recheck; then if $ac_cs_recheck; then
set X /bin/bash './configure' '--with-sdl3-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 shift
\printf "%s\n" "running CONFIG_SHELL=/bin/bash $*" >&6 \printf "%s\n" "running CONFIG_SHELL=/bin/bash $*" >&6
CONFIG_SHELL='/bin/bash' CONFIG_SHELL='/bin/bash'

338
php-sdl3/configure vendored
View File

@ -802,6 +802,10 @@ RANLIB
AR AR
ECHO ECHO
LN_S LN_S
SDL3_TTF_LIBS
SDL3_TTF_CFLAGS
SDL3_IMAGE_LIBS
SDL3_IMAGE_CFLAGS
SDL3_GFX_LIBS SDL3_GFX_LIBS
SDL3_GFX_CFLAGS SDL3_GFX_CFLAGS
SDL3_LIBS SDL3_LIBS
@ -884,6 +888,8 @@ with_php_config
enable_ enable_
with_sdl3 with_sdl3
with_sdl3_gfx with_sdl3_gfx
with_sdl3_image
with_sdl3_ttf
enable_shared enable_shared
enable_static enable_static
enable_fast_install enable_fast_install
@ -907,7 +913,11 @@ CPP
SDL3_CFLAGS SDL3_CFLAGS
SDL3_LIBS SDL3_LIBS
SDL3_GFX_CFLAGS 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. # Initialize some variables set by options.
@ -1544,6 +1554,14 @@ Extension:
SDL3_gfx installation. 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: Libtool:
--enable-shared=PKGS Build shared libraries default=yes --enable-shared=PKGS Build shared libraries default=yes
--enable-static=PKGS Build static 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 C compiler flags for SDL3_GFX, overriding pkg-config
SDL3_GFX_LIBS SDL3_GFX_LIBS
linker flags for SDL3_GFX, overriding pkg-config 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 Use these variables to override the choices made by 'configure' or to help
it to find libraries and programs with nonstandard names/locations. 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 "$PHP_SDL3" != "no"; then
if test -d "$PHP_SDL3"; then if test -d "$PHP_SDL3"; then
PKG_CONFIG_PATH="$PHP_SDL3/lib/pkgconfig:$PHP_SDL3/share/pkgconfig:$PKG_CONFIG_PATH" 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
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=. ext_builddir=.
@ -5825,7 +6123,7 @@ ia64-*-hpux*)
;; ;;
*-*-irix6*) *-*-irix6*)
# Find out which ABI we are using. # 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 if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
(eval $ac_compile) 2>&5 (eval $ac_compile) 2>&5
ac_status=$? ac_status=$?
@ -7204,7 +7502,7 @@ else case e in #(
LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym"
cat > conftest.$ac_ext <<EOF cat > conftest.$ac_ext <<EOF
#line 7207 "configure" #line 7505 "configure"
#include "confdefs.h" #include "confdefs.h"
int main(void) { int main(void) {
; return 0; } ; return 0; }
@ -7366,11 +7664,11 @@ else case e in #(
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'` -e 's:$: $lt_compiler_flag:'`
(eval echo "\"configure:7369: $lt_compile\"" >&5) (eval echo "\"configure:7667: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err) (eval "$lt_compile" 2>conftest.err)
ac_status=$? ac_status=$?
cat conftest.err >&5 cat conftest.err >&5
echo "configure:7373: \$? = $ac_status" >&5 echo "configure:7671: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized # The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output. # So say no if there are warnings other than the usual output.
@ -7666,11 +7964,11 @@ else case e in #(
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'` -e 's:$: $lt_compiler_flag:'`
(eval echo "\"configure:7669: $lt_compile\"" >&5) (eval echo "\"configure:7967: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err) (eval "$lt_compile" 2>conftest.err)
ac_status=$? ac_status=$?
cat conftest.err >&5 cat conftest.err >&5
echo "configure:7673: \$? = $ac_status" >&5 echo "configure:7971: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized # The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output. # So say no if there are warnings other than the usual output.
@ -7774,11 +8072,11 @@ else case e in #(
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'` -e 's:$: $lt_compiler_flag:'`
(eval echo "\"configure:7777: $lt_compile\"" >&5) (eval echo "\"configure:8075: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err) (eval "$lt_compile" 2>out/conftest.err)
ac_status=$? ac_status=$?
cat out/conftest.err >&5 cat out/conftest.err >&5
echo "configure:7781: \$? = $ac_status" >&5 echo "configure:8079: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext if (exit $ac_status) && test -s out/conftest2.$ac_objext
then then
# The compiler can only warn and ignore the option if not recognized # The compiler can only warn and ignore the option if not recognized
@ -8239,7 +8537,7 @@ _LT_EOF
# Determine the default libpath from the value encoded in an empty executable. # Determine the default libpath from the value encoded in an empty executable.
cat > conftest.$ac_ext <<EOF cat > conftest.$ac_ext <<EOF
#line 8242 "configure" #line 8540 "configure"
#include "confdefs.h" #include "confdefs.h"
int main(void) { int main(void) {
; return 0; } ; 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. # Determine the default libpath from the value encoded in an empty executable.
cat > conftest.$ac_ext <<EOF cat > conftest.$ac_ext <<EOF
#line 8284 "configure" #line 8582 "configure"
#include "confdefs.h" #include "confdefs.h"
int main(void) { int main(void) {
; return 0; } ; return 0; }
@ -9862,7 +10160,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<EOF cat > conftest.$ac_ext <<EOF
#line 9865 "configure" #line 10163 "configure"
#include "confdefs.h" #include "confdefs.h"
#if HAVE_DLFCN_H #if HAVE_DLFCN_H
@ -9961,7 +10259,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<EOF cat > conftest.$ac_ext <<EOF
#line 9964 "configure" #line 10262 "configure"
#include "confdefs.h" #include "confdefs.h"
#if HAVE_DLFCN_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. # Determine the default libpath from the value encoded in an empty executable.
cat > conftest.$ac_ext <<EOF cat > conftest.$ac_ext <<EOF
#line 11033 "configure" #line 11331 "configure"
#include "confdefs.h" #include "confdefs.h"
int main(void) { int main(void) {
; return 0; } ; 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. # Determine the default libpath from the value encoded in an empty executable.
cat > conftest.$ac_ext <<EOF cat > conftest.$ac_ext <<EOF
#line 11076 "configure" #line 11374 "configure"
#include "confdefs.h" #include "confdefs.h"
int main(void) { int main(void) {
; return 0; } ; return 0; }
@ -12326,11 +12624,11 @@ else case e in #(
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'` -e 's:$: $lt_compiler_flag:'`
(eval echo "\"configure:12329: $lt_compile\"" >&5) (eval echo "\"configure:12627: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err) (eval "$lt_compile" 2>conftest.err)
ac_status=$? ac_status=$?
cat conftest.err >&5 cat conftest.err >&5
echo "configure:12333: \$? = $ac_status" >&5 echo "configure:12631: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized # The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output. # So say no if there are warnings other than the usual output.
@ -12434,11 +12732,11 @@ else case e in #(
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'` -e 's:$: $lt_compiler_flag:'`
(eval echo "\"configure:12437: $lt_compile\"" >&5) (eval echo "\"configure:12735: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err) (eval "$lt_compile" 2>out/conftest.err)
ac_status=$? ac_status=$?
cat out/conftest.err >&5 cat out/conftest.err >&5
echo "configure:12441: \$? = $ac_status" >&5 echo "configure:12739: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext if (exit $ac_status) && test -s out/conftest2.$ac_objext
then then
# The compiler can only warn and ignore the option if not recognized # The compiler can only warn and ignore the option if not recognized

View File

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

View File

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

View File

@ -85,7 +85,7 @@ AR_FLAGS="cru"
LTCC="cc" LTCC="cc"
# LTCC compiler flags. # 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. # A language-specific compiler.
CC="cc" CC="cc"

View File

@ -14,7 +14,7 @@ library_names='sdl3.so sdl3.so sdl3.so'
old_library='' old_library=''
# Libraries that this one depends upon. # 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. # Version information for sdl3.
current=0 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" #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 #endif

View File

@ -6,13 +6,17 @@
#include "php.h" #include "php.h"
#include "php_sdl3.h" #include "php_sdl3.h"
#include "helper.h" #include "helper.h"
#include "sdl3_image.h"
#include "sdl3_ttf.h"
#include "sdl3_events.h"
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <SDL3_gfx/SDL3_gfxPrimitives.h> #include <SDL3_gfx/SDL3_gfxPrimitives.h>
#include <math.h> #include <math.h>
// Resource handles // Resource handles (nicht static, damit sie in anderen Modulen verfügbar sind)
static int le_sdl_window; int le_sdl_window;
static int le_sdl_renderer; int le_sdl_renderer;
int le_sdl_texture;
// Destructor for window resource // Destructor for window resource
static void sdl_window_dtor(zend_resource *rsrc) { 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) { PHP_MINIT_FUNCTION(sdl3) {
le_sdl_window = zend_register_list_destructors_ex(sdl_window_dtor, NULL, "SDL_Window", module_number); le_sdl_window = zend_register_list_destructors_ex(sdl_window_dtor, NULL, "SDL_Window", module_number);
le_sdl_renderer = zend_register_list_destructors_ex(sdl_renderer_dtor, NULL, "SDL_Renderer", module_number); le_sdl_renderer = zend_register_list_destructors_ex(sdl_renderer_dtor, NULL, "SDL_Renderer", module_number);
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; return SUCCESS;
} }
@ -54,19 +90,60 @@ PHP_FUNCTION(sdl_quit) {
PHP_FUNCTION(sdl_create_window) { PHP_FUNCTION(sdl_create_window) {
char *title; char *title;
size_t title_len; 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(); 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) { if (!win) {
RETURN_FALSE; RETURN_FALSE;
} }
RETURN_RES(zend_register_resource(win, le_sdl_window)); 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) { PHP_FUNCTION(sdl_create_renderer) {
zval *win_res; zval *win_res;
SDL_Window *win; SDL_Window *win;
@ -185,6 +262,79 @@ PHP_FUNCTION(sdl_get_error) {
RETURN_STRING(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) PHP_FUNCTION(sdl_rounded_box)
{ {
zval *ren_res; zval *ren_res;
@ -237,18 +387,40 @@ PHP_FUNCTION(sdl_rounded_box_ex)
int r_right = (rad_tr > rad_br) ? rad_tr : rad_br; int r_right = (rad_tr > rad_br) ? rad_tr : rad_br;
// 1) center vertical band (zwischen links und rechts Radien), ganze Höhe // 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); if (center.w > 0 && center.h > 0) SDL_RenderFillRect(ren, &center);
// 2) left vertical rectangle (zwischen oberen und unteren Ecken links) // 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); if (leftRect.w > 0 && leftRect.h > 0) SDL_RenderFillRect(ren, &leftRect);
// 3) right vertical rectangle (zwischen oberen und unteren Ecken rechts) // 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 }; 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); 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_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_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); 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; 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_BEGIN_ARG_INFO_EX(arginfo_sdl_init, 0, 0, 1)
ZEND_ARG_INFO(0, flags) ZEND_ARG_INFO(0, flags)
ZEND_END_ARG_INFO() 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, title)
ZEND_ARG_INFO(0, w) ZEND_ARG_INFO(0, w)
ZEND_ARG_INFO(0, h) 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_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_create_renderer, 0, 0, 1) ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_create_renderer, 0, 0, 1)
@ -302,6 +580,17 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_get_error, 0, 0, 0) ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_get_error, 0, 0, 0)
ZEND_END_ARG_INFO() 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_BEGIN_ARG_INFO_EX(arginfo_sdl_rounded_box, 0, 0, 10)
ZEND_ARG_INFO(0, renderer) ZEND_ARG_INFO(0, renderer)
ZEND_ARG_INFO(0, x1) 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_ARG_INFO(0, a)
ZEND_END_ARG_INFO() 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[] = { const zend_function_entry sdl3_functions[] = {
// SDL3 Core
PHP_FE(sdl_init, arginfo_sdl_init) PHP_FE(sdl_init, arginfo_sdl_init)
PHP_FE(sdl_quit, arginfo_sdl_quit) PHP_FE(sdl_quit, arginfo_sdl_quit)
PHP_FE(sdl_create_window, arginfo_sdl_create_window) 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_create_renderer, arginfo_sdl_create_renderer)
PHP_FE(sdl_set_render_draw_color, arginfo_sdl_set_render_draw_color) PHP_FE(sdl_set_render_draw_color, arginfo_sdl_set_render_draw_color)
PHP_FE(sdl_render_clear, arginfo_sdl_render_clear) PHP_FE(sdl_render_clear, arginfo_sdl_render_clear)
@ -342,8 +652,33 @@ const zend_function_entry sdl3_functions[] = {
PHP_FE(sdl_render_present, arginfo_sdl_render_present) PHP_FE(sdl_render_present, arginfo_sdl_render_present)
PHP_FE(sdl_delay, arginfo_sdl_delay) PHP_FE(sdl_delay, arginfo_sdl_delay)
PHP_FE(sdl_get_error, arginfo_sdl_get_error) 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, arginfo_sdl_rounded_box)
PHP_FE(sdl_rounded_box_ex, arginfo_sdl_rounded_box_ex) 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 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/Zend/zend_constants.h \
/usr/local/include/php/main/php_reentrancy.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/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='' old_library=''
# Libraries that this one depends upon. # 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. # Version information for sdl3.
current=0 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 array $windows = [];
protected int $nextWindowId = 0; protected int $nextWindowId = 0;
protected bool $running = true; protected bool $running = true;
protected array $closedWindowIds = []; // Track recently closed window IDs
public function __construct() public function __construct()
{ {
self::$instance = $this; 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; 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 * Get all windows
*/ */
@ -75,12 +88,52 @@ class Application
echo 'Layoutout: ' . PHP_EOL; echo 'Layoutout: ' . PHP_EOL;
} }
} }
// Poll events globally for all windows (event queue mode, if available) // SDL3: Poll all events globally and distribute to the correct windows
rgfw_pollEvents(); $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 ($this->windows as $windowId => $window) { foreach ($events as $event) {
$window->handleEvents(); $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->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) // Update async tasks (global)
@ -98,13 +151,25 @@ class Application
// Remove closed windows // Remove closed windows
foreach ($this->windows as $windowId => $window) { foreach ($this->windows as $windowId => $window) {
if ($window->shouldClose()) { if ($window->shouldClose()) {
$window->cleanup(); // Frees the TextRenderer to prevent "fat text" bug // Track this window ID as recently closed
rgfw_window_close($window->getWindowResource()); // Now, safely close the native window $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]); unset($this->windows[$windowId]);
$oneWindowIsClosed = true; $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) { if ($oneWindowIsClosed) {
foreach ($this->windows as $windowId => $window) { foreach ($this->windows as $windowId => $window) {
$window->setShouldBeReLayouted(true); $window->setShouldBeReLayouted(true);

View File

@ -8,28 +8,33 @@ namespace PHPNative\Framework;
class TextRenderer class TextRenderer
{ {
private $window; private $renderer;
private $font = null;
private bool $initialized = false; private bool $initialized = false;
private string $fontPath; private string $fontPath;
private int $fontSize; private int $fontSize;
private float $colorR = 1.0;
private float $colorG = 1.0;
private float $colorB = 1.0;
private float $colorA = 1.0;
/** /**
* Constructor * 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 string|null $fontPath Path to TTF font file
* @param int $fontSize Default font size * @param int $fontSize Default font size
* @param int $atlasWidth Font atlas texture width (default 512) * @param int $atlasWidth Font atlas texture width (ignored, for compatibility)
* @param int $atlasHeight Font atlas texture height (default 512) * @param int $atlasHeight Font atlas texture height (ignored, for compatibility)
* @return bool Success * @return bool Success
*/ */
public function init( public function init(
@ -51,13 +56,16 @@ class TextRenderer
$this->fontPath = $fontPath; $this->fontPath = $fontPath;
$this->fontSize = $fontSize; $this->fontSize = $fontSize;
$this->initialized = rfont_init($this->window, $fontPath, $fontSize, $atlasWidth, $atlasHeight); $this->font = ttf_open_font($fontPath, $fontSize);
if (!$this->initialized) { if (!$this->font) {
error_log("Failed to initialize RFont with font: {$fontPath}"); error_log("Failed to open font: {$fontPath}");
$this->initialized = false;
return false; return false;
} }
$this->initialized = true;
// Set default color to white // Set default color to white
$this->setColor(1.0, 1.0, 1.0, 1.0); $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 public function drawText(string $text, int $x, int $y, null|int $size = null): void
{ {
if (!$this->initialized) { if (!$this->initialized || !$this->font) {
return; return;
} }
if ($size === null) { // SDL_ttf doesn't support variable font sizes easily
$size = $this->fontSize; // 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 public function setColor(float $r, float $g, float $b, float $a = 1.0): void
{ {
if (!$this->initialized) { $this->colorR = $r;
return; $this->colorG = $g;
} $this->colorB = $b;
$this->colorA = $a;
rfont_setColor($this->window, $r, $g, $b, $a);
} }
/** /**
@ -140,24 +175,13 @@ class TextRenderer
*/ */
public function measureText(string $text, null|int $size = null): array public function measureText(string $text, null|int $size = null): array
{ {
if (!$this->initialized) { if (!$this->initialized || !$this->font) {
return [0, 0]; return [0, 0];
} }
if ($size === null) { // Get text size using TTF
$size = $this->fontSize; $dimensions = ttf_size_text($this->font, $text);
} return [(int) $dimensions['w'], (int) $dimensions['h']];
// 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];
} }
/** /**
@ -168,20 +192,18 @@ class TextRenderer
*/ */
public function updateFramebuffer(int $width, int $height): void public function updateFramebuffer(int $width, int $height): void
{ {
if (!$this->initialized) { // SDL3 handles this automatically through the renderer
return; // This method is kept for compatibility but does nothing
}
rfont_setFramebuffer($this->window, $width, $height);
} }
/** /**
* Free RFont resources * Free TTF resources
*/ */
public function free(): void public function free(): void
{ {
if ($this->initialized) { if ($this->initialized && $this->font) {
rfont_free($this->window); ttf_close_font($this->font);
$this->font = null;
$this->initialized = false; $this->initialized = false;
} }
} }

View File

@ -14,6 +14,10 @@ class Flex implements Parser
if (count($output_array[0]) > 0) { if (count($output_array[0]) > 0) {
return new \PHPNative\Tailwind\Style\Flex(type:FlexTypeEnum::none); 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); preg_match_all('/flex-1/', $style, $output_array);
if (count($output_array[0]) > 0) { if (count($output_array[0]) > 0) {
return new \PHPNative\Tailwind\Style\Flex(type:FlexTypeEnum::one); 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); preg_match_all('/h-(\d*)/', $style, $output_array);
if (!$found && count($output_array[0]) > 0) { 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); 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); preg_match_all('/m-(\d*)/', $style, $output_array);
if (count($output_array[0]) > 0) { if (count($output_array[0]) > 0) {
$l = (int)$output_array[1][0]; $l = (int)$output_array[1][0] * 4;
$r = (int)$output_array[1][0]; $r = (int)$output_array[1][0] * 4;
$t = (int)$output_array[1][0]; $t = (int)$output_array[1][0] * 4;
$b = (int)$output_array[1][0]; $b = (int)$output_array[1][0] * 4;
} }
preg_match_all('/mx-(\d*)/', $style, $output_array); preg_match_all('/mx-(\d*)/', $style, $output_array);
if (count($output_array[0]) > 0) { if (count($output_array[0]) > 0) {
$l = (int)$output_array[1][0]; $l = (int)$output_array[1][0] * 4;
$r = (int)$output_array[1][0]; $r = (int)$output_array[1][0] * 4;
} }
preg_match_all('/my-(\d*)/', $style, $output_array); preg_match_all('/my-(\d*)/', $style, $output_array);
if (count($output_array[0]) > 0) { if (count($output_array[0]) > 0) {
$t = (int)$output_array[1][0]; $t = (int)$output_array[1][0] * 4;
$b = (int)$output_array[1][0]; $b = (int)$output_array[1][0] * 4;
} }
preg_match_all('/mt-(\d*)/', $style, $output_array); preg_match_all('/mt-(\d*)/', $style, $output_array);
if (count($output_array[0]) > 0) { 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); preg_match_all('/mb-(\d*)/', $style, $output_array);
if (count($output_array[0]) > 0) { 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); preg_match_all('/ml-(\d*)/', $style, $output_array);
if (count($output_array[0]) > 0) { 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); preg_match_all('/mr-(\d*)/', $style, $output_array);
if (count($output_array[0]) > 0) { 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) { 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); preg_match_all('/p-(\d*)/', $style, $output_array);
if (count($output_array[0]) > 0) { if (count($output_array[0]) > 0) {
$l = (int)$output_array[1][0]; // Tailwind uses a 4px scale
$r = (int)$output_array[1][0]; $l = (int)$output_array[1][0] * 4;
$t = (int)$output_array[1][0]; $r = (int)$output_array[1][0] * 4;
$b = (int)$output_array[1][0]; $t = (int)$output_array[1][0] * 4;
$b = (int)$output_array[1][0] * 4;
} }
preg_match_all('/px-(\d*)/', $style, $output_array); preg_match_all('/px-(\d*)/', $style, $output_array);
if (count($output_array[0]) > 0) { if (count($output_array[0]) > 0) {
$l = (int)$output_array[1][0]; $l = (int)$output_array[1][0] * 4;
$r = (int)$output_array[1][0]; $r = (int)$output_array[1][0] * 4;
} }
preg_match_all('/py-(\d*)/', $style, $output_array); preg_match_all('/py-(\d*)/', $style, $output_array);
if (count($output_array[0]) > 0) { if (count($output_array[0]) > 0) {
$t = (int)$output_array[1][0]; $t = (int)$output_array[1][0] * 4;
$b = (int)$output_array[1][0]; $b = (int)$output_array[1][0] * 4;
} }
preg_match_all('/pt-(\d*)/', $style, $output_array); preg_match_all('/pt-(\d*)/', $style, $output_array);
if (count($output_array[0]) > 0) { 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); preg_match_all('/pb-(\d*)/', $style, $output_array);
if (count($output_array[0]) > 0) { 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); preg_match_all('/pl-(\d*)/', $style, $output_array);
if (count($output_array[0]) > 0) { 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); preg_match_all('/pr-(\d*)/', $style, $output_array);
if (count($output_array[0]) > 0) { 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) { 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); preg_match_all('/w-(\d*)/', $style, $output_array);
if (!$found && count($output_array[0]) > 0) { 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); preg_match_all('/(w-screen|w-full)/', $style, $output_array);

View File

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

View File

@ -27,7 +27,19 @@ abstract class Component
public function __construct( public function __construct(
protected string $style = '', 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 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) { if (!$this->visible) {
return; return;
@ -112,43 +124,49 @@ abstract class Component
($bg = $this->computedStyles[\PHPNative\Tailwind\Style\Background::class]) ($bg = $this->computedStyles[\PHPNative\Tailwind\Style\Background::class])
) { ) {
if ($this->currentState == StateEnum::hover) { 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 { } 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 ( if (
isset($this->computedStyles[\PHPNative\Tailwind\Style\Border::class]) && isset($this->computedStyles[\PHPNative\Tailwind\Style\Border::class]) &&
($border = $this->computedStyles[\PHPNative\Tailwind\Style\Border::class]) ($border = $this->computedStyles[\PHPNative\Tailwind\Style\Border::class])
) { ) {
rsgl_drawRoundRectExF( // SDL3: sdl_rounded_box_ex uses (x1, y1, x2, y2) instead of (x, y, w, h)
$window, $x2 = $this->viewport->x + $this->viewport->width;
$y2 = $this->viewport->y + $this->viewport->height;
sdl_rounded_box_ex(
$renderer,
$this->viewport->x, $this->viewport->x,
$this->viewport->y, $this->viewport->y,
$this->viewport->width, $x2,
$this->viewport->height, $y2,
$border->roundTopLeft ?? 0,
$border->roundTopLeft ?? 0, $border->roundTopLeft ?? 0,
$border->roundTopRight ?? 0, $border->roundTopRight ?? 0,
$border->roundTopRight ?? 0,
$border->roundBottomLeft ?? 0,
$border->roundBottomLeft ?? 0,
$border->roundBottomRight ?? 0,
$border->roundBottomRight ?? 0, $border->roundBottomRight ?? 0,
$border->roundBottomLeft ?? 0,
$bg->color->red,
$bg->color->green,
$bg->color->blue,
$bg->color->alpha,
); );
} else { } else {
rsgl_drawRectF( sdl_render_fill_rect(
$window, $renderer,
$this->viewport->x, [
$this->viewport->y, 'x' => $this->viewport->x,
$this->viewport->width, 'y' => $this->viewport->y,
$this->viewport->height, '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) { if (!$this->visible) {
return; return;
@ -156,8 +174,8 @@ abstract class Component
// Render children // Render children
foreach ($this->children as $child) { foreach ($this->children as $child) {
$child->render($window, $textRenderer); $child->render($renderer, $textRenderer);
$child->renderContent($window, $textRenderer); $child->renderContent($renderer, $textRenderer);
} }
} }
@ -234,4 +252,32 @@ abstract class Component
} }
return false; 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); 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 public function layout(null|TextRenderer $textRenderer = null): void
{ {
// Call parent to compute styles and setup viewports // Call parent to compute styles and setup viewports
@ -131,9 +143,10 @@ class Container extends Component
} }
if (!$hasExplicitHeight) { 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; $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)); $this->contentViewport->height = max(0, $this->viewport->height - ((int) $paddingY));
} }
} }
@ -186,18 +199,20 @@ class Container extends Component
if (!$hasExplicitSize) { if (!$hasExplicitSize) {
// Need to measure natural size - do a temporary layout // 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( $tempViewport = new Viewport(
x: $this->contentViewport->x, x: $this->contentViewport->x,
y: $this->contentViewport->y, y: $this->contentViewport->y,
width: $isRow ? $availableSpace : $this->contentViewport->width, width: $isRow ? 9999 : $this->contentViewport->width,
height: $isRow ? $this->contentViewport->height : $availableSpace, height: $isRow ? $this->contentViewport->height : 9999,
windowWidth: $this->contentViewport->windowWidth, windowWidth: $this->contentViewport->windowWidth,
windowHeight: $this->contentViewport->windowHeight, windowHeight: $this->contentViewport->windowHeight,
); );
$child->setViewport($tempViewport); $child->setViewport($tempViewport);
$child->layout($textRenderer); $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; $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) { if (!$this->visible) {
return; return;
@ -322,25 +337,21 @@ class Container extends Component
// Apply scroll offset to children // Apply scroll offset to children
if ($overflow['x'] || $overflow['y']) { if ($overflow['x'] || $overflow['y']) {
// Enable scissor test for clipping // Enable clipping
$scissorX = (int) $this->contentViewport->x; $scissorX = (int) $this->contentViewport->x;
$scissorY = (int) $this->contentViewport->y; $scissorY = (int) $this->contentViewport->y;
$scissorW = (int) $this->contentViewport->width; $scissorW = (int) $this->contentViewport->width;
$scissorH = (int) $this->contentViewport->height; $scissorH = (int) $this->contentViewport->height;
// IMPORTANT: RSGL uses batch rendering! // SDL3: Set clip rect for clipping
// We need to flush the batch before/after scissor to ensure correct clipping sdl_set_render_clip_rect($renderer, [
'x' => $scissorX,
'y' => $scissorY,
'w' => $scissorW,
'h' => $scissorH
]);
// Step 1: Flush any pending draw calls before scissor // Render children with scroll offset
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)
foreach ($this->children as $child) { foreach ($this->children as $child) {
// Apply scroll offset recursively to child and all its descendants // Apply scroll offset recursively to child and all its descendants
$child->applyScrollOffset((int) $this->scrollX, (int) $this->scrollY); $child->applyScrollOffset((int) $this->scrollX, (int) $this->scrollY);
@ -354,30 +365,26 @@ class Container extends Component
$childViewport->y < ($scissorY + $scissorH); $childViewport->y < ($scissorY + $scissorH);
if ($isVisible) { if ($isVisible) {
// Render - batches draw calls $child->render($renderer, $textRenderer);
$child->render($textRenderer); $child->renderContent($renderer, $textRenderer);
$child->renderContent($textRenderer);
} }
// Restore by applying negative offset // Restore by applying negative offset
$child->applyScrollOffset((int) -$this->scrollX, (int) -$this->scrollY); $child->applyScrollOffset((int) -$this->scrollX, (int) -$this->scrollY);
} }
// Step 4: Flush the batch while scissor is still active // Disable clipping
rsgl_render($window); sdl_set_render_clip_rect($renderer, null);
// Step 5: Disable scissor test
rsgl_scissorEnd($window);
// Render scrollbars // Render scrollbars
$this->renderScrollbars($overflow); $this->renderScrollbars($renderer, $overflow);
} else { } else {
// No overflow, render normally // 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 $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; $scrollbarX = ($this->contentViewport->x + $this->contentViewport->width) - self::SCROLLBAR_WIDTH;
// Track // Track
rsgl_setColor($this->window, 200, 200, 200, 100); sdl_set_render_draw_color($renderer, 200, 200, 200, 100);
rsgl_drawRectF( sdl_render_fill_rect(
$this->window, $renderer,
(int) $scrollbarX, [
(int) $this->contentViewport->y, 'x' => (int) $scrollbarX,
(int) self::SCROLLBAR_WIDTH, 'y' => (int) $this->contentViewport->y,
(int) $scrollbarHeight, 'w' => (int) self::SCROLLBAR_WIDTH,
'h' => (int) $scrollbarHeight,
]
); );
// Thumb // Thumb - using sdl_rounded_box for rounded rectangle
rsgl_setColor( $thumbX = (int) ($scrollbarX + 2);
$this->window, $thumbW = (int) (self::SCROLLBAR_WIDTH - 4);
sdl_rounded_box(
$renderer,
$thumbX,
(int) $thumbY,
$thumbX + $thumbW,
(int) ($thumbY + $thumbHeight),
4,
$scrollbarColor[0], $scrollbarColor[0],
$scrollbarColor[1], $scrollbarColor[1],
$scrollbarColor[2], $scrollbarColor[2],
$scrollbarColor[3], $scrollbarColor[3],
); );
rsgl_drawRoundRectF(
$this->window,
(int) ($scrollbarX + 2),
(int) $thumbY,
(int) (self::SCROLLBAR_WIDTH - 4),
(int) $thumbHeight,
4,
4,
);
} }
// Horizontal scrollbar // Horizontal scrollbar
@ -435,32 +442,32 @@ class Container extends Component
$scrollbarY = ($this->contentViewport->y + $this->contentViewport->height) - self::SCROLLBAR_WIDTH; $scrollbarY = ($this->contentViewport->y + $this->contentViewport->height) - self::SCROLLBAR_WIDTH;
// Track // Track
rsgl_setColor($this->window, 200, 200, 200, 100); sdl_set_render_draw_color($renderer, 200, 200, 200, 100);
rsgl_drawRectF( sdl_render_fill_rect(
$this->window, $renderer,
(int) $this->contentViewport->x, [
(int) $scrollbarY, 'x' => (int) $this->contentViewport->x,
(int) $scrollbarWidth, 'y' => (int) $scrollbarY,
(int) self::SCROLLBAR_WIDTH, 'w' => (int) $scrollbarWidth,
'h' => (int) self::SCROLLBAR_WIDTH,
]
); );
// Thumb // Thumb - using sdl_rounded_box for rounded rectangle
rsgl_setColor( $thumbY = (int) ($scrollbarY + 2);
$this->window, $thumbH = (int) (self::SCROLLBAR_WIDTH - 4);
sdl_rounded_box(
$renderer,
(int) $thumbX,
$thumbY,
(int) ($thumbX + $thumbWidth),
$thumbY + $thumbH,
4,
$scrollbarColor[0], $scrollbarColor[0],
$scrollbarColor[1], $scrollbarColor[1],
$scrollbarColor[2], $scrollbarColor[2],
$scrollbarColor[3], $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 function __construct(
public string $text = '', public string $text = '',
public string $style = '', public string $style = '',
) {} ) {
parent::__construct($style);
}
public function setText(string $text): void 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 class Window
{ {
private mixed $window = null; private mixed $window = null;
private mixed $renderer = null;
private int $windowId = 0;
private null|Component $rootComponent = null; private null|Component $rootComponent = null;
private ?TextRenderer $textRenderer; private ?TextRenderer $textRenderer;
private float $mouseX = 0; private float $mouseX = 0;
@ -24,25 +26,50 @@ class Window
private int $x = 100, private int $x = 100,
private int $y = 100, private int $y = 100,
) { ) {
// Create window // Initialize SDL if not already done
$this->window = rgfw_createWindow($title, $x, $y, $width, $height, 0); static $sdlInitialized = false;
if (!$this->window) { if (!$sdlInitialized) {
throw new \Exception('Failed to create window'); if (!sdl_init(SDL_INIT_VIDEO)) {
throw new \Exception('Failed to initialize SDL: ' . sdl_get_error());
}
// Initialize TTF
if (!ttf_init()) {
throw new \Exception('Failed to initialize TTF: ' . sdl_get_error());
}
$sdlInitialized = true;
} }
// Initialize RSGL renderer // Create window with resizable flag for normal window decorations
if (!rsgl_init($this->window)) { // SDL_WINDOW_RESIZABLE gives you the standard window controls (close, minimize, maximize)
throw new \Exception('Failed to initialize RSGL renderer'); $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 // Initialize text renderer
$this->textRenderer = new TextRenderer($this->window); $this->textRenderer = new TextRenderer($this->renderer);
if (!$this->textRenderer->init()) { if (!$this->textRenderer->init()) {
error_log('Warning: Failed to initialize text renderer. Text rendering will not be available.'); error_log('Warning: Failed to initialize text renderer. Text rendering will not be available.');
} }
// Get actual window size // Get actual window size
$size = rgfw_window_getSize($this->window); $size = sdl_get_window_size($this->window);
$this->width = $size[0]; $this->width = $size[0];
$this->height = $size[1]; $this->height = $size[1];
$this->viewport = new Viewport( $this->viewport = new Viewport(
@ -88,7 +115,7 @@ class Window
public function shouldClose(): bool public function shouldClose(): bool
{ {
return $this->shouldClose || rgfw_window_shouldClose($this->window); return $this->shouldClose;
} }
public function close(): void public function close(): void
@ -101,48 +128,69 @@ class Window
return $this->window; return $this->window;
} }
/** public function getRendererResource(): mixed
* Handle window and input events
*/
public function handleEvents(): void
{ {
while ($event = rgfw_window_checkQueuedEvent($this->window)) { return $this->renderer;
// 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',
];
$typeName = $eventTypes[$event['type']] ?? ('UNKNOWN(' . $event['type'] . ')');
error_log("[{$this->title}] Event: {$typeName}");
}
switch ($event['type']) { public function getWindowId(): int
case RGFW_quit: {
return $this->windowId;
}
/**
* Process a single event for this window
* Called by Application with pre-filtered events
*/
public function processEvent(array $event): void
{
// Debug output - can be removed later
if (defined('DEBUG_EVENTS') && DEBUG_EVENTS) {
$eventTypes = [
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}] (ID:{$this->windowId}) Event: {$typeName}");
}
switch ($event['type']) {
case SDL_EVENT_QUIT:
case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
$this->shouldClose = true; $this->shouldClose = true;
break; break;
case RGFW_keyPressed: case SDL_EVENT_KEY_DOWN:
$keyCode = $event['keyCode'] ?? 0; $keycode = $event['keycode'] ?? 0;
if ($keyCode == RGFW_Escape) {
$this->shouldClose = true; // Propagate key event to root component
if ($this->rootComponent) {
$this->rootComponent->handleKeyDown($keycode);
} }
break; 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) // Update window dimensions (from event data)
$newWidth = $event[0] ?? $this->width; $newWidth = $event['data1'] ?? $this->width;
$newHeight = $event[1] ?? $this->height; $newHeight = $event['data2'] ?? $this->height;
$this->width = $newWidth; $this->width = $newWidth;
$this->height = $newHeight; $this->height = $newHeight;
// Update RSGL renderer size and viewport
rsgl_updateRendererSize($this->window);
// Update text renderer framebuffer // Update text renderer framebuffer
if ($this->textRenderer && $this->textRenderer->isInitialized()) { if ($this->textRenderer && $this->textRenderer->isInitialized()) {
$this->textRenderer->updateFramebuffer($newWidth, $newHeight); $this->textRenderer->updateFramebuffer($newWidth, $newHeight);
@ -157,9 +205,9 @@ class Window
$this->shouldBeReLayouted = true; $this->shouldBeReLayouted = true;
break; break;
case RGFW_mousePosChanged: case SDL_EVENT_MOUSE_MOTION:
$this->mouseX = $event[0] ?? 0; $this->mouseX = $event['x'] ?? 0;
$this->mouseY = $event[1] ?? 0; $this->mouseY = $event['y'] ?? 0;
// Propagate mouse move to root component // Propagate mouse move to root component
if ($this->rootComponent) { if ($this->rootComponent) {
@ -167,7 +215,7 @@ class Window
} }
break; break;
case RGFW_mouseButtonPressed: case SDL_EVENT_MOUSE_BUTTON_DOWN:
$button = $event['button'] ?? 0; $button = $event['button'] ?? 0;
// Propagate click to root component // Propagate click to root component
if ($this->rootComponent) { if ($this->rootComponent) {
@ -175,7 +223,7 @@ class Window
} }
break; break;
case RGFW_mouseButtonReleased: case SDL_EVENT_MOUSE_BUTTON_UP:
$button = $event['button'] ?? 0; $button = $event['button'] ?? 0;
// Propagate release to root component // Propagate release to root component
@ -184,8 +232,8 @@ class Window
} }
break; break;
case RGFW_mouseScroll: case SDL_EVENT_MOUSE_WHEEL:
$deltaY = $event[1] ?? 0; $deltaY = $event['y'] ?? 0;
// Propagate wheel to root component // Propagate wheel to root component
if ($this->rootComponent) { if ($this->rootComponent) {
@ -193,7 +241,6 @@ class Window
} }
break; break;
} }
}
} }
/** /**
@ -202,36 +249,9 @@ class Window
*/ */
public function updateMousePosition(): void public function updateMousePosition(): void
{ {
if (!$this->rootComponent) { // With SDL3, mouse position is tracked through SDL_EVENT_MOUSE_MOTION events
return; // This method is kept for compatibility but does nothing
} // Hover states are updated in handleEvents() via SDL_EVENT_MOUSE_MOTION
// 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;
}
}
} }
/** /**
@ -266,20 +286,19 @@ class Window
*/ */
public function render(): void 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 // Only render content if window has been laid out
// This can happen when windows are created during async callbacks // This can happen when windows are created during async callbacks
if ($this->hasBeenLaidOut && $this->rootComponent) { if ($this->hasBeenLaidOut && $this->rootComponent) {
rgfw_window_makeCurrent($this->window); $this->rootComponent->render($this->renderer, $this->textRenderer);
rsgl_clear($this->window, 255, 255, 255, 255); $this->rootComponent->renderContent($this->renderer, $this->textRenderer);
$this->rootComponent->render($this->window, $this->textRenderer);
$this->rootComponent->renderContent($this->window, $this->textRenderer);
rsgl_render($this->window);
rgfw_window_swapBuffers($this->window);
} }
// 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 public function setShouldBeReLayouted(bool $shouldBeReLayouted): void
{ {
$this->shouldBeReLayouted = $shouldBeReLayouted; $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);