diff --git a/examples/ButtonExample.php b/examples/ButtonExample.php deleted file mode 100644 index 5d0c9b9..0000000 --- a/examples/ButtonExample.php +++ /dev/null @@ -1,75 +0,0 @@ -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(); diff --git a/examples/FlexLayout.php b/examples/FlexLayout.php deleted file mode 100644 index b0446b6..0000000 --- a/examples/FlexLayout.php +++ /dev/null @@ -1,72 +0,0 @@ -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(); diff --git a/examples/OverflowScroll.php b/examples/OverflowScroll.php deleted file mode 100644 index 44c2270..0000000 --- a/examples/OverflowScroll.php +++ /dev/null @@ -1,64 +0,0 @@ -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(); diff --git a/examples/SimpleButtonExample.php b/examples/SimpleButtonExample.php deleted file mode 100644 index 5a7af29..0000000 --- a/examples/SimpleButtonExample.php +++ /dev/null @@ -1,71 +0,0 @@ -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(); diff --git a/examples/TestStack.php b/examples/TestStack.php deleted file mode 100644 index 709e685..0000000 --- a/examples/TestStack.php +++ /dev/null @@ -1,29 +0,0 @@ -addComponent($label); - $scrollContainer->addComponent($item); -} -$container->addComponent($scrollContainer); -$app->setRoot($container); -$app->run(); diff --git a/examples/Todo.php b/examples/Todo.php deleted file mode 100644 index 55a928c..0000000 --- a/examples/Todo.php +++ /dev/null @@ -1,43 +0,0 @@ -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(); diff --git a/examples/advanced_multi_window_example.php b/examples/advanced_multi_window_example.php deleted file mode 100644 index e7c1693..0000000 --- a/examples/advanced_multi_window_example.php +++ /dev/null @@ -1,185 +0,0 @@ -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(); diff --git a/examples/async_button_example.php b/examples/async_button_example.php deleted file mode 100644 index 20e1a7b..0000000 --- a/examples/async_button_example.php +++ /dev/null @@ -1,75 +0,0 @@ -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(); diff --git a/examples/async_http_example.php b/examples/async_http_example.php deleted file mode 100644 index c6cbc5f..0000000 --- a/examples/async_http_example.php +++ /dev/null @@ -1,126 +0,0 @@ -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(); diff --git a/examples/multi_process_windows.php b/examples/multi_process_windows.php deleted file mode 100644 index 1e97034..0000000 --- a/examples/multi_process_windows.php +++ /dev/null @@ -1,160 +0,0 @@ -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(); -} diff --git a/examples/multi_window_example.php b/examples/multi_window_example.php deleted file mode 100644 index 046ce76..0000000 --- a/examples/multi_window_example.php +++ /dev/null @@ -1,161 +0,0 @@ -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(); diff --git a/examples/simple_two_windows.php b/examples/simple_two_windows.php deleted file mode 100644 index 70c84b1..0000000 --- a/examples/simple_two_windows.php +++ /dev/null @@ -1,135 +0,0 @@ -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"; diff --git a/examples/simple_window_example.php b/examples/simple_window_example.php deleted file mode 100644 index 03f532a..0000000 --- a/examples/simple_window_example.php +++ /dev/null @@ -1,65 +0,0 @@ -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(); diff --git a/examples/test_flex_col.php b/examples/test_flex_col.php new file mode 100644 index 0000000..7134f31 --- /dev/null +++ b/examples/test_flex_col.php @@ -0,0 +1,42 @@ +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(); diff --git a/examples/test_textinput.php b/examples/test_textinput.php new file mode 100644 index 0000000..8dbd3a7 --- /dev/null +++ b/examples/test_textinput.php @@ -0,0 +1,47 @@ +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(); diff --git a/examples/test_textinput_simple.php b/examples/test_textinput_simple.php new file mode 100644 index 0000000..efc41c9 --- /dev/null +++ b/examples/test_textinput_simple.php @@ -0,0 +1,39 @@ +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(); diff --git a/examples/threaded_framework_windows.php b/examples/threaded_framework_windows.php deleted file mode 100644 index 83819c1..0000000 --- a/examples/threaded_framework_windows.php +++ /dev/null @@ -1,153 +0,0 @@ -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"; -} diff --git a/examples/threaded_two_windows.php b/examples/threaded_two_windows.php deleted file mode 100644 index 5f102d7..0000000 --- a/examples/threaded_two_windows.php +++ /dev/null @@ -1,170 +0,0 @@ -= $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"; -} diff --git a/examples/todo_app.php b/examples/todo_app.php new file mode 100644 index 0000000..dd00f73 --- /dev/null +++ b/examples/todo_app.php @@ -0,0 +1,129 @@ +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(); diff --git a/php-sdl3/.libs/sdl3.lai b/php-sdl3/.libs/sdl3.lai index 53cf407..b031f4b 100644 --- a/php-sdl3/.libs/sdl3.lai +++ b/php-sdl3/.libs/sdl3.lai @@ -14,7 +14,7 @@ library_names='sdl3.so sdl3.so sdl3.so' old_library='' # Libraries that this one depends upon. -dependency_libs=' -L/usr/local/lib -lSDL3_gfx -lSDL3' +dependency_libs=' -L/usr/local/lib -lSDL3_gfx -lSDL3_image -lSDL3_ttf -lSDL3' # Version information for sdl3. current=0 diff --git a/php-sdl3/.libs/sdl3.o b/php-sdl3/.libs/sdl3.o index 20339f9..dc00efc 100644 Binary files a/php-sdl3/.libs/sdl3.o and b/php-sdl3/.libs/sdl3.o differ diff --git a/php-sdl3/.libs/sdl3.so b/php-sdl3/.libs/sdl3.so index fe3f364..dd5a381 100755 Binary files a/php-sdl3/.libs/sdl3.so and b/php-sdl3/.libs/sdl3.so differ diff --git a/php-sdl3/.libs/sdl3_events.o b/php-sdl3/.libs/sdl3_events.o new file mode 100644 index 0000000..a5aa257 Binary files /dev/null and b/php-sdl3/.libs/sdl3_events.o differ diff --git a/php-sdl3/.libs/sdl3_image.o b/php-sdl3/.libs/sdl3_image.o new file mode 100644 index 0000000..c337a46 Binary files /dev/null and b/php-sdl3/.libs/sdl3_image.o differ diff --git a/php-sdl3/.libs/sdl3_ttf.o b/php-sdl3/.libs/sdl3_ttf.o new file mode 100644 index 0000000..04225ea Binary files /dev/null and b/php-sdl3/.libs/sdl3_ttf.o differ diff --git a/php-sdl3/Makefile b/php-sdl3/Makefile index 5def62b..6c0d65b 100644 --- a/php-sdl3/Makefile +++ b/php-sdl3/Makefile @@ -7,7 +7,7 @@ SED = /usr/bin/sed AWK = nawk SHLIB_SUFFIX_NAME = so SHLIB_DL_SUFFIX_NAME = so -shared_objects_sdl3 = sdl3.lo helper.lo +shared_objects_sdl3 = sdl3.lo helper.lo sdl3_image.lo sdl3_ttf.lo sdl3_events.lo PHP_PECL_EXTENSION = sdl3 PHP_MODULES = $(phplibdir)/sdl3.la PHP_ZEND_EX = @@ -18,7 +18,7 @@ exec_prefix = $(prefix) libdir = ${exec_prefix}/lib phpincludedir = /usr/local/include/php CC = cc -CFLAGS = -g -O2 -I/usr/include/pipewire-0.3 -I/usr/include/spa-0.2 -D_REENTRANT -I/usr/include/libdrm -I/usr/include/libdecor-0 -I/usr/local/include -I/usr/include/pipewire-0.3 -I/usr/include/spa-0.2 -D_REENTRANT -I/usr/include/libdrm -I/usr/include/libdecor-0 +CFLAGS = -g -O2 -I/usr/include/pipewire-0.3 -I/usr/include/spa-0.2 -D_REENTRANT -I/usr/include/libdrm -I/usr/include/libdecor-0 -I/usr/local/include -I/usr/include/pipewire-0.3 -I/usr/include/spa-0.2 -D_REENTRANT -I/usr/include/libdrm -I/usr/include/libdecor-0 -I/usr/include/pipewire-0.3 -I/usr/include/spa-0.2 -D_REENTRANT -I/usr/include/libdrm -I/usr/include/libdecor-0 -I/usr/include/libpng16 -I/usr/include/x86_64-linux-gnu -I/usr/include/webp -I/usr/include/pipewire-0.3 -I/usr/include/spa-0.2 -D_REENTRANT -I/usr/include/libdrm -I/usr/include/libdecor-0 -I/usr/include/harfbuzz -I/usr/include/freetype2 -I/usr/include/libpng16 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/sysprof-6 -pthread CFLAGS_CLEAN = $(CFLAGS) -D_GNU_SOURCE CPP = cc -E CPPFLAGS = -DHAVE_CONFIG_H @@ -30,7 +30,7 @@ PHP_EXECUTABLE = /usr/local/bin/php EXTRA_LDFLAGS = EXTRA_LIBS = INCLUDES = -I/usr/local/include/php -I/usr/local/include/php/main -I/usr/local/include/php/TSRM -I/usr/local/include/php/Zend -I/usr/local/include/php/ext -I/usr/local/include/php/ext/date/lib -LDFLAGS = -lSDL3 -L/usr/local/lib -lSDL3_gfx -lSDL3 +LDFLAGS = -lSDL3 -L/usr/local/lib -lSDL3_gfx -lSDL3 -lSDL3_image -lSDL3 -lSDL3_ttf -lSDL3 LIBTOOL = $(SHELL) $(top_builddir)/libtool SHELL = /bin/bash INSTALL_HEADERS = @@ -208,6 +208,15 @@ sdl3.lo: /home/thomas/projekte/phpnative/framework/php-sdl3/sdl3.c -include helper.dep helper.lo: /home/thomas/projekte/phpnative/framework/php-sdl3/helper.c $(LIBTOOL) --tag=CC --mode=compile $(CC) -I. -I/home/thomas/projekte/phpnative/framework/php-sdl3 $(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) -DZEND_COMPILE_DL_EXT=1 -c /home/thomas/projekte/phpnative/framework/php-sdl3/helper.c -o helper.lo -MMD -MF helper.dep -MT helper.lo +-include sdl3_image.dep +sdl3_image.lo: /home/thomas/projekte/phpnative/framework/php-sdl3/sdl3_image.c + $(LIBTOOL) --tag=CC --mode=compile $(CC) -I. -I/home/thomas/projekte/phpnative/framework/php-sdl3 $(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) -DZEND_COMPILE_DL_EXT=1 -c /home/thomas/projekte/phpnative/framework/php-sdl3/sdl3_image.c -o sdl3_image.lo -MMD -MF sdl3_image.dep -MT sdl3_image.lo +-include sdl3_ttf.dep +sdl3_ttf.lo: /home/thomas/projekte/phpnative/framework/php-sdl3/sdl3_ttf.c + $(LIBTOOL) --tag=CC --mode=compile $(CC) -I. -I/home/thomas/projekte/phpnative/framework/php-sdl3 $(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) -DZEND_COMPILE_DL_EXT=1 -c /home/thomas/projekte/phpnative/framework/php-sdl3/sdl3_ttf.c -o sdl3_ttf.lo -MMD -MF sdl3_ttf.dep -MT sdl3_ttf.lo +-include sdl3_events.dep +sdl3_events.lo: /home/thomas/projekte/phpnative/framework/php-sdl3/sdl3_events.c + $(LIBTOOL) --tag=CC --mode=compile $(CC) -I. -I/home/thomas/projekte/phpnative/framework/php-sdl3 $(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) -DZEND_COMPILE_DL_EXT=1 -c /home/thomas/projekte/phpnative/framework/php-sdl3/sdl3_events.c -o sdl3_events.lo -MMD -MF sdl3_events.dep -MT sdl3_events.lo $(phplibdir)/sdl3.la: ./sdl3.la $(LIBTOOL) --tag=CC --mode=install cp ./sdl3.la $(phplibdir) diff --git a/php-sdl3/Makefile.objects b/php-sdl3/Makefile.objects index b2b6eab..6f30c55 100644 --- a/php-sdl3/Makefile.objects +++ b/php-sdl3/Makefile.objects @@ -4,6 +4,15 @@ sdl3.lo: /home/thomas/projekte/phpnative/framework/php-sdl3/sdl3.c -include helper.dep helper.lo: /home/thomas/projekte/phpnative/framework/php-sdl3/helper.c $(LIBTOOL) --tag=CC --mode=compile $(CC) -I. -I/home/thomas/projekte/phpnative/framework/php-sdl3 $(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) -DZEND_COMPILE_DL_EXT=1 -c /home/thomas/projekte/phpnative/framework/php-sdl3/helper.c -o helper.lo -MMD -MF helper.dep -MT helper.lo +-include sdl3_image.dep +sdl3_image.lo: /home/thomas/projekte/phpnative/framework/php-sdl3/sdl3_image.c + $(LIBTOOL) --tag=CC --mode=compile $(CC) -I. -I/home/thomas/projekte/phpnative/framework/php-sdl3 $(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) -DZEND_COMPILE_DL_EXT=1 -c /home/thomas/projekte/phpnative/framework/php-sdl3/sdl3_image.c -o sdl3_image.lo -MMD -MF sdl3_image.dep -MT sdl3_image.lo +-include sdl3_ttf.dep +sdl3_ttf.lo: /home/thomas/projekte/phpnative/framework/php-sdl3/sdl3_ttf.c + $(LIBTOOL) --tag=CC --mode=compile $(CC) -I. -I/home/thomas/projekte/phpnative/framework/php-sdl3 $(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) -DZEND_COMPILE_DL_EXT=1 -c /home/thomas/projekte/phpnative/framework/php-sdl3/sdl3_ttf.c -o sdl3_ttf.lo -MMD -MF sdl3_ttf.dep -MT sdl3_ttf.lo +-include sdl3_events.dep +sdl3_events.lo: /home/thomas/projekte/phpnative/framework/php-sdl3/sdl3_events.c + $(LIBTOOL) --tag=CC --mode=compile $(CC) -I. -I/home/thomas/projekte/phpnative/framework/php-sdl3 $(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) -DZEND_COMPILE_DL_EXT=1 -c /home/thomas/projekte/phpnative/framework/php-sdl3/sdl3_events.c -o sdl3_events.lo -MMD -MF sdl3_events.dep -MT sdl3_events.lo $(phplibdir)/sdl3.la: ./sdl3.la $(LIBTOOL) --tag=CC --mode=install cp ./sdl3.la $(phplibdir) diff --git a/php-sdl3/button_example.php b/php-sdl3/button_example.php new file mode 100644 index 0000000..262c1d4 --- /dev/null +++ b/php-sdl3/button_example.php @@ -0,0 +1,148 @@ + 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(); diff --git a/php-sdl3/button_with_text.php b/php-sdl3/button_with_text.php new file mode 100644 index 0000000..3e64c8d --- /dev/null +++ b/php-sdl3/button_with_text.php @@ -0,0 +1,166 @@ + 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(); diff --git a/php-sdl3/config.m4 b/php-sdl3/config.m4 index fbd95ad..84e1352 100644 --- a/php-sdl3/config.m4 +++ b/php-sdl3/config.m4 @@ -6,6 +6,14 @@ PHP_ARG_WITH(sdl3_gfx, [for sdl3_gfx support], [ AS_HELP_STRING([--with-sdl3-gfx[=DIR]], [Enable sdl3_gfx support. DIR is the prefix for SDL3_gfx installation.]) ]) +PHP_ARG_WITH(sdl3_image, [for sdl3_image support], [ +AS_HELP_STRING([--with-sdl3-image[=DIR]], [Enable sdl3_image support. DIR is the prefix for SDL3_image installation.]) +]) + +PHP_ARG_WITH(sdl3_ttf, [for sdl3_ttf support], [ +AS_HELP_STRING([--with-sdl3-ttf[=DIR]], [Enable sdl3_ttf support. DIR is the prefix for SDL3_ttf installation.]) +]) + if test "$PHP_SDL3" != "no"; then if test -d "$PHP_SDL3"; then PKG_CONFIG_PATH="$PHP_SDL3/lib/pkgconfig:$PHP_SDL3/share/pkgconfig:$PKG_CONFIG_PATH" @@ -31,7 +39,33 @@ if test "$PHP_SDL3" != "no"; then ]) fi - SDL_SOURCE_FILES="sdl3.c helper.c" - + if test "$PHP_SDL3_IMAGE" != "no"; then + if test -d "$PHP_SDL3_IMAGE"; then + PKG_CONFIG_PATH="$PHP_SDL3_IMAGE/lib/pkgconfig:$PHP_SDL3_IMAGE/share/pkgconfig:$PKG_CONFIG_PATH" + fi + + PKG_CHECK_MODULES([SDL3_IMAGE], [sdl3-image >= 3.0.0], [ + CFLAGS="$CFLAGS $SDL3_IMAGE_CFLAGS" + LDFLAGS="$LDFLAGS $SDL3_IMAGE_LIBS" + ],[ + AC_MSG_ERROR([SDL3_image not found. Please check your installation or use --with-sdl3-image=/path/to/sdl3_image]) + ]) + fi + + if test "$PHP_SDL3_TTF" != "no"; then + if test -d "$PHP_SDL3_TTF"; then + PKG_CONFIG_PATH="$PHP_SDL3_TTF/lib/pkgconfig:$PHP_SDL3_TTF/share/pkgconfig:$PKG_CONFIG_PATH" + fi + + PKG_CHECK_MODULES([SDL3_TTF], [sdl3-ttf >= 3.0.0], [ + CFLAGS="$CFLAGS $SDL3_TTF_CFLAGS" + LDFLAGS="$LDFLAGS $SDL3_TTF_LIBS" + ],[ + AC_MSG_ERROR([SDL3_ttf not found. Please check your installation or use --with-sdl3-ttf=/path/to/sdl3_ttf]) + ]) + fi + + SDL_SOURCE_FILES="sdl3.c helper.c sdl3_image.c sdl3_ttf.c sdl3_events.c" + PHP_NEW_EXTENSION(sdl3, $SDL_SOURCE_FILES, $ext_shared) fi diff --git a/php-sdl3/config.nice b/php-sdl3/config.nice index d19f769..855d245 100755 --- a/php-sdl3/config.nice +++ b/php-sdl3/config.nice @@ -3,5 +3,8 @@ # Created by configure './configure' \ +'--with-sdl3=/usr/local' \ '--with-sdl3-gfx=/usr/local' \ +'--with-sdl3-image=/usr/local' \ +'--with-sdl3-ttf=/usr/local' \ "$@" diff --git a/php-sdl3/config.status b/php-sdl3/config.status index 3b780be..4c8b12a 100755 --- a/php-sdl3/config.status +++ b/php-sdl3/config.status @@ -413,7 +413,7 @@ $config_headers Report bugs to the package provider." -ac_cs_config='--with-sdl3-gfx=/usr/local' +ac_cs_config='--with-sdl3=/usr/local --with-sdl3-gfx=/usr/local --with-sdl3-image=/usr/local --with-sdl3-ttf=/usr/local' ac_cs_version="\ config.status configured by ./configure, generated by GNU Autoconf 2.72, @@ -494,7 +494,7 @@ if $ac_cs_silent; then fi if $ac_cs_recheck; then - set X /bin/bash './configure' '--with-sdl3-gfx=/usr/local' $ac_configure_extra_args --no-create --no-recursion + set X /bin/bash './configure' '--with-sdl3=/usr/local' '--with-sdl3-gfx=/usr/local' '--with-sdl3-image=/usr/local' '--with-sdl3-ttf=/usr/local' $ac_configure_extra_args --no-create --no-recursion shift \printf "%s\n" "running CONFIG_SHELL=/bin/bash $*" >&6 CONFIG_SHELL='/bin/bash' diff --git a/php-sdl3/configure b/php-sdl3/configure index 007479b..e7a3047 100755 --- a/php-sdl3/configure +++ b/php-sdl3/configure @@ -802,6 +802,10 @@ RANLIB AR ECHO LN_S +SDL3_TTF_LIBS +SDL3_TTF_CFLAGS +SDL3_IMAGE_LIBS +SDL3_IMAGE_CFLAGS SDL3_GFX_LIBS SDL3_GFX_CFLAGS SDL3_LIBS @@ -884,6 +888,8 @@ with_php_config enable_ with_sdl3 with_sdl3_gfx +with_sdl3_image +with_sdl3_ttf enable_shared enable_static enable_fast_install @@ -907,7 +913,11 @@ CPP SDL3_CFLAGS SDL3_LIBS SDL3_GFX_CFLAGS -SDL3_GFX_LIBS' +SDL3_GFX_LIBS +SDL3_IMAGE_CFLAGS +SDL3_IMAGE_LIBS +SDL3_TTF_CFLAGS +SDL3_TTF_LIBS' # Initialize some variables set by options. @@ -1544,6 +1554,14 @@ Extension: SDL3_gfx installation. + --with-sdl3-image=DIR Enable sdl3_image support. DIR is the prefix for + SDL3_image installation. + + + --with-sdl3-ttf=DIR Enable sdl3_ttf support. DIR is the prefix for + SDL3_ttf installation. + + Libtool: --enable-shared=PKGS Build shared libraries default=yes --enable-static=PKGS Build static libraries default=yes @@ -1575,6 +1593,14 @@ Some influential environment variables: C compiler flags for SDL3_GFX, overriding pkg-config SDL3_GFX_LIBS linker flags for SDL3_GFX, overriding pkg-config + SDL3_IMAGE_CFLAGS + C compiler flags for SDL3_IMAGE, overriding pkg-config + SDL3_IMAGE_LIBS + linker flags for SDL3_IMAGE, overriding pkg-config + SDL3_TTF_CFLAGS + C compiler flags for SDL3_TTF, overriding pkg-config + SDL3_TTF_LIBS + linker flags for SDL3_TTF, overriding pkg-config Use these variables to override the choices made by 'configure' or to help it to find libraries and programs with nonstandard names/locations. @@ -4801,6 +4827,108 @@ printf "%s\n" "$ext_output" >&6; } + +php_with_sdl3_image=no + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for sdl3_image support" >&5 +printf %s "checking for sdl3_image support... " >&6; } + +# Check whether --with-sdl3_image was given. +if test ${with_sdl3_image+y} +then : + withval=$with_sdl3_image; PHP_SDL3_IMAGE=$withval +else case e in #( + e) + PHP_SDL3_IMAGE=no + test "$PHP_ENABLE_ALL" && PHP_SDL3_IMAGE=$PHP_ENABLE_ALL + ;; +esac +fi + + + +ext_output="yes, shared" +ext_shared=yes +case $PHP_SDL3_IMAGE in +shared,*) + PHP_SDL3_IMAGE=$(echo "$PHP_SDL3_IMAGE"|$SED 's/^shared,//') + ;; +shared) + PHP_SDL3_IMAGE=yes + ;; +no) + ext_output=no + ext_shared=no + ;; +*) + ext_output=yes + ext_shared=no + ;; +esac + + + ext_output="yes, shared" + ext_shared=yes + test "$PHP_SDL3_IMAGE" = "no" && PHP_SDL3_IMAGE=yes + + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ext_output" >&5 +printf "%s\n" "$ext_output" >&6; } + + + + + +php_with_sdl3_ttf=no + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for sdl3_ttf support" >&5 +printf %s "checking for sdl3_ttf support... " >&6; } + +# Check whether --with-sdl3_ttf was given. +if test ${with_sdl3_ttf+y} +then : + withval=$with_sdl3_ttf; PHP_SDL3_TTF=$withval +else case e in #( + e) + PHP_SDL3_TTF=no + test "$PHP_ENABLE_ALL" && PHP_SDL3_TTF=$PHP_ENABLE_ALL + ;; +esac +fi + + + +ext_output="yes, shared" +ext_shared=yes +case $PHP_SDL3_TTF in +shared,*) + PHP_SDL3_TTF=$(echo "$PHP_SDL3_TTF"|$SED 's/^shared,//') + ;; +shared) + PHP_SDL3_TTF=yes + ;; +no) + ext_output=no + ext_shared=no + ;; +*) + ext_output=yes + ext_shared=no + ;; +esac + + + ext_output="yes, shared" + ext_shared=yes + test "$PHP_SDL3_TTF" = "no" && PHP_SDL3_TTF=yes + + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ext_output" >&5 +printf "%s\n" "$ext_output" >&6; } + + + + if test "$PHP_SDL3" != "no"; then if test -d "$PHP_SDL3"; then PKG_CONFIG_PATH="$PHP_SDL3/lib/pkgconfig:$PHP_SDL3/share/pkgconfig:$PKG_CONFIG_PATH" @@ -4970,7 +5098,177 @@ printf "%s\n" "yes" >&6; } fi fi - SDL_SOURCE_FILES="sdl3.c helper.c" + if test "$PHP_SDL3_IMAGE" != "no"; then + if test -d "$PHP_SDL3_IMAGE"; then + PKG_CONFIG_PATH="$PHP_SDL3_IMAGE/lib/pkgconfig:$PHP_SDL3_IMAGE/share/pkgconfig:$PKG_CONFIG_PATH" + fi + + +pkg_failed=no +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for sdl3-image >= 3.0.0" >&5 +printf %s "checking for sdl3-image >= 3.0.0... " >&6; } + +if test -n "$SDL3_IMAGE_CFLAGS"; then + pkg_cv_SDL3_IMAGE_CFLAGS="$SDL3_IMAGE_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"sdl3-image >= 3.0.0\""; } >&5 + ($PKG_CONFIG --exists --print-errors "sdl3-image >= 3.0.0") 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_SDL3_IMAGE_CFLAGS=`$PKG_CONFIG --cflags "sdl3-image >= 3.0.0" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$SDL3_IMAGE_LIBS"; then + pkg_cv_SDL3_IMAGE_LIBS="$SDL3_IMAGE_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"sdl3-image >= 3.0.0\""; } >&5 + ($PKG_CONFIG --exists --print-errors "sdl3-image >= 3.0.0") 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_SDL3_IMAGE_LIBS=`$PKG_CONFIG --libs "sdl3-image >= 3.0.0" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + SDL3_IMAGE_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "sdl3-image >= 3.0.0" 2>&1` + else + SDL3_IMAGE_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "sdl3-image >= 3.0.0" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$SDL3_IMAGE_PKG_ERRORS" >&5 + + + as_fn_error $? "SDL3_image not found. Please check your installation or use --with-sdl3-image=/path/to/sdl3_image" "$LINENO" 5 + +elif test $pkg_failed = untried; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } + + as_fn_error $? "SDL3_image not found. Please check your installation or use --with-sdl3-image=/path/to/sdl3_image" "$LINENO" 5 + +else + SDL3_IMAGE_CFLAGS=$pkg_cv_SDL3_IMAGE_CFLAGS + SDL3_IMAGE_LIBS=$pkg_cv_SDL3_IMAGE_LIBS + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +printf "%s\n" "yes" >&6; } + + CFLAGS="$CFLAGS $SDL3_IMAGE_CFLAGS" + LDFLAGS="$LDFLAGS $SDL3_IMAGE_LIBS" + +fi + fi + + if test "$PHP_SDL3_TTF" != "no"; then + if test -d "$PHP_SDL3_TTF"; then + PKG_CONFIG_PATH="$PHP_SDL3_TTF/lib/pkgconfig:$PHP_SDL3_TTF/share/pkgconfig:$PKG_CONFIG_PATH" + fi + + +pkg_failed=no +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for sdl3-ttf >= 3.0.0" >&5 +printf %s "checking for sdl3-ttf >= 3.0.0... " >&6; } + +if test -n "$SDL3_TTF_CFLAGS"; then + pkg_cv_SDL3_TTF_CFLAGS="$SDL3_TTF_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"sdl3-ttf >= 3.0.0\""; } >&5 + ($PKG_CONFIG --exists --print-errors "sdl3-ttf >= 3.0.0") 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_SDL3_TTF_CFLAGS=`$PKG_CONFIG --cflags "sdl3-ttf >= 3.0.0" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$SDL3_TTF_LIBS"; then + pkg_cv_SDL3_TTF_LIBS="$SDL3_TTF_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"sdl3-ttf >= 3.0.0\""; } >&5 + ($PKG_CONFIG --exists --print-errors "sdl3-ttf >= 3.0.0") 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_SDL3_TTF_LIBS=`$PKG_CONFIG --libs "sdl3-ttf >= 3.0.0" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + SDL3_TTF_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "sdl3-ttf >= 3.0.0" 2>&1` + else + SDL3_TTF_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "sdl3-ttf >= 3.0.0" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$SDL3_TTF_PKG_ERRORS" >&5 + + + as_fn_error $? "SDL3_ttf not found. Please check your installation or use --with-sdl3-ttf=/path/to/sdl3_ttf" "$LINENO" 5 + +elif test $pkg_failed = untried; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } + + as_fn_error $? "SDL3_ttf not found. Please check your installation or use --with-sdl3-ttf=/path/to/sdl3_ttf" "$LINENO" 5 + +else + SDL3_TTF_CFLAGS=$pkg_cv_SDL3_TTF_CFLAGS + SDL3_TTF_LIBS=$pkg_cv_SDL3_TTF_LIBS + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +printf "%s\n" "yes" >&6; } + + CFLAGS="$CFLAGS $SDL3_TTF_CFLAGS" + LDFLAGS="$LDFLAGS $SDL3_TTF_LIBS" + +fi + fi + + SDL_SOURCE_FILES="sdl3.c helper.c sdl3_image.c sdl3_ttf.c sdl3_events.c" ext_builddir=. @@ -5825,7 +6123,7 @@ ia64-*-hpux*) ;; *-*-irix6*) # Find out which ABI we are using. - echo '#line 5828 "configure"' > conftest.$ac_ext + echo '#line 6126 "configure"' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? @@ -7204,7 +7502,7 @@ else case e in #( LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" cat > conftest.$ac_ext <&5) + (eval echo "\"configure:7667: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "configure:7373: \$? = $ac_status" >&5 + echo "configure:7671: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -7666,11 +7964,11 @@ else case e in #( -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"configure:7669: $lt_compile\"" >&5) + (eval echo "\"configure:7967: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "configure:7673: \$? = $ac_status" >&5 + echo "configure:7971: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -7774,11 +8072,11 @@ else case e in #( -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"configure:7777: $lt_compile\"" >&5) + (eval echo "\"configure:8075: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "configure:7781: \$? = $ac_status" >&5 + echo "configure:8079: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -8239,7 +8537,7 @@ _LT_EOF # Determine the default libpath from the value encoded in an empty executable. cat > conftest.$ac_ext < conftest.$ac_ext < conftest.$ac_ext < conftest.$ac_ext < conftest.$ac_ext < conftest.$ac_ext <&5) + (eval echo "\"configure:12627: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "configure:12333: \$? = $ac_status" >&5 + echo "configure:12631: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -12434,11 +12732,11 @@ else case e in #( -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"configure:12437: $lt_compile\"" >&5) + (eval echo "\"configure:12735: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "configure:12441: \$? = $ac_status" >&5 + echo "configure:12739: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized diff --git a/php-sdl3/draw_rounded_ex.php b/php-sdl3/draw_rounded_ex.php index 9487a85..ffdf36a 100644 --- a/php-sdl3/draw_rounded_ex.php +++ b/php-sdl3/draw_rounded_ex.php @@ -33,10 +33,10 @@ sdl_render_clear($renderer); // Ein grünes, abgerundetes Rechteck mit verschiedenen Radien zeichnen // Radien: Oben-Links, Oben-Rechts, Unten-Rechts, Unten-Links -$rad_tl = 20; -$rad_tr = 20; -$rad_br = 20; -$rad_bl = 20; +$rad_tl = 40; +$rad_tr = 10; +$rad_br = 40; +$rad_bl = 10; sdl_rounded_box_ex($renderer, 100, 100, 200, 200, $rad_tl, $rad_tr, $rad_br, $rad_bl, 30, 200, 70, 255); // Alles auf dem Bildschirm anzeigen diff --git a/php-sdl3/helper.h b/php-sdl3/helper.h index eb22e68..9bb85f4 100644 --- a/php-sdl3/helper.h +++ b/php-sdl3/helper.h @@ -2,34 +2,33 @@ #define PHP_SDL3_HELPER #include +#include #include "math.h" -static void draw_hline(SDL_Renderer *renderer, int x1, int x2, int y) { - if (x2 < x1) return; - SDL_RenderLine(renderer, x1, y, x2, y); -} -// Zeichnet einen gefüllten Viertel-Kreis (filled quarter circle). +// Zeichnet einen gefüllten Viertel-Kreis mit Anti-Aliasing (filled quarter circle). // quadrant: 0 = top-left, 1 = top-right, 2 = bottom-right, 3 = bottom-left static void filled_quarter_circle(SDL_Renderer *renderer, int cx, int cy, int r, int quadrant) { if (r <= 0) return; - int r2 = r * r; - for (int dy = 0; dy <= r; ++dy) { - // dy ist vertikale Distanz vom Kreiszentrum (0..r) - int dx = (int)floor(sqrt((double)r2 - (double)dy * dy)); - switch (quadrant) { - case 0: // top-left: y = cy - dy, x in [cx - dx, cx] - draw_hline(renderer, cx - dx, cx, cy - dy); - break; - case 1: // top-right: y = cy - dy, x in [cx, cx + dx] - draw_hline(renderer, cx, cx + dx, cy - dy); - break; - case 2: // bottom-right: y = cy + dy, x in [cx, cx + dx] - draw_hline(renderer, cx, cx + dx, cy + dy); - break; - case 3: // bottom-left: y = cy + dy, x in [cx - dx, cx] - draw_hline(renderer, cx - dx, cx, cy + dy); - break; - } + + // Get current draw color + Uint8 r_col, g_col, b_col, a_col; + SDL_GetRenderDrawColor(renderer, &r_col, &g_col, &b_col, &a_col); + + // Use SDL_gfx filledCircle with proper quadrant rendering + // We'll draw the arc using the primitives library + switch (quadrant) { + case 0: // top-left + filledPieRGBA(renderer, cx, cy, r, 180, 270, r_col, g_col, b_col, a_col); + break; + case 1: // top-right + filledPieRGBA(renderer, cx, cy, r, 270, 360, r_col, g_col, b_col, a_col); + break; + case 2: // bottom-right + filledPieRGBA(renderer, cx, cy, r, 0, 90, r_col, g_col, b_col, a_col); + break; + case 3: // bottom-left + filledPieRGBA(renderer, cx, cy, r, 90, 180, r_col, g_col, b_col, a_col); + break; } } diff --git a/php-sdl3/libtool b/php-sdl3/libtool index 2658e46..1e0e0e4 100755 --- a/php-sdl3/libtool +++ b/php-sdl3/libtool @@ -85,7 +85,7 @@ AR_FLAGS="cru" LTCC="cc" # LTCC compiler flags. -LTCFLAGS="-g -O2 -I/usr/include/pipewire-0.3 -I/usr/include/spa-0.2 -D_REENTRANT -I/usr/include/libdrm -I/usr/include/libdecor-0 -I/usr/local/include -I/usr/include/pipewire-0.3 -I/usr/include/spa-0.2 -D_REENTRANT -I/usr/include/libdrm -I/usr/include/libdecor-0 " +LTCFLAGS="-g -O2 -I/usr/include/pipewire-0.3 -I/usr/include/spa-0.2 -D_REENTRANT -I/usr/include/libdrm -I/usr/include/libdecor-0 -I/usr/local/include -I/usr/include/pipewire-0.3 -I/usr/include/spa-0.2 -D_REENTRANT -I/usr/include/libdrm -I/usr/include/libdecor-0 -I/usr/include/pipewire-0.3 -I/usr/include/spa-0.2 -D_REENTRANT -I/usr/include/libdrm -I/usr/include/libdecor-0 -I/usr/include/libpng16 -I/usr/include/x86_64-linux-gnu -I/usr/include/webp -I/usr/include/pipewire-0.3 -I/usr/include/spa-0.2 -D_REENTRANT -I/usr/include/libdrm -I/usr/include/libdecor-0 -I/usr/include/harfbuzz -I/usr/include/freetype2 -I/usr/include/libpng16 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/sysprof-6 -pthread " # A language-specific compiler. CC="cc" diff --git a/php-sdl3/modules/sdl3.la b/php-sdl3/modules/sdl3.la index 53cf407..b031f4b 100644 --- a/php-sdl3/modules/sdl3.la +++ b/php-sdl3/modules/sdl3.la @@ -14,7 +14,7 @@ library_names='sdl3.so sdl3.so sdl3.so' old_library='' # Libraries that this one depends upon. -dependency_libs=' -L/usr/local/lib -lSDL3_gfx -lSDL3' +dependency_libs=' -L/usr/local/lib -lSDL3_gfx -lSDL3_image -lSDL3_ttf -lSDL3' # Version information for sdl3. current=0 diff --git a/php-sdl3/modules/sdl3.so b/php-sdl3/modules/sdl3.so index fe3f364..dd5a381 100755 Binary files a/php-sdl3/modules/sdl3.so and b/php-sdl3/modules/sdl3.so differ diff --git a/php-sdl3/multi_window_example.php b/php-sdl3/multi_window_example.php new file mode 100644 index 0000000..0990fea --- /dev/null +++ b/php-sdl3/multi_window_example.php @@ -0,0 +1,243 @@ += $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(); diff --git a/php-sdl3/php_sdl3.h b/php-sdl3/php_sdl3.h index ab0ca69..ab5a5c7 100644 --- a/php-sdl3/php_sdl3.h +++ b/php-sdl3/php_sdl3.h @@ -6,4 +6,10 @@ extern zend_module_entry sdl3_module_entry; #define PHP_SDL3_VERSION "0.1.0" +// Resource handles exportieren, damit sie in anderen Modulen verfügbar sind +extern int le_sdl_window; +extern int le_sdl_renderer; +extern int le_sdl_texture; +extern int le_sdl_surface; + #endif diff --git a/php-sdl3/sdl3.c b/php-sdl3/sdl3.c index 22e2592..f32270f 100644 --- a/php-sdl3/sdl3.c +++ b/php-sdl3/sdl3.c @@ -6,13 +6,17 @@ #include "php.h" #include "php_sdl3.h" #include "helper.h" +#include "sdl3_image.h" +#include "sdl3_ttf.h" +#include "sdl3_events.h" #include #include #include -// Resource handles -static int le_sdl_window; -static int le_sdl_renderer; +// Resource handles (nicht static, damit sie in anderen Modulen verfügbar sind) +int le_sdl_window; +int le_sdl_renderer; +int le_sdl_texture; // Destructor for window resource static void sdl_window_dtor(zend_resource *rsrc) { @@ -30,9 +34,41 @@ static void sdl_renderer_dtor(zend_resource *rsrc) { } } +// Destructor for texture resource +static void sdl_texture_dtor(zend_resource *rsrc) { + SDL_Texture *tex = (SDL_Texture *)rsrc->ptr; + if (tex) { + SDL_DestroyTexture(tex); + } +} + PHP_MINIT_FUNCTION(sdl3) { le_sdl_window = zend_register_list_destructors_ex(sdl_window_dtor, NULL, "SDL_Window", module_number); le_sdl_renderer = zend_register_list_destructors_ex(sdl_renderer_dtor, NULL, "SDL_Renderer", module_number); + le_sdl_texture = zend_register_list_destructors_ex(sdl_texture_dtor, NULL, "SDL_Texture", module_number); + + // Registriere SDL3_image und SDL3_ttf Resources + sdl3_image_register_resources(module_number); + sdl3_ttf_register_resources(module_number); + + // Registriere Event-Konstanten + sdl3_events_register_constants(module_number); + + // SDL Init Flags + REGISTER_LONG_CONSTANT("SDL_INIT_VIDEO", SDL_INIT_VIDEO, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SDL_INIT_AUDIO", SDL_INIT_AUDIO, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SDL_INIT_EVENTS", SDL_INIT_EVENTS, CONST_CS | CONST_PERSISTENT); + + // SDL Window Flags + REGISTER_LONG_CONSTANT("SDL_WINDOW_FULLSCREEN", SDL_WINDOW_FULLSCREEN, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SDL_WINDOW_OPENGL", SDL_WINDOW_OPENGL, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SDL_WINDOW_HIDDEN", SDL_WINDOW_HIDDEN, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SDL_WINDOW_BORDERLESS", SDL_WINDOW_BORDERLESS, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SDL_WINDOW_RESIZABLE", SDL_WINDOW_RESIZABLE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SDL_WINDOW_MINIMIZED", SDL_WINDOW_MINIMIZED, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SDL_WINDOW_MAXIMIZED", SDL_WINDOW_MAXIMIZED, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SDL_WINDOW_HIGH_PIXEL_DENSITY", SDL_WINDOW_HIGH_PIXEL_DENSITY, CONST_CS | CONST_PERSISTENT); + return SUCCESS; } @@ -54,19 +90,60 @@ PHP_FUNCTION(sdl_quit) { PHP_FUNCTION(sdl_create_window) { char *title; size_t title_len; - zend_long w, h; + zend_long w, h, flags = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sll", &title, &title_len, &w, &h) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sll|l", &title, &title_len, &w, &h, &flags) == FAILURE) { RETURN_THROWS(); } - SDL_Window *win = SDL_CreateWindow(title, (int)w, (int)h, 0); + SDL_Window *win = SDL_CreateWindow(title, (int)w, (int)h, (SDL_WindowFlags)flags); if (!win) { RETURN_FALSE; } RETURN_RES(zend_register_resource(win, le_sdl_window)); } +PHP_FUNCTION(sdl_destroy_window) { + zval *win_res; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &win_res) == FAILURE) { + RETURN_THROWS(); + } + + // This will call the destructor and free the resource + zend_list_close(Z_RES_P(win_res)); + RETURN_TRUE; +} + +PHP_FUNCTION(sdl_destroy_renderer) { + zval *ren_res; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &ren_res) == FAILURE) { + RETURN_THROWS(); + } + + // This will call the destructor and free the resource + zend_list_close(Z_RES_P(ren_res)); + RETURN_TRUE; +} + +PHP_FUNCTION(sdl_get_window_id) { + zval *win_res; + SDL_Window *win; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &win_res) == FAILURE) { + RETURN_THROWS(); + } + + win = (SDL_Window *)zend_fetch_resource(Z_RES_P(win_res), "SDL_Window", le_sdl_window); + if (!win) { + RETURN_FALSE; + } + + SDL_WindowID window_id = SDL_GetWindowID(win); + RETURN_LONG(window_id); +} + PHP_FUNCTION(sdl_create_renderer) { zval *win_res; SDL_Window *win; @@ -185,6 +262,79 @@ PHP_FUNCTION(sdl_get_error) { RETURN_STRING(error); } +PHP_FUNCTION(sdl_create_texture_from_surface) { + zval *ren_res, *surf_res; + SDL_Renderer *renderer; + SDL_Surface *surface; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr", &ren_res, &surf_res) == FAILURE) { + RETURN_THROWS(); + } + + renderer = (SDL_Renderer *)zend_fetch_resource(Z_RES_P(ren_res), "SDL_Renderer", le_sdl_renderer); + if (!renderer) { + RETURN_FALSE; + } + + surface = (SDL_Surface *)zend_fetch_resource(Z_RES_P(surf_res), "SDL_Surface", le_sdl_surface); + if (!surface) { + RETURN_FALSE; + } + + SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, surface); + if (!texture) { + php_error_docref(NULL, E_WARNING, "Failed to create texture: %s", SDL_GetError()); + RETURN_FALSE; + } + + RETURN_RES(zend_register_resource(texture, le_sdl_texture)); +} + +PHP_FUNCTION(sdl_render_texture) { + zval *ren_res, *tex_res; + SDL_Renderer *renderer; + SDL_Texture *texture; + zval *dst_arr = NULL; + HashTable *dst_ht; + zval *data; + zend_long x, y, w, h; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr|a", &ren_res, &tex_res, &dst_arr) == FAILURE) { + RETURN_THROWS(); + } + + renderer = (SDL_Renderer *)zend_fetch_resource(Z_RES_P(ren_res), "SDL_Renderer", le_sdl_renderer); + if (!renderer) { + RETURN_FALSE; + } + + texture = (SDL_Texture *)zend_fetch_resource(Z_RES_P(tex_res), "SDL_Texture", le_sdl_texture); + if (!texture) { + RETURN_FALSE; + } + + if (dst_arr != NULL) { + // Mit Zielrechteck + dst_ht = Z_ARRVAL_P(dst_arr); + if (((data = zend_hash_str_find(dst_ht, "x", 1)) != NULL && (x = zval_get_long(data), true)) && + ((data = zend_hash_str_find(dst_ht, "y", 1)) != NULL && (y = zval_get_long(data), true)) && + ((data = zend_hash_str_find(dst_ht, "w", 1)) != NULL && (w = zval_get_long(data), true)) && + ((data = zend_hash_str_find(dst_ht, "h", 1)) != NULL && (h = zval_get_long(data), true))) { + + SDL_FRect dstrect = {(float)x, (float)y, (float)w, (float)h}; + SDL_RenderTexture(renderer, texture, NULL, &dstrect); + RETURN_TRUE; + } + + zend_throw_error(NULL, "Invalid destination rectangle. Expected ['x'=>int, 'y'=>int, 'w'=>int, 'h'=>int]"); + RETURN_THROWS(); + } else { + // Ohne Zielrechteck - volle Größe + SDL_RenderTexture(renderer, texture, NULL, NULL); + RETURN_TRUE; + } +} + PHP_FUNCTION(sdl_rounded_box) { zval *ren_res; @@ -237,18 +387,40 @@ PHP_FUNCTION(sdl_rounded_box_ex) int r_right = (rad_tr > rad_br) ? rad_tr : rad_br; // 1) center vertical band (zwischen links und rechts Radien), ganze Höhe - SDL_FRect center = { x1 + rad_tl, y1, x2 - x1-rad_tl-rad_br, y2-y1 }; + SDL_FRect center = { x1 + r_left, y1, x2 - x1-r_left-r_right, y2-y1 }; if (center.w > 0 && center.h > 0) SDL_RenderFillRect(ren, ¢er); // 2) left vertical rectangle (zwischen oberen und unteren Ecken links) - SDL_FRect leftRect = { x1, y1 + rad_tl, rad_bl, y2-rad_tl-rad_bl-y1 }; + SDL_FRect leftRect = { x1, y1 + rad_tl, r_left, y2 - y1 - rad_tl - rad_bl }; if (leftRect.w > 0 && leftRect.h > 0) SDL_RenderFillRect(ren, &leftRect); // 3) right vertical rectangle (zwischen oberen und unteren Ecken rechts) - SDL_FRect rightRect = { x2 - r_right, y1 + rad_tr, r_right, y2-y1-rad_tr - rad_br }; + SDL_FRect rightRect = { x2 - r_right, y1 + rad_tr, r_right, y2 - y1 - rad_tr - rad_br }; if (rightRect.w > 0 && rightRect.h > 0) SDL_RenderFillRect(ren, &rightRect); - // 4) vier gefüllte Viertel-Kreise in den Ecken + // 4) Horizontale Füllrechtecke für Lücken zwischen Ecken und vertikalen Rechtecken + // Oben links: wenn rad_tl < r_left + if (rad_tl < r_left) { + SDL_FRect topLeft = { x1 + rad_tl, y1, r_left - rad_tl, rad_tl }; + if (topLeft.w > 0 && topLeft.h > 0) SDL_RenderFillRect(ren, &topLeft); + } + // Oben rechts: wenn rad_tr < r_right + if (rad_tr < r_right) { + SDL_FRect topRight = { x2 - r_right, y1, r_right - rad_tr, rad_tr }; + if (topRight.w > 0 && topRight.h > 0) SDL_RenderFillRect(ren, &topRight); + } + // Unten rechts: wenn rad_br < r_right + if (rad_br < r_right) { + SDL_FRect bottomRight = { x2 - r_right, y2 - rad_br, r_right - rad_br, rad_br }; + if (bottomRight.w > 0 && bottomRight.h > 0) SDL_RenderFillRect(ren, &bottomRight); + } + // Unten links: wenn rad_bl < r_left + if (rad_bl < r_left) { + SDL_FRect bottomLeft = { x1 + rad_bl, y2 - rad_bl, r_left - rad_bl, rad_bl }; + if (bottomLeft.w > 0 && bottomLeft.h > 0) SDL_RenderFillRect(ren, &bottomLeft); + } + + // 5) vier gefüllte Viertel-Kreise in den Ecken if (rad_tl > 0) filled_quarter_circle(ren, x1 + rad_tl, y1 + rad_tl, rad_tl, 0); if (rad_tr > 0) filled_quarter_circle(ren, x2 - rad_tr, y1 + rad_tr, rad_tr, 1); if (rad_br > 0) filled_quarter_circle(ren, x2 - rad_br, y2- rad_br, rad_br, 2); @@ -257,6 +429,99 @@ PHP_FUNCTION(sdl_rounded_box_ex) RETURN_TRUE; } +PHP_FUNCTION(sdl_set_render_clip_rect) { + zval *ren_res; + SDL_Renderer *ren; + zval *rect_arr = NULL; + HashTable *rect_ht; + zval *data; + zend_long x, y, w, h; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|a!", &ren_res, &rect_arr) == FAILURE) { + RETURN_THROWS(); + } + + ren = (SDL_Renderer *)zend_fetch_resource(Z_RES_P(ren_res), "SDL_Renderer", le_sdl_renderer); + if (!ren) { + RETURN_FALSE; + } + + if (rect_arr == NULL || Z_TYPE_P(rect_arr) == IS_NULL) { + // Disable clipping + SDL_SetRenderClipRect(ren, NULL); + RETURN_TRUE; + } + + rect_ht = Z_ARRVAL_P(rect_arr); + if (((data = zend_hash_str_find(rect_ht, "x", 1)) != NULL && (x = zval_get_long(data), true)) && + ((data = zend_hash_str_find(rect_ht, "y", 1)) != NULL && (y = zval_get_long(data), true)) && + ((data = zend_hash_str_find(rect_ht, "w", 1)) != NULL && (w = zval_get_long(data), true)) && + ((data = zend_hash_str_find(rect_ht, "h", 1)) != NULL && (h = zval_get_long(data), true))) { + + SDL_Rect rect = {(int)x, (int)y, (int)w, (int)h}; + SDL_SetRenderClipRect(ren, &rect); + RETURN_TRUE; + } + + zend_throw_error(NULL, "Invalid rectangle array passed to sdl_set_render_clip_rect. Expected ['x'=>int, 'y'=>int, 'w'=>int, 'h'=>int] or null"); + RETURN_THROWS(); +} + +PHP_FUNCTION(sdl_get_window_size) { + zval *win_res; + SDL_Window *win; + int w, h; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &win_res) == FAILURE) { + RETURN_THROWS(); + } + + win = (SDL_Window *)zend_fetch_resource(Z_RES_P(win_res), "SDL_Window", le_sdl_window); + if (!win) { + RETURN_FALSE; + } + + SDL_GetWindowSize(win, &w, &h); + + array_init(return_value); + add_index_long(return_value, 0, w); + add_index_long(return_value, 1, h); +} + +PHP_FUNCTION(sdl_start_text_input) { + zval *win_res; + SDL_Window *win; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &win_res) == FAILURE) { + RETURN_THROWS(); + } + + win = (SDL_Window *)zend_fetch_resource(Z_RES_P(win_res), "SDL_Window", le_sdl_window); + if (!win) { + RETURN_FALSE; + } + + SDL_StartTextInput(win); + RETURN_TRUE; +} + +PHP_FUNCTION(sdl_stop_text_input) { + zval *win_res; + SDL_Window *win; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &win_res) == FAILURE) { + RETURN_THROWS(); + } + + win = (SDL_Window *)zend_fetch_resource(Z_RES_P(win_res), "SDL_Window", le_sdl_window); + if (!win) { + RETURN_FALSE; + } + + SDL_StopTextInput(win); + RETURN_TRUE; +} + ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_init, 0, 0, 1) ZEND_ARG_INFO(0, flags) ZEND_END_ARG_INFO() @@ -268,6 +533,19 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_create_window, 0, 0, 3) ZEND_ARG_INFO(0, title) ZEND_ARG_INFO(0, w) ZEND_ARG_INFO(0, h) + ZEND_ARG_INFO(0, flags) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_destroy_window, 0, 0, 1) + ZEND_ARG_INFO(0, window) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_destroy_renderer, 0, 0, 1) + ZEND_ARG_INFO(0, renderer) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_get_window_id, 0, 0, 1) + ZEND_ARG_INFO(0, window) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_create_renderer, 0, 0, 1) @@ -302,6 +580,17 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_get_error, 0, 0, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_create_texture_from_surface, 0, 0, 2) + ZEND_ARG_INFO(0, renderer) + ZEND_ARG_INFO(0, surface) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_render_texture, 0, 0, 2) + ZEND_ARG_INFO(0, renderer) + ZEND_ARG_INFO(0, texture) + ZEND_ARG_INFO(0, dstrect) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_rounded_box, 0, 0, 10) ZEND_ARG_INFO(0, renderer) ZEND_ARG_INFO(0, x1) @@ -331,10 +620,31 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_rounded_box_ex, 0, 0, 13) ZEND_ARG_INFO(0, a) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_set_render_clip_rect, 0, 0, 1) + ZEND_ARG_INFO(0, renderer) + ZEND_ARG_INFO(0, rect) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_get_window_size, 0, 0, 1) + ZEND_ARG_INFO(0, window) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_start_text_input, 0, 0, 1) + ZEND_ARG_INFO(0, window) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_sdl_stop_text_input, 0, 0, 1) + ZEND_ARG_INFO(0, window) +ZEND_END_ARG_INFO() + const zend_function_entry sdl3_functions[] = { + // SDL3 Core PHP_FE(sdl_init, arginfo_sdl_init) PHP_FE(sdl_quit, arginfo_sdl_quit) PHP_FE(sdl_create_window, arginfo_sdl_create_window) + PHP_FE(sdl_destroy_window, arginfo_sdl_destroy_window) + PHP_FE(sdl_destroy_renderer, arginfo_sdl_destroy_renderer) + PHP_FE(sdl_get_window_id, arginfo_sdl_get_window_id) PHP_FE(sdl_create_renderer, arginfo_sdl_create_renderer) PHP_FE(sdl_set_render_draw_color, arginfo_sdl_set_render_draw_color) PHP_FE(sdl_render_clear, arginfo_sdl_render_clear) @@ -342,8 +652,33 @@ const zend_function_entry sdl3_functions[] = { PHP_FE(sdl_render_present, arginfo_sdl_render_present) PHP_FE(sdl_delay, arginfo_sdl_delay) PHP_FE(sdl_get_error, arginfo_sdl_get_error) + PHP_FE(sdl_create_texture_from_surface, arginfo_sdl_create_texture_from_surface) + PHP_FE(sdl_render_texture, arginfo_sdl_render_texture) PHP_FE(sdl_rounded_box, arginfo_sdl_rounded_box) PHP_FE(sdl_rounded_box_ex, arginfo_sdl_rounded_box_ex) + PHP_FE(sdl_set_render_clip_rect, arginfo_sdl_set_render_clip_rect) + PHP_FE(sdl_get_window_size, arginfo_sdl_get_window_size) + PHP_FE(sdl_start_text_input, arginfo_sdl_start_text_input) + PHP_FE(sdl_stop_text_input, arginfo_sdl_stop_text_input) + + // SDL3_image + PHP_FE(img_load, arginfo_img_load) + PHP_FE(img_load_texture, arginfo_img_load_texture) + + // SDL3_ttf + PHP_FE(ttf_init, arginfo_ttf_init) + PHP_FE(ttf_open_font, arginfo_ttf_open_font) + PHP_FE(ttf_close_font, arginfo_ttf_close_font) + PHP_FE(ttf_render_text_solid, arginfo_ttf_render_text_solid) + PHP_FE(ttf_render_text_blended, arginfo_ttf_render_text_blended) + PHP_FE(ttf_render_text_shaded, arginfo_ttf_render_text_shaded) + PHP_FE(ttf_size_text, arginfo_ttf_size_text) + + // SDL3 Events + PHP_FE(sdl_poll_event, arginfo_sdl_poll_event) + PHP_FE(sdl_wait_event, arginfo_sdl_wait_event) + PHP_FE(sdl_wait_event_timeout, arginfo_sdl_wait_event_timeout) + PHP_FE_END }; diff --git a/php-sdl3/sdl3.dep b/php-sdl3/sdl3.dep index ed877e9..febfc8e 100644 --- a/php-sdl3/sdl3.dep +++ b/php-sdl3/sdl3.dep @@ -91,4 +91,7 @@ sdl3.lo: /home/thomas/projekte/phpnative/framework/php-sdl3/sdl3.c \ /usr/local/include/php/Zend/zend_constants.h \ /usr/local/include/php/main/php_reentrancy.h \ /home/thomas/projekte/phpnative/framework/php-sdl3/php_sdl3.h \ - /home/thomas/projekte/phpnative/framework/php-sdl3/helper.h + /home/thomas/projekte/phpnative/framework/php-sdl3/helper.h \ + /home/thomas/projekte/phpnative/framework/php-sdl3/sdl3_image.h \ + /home/thomas/projekte/phpnative/framework/php-sdl3/sdl3_ttf.h \ + /home/thomas/projekte/phpnative/framework/php-sdl3/sdl3_events.h diff --git a/php-sdl3/sdl3.la b/php-sdl3/sdl3.la index f39669b..db718ad 100644 --- a/php-sdl3/sdl3.la +++ b/php-sdl3/sdl3.la @@ -14,7 +14,7 @@ library_names='sdl3.so sdl3.so sdl3.so' old_library='' # Libraries that this one depends upon. -dependency_libs=' -L/usr/local/lib -lSDL3_gfx -lSDL3' +dependency_libs=' -L/usr/local/lib -lSDL3_gfx -lSDL3_image -lSDL3_ttf -lSDL3' # Version information for sdl3. current=0 diff --git a/php-sdl3/sdl3_events.c b/php-sdl3/sdl3_events.c new file mode 100644 index 0000000..9f632aa --- /dev/null +++ b/php-sdl3/sdl3_events.c @@ -0,0 +1,175 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_sdl3.h" +#include "sdl3_events.h" +#include + +// 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); +} diff --git a/php-sdl3/sdl3_events.dep b/php-sdl3/sdl3_events.dep new file mode 100644 index 0000000..18a2b1c --- /dev/null +++ b/php-sdl3/sdl3_events.dep @@ -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 diff --git a/php-sdl3/sdl3_events.h b/php-sdl3/sdl3_events.h new file mode 100644 index 0000000..3c94642 --- /dev/null +++ b/php-sdl3/sdl3_events.h @@ -0,0 +1,26 @@ +#ifndef PHP_SDL3_EVENTS_H +#define PHP_SDL3_EVENTS_H + +#include "php.h" +#include + +// 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 diff --git a/php-sdl3/sdl3_events.lo b/php-sdl3/sdl3_events.lo new file mode 100644 index 0000000..7634471 --- /dev/null +++ b/php-sdl3/sdl3_events.lo @@ -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 + diff --git a/php-sdl3/sdl3_image.c b/php-sdl3/sdl3_image.c new file mode 100644 index 0000000..47bda16 --- /dev/null +++ b/php-sdl3/sdl3_image.c @@ -0,0 +1,66 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_sdl3.h" +#include "sdl3_image.h" +#include +#include + +// 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)); +} diff --git a/php-sdl3/sdl3_image.dep b/php-sdl3/sdl3_image.dep new file mode 100644 index 0000000..4f51eca --- /dev/null +++ b/php-sdl3/sdl3_image.dep @@ -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 diff --git a/php-sdl3/sdl3_image.h b/php-sdl3/sdl3_image.h new file mode 100644 index 0000000..e15b0c1 --- /dev/null +++ b/php-sdl3/sdl3_image.h @@ -0,0 +1,31 @@ +#ifndef PHP_SDL3_IMAGE_H +#define PHP_SDL3_IMAGE_H + +#include "php.h" +#include +#include + +// 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 diff --git a/php-sdl3/sdl3_image.lo b/php-sdl3/sdl3_image.lo new file mode 100644 index 0000000..34a8cba --- /dev/null +++ b/php-sdl3/sdl3_image.lo @@ -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 + diff --git a/php-sdl3/sdl3_ttf.c b/php-sdl3/sdl3_ttf.c new file mode 100644 index 0000000..3bb6ef9 --- /dev/null +++ b/php-sdl3/sdl3_ttf.c @@ -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 +#include + +// 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); +} diff --git a/php-sdl3/sdl3_ttf.dep b/php-sdl3/sdl3_ttf.dep new file mode 100644 index 0000000..fdc3627 --- /dev/null +++ b/php-sdl3/sdl3_ttf.dep @@ -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 diff --git a/php-sdl3/sdl3_ttf.h b/php-sdl3/sdl3_ttf.h new file mode 100644 index 0000000..5733e3c --- /dev/null +++ b/php-sdl3/sdl3_ttf.h @@ -0,0 +1,71 @@ +#ifndef PHP_SDL3_TTF_H +#define PHP_SDL3_TTF_H + +#include "php.h" +#include +#include + +// 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 diff --git a/php-sdl3/sdl3_ttf.lo b/php-sdl3/sdl3_ttf.lo new file mode 100644 index 0000000..9ea4777 --- /dev/null +++ b/php-sdl3/sdl3_ttf.lo @@ -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 + diff --git a/php-sdl3/test_functions.php b/php-sdl3/test_functions.php new file mode 100644 index 0000000..9726395 --- /dev/null +++ b/php-sdl3/test_functions.php @@ -0,0 +1,35 @@ +nextWindowId++; + $this->windows[$windowId] = $window; + } + /** * Get all windows */ @@ -75,12 +88,52 @@ class Application echo 'Layoutout: ' . PHP_EOL; } } - // Poll events globally for all windows (event queue mode, if available) - rgfw_pollEvents(); + // SDL3: Poll all events globally and distribute to the correct windows + $events = []; + while ($event = sdl_poll_event()) { + $events[] = $event; + } - // Handle events for all windows (now that layout is done) - foreach ($this->windows as $windowId => $window) { - $window->handleEvents(); + // Distribute events to windows based on window_id + foreach ($events as $event) { + $eventWindowId = $event['window_id'] ?? null; + + // Debug event distribution + if (defined('DEBUG_EVENTS') && DEBUG_EVENTS && $eventWindowId !== null) { + $eventType = $event['type']; + error_log("[Application] Distributing event type={$eventType} to window_id={$eventWindowId}"); + } + + // Global events (like QUIT) go to all windows + if ($eventWindowId === null) { + foreach ($this->windows as $windowId => $window) { + $window->processEvent($event); + } + } else { + // Window-specific events go to the correct window + $delivered = false; + foreach ($this->windows as $windowId => $window) { + if ($window->getWindowId() === $eventWindowId) { + $window->processEvent($event); + $delivered = true; + break; + } + } + + if (!$delivered) { + // Check if this is a recently closed window (expected) + if (!in_array($eventWindowId, $this->closedWindowIds)) { + if (defined('DEBUG_EVENTS') && DEBUG_EVENTS) { + error_log("[Application] WARNING: Event for window_id={$eventWindowId} could not be delivered (window not found)"); + } + } else { + // This is a recently closed window, events are expected + if (defined('DEBUG_EVENTS') && DEBUG_EVENTS) { + error_log("[Application] Ignoring event for recently closed window_id={$eventWindowId}"); + } + } + } + } } // Update async tasks (global) @@ -98,13 +151,25 @@ class Application // Remove closed windows foreach ($this->windows as $windowId => $window) { if ($window->shouldClose()) { - $window->cleanup(); // Frees the TextRenderer to prevent "fat text" bug - rgfw_window_close($window->getWindowResource()); // Now, safely close the native window + // Track this window ID as recently closed + $sdlWindowId = $window->getWindowId(); + if (!in_array($sdlWindowId, $this->closedWindowIds)) { + $this->closedWindowIds[] = $sdlWindowId; + } + + // Explicitly destroy the window (closes SDL window, renderer, and text renderer) + $window->destroy(); unset($this->windows[$windowId]); $oneWindowIsClosed = true; } } + // Clean up old closed window IDs after 10 frames (to prevent memory leak) + // This gives SDL enough time to stop sending events for closed windows + if (count($this->closedWindowIds) > 10) { + array_shift($this->closedWindowIds); + } + if ($oneWindowIsClosed) { foreach ($this->windows as $windowId => $window) { $window->setShouldBeReLayouted(true); diff --git a/src/Framework/TextRenderer.php b/src/Framework/TextRenderer.php index df9bdaf..4d7ff44 100644 --- a/src/Framework/TextRenderer.php +++ b/src/Framework/TextRenderer.php @@ -8,28 +8,33 @@ namespace PHPNative\Framework; class TextRenderer { - private $window; + private $renderer; + private $font = null; private bool $initialized = false; private string $fontPath; private int $fontSize; + private float $colorR = 1.0; + private float $colorG = 1.0; + private float $colorB = 1.0; + private float $colorA = 1.0; /** * Constructor * - * @param resource $window RGFW window resource + * @param resource $renderer SDL Renderer resource */ - public function __construct($window) + public function __construct($renderer) { - $this->window = $window; + $this->renderer = $renderer; } /** - * Initialize RFont with a font file + * Initialize TTF with a font file * * @param string|null $fontPath Path to TTF font file * @param int $fontSize Default font size - * @param int $atlasWidth Font atlas texture width (default 512) - * @param int $atlasHeight Font atlas texture height (default 512) + * @param int $atlasWidth Font atlas texture width (ignored, for compatibility) + * @param int $atlasHeight Font atlas texture height (ignored, for compatibility) * @return bool Success */ public function init( @@ -51,13 +56,16 @@ class TextRenderer $this->fontPath = $fontPath; $this->fontSize = $fontSize; - $this->initialized = rfont_init($this->window, $fontPath, $fontSize, $atlasWidth, $atlasHeight); + $this->font = ttf_open_font($fontPath, $fontSize); - if (!$this->initialized) { - error_log("Failed to initialize RFont with font: {$fontPath}"); + if (!$this->font) { + error_log("Failed to open font: {$fontPath}"); + $this->initialized = false; return false; } + $this->initialized = true; + // Set default color to white $this->setColor(1.0, 1.0, 1.0, 1.0); @@ -103,15 +111,43 @@ class TextRenderer */ public function drawText(string $text, int $x, int $y, null|int $size = null): void { - if (!$this->initialized) { + if (!$this->initialized || !$this->font) { return; } - if ($size === null) { - $size = $this->fontSize; + // SDL_ttf doesn't support variable font sizes easily + // We use the font size that was set when the font was opened + // For different sizes, you'd need to open multiple font instances + + // Convert float color (0.0-1.0) to int (0-255) + $r = (int) ($this->colorR * 255); + $g = (int) ($this->colorG * 255); + $b = (int) ($this->colorB * 255); + + // Render text to surface with anti-aliasing (blended mode for smooth text) + $surface = ttf_render_text_blended($this->font, $text, $r, $g, $b); + if (!$surface) { + return; } - rfont_drawText($this->window, $text, $x, $y, $size); + // Create texture from surface + $texture = sdl_create_texture_from_surface($this->renderer, $surface); + if (!$texture) { + return; + } + + // Get text size + $textSize = ttf_size_text($this->font, $text); + + // Render texture + sdl_render_texture($this->renderer, $texture, [ + 'x' => $x, + 'y' => $y, + 'w' => $textSize['w'], + 'h' => $textSize['h'] + ]); + + // Note: Texture and surface are automatically cleaned up by PHP resource destructors } /** @@ -124,11 +160,10 @@ class TextRenderer */ public function setColor(float $r, float $g, float $b, float $a = 1.0): void { - if (!$this->initialized) { - return; - } - - rfont_setColor($this->window, $r, $g, $b, $a); + $this->colorR = $r; + $this->colorG = $g; + $this->colorB = $b; + $this->colorA = $a; } /** @@ -140,24 +175,13 @@ class TextRenderer */ public function measureText(string $text, null|int $size = null): array { - if (!$this->initialized) { + if (!$this->initialized || !$this->font) { return [0, 0]; } - if ($size === null) { - $size = $this->fontSize; - } - - // Try to use rfont_getTextSize if available, otherwise estimate - if (function_exists('rfont_getTextSize')) { - $dimensions = rfont_getTextSize($this->window, $text, $size); - return [(int) $dimensions[0], (int) $dimensions[1]]; - } - - // Fallback: estimate based on font size - $width = strlen($text) * ($size * 0.6); // Rough estimate - $height = $size * 1.2; // Line height - return [(int) $width, (int) $height]; + // Get text size using TTF + $dimensions = ttf_size_text($this->font, $text); + return [(int) $dimensions['w'], (int) $dimensions['h']]; } /** @@ -168,20 +192,18 @@ class TextRenderer */ public function updateFramebuffer(int $width, int $height): void { - if (!$this->initialized) { - return; - } - - rfont_setFramebuffer($this->window, $width, $height); + // SDL3 handles this automatically through the renderer + // This method is kept for compatibility but does nothing } /** - * Free RFont resources + * Free TTF resources */ public function free(): void { - if ($this->initialized) { - rfont_free($this->window); + if ($this->initialized && $this->font) { + ttf_close_font($this->font); + $this->font = null; $this->initialized = false; } } diff --git a/src/Tailwind/Parser/Flex.php b/src/Tailwind/Parser/Flex.php index 69bd18b..5652452 100644 --- a/src/Tailwind/Parser/Flex.php +++ b/src/Tailwind/Parser/Flex.php @@ -14,6 +14,10 @@ class Flex implements Parser if (count($output_array[0]) > 0) { return new \PHPNative\Tailwind\Style\Flex(type:FlexTypeEnum::none); } + preg_match_all('/flex-grow/', $style, $output_array); + if (count($output_array[0]) > 0) { + return new \PHPNative\Tailwind\Style\Flex(type:FlexTypeEnum::grow); + } preg_match_all('/flex-1/', $style, $output_array); if (count($output_array[0]) > 0) { return new \PHPNative\Tailwind\Style\Flex(type:FlexTypeEnum::one); diff --git a/src/Tailwind/Parser/Height.php b/src/Tailwind/Parser/Height.php index 06aaf58..1c8c78e 100644 --- a/src/Tailwind/Parser/Height.php +++ b/src/Tailwind/Parser/Height.php @@ -24,7 +24,8 @@ class Height implements Parser preg_match_all('/h-(\d*)/', $style, $output_array); if (!$found && count($output_array[0]) > 0) { - $value = (int)$output_array[1][0]; + // Tailwind uses a 4px scale: h-1 = 4px, h-10 = 40px, etc. + $value = (int)$output_array[1][0] * 4; } preg_match_all('/(h-full|h-screen)/', $style, $output_array); diff --git a/src/Tailwind/Parser/Margin.php b/src/Tailwind/Parser/Margin.php index 6ebbb68..1b4653d 100644 --- a/src/Tailwind/Parser/Margin.php +++ b/src/Tailwind/Parser/Margin.php @@ -15,42 +15,42 @@ class Margin implements Parser preg_match_all('/m-(\d*)/', $style, $output_array); if (count($output_array[0]) > 0) { - $l = (int)$output_array[1][0]; - $r = (int)$output_array[1][0]; - $t = (int)$output_array[1][0]; - $b = (int)$output_array[1][0]; + $l = (int)$output_array[1][0] * 4; + $r = (int)$output_array[1][0] * 4; + $t = (int)$output_array[1][0] * 4; + $b = (int)$output_array[1][0] * 4; } preg_match_all('/mx-(\d*)/', $style, $output_array); if (count($output_array[0]) > 0) { - $l = (int)$output_array[1][0]; - $r = (int)$output_array[1][0]; + $l = (int)$output_array[1][0] * 4; + $r = (int)$output_array[1][0] * 4; } preg_match_all('/my-(\d*)/', $style, $output_array); if (count($output_array[0]) > 0) { - $t = (int)$output_array[1][0]; - $b = (int)$output_array[1][0]; + $t = (int)$output_array[1][0] * 4; + $b = (int)$output_array[1][0] * 4; } preg_match_all('/mt-(\d*)/', $style, $output_array); if (count($output_array[0]) > 0) { - $t = (int)$output_array[1][0]; + $t = (int)$output_array[1][0] * 4; } preg_match_all('/mb-(\d*)/', $style, $output_array); if (count($output_array[0]) > 0) { - $b = (int)$output_array[1][0]; + $b = (int)$output_array[1][0] * 4; } preg_match_all('/ml-(\d*)/', $style, $output_array); if (count($output_array[0]) > 0) { - $l = (int)$output_array[1][0]; + $l = (int)$output_array[1][0] * 4; } preg_match_all('/mr-(\d*)/', $style, $output_array); if (count($output_array[0]) > 0) { - $r = (int)$output_array[1][0]; + $r = (int)$output_array[1][0] * 4; } if($l != null || $r != null || $t != null || $b != null) { diff --git a/src/Tailwind/Parser/Padding.php b/src/Tailwind/Parser/Padding.php index ccb537a..7acb0fe 100644 --- a/src/Tailwind/Parser/Padding.php +++ b/src/Tailwind/Parser/Padding.php @@ -15,42 +15,43 @@ class Padding implements Parser preg_match_all('/p-(\d*)/', $style, $output_array); if (count($output_array[0]) > 0) { - $l = (int)$output_array[1][0]; - $r = (int)$output_array[1][0]; - $t = (int)$output_array[1][0]; - $b = (int)$output_array[1][0]; + // Tailwind uses a 4px scale + $l = (int)$output_array[1][0] * 4; + $r = (int)$output_array[1][0] * 4; + $t = (int)$output_array[1][0] * 4; + $b = (int)$output_array[1][0] * 4; } preg_match_all('/px-(\d*)/', $style, $output_array); if (count($output_array[0]) > 0) { - $l = (int)$output_array[1][0]; - $r = (int)$output_array[1][0]; + $l = (int)$output_array[1][0] * 4; + $r = (int)$output_array[1][0] * 4; } preg_match_all('/py-(\d*)/', $style, $output_array); if (count($output_array[0]) > 0) { - $t = (int)$output_array[1][0]; - $b = (int)$output_array[1][0]; + $t = (int)$output_array[1][0] * 4; + $b = (int)$output_array[1][0] * 4; } preg_match_all('/pt-(\d*)/', $style, $output_array); if (count($output_array[0]) > 0) { - $t = (int)$output_array[1][0]; + $t = (int)$output_array[1][0] * 4; } preg_match_all('/pb-(\d*)/', $style, $output_array); if (count($output_array[0]) > 0) { - $b = (int)$output_array[1][0]; + $b = (int)$output_array[1][0] * 4; } preg_match_all('/pl-(\d*)/', $style, $output_array); if (count($output_array[0]) > 0) { - $l = (int)$output_array[1][0]; + $l = (int)$output_array[1][0] * 4; } preg_match_all('/pr-(\d*)/', $style, $output_array); if (count($output_array[0]) > 0) { - $r = (int)$output_array[1][0]; + $r = (int)$output_array[1][0] * 4; } if($l != null || $r != null || $t != null || $b != null) { diff --git a/src/Tailwind/Parser/Width.php b/src/Tailwind/Parser/Width.php index 348c48f..9f3dce7 100644 --- a/src/Tailwind/Parser/Width.php +++ b/src/Tailwind/Parser/Width.php @@ -24,7 +24,8 @@ class Width implements Parser preg_match_all('/w-(\d*)/', $style, $output_array); if (!$found && count($output_array[0]) > 0) { - $value = (int)$output_array[1][0]; + // Tailwind uses a 4px scale: w-1 = 4px, w-10 = 40px, etc. + $value = (int)$output_array[1][0] * 4; } preg_match_all('/(w-screen|w-full)/', $style, $output_array); diff --git a/src/Tailwind/Style/FlexTypeEnum.php b/src/Tailwind/Style/FlexTypeEnum.php index d090c49..10bd7ec 100644 --- a/src/Tailwind/Style/FlexTypeEnum.php +++ b/src/Tailwind/Style/FlexTypeEnum.php @@ -3,10 +3,11 @@ declare(strict_types=1); namespace PHPNative\Tailwind\Style; -enum FlexTypeEnum +enum FlexTypeEnum { case none; case auto; case initial; case one; + case grow; } \ No newline at end of file diff --git a/src/Ui/Component.php b/src/Ui/Component.php index 8e2e8af..7b26eec 100644 --- a/src/Ui/Component.php +++ b/src/Ui/Component.php @@ -27,7 +27,19 @@ abstract class Component public function __construct( protected string $style = '', - ) {} + ) { + // Initialize viewports with default values + // These will be properly set during layout() + $this->viewport = new Viewport( + x: 0, + y: 0, + width: 0, + height: 0, + windowWidth: 800, + windowHeight: 600 + ); + $this->contentViewport = clone $this->viewport; + } public function setViewport(Viewport $viewport): void { @@ -101,7 +113,7 @@ abstract class Component } } - public function render(&$window, null|TextRenderer $textRenderer = null): void + public function render(&$renderer, null|TextRenderer $textRenderer = null): void { if (!$this->visible) { return; @@ -112,43 +124,49 @@ abstract class Component ($bg = $this->computedStyles[\PHPNative\Tailwind\Style\Background::class]) ) { if ($this->currentState == StateEnum::hover) { - rsgl_setColor($window, $bg->color->red, $bg->color->green, $bg->color->blue, 10); + sdl_set_render_draw_color($renderer, $bg->color->red, $bg->color->green, $bg->color->blue, 10); } else { - rsgl_setColor($window, $bg->color->red, $bg->color->green, $bg->color->blue, $bg->color->alpha); + sdl_set_render_draw_color($renderer, $bg->color->red, $bg->color->green, $bg->color->blue, $bg->color->alpha); } if ( isset($this->computedStyles[\PHPNative\Tailwind\Style\Border::class]) && ($border = $this->computedStyles[\PHPNative\Tailwind\Style\Border::class]) ) { - rsgl_drawRoundRectExF( - $window, + // SDL3: sdl_rounded_box_ex uses (x1, y1, x2, y2) instead of (x, y, w, h) + $x2 = $this->viewport->x + $this->viewport->width; + $y2 = $this->viewport->y + $this->viewport->height; + + sdl_rounded_box_ex( + $renderer, $this->viewport->x, $this->viewport->y, - $this->viewport->width, - $this->viewport->height, - $border->roundTopLeft ?? 0, + $x2, + $y2, $border->roundTopLeft ?? 0, $border->roundTopRight ?? 0, - $border->roundTopRight ?? 0, - $border->roundBottomLeft ?? 0, - $border->roundBottomLeft ?? 0, - $border->roundBottomRight ?? 0, $border->roundBottomRight ?? 0, + $border->roundBottomLeft ?? 0, + $bg->color->red, + $bg->color->green, + $bg->color->blue, + $bg->color->alpha, ); } else { - rsgl_drawRectF( - $window, - $this->viewport->x, - $this->viewport->y, - $this->viewport->width, - $this->viewport->height, + sdl_render_fill_rect( + $renderer, + [ + 'x' => $this->viewport->x, + 'y' => $this->viewport->y, + 'w' => $this->viewport->width, + 'h' => $this->viewport->height, + ] ); } } } - public function renderContent(&$window, null|TextRenderer $textRenderer = null): void + public function renderContent(&$renderer, null|TextRenderer $textRenderer = null): void { if (!$this->visible) { return; @@ -156,8 +174,8 @@ abstract class Component // Render children foreach ($this->children as $child) { - $child->render($window, $textRenderer); - $child->renderContent($window, $textRenderer); + $child->render($renderer, $textRenderer); + $child->renderContent($renderer, $textRenderer); } } @@ -234,4 +252,32 @@ abstract class Component } return false; } + + /** + * Handle text input event + * @param string $text The input text + */ + public function handleTextInput(string $text): void + { + // Default implementation: propagate to children + foreach ($this->children as $child) { + $child->handleTextInput($text); + } + } + + /** + * Handle key down event + * @param int $keycode SDL keycode + * @return bool True if event was handled + */ + public function handleKeyDown(int $keycode): bool + { + // Default implementation: propagate to children + foreach ($this->children as $child) { + if ($child->handleKeyDown($keycode)) { + return true; + } + } + return false; + } } diff --git a/src/Ui/Widget/Checkbox.php b/src/Ui/Widget/Checkbox.php new file mode 100644 index 0000000..7b82dff --- /dev/null +++ b/src/Ui/Widget/Checkbox.php @@ -0,0 +1,110 @@ +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); + } + } +} diff --git a/src/Ui/Widget/Container.php b/src/Ui/Widget/Container.php index 9b0e4b0..1907eb3 100644 --- a/src/Ui/Widget/Container.php +++ b/src/Ui/Widget/Container.php @@ -38,6 +38,18 @@ class Container extends Component parent::__construct($style); } + /** + * Clear all child components + */ + public function clearChildren(): void + { + $this->children = []; + $this->contentWidth = 0; + $this->contentHeight = 0; + $this->scrollX = 0; + $this->scrollY = 0; + } + public function layout(null|TextRenderer $textRenderer = null): void { // Call parent to compute styles and setup viewports @@ -131,9 +143,10 @@ class Container extends Component } if (!$hasExplicitHeight) { - // Set viewport to min(contentSize + padding, availableSize) + // Set viewport to content height + padding (don't expand to fill available space unnecessarily) $desiredHeight = $this->contentHeight + $paddingY; - $this->viewport->height = (int) min($desiredHeight, $availableHeight); + // Only limit to availableHeight if we're not trying to measure natural size + $this->viewport->height = (int) $desiredHeight; $this->contentViewport->height = max(0, $this->viewport->height - ((int) $paddingY)); } } @@ -186,18 +199,20 @@ class Container extends Component if (!$hasExplicitSize) { // Need to measure natural size - do a temporary layout + // For flex-col without explicit height, give minimal space to measure intrinsic size + // For flex-row without explicit width, give minimal space to measure intrinsic size $tempViewport = new Viewport( x: $this->contentViewport->x, y: $this->contentViewport->y, - width: $isRow ? $availableSpace : $this->contentViewport->width, - height: $isRow ? $this->contentViewport->height : $availableSpace, + width: $isRow ? 9999 : $this->contentViewport->width, + height: $isRow ? $this->contentViewport->height : 9999, windowWidth: $this->contentViewport->windowWidth, windowHeight: $this->contentViewport->windowHeight, ); $child->setViewport($tempViewport); $child->layout($textRenderer); - // Get natural size + // Get natural size (the actual size the child computed after layout) $size = $isRow ? $child->getViewport()->width : $child->getViewport()->height; } @@ -309,7 +324,7 @@ class Container extends Component ]; } - public function renderContent(&$window, null|TextRenderer $textRenderer = null): void + public function renderContent(&$renderer, null|TextRenderer $textRenderer = null): void { if (!$this->visible) { return; @@ -322,25 +337,21 @@ class Container extends Component // Apply scroll offset to children if ($overflow['x'] || $overflow['y']) { - // Enable scissor test for clipping + // Enable clipping $scissorX = (int) $this->contentViewport->x; $scissorY = (int) $this->contentViewport->y; $scissorW = (int) $this->contentViewport->width; $scissorH = (int) $this->contentViewport->height; - // IMPORTANT: RSGL uses batch rendering! - // We need to flush the batch before/after scissor to ensure correct clipping + // SDL3: Set clip rect for clipping + sdl_set_render_clip_rect($renderer, [ + 'x' => $scissorX, + 'y' => $scissorY, + 'w' => $scissorW, + 'h' => $scissorH + ]); - // Step 1: Flush any pending draw calls before scissor - rsgl_render($window); - - // Step 2: Enable scissor test for clipping - $windowHeight = $this->contentViewport->windowHeight; - $invertedY = $windowHeight - ($scissorY + $scissorH); - - rsgl_scissorStart($window, $scissorX, $invertedY, $scissorW, $scissorH); - - // Step 3: Render children with scroll offset (batches draw calls) + // Render children with scroll offset foreach ($this->children as $child) { // Apply scroll offset recursively to child and all its descendants $child->applyScrollOffset((int) $this->scrollX, (int) $this->scrollY); @@ -354,30 +365,26 @@ class Container extends Component $childViewport->y < ($scissorY + $scissorH); if ($isVisible) { - // Render - batches draw calls - $child->render($textRenderer); - $child->renderContent($textRenderer); + $child->render($renderer, $textRenderer); + $child->renderContent($renderer, $textRenderer); } // Restore by applying negative offset $child->applyScrollOffset((int) -$this->scrollX, (int) -$this->scrollY); } - // Step 4: Flush the batch while scissor is still active - rsgl_render($window); - - // Step 5: Disable scissor test - rsgl_scissorEnd($window); + // Disable clipping + sdl_set_render_clip_rect($renderer, null); // Render scrollbars - $this->renderScrollbars($overflow); + $this->renderScrollbars($renderer, $overflow); } else { // No overflow, render normally - parent::renderContent($window, $textRenderer); + parent::renderContent($renderer, $textRenderer); } } - private function renderScrollbars(array $overflow): void + private function renderScrollbars(&$renderer, array $overflow): void { $scrollbarColor = [100, 100, 100, 200]; // Gray with some transparency @@ -394,32 +401,32 @@ class Container extends Component $scrollbarX = ($this->contentViewport->x + $this->contentViewport->width) - self::SCROLLBAR_WIDTH; // Track - rsgl_setColor($this->window, 200, 200, 200, 100); - rsgl_drawRectF( - $this->window, - (int) $scrollbarX, - (int) $this->contentViewport->y, - (int) self::SCROLLBAR_WIDTH, - (int) $scrollbarHeight, + sdl_set_render_draw_color($renderer, 200, 200, 200, 100); + sdl_render_fill_rect( + $renderer, + [ + 'x' => (int) $scrollbarX, + 'y' => (int) $this->contentViewport->y, + 'w' => (int) self::SCROLLBAR_WIDTH, + 'h' => (int) $scrollbarHeight, + ] ); - // Thumb - rsgl_setColor( - $this->window, + // Thumb - using sdl_rounded_box for rounded rectangle + $thumbX = (int) ($scrollbarX + 2); + $thumbW = (int) (self::SCROLLBAR_WIDTH - 4); + sdl_rounded_box( + $renderer, + $thumbX, + (int) $thumbY, + $thumbX + $thumbW, + (int) ($thumbY + $thumbHeight), + 4, $scrollbarColor[0], $scrollbarColor[1], $scrollbarColor[2], $scrollbarColor[3], ); - rsgl_drawRoundRectF( - $this->window, - (int) ($scrollbarX + 2), - (int) $thumbY, - (int) (self::SCROLLBAR_WIDTH - 4), - (int) $thumbHeight, - 4, - 4, - ); } // Horizontal scrollbar @@ -435,32 +442,32 @@ class Container extends Component $scrollbarY = ($this->contentViewport->y + $this->contentViewport->height) - self::SCROLLBAR_WIDTH; // Track - rsgl_setColor($this->window, 200, 200, 200, 100); - rsgl_drawRectF( - $this->window, - (int) $this->contentViewport->x, - (int) $scrollbarY, - (int) $scrollbarWidth, - (int) self::SCROLLBAR_WIDTH, + sdl_set_render_draw_color($renderer, 200, 200, 200, 100); + sdl_render_fill_rect( + $renderer, + [ + 'x' => (int) $this->contentViewport->x, + 'y' => (int) $scrollbarY, + 'w' => (int) $scrollbarWidth, + 'h' => (int) self::SCROLLBAR_WIDTH, + ] ); - // Thumb - rsgl_setColor( - $this->window, + // Thumb - using sdl_rounded_box for rounded rectangle + $thumbY = (int) ($scrollbarY + 2); + $thumbH = (int) (self::SCROLLBAR_WIDTH - 4); + sdl_rounded_box( + $renderer, + (int) $thumbX, + $thumbY, + (int) ($thumbX + $thumbWidth), + $thumbY + $thumbH, + 4, $scrollbarColor[0], $scrollbarColor[1], $scrollbarColor[2], $scrollbarColor[3], ); - rsgl_drawRoundRectF( - $this->window, - (int) $thumbX, - (int) ($scrollbarY + 2), - (int) $thumbWidth, - (int) (self::SCROLLBAR_WIDTH - 4), - 4, - 4, - ); } } diff --git a/src/Ui/Widget/Label.php b/src/Ui/Widget/Label.php index f19c5b8..3131007 100644 --- a/src/Ui/Widget/Label.php +++ b/src/Ui/Widget/Label.php @@ -14,7 +14,9 @@ class Label extends Component public function __construct( public string $text = '', public string $style = '', - ) {} + ) { + parent::__construct($style); + } public function setText(string $text): void { diff --git a/src/Ui/Widget/TextInput.php b/src/Ui/Widget/TextInput.php new file mode 100644 index 0000000..9d7a69d --- /dev/null +++ b/src/Ui/Widget/TextInput.php @@ -0,0 +1,246 @@ +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 + } +} diff --git a/src/Ui/Window.php b/src/Ui/Window.php index 3dc7ebc..43439f9 100644 --- a/src/Ui/Window.php +++ b/src/Ui/Window.php @@ -7,6 +7,8 @@ use PHPNative\Framework\TextRenderer; class Window { private mixed $window = null; + private mixed $renderer = null; + private int $windowId = 0; private null|Component $rootComponent = null; private ?TextRenderer $textRenderer; private float $mouseX = 0; @@ -24,25 +26,50 @@ class Window private int $x = 100, private int $y = 100, ) { - // Create window - $this->window = rgfw_createWindow($title, $x, $y, $width, $height, 0); - if (!$this->window) { - throw new \Exception('Failed to create window'); + // Initialize SDL if not already done + static $sdlInitialized = false; + if (!$sdlInitialized) { + if (!sdl_init(SDL_INIT_VIDEO)) { + throw new \Exception('Failed to initialize SDL: ' . sdl_get_error()); + } + + // Initialize TTF + if (!ttf_init()) { + throw new \Exception('Failed to initialize TTF: ' . sdl_get_error()); + } + + $sdlInitialized = true; } - // Initialize RSGL renderer - if (!rsgl_init($this->window)) { - throw new \Exception('Failed to initialize RSGL renderer'); + // Create window with resizable flag for normal window decorations + // SDL_WINDOW_RESIZABLE gives you the standard window controls (close, minimize, maximize) + $flags = SDL_WINDOW_RESIZABLE; + + $this->window = sdl_create_window($title, $width, $height, $flags); + if (!$this->window) { + throw new \Exception('Failed to create window: ' . sdl_get_error()); + } + + // Get window ID for event routing + $this->windowId = sdl_get_window_id($this->window); + + // Enable text input for this window + sdl_start_text_input($this->window); + + // Create renderer + $this->renderer = sdl_create_renderer($this->window); + if (!$this->renderer) { + throw new \Exception('Failed to create renderer: ' . sdl_get_error()); } // Initialize text renderer - $this->textRenderer = new TextRenderer($this->window); + $this->textRenderer = new TextRenderer($this->renderer); if (!$this->textRenderer->init()) { error_log('Warning: Failed to initialize text renderer. Text rendering will not be available.'); } // Get actual window size - $size = rgfw_window_getSize($this->window); + $size = sdl_get_window_size($this->window); $this->width = $size[0]; $this->height = $size[1]; $this->viewport = new Viewport( @@ -88,7 +115,7 @@ class Window public function shouldClose(): bool { - return $this->shouldClose || rgfw_window_shouldClose($this->window); + return $this->shouldClose; } public function close(): void @@ -101,48 +128,69 @@ class Window return $this->window; } - /** - * Handle window and input events - */ - public function handleEvents(): void + public function getRendererResource(): mixed { - while ($event = rgfw_window_checkQueuedEvent($this->window)) { - // Debug output - can be removed later - if (defined('DEBUG_EVENTS') && DEBUG_EVENTS) { - $eventTypes = [ - RGFW_quit => 'QUIT', - RGFW_keyPressed => 'KEY_PRESSED', - RGFW_mouseButtonPressed => 'MOUSE_PRESSED', - RGFW_mouseButtonReleased => 'MOUSE_RELEASED', - RGFW_mousePosChanged => 'MOUSE_MOVE', - ]; - $typeName = $eventTypes[$event['type']] ?? ('UNKNOWN(' . $event['type'] . ')'); - error_log("[{$this->title}] Event: {$typeName}"); - } + return $this->renderer; + } - switch ($event['type']) { - case RGFW_quit: + public function getWindowId(): int + { + return $this->windowId; + } + + /** + * Process a single event for this window + * Called by Application with pre-filtered events + */ + public function processEvent(array $event): void + { + // 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; break; - case RGFW_keyPressed: - $keyCode = $event['keyCode'] ?? 0; - if ($keyCode == RGFW_Escape) { - $this->shouldClose = true; + case SDL_EVENT_KEY_DOWN: + $keycode = $event['keycode'] ?? 0; + + // Propagate key event to root component + if ($this->rootComponent) { + $this->rootComponent->handleKeyDown($keycode); } break; - case RGFW_windowResized: + case SDL_EVENT_TEXT_INPUT: + $text = $event['text'] ?? ''; + + // Propagate text input to root component + if ($this->rootComponent && !empty($text)) { + $this->rootComponent->handleTextInput($text); + } + break; + + case SDL_EVENT_WINDOW_RESIZED: // Update window dimensions (from event data) - $newWidth = $event[0] ?? $this->width; - $newHeight = $event[1] ?? $this->height; + $newWidth = $event['data1'] ?? $this->width; + $newHeight = $event['data2'] ?? $this->height; $this->width = $newWidth; $this->height = $newHeight; - // Update RSGL renderer size and viewport - rsgl_updateRendererSize($this->window); - // Update text renderer framebuffer if ($this->textRenderer && $this->textRenderer->isInitialized()) { $this->textRenderer->updateFramebuffer($newWidth, $newHeight); @@ -157,9 +205,9 @@ class Window $this->shouldBeReLayouted = true; break; - case RGFW_mousePosChanged: - $this->mouseX = $event[0] ?? 0; - $this->mouseY = $event[1] ?? 0; + case SDL_EVENT_MOUSE_MOTION: + $this->mouseX = $event['x'] ?? 0; + $this->mouseY = $event['y'] ?? 0; // Propagate mouse move to root component if ($this->rootComponent) { @@ -167,7 +215,7 @@ class Window } break; - case RGFW_mouseButtonPressed: + case SDL_EVENT_MOUSE_BUTTON_DOWN: $button = $event['button'] ?? 0; // Propagate click to root component if ($this->rootComponent) { @@ -175,7 +223,7 @@ class Window } break; - case RGFW_mouseButtonReleased: + case SDL_EVENT_MOUSE_BUTTON_UP: $button = $event['button'] ?? 0; // Propagate release to root component @@ -184,8 +232,8 @@ class Window } break; - case RGFW_mouseScroll: - $deltaY = $event[1] ?? 0; + case SDL_EVENT_MOUSE_WHEEL: + $deltaY = $event['y'] ?? 0; // Propagate wheel to root component if ($this->rootComponent) { @@ -193,7 +241,6 @@ class Window } break; } - } } /** @@ -202,36 +249,9 @@ class Window */ public function updateMousePosition(): void { - if (!$this->rootComponent) { - return; - } - - // Try to get current mouse position relative to this window - if (function_exists('rgfw_window_getMousePoint')) { - $mousePos = rgfw_window_getMousePoint($this->window); - if ($mousePos !== false && is_array($mousePos)) { - $newX = $mousePos[0] ?? $this->mouseX; - $newY = $mousePos[1] ?? $this->mouseY; - - // Only update if position changed to avoid unnecessary updates - if ($newX !== $this->mouseX || $newY !== $this->mouseY) { - $this->mouseX = $newX; - $this->mouseY = $newY; - $this->rootComponent->handleMouseMove($this->mouseX, $this->mouseY); - } - } - } else { - // rgfw_window_getMousePoint() not available - // Hover will only work when window receives mouse events - // This is normal OS behavior - only focused windows get mouse events - static $warningShown = false; - if (!$warningShown && defined('DEBUG_EVENTS') && DEBUG_EVENTS) { - error_log( - 'Note: rgfw_window_getMousePoint() not available. Hover states will only work for focused windows.', - ); - $warningShown = true; - } - } + // With SDL3, mouse position is tracked through SDL_EVENT_MOUSE_MOTION events + // This method is kept for compatibility but does nothing + // Hover states are updated in handleEvents() via SDL_EVENT_MOUSE_MOTION } /** @@ -266,20 +286,19 @@ class Window */ public function render(): void { - // Always clear the window to prevent black screens (white background, fully opaque) + // Clear the window with white background + sdl_set_render_draw_color($this->renderer, 255, 255, 255, 255); + sdl_render_clear($this->renderer); // Only render content if window has been laid out // This can happen when windows are created during async callbacks if ($this->hasBeenLaidOut && $this->rootComponent) { - rgfw_window_makeCurrent($this->window); - rsgl_clear($this->window, 255, 255, 255, 255); - $this->rootComponent->render($this->window, $this->textRenderer); - $this->rootComponent->renderContent($this->window, $this->textRenderer); - rsgl_render($this->window); - rgfw_window_swapBuffers($this->window); + $this->rootComponent->render($this->renderer, $this->textRenderer); + $this->rootComponent->renderContent($this->renderer, $this->textRenderer); } - // Always swap buffers to display the cleared window + // Present the rendered content + sdl_render_present($this->renderer); } /** @@ -293,6 +312,27 @@ class Window } } + /** + * Destroy the window and all resources + * This explicitly closes the SDL window + */ + public function destroy(): void + { + // First clean up text renderer + $this->cleanup(); + + // Explicitly destroy SDL renderer and window + if ($this->renderer) { + sdl_destroy_renderer($this->renderer); + $this->renderer = null; + } + + if ($this->window) { + sdl_destroy_window($this->window); + $this->window = null; + } + } + public function setShouldBeReLayouted(bool $shouldBeReLayouted): void { $this->shouldBeReLayouted = $shouldBeReLayouted; diff --git a/test_multi_window.php b/test_multi_window.php new file mode 100644 index 0000000..0b5700a --- /dev/null +++ b/test_multi_window.php @@ -0,0 +1,43 @@ +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(); diff --git a/test_queue_events.php b/test_queue_events.php deleted file mode 100644 index 9787922..0000000 --- a/test_queue_events.php +++ /dev/null @@ -1,75 +0,0 @@ -