Skip to content

Conversation

@Starbuck5
Copy link
Member

Ankith has had 2 PRs stalled for a while because he and I are currently not in agreement on some of the details, like window syncing and how to create palettes. #3577 and #3578.

In this PR I took his changes and removed some things I didn't have the time to review, didn't understand, or disagreed with, in an effort to keep moving this effort forward. So this PR is basically an approval for some bits of the code.

I specifically edited the changes so that Ankith remains the primary author in git.

@Starbuck5 Starbuck5 requested a review from a team as a code owner December 14, 2025 19:45
@Starbuck5 Starbuck5 added Surface pygame.Surface display pygame.display sdl3 window pygame.Window labels Dec 14, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 14, 2025

📝 Walkthrough

Walkthrough

This PR refactors SDL3 compatibility across multiple modules, introducing wrapper functions for consistent boolean return values (PG_GL_SetSwapInterval, PG_BlitSurface, PG_SetWindowFullscreen), unifies palette access via PG_GetSurfacePalette, updates display and window fullscreen handling logic, and corrects documentation for pgSurface_Blit return values. Test conditionals skip SDL version ranges where RLE handling was temporarily broken.

Changes

Cohort / File(s) Summary
Documentation
docs/reST/c_api/surface.rst
Corrected pgSurface_Blit return value documentation: now states 0 on success and 1 on exception (reversed from prior).
Header Wrappers
src_c/_pygame.h
Added PG_GL_SetSwapInterval macro alias and static inline function that wraps SDL_GL_SetSwapInterval, returning true on success (SDL return 0), false otherwise; available in SDL 3+ path.
Display & GL Attributes
src_c/display.c
Refactored pg_gl_set_attribute and pg_gl_get_attribute to use SDL_VERSION_ATLEAST branches treating success as boolean; updated pg_get_active to guard against NULL window; modified PG_SetWindowFullscreen signature from int to bool return; adjusted all fullscreen and vsync call sites to use boolean logic; updated pg_toggle_fullscreen to consistently return PyLong_FromLong(1); improved error handling in pg_message_box for SDL3.
Window Fullscreen & Focus
src_c/window.c
Added SDL3 branch in window_set_windowed using SDL_SetWindowFullscreen with error handling; changed window_focus local variable type from SDL_bool to int; moved legacy SDL2 path under #else.
Surface Blit Utility
src_c/surface.c
Introduced new public function PG_BlitSurface(src, srcrect, dst, dstrect) providing SDL3-aware blit with clipping; replaced SDL_VERSION_ATLEAST guarded RLE/alpha blocks with SDL_MUSTLOCK checks; updated pgSurface_Blit to use PG_BlitSurface where applicable; adjusted error return codes in surf_get_flags and related paths to align with SDL3 expectations.
Palette Abstraction
src_c/font.c, src_c/pixelcopy.c
Replaced conditional palette retrieval (SDL_GetSurfacePalette vs. format->palette) with unified PG_GetSurfacePalette wrapper in both modules, eliminating SDL version branching.
Surface Fill Error Handling
src_c/surface_fill.c
Updated surface_fill_blend default branch to set result to -1 before breaking after calling SDL_SetError.
Tests
test/surface_test.py
Added unittest.skipIf decorators to test_solarwolf_rle_usage and test_solarwolf_rle_usage_2 to skip on SDL 2.32.50–2.32.56 and SDL 3.0.0–3.2.22 where RLE handling was briefly broken.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

  • display.c: Dense refactoring of boolean return values, SDL version branching, and fullscreen/GL attribute handling across multiple code paths; requires careful verification of SDL2/SDL3 compatibility logic.
  • surface.c: New PG_BlitSurface public function with clipping logic; significant replacements of RLE/alpha blit paths and error return code adjustments; needs verification of clipping behavior and SDL3 compatibility.
  • window.c & display.c fullscreen paths: Interrelated changes to fullscreen control flow across multiple functions; requires tracing error handling through both modules.
  • Palette abstraction changes: Verify PG_GetSurfacePalette behavior in font.c and pixelcopy.c is equivalent to prior conditional logic.

Possibly related PRs

Suggested labels

font, docs, bugfix

Suggested reviewers

  • ankith26
  • damusss

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main objective: applying SDL3 runtime fixes derived from Ankith's earlier PRs.
Description check ✅ Passed The description clearly explains the context, rationale, and scope of the PR by referencing the two source PRs and explaining what was retained or excluded.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ef3ac0c and aa02bc6.

📒 Files selected for processing (9)
  • docs/reST/c_api/surface.rst (1 hunks)
  • src_c/_pygame.h (2 hunks)
  • src_c/display.c (25 hunks)
  • src_c/font.c (1 hunks)
  • src_c/pixelcopy.c (1 hunks)
  • src_c/surface.c (5 hunks)
  • src_c/surface_fill.c (1 hunks)
  • src_c/window.c (2 hunks)
  • test/surface_test.py (2 hunks)
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: Starbuck5
Repo: pygame-community/pygame-ce PR: 3573
File: src_c/_pygame.h:108-133
Timestamp: 2025-09-01T20:18:57.500Z
Learning: In pygame-ce PR #3573, PG_SURF_BitsPerPixel was changed from a simple macro to a static inline function to handle SDL2/SDL3 differences. SDL2's BitsPerPixel includes padding bits (e.g., RGBX => 32) while SDL3's SDL_BITSPERPIXEL excludes padding (e.g., RGBX => 24). The new implementation bridges this gap rather than removing the helper entirely.
Learnt from: Starbuck5
Repo: pygame-community/pygame-ce PR: 3573
File: src_c/_camera.c:129-131
Timestamp: 2025-08-30T21:11:00.240Z
Learning: When doing SDL2 to SDL3 compatibility changes, Starbuck5 prefers to maintain exact existing behaviors even when other improvements could be made, focusing solely on the compatibility aspect rather than mixing in behavioral fixes.
Learnt from: Starbuck5
Repo: pygame-community/pygame-ce PR: 3573
File: src_c/_pygame.h:115-126
Timestamp: 2025-09-01T20:22:31.010Z
Learning: In pygame-ce PR #3573, the PG_SURF_BitsPerPixel function implementation uses hardcoded return values of 32 for YUY2/UYVY/YVYU FOURCC formats because it's copying the exact logic from SDL's internal SDL_GetMasksForPixelFormat function. This ensures perfect compatibility between SDL2 and SDL3 behaviors rather than using the technically more accurate values that might be returned by SDL_GetPixelFormatDetails.
Learnt from: oddbookworm
Repo: pygame-community/pygame-ce PR: 0
File: :0-0
Timestamp: 2025-11-03T19:49:28.177Z
Learning: For translation pull requests in the pygame-ce repository: Focus only on verifying direct translation accuracy from the English source. Do not suggest new content or enhancements during translation PRs. New content should be added to the English version first in a separate pull request.
Learnt from: damusss
Repo: pygame-community/pygame-ce PR: 3219
File: src_c/window.c:11-13
Timestamp: 2025-10-17T08:53:32.449Z
Learning: SDL3's first stable release is version 3.2.0 (released January 21, 2025). SDL 3.0.x and 3.1.x were preview/pre-release versions not intended for production use, so version checks for SDL3 features in pygame-ce should use SDL_VERSION_ATLEAST(3, 2, 0) to target the first stable release.
📚 Learning: 2025-08-30T21:11:00.240Z
Learnt from: Starbuck5
Repo: pygame-community/pygame-ce PR: 3573
File: src_c/_camera.c:129-131
Timestamp: 2025-08-30T21:11:00.240Z
Learning: When doing SDL2 to SDL3 compatibility changes, Starbuck5 prefers to maintain exact existing behaviors even when other improvements could be made, focusing solely on the compatibility aspect rather than mixing in behavioral fixes.

Applied to files:

  • src_c/window.c
  • src_c/display.c
  • src_c/pixelcopy.c
  • test/surface_test.py
  • src_c/surface.c
📚 Learning: 2025-09-01T20:18:57.500Z
Learnt from: Starbuck5
Repo: pygame-community/pygame-ce PR: 3573
File: src_c/_pygame.h:108-133
Timestamp: 2025-09-01T20:18:57.500Z
Learning: In pygame-ce PR #3573, PG_SURF_BitsPerPixel was changed from a simple macro to a static inline function to handle SDL2/SDL3 differences. SDL2's BitsPerPixel includes padding bits (e.g., RGBX => 32) while SDL3's SDL_BITSPERPIXEL excludes padding (e.g., RGBX => 24). The new implementation bridges this gap rather than removing the helper entirely.

Applied to files:

  • src_c/window.c
  • src_c/surface_fill.c
  • docs/reST/c_api/surface.rst
  • src_c/_pygame.h
  • src_c/display.c
  • src_c/pixelcopy.c
  • src_c/font.c
  • test/surface_test.py
  • src_c/surface.c
📚 Learning: 2025-09-01T20:22:31.010Z
Learnt from: Starbuck5
Repo: pygame-community/pygame-ce PR: 3573
File: src_c/_pygame.h:115-126
Timestamp: 2025-09-01T20:22:31.010Z
Learning: In pygame-ce PR #3573, the PG_SURF_BitsPerPixel function implementation uses hardcoded return values of 32 for YUY2/UYVY/YVYU FOURCC formats because it's copying the exact logic from SDL's internal SDL_GetMasksForPixelFormat function. This ensures perfect compatibility between SDL2 and SDL3 behaviors rather than using the technically more accurate values that might be returned by SDL_GetPixelFormatDetails.

Applied to files:

  • docs/reST/c_api/surface.rst
  • src_c/_pygame.h
  • src_c/display.c
  • src_c/pixelcopy.c
  • src_c/font.c
  • src_c/surface.c
📚 Learning: 2025-10-17T08:53:32.449Z
Learnt from: damusss
Repo: pygame-community/pygame-ce PR: 3219
File: src_c/window.c:11-13
Timestamp: 2025-10-17T08:53:32.449Z
Learning: SDL3's first stable release is version 3.2.0 (released January 21, 2025). SDL 3.0.x and 3.1.x were preview/pre-release versions not intended for production use, so version checks for SDL3 features in pygame-ce should use SDL_VERSION_ATLEAST(3, 2, 0) to target the first stable release.

Applied to files:

  • src_c/display.c
🧬 Code graph analysis (4)
src_c/display.c (2)
src_c/base.c (1)
  • pg_GetDefaultWindow (1925-1929)
src_c/_pygame.h (1)
  • PG_GL_SetSwapInterval (406-410)
src_c/pixelcopy.c (1)
src_c/_pygame.h (1)
  • PG_GetSurfacePalette (273-277)
src_c/font.c (1)
src_c/_pygame.h (1)
  • PG_GetSurfacePalette (273-277)
src_c/surface.c (3)
src_c/base.c (1)
  • pg_EnvShouldBlendAlphaSDL2 (1978-1982)
src_c/transform.c (1)
  • _PgSurface_SrcAlpha (94-116)
src_c/alphablit.c (1)
  • pygame_Blit (1582-1687)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (19)
  • GitHub Check: Debian (Bookworm - 12) [armv7]
  • GitHub Check: msys2 (mingw64, x86_64)
  • GitHub Check: build (ubuntu-22.04)
  • GitHub Check: Debian (Bookworm - 12) [ppc64le]
  • GitHub Check: msys2 (clang64, clang-x86_64)
  • GitHub Check: msys2 (ucrt64, ucrt-x86_64)
  • GitHub Check: build (windows-latest)
  • GitHub Check: Debian (Bookworm - 12) [armv6]
  • GitHub Check: Debian (Bookworm - 12) [s390x]
  • GitHub Check: i686
  • GitHub Check: aarch64
  • GitHub Check: x86_64
  • GitHub Check: build (macos-14)
  • GitHub Check: build (ubuntu-24.04)
  • GitHub Check: AMD64
  • GitHub Check: x86
  • GitHub Check: dev-check
  • GitHub Check: debug_coverage (ubuntu-24.04, 3.14.0)
  • GitHub Check: debug_coverage (ubuntu-24.04, 3.10.17)
🔇 Additional comments (26)
src_c/surface_fill.c (1)

1145-1149: Good: avoid early-return so surface unlock path is preserved.
Setting the SDL error + result = -1 and breaking keeps cleanup consistent and prevents lock leaks.

src_c/font.c (1)

718-726: Looks good; same SDL3 palette-access verification as pixelcopy.c.
The null-check is correct and preserves behavior. Just ensure PG_GetSurfacePalette() is the right abstraction for SDL3 builds.

src_c/pixelcopy.c (1)

1195-1210: No action needed. PG_GetSurfacePalette is correctly implemented with proper SDL2/SDL3 handling: it resolves to SDL_GetSurfacePalette for SDL3 builds (line 162) and provides a static inline wrapper returning surf->format->palette for SDL2 (lines 273-277). The call at line 1200 is safe and appropriately guarded by the SDL_ISPIXELFORMAT_INDEXED check.

src_c/window.c (2)

304-312: SDL3/SDL2 return value handling looks correct.

The SDL3 path correctly checks !SDL_SetWindowFullscreen(...) since SDL3 returns true on success and false on failure, while SDL2 returns 0 on success and non-zero on failure. This follows the same pattern used elsewhere in the codebase for SDL3 compatibility.


380-380: Type change from SDL_bool to int is appropriate.

Using int is correct here because PyArg_ParseTupleAndKeywords with the "p" format specifier expects an int* pointer, not SDL_bool*. This avoids potential type mismatch issues.

src_c/_pygame.h (2)

195-196: SDL3 macro alias is correct.

In SDL3, SDL_GL_SetSwapInterval already returns bool (true on success), so a direct alias works correctly.


405-410: SDL2 wrapper correctly converts return semantics to bool.

The wrapper correctly converts SDL2's SDL_GL_SetSwapInterval return value (0 on success, -1 on failure) to a bool (true on success, false on failure), providing a consistent interface with SDL3.

src_c/surface.c (7)

1547-1554: Formatting change in RLE stripping hack looks fine.

The logic remains the same; only minor formatting adjustments were made to the SDL_Rect initialization.


4450-4518: PG_BlitSurface wrapper correctly handles SDL3/SDL2 differences.

This wrapper addresses SDL3's change where SDL_BlitSurface no longer modifies the destination rectangle. The SDL3 path:

  1. Performs manual rectangle clipping similar to SDL2's internal logic
  2. Updates dstrect with the clipped result
  3. Returns 0 on success, -1 on failure (SDL2-compatible semantics)

The return value conversion at line 4508 (SDL_BlitSurface(...) ? 0 : -1) correctly maps SDL3's bool return (true=success) to SDL2-style int return (0=success).


4532-4535: Error return value change is correct.

The pgSurface_Blit function returns 0 on success and non-zero on error (as seen at line 4661: return result != 0;). Changing the early error return from return 0; (which would incorrectly indicate success) to return 1; correctly signals an error condition.


4594-4621: SDL3 surface format conversion path looks correct.

The SDL3-specific code properly:

  1. Gets pixel format details using SDL_GetPixelFormatDetails
  2. Creates a new format without alpha mask using SDL_GetPixelFormatForMasks
  3. Converts the surface and performs the blit via PG_BlitSurface
  4. Handles the NULL cases appropriately

The refactored structure with unified error handling (result = -1 when src is NULL) is cleaner than the previous SDL2-only code.


4632-4632: SDL_MUSTLOCK condition for RLE check.

This condition checks !SDL_MUSTLOCK(src) && !SDL_MUSTLOCK(dst) to avoid the pygame_Blit path when surfaces have RLE. As noted earlier, SDL3's SDL_MUSTLOCK checks SDL_SURFACE_LOCKED rather than RLE status, which may affect this logic on SDL3. The behavior should be verified with SDL3.

Based on learnings, Starbuck5 prefers maintaining exact existing behaviors during SDL2 to SDL3 compatibility changes.


4640-4640: Using PG_BlitSurface wrapper is correct.

This correctly uses the new wrapper function that handles SDL3's different behavior where dstrect is not modified by SDL_BlitSurface.


3010-3018: Use PG_SurfaceHasRLE instead of SDL_MUSTLOCK to detect actual RLE status on SDL3.

The code at line 3016 uses SDL_MUSTLOCK(surf) to determine if RLE is actually enabled, but on SDL3 this macro checks the SDL_SURFACE_LOCKED flag (surface lock state), not RLE acceleration status. Since line 3011 already uses PG_SurfaceHasRLE which properly abstracts SDL2/SDL3 differences, line 3016 should use the same abstraction rather than relying on SDL_MUSTLOCK for RLE detection.

src_c/display.c (12)

282-286: LGTM! Improved defensive programming.

The explicit null check with early return and clear variable declarations improve code clarity and maintainability.


828-836: Consistent with SDL_GL_SetAttribute - verify return semantics.

This follows the same boolean return pattern as SDL_GL_SetAttribute. The verification request in the previous comment applies here as well.


1212-1212: LGTM! Appropriate fullscreen mode selection.

The non_desktop_fullscreen flag correctly defaults to exclusive fullscreen (value 1) and switches to desktop/borderless fullscreen (value 0) when using SCALED mode or when window dimensions match the desktop resolution. This aligns with the expected behavior of avoiding physical resolution changes when possible.

Also applies to: 1337-1337, 1347-1347


1466-1475: LGTM! Correct error handling for both SDL versions.

The error handling correctly adapts to SDL3's boolean return (checking !result) and SDL2's integer return (checking != 0). Both paths properly raise exceptions on failure.


1564-1568: LGTM! Correct usage of updated PG_SetWindowFullscreen API.

The call correctly treats PG_SetWindowFullscreen as returning a boolean and checks for failure with the ! operator. Error handling is appropriate.


1626-1646: PG_GL_SetSwapInterval usage mostly correct, verify line 1645 intent.

Lines 1626 and 1636 correctly check for failure when enabling vsync. However, line 1645 calls PG_GL_SetSwapInterval(0) to disable vsync without checking the return value.

Question: Is it intentional to ignore errors when disabling vsync (line 1645)? While this might be acceptable as a best-effort operation, it's inconsistent with the error handling on lines 1626 and 1636.


1720-1725: LGTM! Proper vsync validation.

The condition correctly validates that when vsync is requested, it was successfully enabled. This ensures the user's explicit vsync requirement is met or an error is raised.


3192-3193: LGTM! Consistent PG_SetWindowFullscreen usage throughout toggle_fullscreen.

All 11 calls to PG_SetWindowFullscreen consistently:

  • Check for boolean failure with the ! operator
  • Raise appropriate errors on failure
  • Use correct parameters for toggling fullscreen on/off and choosing desktop vs. non-desktop modes

The usage is correct and uniform across all code paths.

Also applies to: 3206-3207, 3246-3247, 3282-3283, 3315-3316, 3350-3351, 3355-3356, 3388-3389, 3413-3414, 3430-3431, 3442-3443


3456-3456: Verify intentional behavior change: unconditional success return.

The function now unconditionally returns PyLong_FromLong(1) at the end, indicating success whenever this line is reached. This represents a behavioral change from potential intermediate failure states.

Observation: All error paths either raise exceptions (returning NULL) or return -1 early. If execution reaches line 3456, the operation has succeeded.

Question: Is this change intentional and properly documented? Previously, the function may have returned different success codes (0 vs 1) to indicate partial success or specific outcomes.

Based on learnings, as per Starbuck5's preference for maintaining exact behaviors during SDL compatibility changes, please verify this behavioral change was intentional.


1135-1186: LGTM! Good API unification across SDL versions.

The function signature change to static bool provides consistent return semantics across SDL2 and SDL3. The SDL2 wrapper correctly converts the integer return (== 0) to boolean, and the SDL3 implementation properly handles the boolean return from SDL_SetWindowFullscreen.


808-816: No action needed. The code correctly implements SDL3 compatibility: SDL_GL_SetAttribute returns bool in SDL3 (true on success, false on failure), and the condition if (!SDL_GL_SetAttribute(flag, value)) properly handles the false return value by raising an error.


3766-3770: LGTM! Correct SDL_ShowMessageBox error handling for both versions.

The error handling correctly adapts to SDL3's boolean return (checking !result for false on line 3767) and SDL2's integer return (checking if (result) for non-zero error code on line 3769). SDL3 returns true on success and false on failure, while SDL2 returns 0 on success and a negative error code on failure. Both code paths properly raise exceptions on error.

Copy link
Member

@ankith26 ankith26 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough, these changes are relatively safer to merge and I would also like to get them in earlier.

Feels weird to approve my own work but this approval is really yours 😉

I will merge this in now and rebase my own PRs on top of the new main

@ankith26 ankith26 added this to the 2.5.7 milestone Dec 16, 2025
@ankith26 ankith26 merged commit ee899f3 into pygame-community:main Dec 16, 2025
28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

display pygame.display sdl3 Surface pygame.Surface window pygame.Window

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants