SDL_shadercross: Synchronize shared SDL cmake/wikiheaders sources

From fdf711c8e2aed5b756e3f9091b09a6f68352ee24 Mon Sep 17 00:00:00 2001
From: Anonymous Maarten <[EMAIL REDACTED]>
Date: Sat, 29 Nov 2025 00:01:59 +0100
Subject: [PATCH] Synchronize shared SDL cmake/wikiheaders sources

---
 build-scripts/wikiheaders.pl    | 258 +++++++++++++++++++++++---------
 cmake/PrivateSdlFunctions.cmake |   8 +-
 cmake/sdlcpu.cmake              |  12 +-
 3 files changed, 201 insertions(+), 77 deletions(-)

diff --git a/build-scripts/wikiheaders.pl b/build-scripts/wikiheaders.pl
index 9859149..bd843de 100755
--- a/build-scripts/wikiheaders.pl
+++ b/build-scripts/wikiheaders.pl
@@ -32,10 +32,16 @@
 my $incsubdir = 'include';
 my $readmesubdir = undef;
 my $apiprefixregex = undef;
+my $apipropertyregex = undef;
 my $versionfname = 'include/SDL_version.h';
 my $versionmajorregex = '\A\#define\s+SDL_MAJOR_VERSION\s+(\d+)\Z';
 my $versionminorregex = '\A\#define\s+SDL_MINOR_VERSION\s+(\d+)\Z';
 my $versionmicroregex = '\A\#define\s+SDL_MICRO_VERSION\s+(\d+)\Z';
+my $wikidocsectionsym = 'SDL_WIKI_DOCUMENTATION_SECTION';
+my $forceinlinesym = 'SDL_FORCE_INLINE';
+my $deprecatedsym = 'SDL_DEPRECATED';
+my $declspecsym = '(?:SDLMAIN_|SDL_)?DECLSPEC';
+my $callconvsym = 'SDLCALL';
 my $mainincludefname = 'SDL.h';
 my $selectheaderregex = '\ASDL.*?\.h\Z';
 my $projecturl = 'https://libsdl.org/';
@@ -43,7 +49,6 @@
 my $bugreporturl = 'https://github.com/libsdl-org/sdlwiki/issues/new';
 my $srcpath = undef;
 my $wikipath = undef;
-my $wikireadmesubdir = 'README';
 my $warn_about_missing = 0;
 my $copy_direction = 0;
 my $optionsfname = undef;
@@ -58,6 +63,11 @@
 my $quickrefurl = undef;
 my $quickrefdesc = undef;
 my $quickrefmacroregex = undef;
+my $envvarenabled = 0;
+my $envvartitle = 'Environment Variables';
+my $envvardesc = undef;
+my $envvarsymregex = undef;
+my $envvarsymreplace = undef;
 my $changeformat = undef;
 my $manpath = undef;
 my $gitrev = undef;
@@ -111,6 +121,7 @@
             $srcpath = $val, next if $key eq 'srcpath';
             $wikipath = $val, next if $key eq 'wikipath';
             $apiprefixregex = $val, next if $key eq 'apiprefixregex';
+            $apipropertyregex = $val, next if $key eq 'apipropertyregex';
             $projectfullname = $val, next if $key eq 'projectfullname';
             $projectshortname = $val, next if $key eq 'projectshortname';
             $wikisubdir = $val, next if $key eq 'wikisubdir';
@@ -136,6 +147,17 @@
             $quickrefurl = $val, next if $key eq 'quickrefurl';
             $quickrefdesc = $val, next if $key eq 'quickrefdesc';
             $quickrefmacroregex = $val, next if $key eq 'quickrefmacroregex';
+            $envvarenabled = int($val), next if $key eq 'envvarenabled';
+            $envvartitle = $val, next if $key eq 'envvartitle';
+            $envvardesc = $val, next if $key eq 'envvardesc';
+            $envvarsymregex = $val, next if $key eq 'envvarsymregex';
+            $envvarsymreplace = $val, next if $key eq 'envvarsymreplace';
+            $wikidocsectionsym = $val, next if $key eq 'wikidocsectionsym';
+            $forceinlinesym = $val, next if $key eq 'forceinlinesym';
+            $deprecatedsym = $val, next if $key eq 'deprecatedsym';
+            $declspecsym = $val, next if $key eq 'declspecsym';
+            $callconvsym = $val, next if $key eq 'callconvsym';
+
         }
     }
     close(OPTIONS);
@@ -342,7 +364,7 @@ sub wikify_chunk {
 
         # Convert obvious API things to wikilinks.
         if (defined $apiprefixregex) {
-            $str =~ s/(\A|[^\/a-zA-Z0-9_])($apiprefixregex[a-zA-Z0-9_]+)/$1\[$2\]\($2\)/gms;
+            $str =~ s/(\A|[^\/a-zA-Z0-9_\[])($apiprefixregex[a-zA-Z0-9_]+)/$1\[$2\]\($2\)/gms;
         }
 
         $str = $codedstr . $str;
@@ -424,7 +446,12 @@ sub dewikify_chunk {
             $str .= "\n```$codelang\n$code\n```\n";
         }
     } elsif ($dewikify_mode eq 'manpage') {
-        $str =~ s/\./\\[char46]/gms;  # make sure these can't become control codes.
+        # make sure these can't become part of roff syntax.
+        $str =~ s/\\/\\(rs/gms;
+        $str =~ s/\./\\[char46]/gms;
+        $str =~ s/"/\\(dq/gms;
+        $str =~ s/'/\\(aq/gms;
+
         if ($wikitype eq 'mediawiki') {
             # Dump obvious wikilinks.
             if (defined $apiprefixregex) {
@@ -449,33 +476,52 @@ sub dewikify_chunk {
             # bullets
             $str =~ s/^\* /\n\\\(bu /gm;
         } elsif ($wikitype eq 'md') {
+            # bullets
+            $str =~ s/^\- /\n\\(bu /gm;
+            # merge paragraphs
+            $str =~ s/^[ \t]+//gm;
+            $str =~ s/([^\-\n])\n([^\-\n])/$1 $2/g;
+            $str =~ s/\n\n/\n.PP\n/g;
+
             # Dump obvious wikilinks.
             if (defined $apiprefixregex) {
-                $str =~ s/\[(\`?$apiprefixregex[a-zA-Z0-9_]+\`?)\]\($apiprefixregex[a-zA-Z0-9_]+\)/\n.BR $1\n/gms;
+                my $apr = $apiprefixregex;
+                if(!($apr =~ /\A\(.*\)\Z/s)) {
+                    # we're relying on the apiprefixregex having a capturing group.
+                    $apr = "(" . $apr . ")";
+                }
+                $str =~ s/(\S*?)\[\`?($apr[a-zA-Z0-9_]+)\`?\]\($apr[a-zA-Z0-9_]+\)(\S*)\s*/\n.BR "" "$1" "$2" "$5"\n/gm;
+                # handle cases like "[x](x), [y](y), [z](z)" being separated.
+                while($str =~ s/(\.BR[^\n]*)\n\n\.BR/$1\n.BR/gm) {}
             }
 
             # links
             $str =~ s/\[(.*?)]\((https?\:\/\/.*?)\)/\n.URL "$2" "$1"\n/g;
 
             # <code></code> is also popular.  :/
-            $str =~ s/\s*\`(.*?)\`\s*/\n.BR $1\n/gms;
+            $str =~ s/\s*(\S*?)\`([^\n]*?)\`(\S*)\s*/\n.BR "" "$1" "$2" "$3"\n/gms;
 
             # bold+italic (this looks bad, just make it bold).
-            $str =~ s/\s*\*\*\*(.*?)\*\*\*\s*/\n.B $1\n/gms;
+            $str =~ s/\s*(\S*?)\*\*\*([^\n]*?)\*\*\*(\S*)\s*/\n.BR "" "$1" "$2" "$3"\n/gms;
 
             # bold
-            $str =~ s/\s*\*\*(.*?)\*\*\s*/\n.B $1\n/gms;
+            $str =~ s/\s*(\S*?)\*\*([^\n]*?)\*\*(\S*)\s*/\n.BR "" "$1" "$2" "$3"\n/gms;
 
             # italic
-            $str =~ s/\s*\*(.*?)\*\s*/\n.I $1\n/gms;
-
-            # bullets
-            $str =~ s/^\- /\n\\\(bu /gm;
+            $str =~ s/\s*(\S*?)\*([^\n]*?)\*(\S*)\s*/\n.IR "" "$1" "$2" "$3"\n/gms;
         }
 
+        # cleanup unnecessary quotes
+        $str =~ s/(\.[IB]R?)(.*?) ""\n/$1$2\n/gm;
+        $str =~ s/(\.[IB]R?) "" ""(.*?)\n/$1$2\n/gm;
+        $str =~ s/"(\S+)"/$1/gm;
+        # cleanup unnecessary whitespace
+        $str =~ s/ +\n/\n/gm;
+
         if (defined $code) {
             $code =~ s/\A\n+//gms;
             $code =~ s/\n+\Z//gms;
+            $code =~ s/\\/\\(rs/gms;
             if ($dewikify_manpage_code_indent) {
                 $str .= "\n.IP\n"
             } else {
@@ -580,7 +626,7 @@ sub dewikify {
             $retval .= dewikify_chunk($wikitype, $1, $2, $3);
         }
     } elsif ($wikitype eq 'md') {
-        while ($str =~ s/\A(.*?)\n```(.*?)\n(.*?)\n```\n//ms) {
+        while ($str =~ s/\A(.*?)\n?```(.*?)\n(.*?)\n```\n//ms) {
             $retval .= dewikify_chunk($wikitype, $1, $2, $3);
         }
     }
@@ -715,6 +761,7 @@ sub print_undocumented_section {
     }
 }
 
+# !!! FIXME: generalize this for other libraries to use.
 sub strip_fn_declaration_metadata {
     my $decl = shift;
     $decl =~ s/SDL_(PRINTF|SCANF)_FORMAT_STRING\s*//;  # don't want this metadata as part of the documentation.
@@ -803,21 +850,23 @@ sub print_big_ascii_string {
             die("Don't have a big ascii entry for '$ch'!\n") if not defined $rowsref;
             my $row = @$rowsref[$rownum];
 
+            my $outstr = '';
             if ($lowascii) {
                 my @x = split //, $row;
                 foreach (@x) {
-                    my $v = ($_ eq "\x{2588}") ? 'X' : ' ';
-                    print $fh $v;
+                    $outstr .= ($_ eq "\x{2588}") ? 'X' : ' ';
                 }
             } else {
-                print $fh $row;
+                $outstr = $row;
             }
 
             $charidx++;
-
-            if ($charidx < $charcount) {
-                print $fh " ";
+            if ($charidx == $charcount) {
+                $outstr =~ s/\s*\Z//;  # dump extra spaces at the end of the line.
+            } else {
+                $outstr .= ' ';   # space between glyphs.
             }
+            print $fh $outstr;
         }
         print $fh "\n";
     }
@@ -1008,10 +1057,58 @@ sub generate_quickref {
 }
 
 
+sub generate_envvar_wiki_page {
+    my $briefsref = shift;
+    my $path = shift;
+
+    return if not $envvarenabled or not defined $envvarsymregex or not defined $envvarsymreplace;
+
+    my $replace = "\"$envvarsymreplace\"";
+    my $tmppath = "$path.tmp";
+    open(my $fh, '>', $tmppath) or die("Can't open '$tmppath': $!\n");
+
+    print $fh "<!-- DO NOT EDIT THIS PAGE ON THE WIKI. IT WILL BE OVERWRITTEN BY WIKIHEADERS AND CHANGES WILL BE LOST! -->\n\n";
+    print $fh "# $envvartitle\n\n";
+
+    if (defined $envvardesc) {
+        my $desc = "$envvardesc";
+        $desc =~ s/\\n/\n/g;  # replace "\n" strings with actual newlines.
+        print $fh "$desc\n\n";
+    }
+
+    print $fh "## Environment Variable List\n\n";
+
+    foreach (sort keys %headersyms) {
+        my $sym = $_;
+        next if $headersymstype{$sym} != 2;  # not a #define? skip it.
+        my $hint = "$_";
+        next if not $hint =~ s/$envvarsymregex/$replace/ee;
+
+        my $brief = $$briefsref{$sym};
+        if (not defined $brief) {
+            $brief = '';
+        } else {
+            $brief = "$brief";
+            chomp($brief);
+            my $thiswikitype = defined $wikitypes{$sym} ? $wikitypes{$sym} : 'md';  # default to MarkDown for new stuff.
+            $brief = ": " . dewikify($thiswikitype, $brief);
+        }
+        print $fh "- [$hint]($sym)$brief\n";
+    }
+
+    print $fh "\n";
+
+    close($fh);
+
+    rename($tmppath, $path) or die("Can't rename '$tmppath' to '$path': $!\n");
+}
+
+
+
+
 my $incpath = "$srcpath";
 $incpath .= "/$incsubdir" if $incsubdir ne '';
 
-my $wikireadmepath = "$wikipath/$wikireadmesubdir";
 my $readmepath = undef;
 if (defined $readmesubdir) {
     $readmepath = "$srcpath/$readmesubdir";
@@ -1068,7 +1165,7 @@ sub generate_quickref {
         } elsif ($ignoring_lines) {
             push @contents, $_;
             next;
-        } elsif (/\A\s*\#\s*ifndef\s+SDL_WIKI_DOCUMENTATION_SECTION\s*\Z/) {
+        } elsif (/\A\s*\#\s*ifndef\s+$wikidocsectionsym\s*\Z/) {
             $ignoring_lines = 1;
             push @contents, $_;
             next;
@@ -1077,13 +1174,13 @@ sub generate_quickref {
             #print("CATEGORY FOR '$dent' CHANGED TO " . (defined($current_wiki_category) ? "'$current_wiki_category'" : '(undef)') . "\n");
             push @contents, $_;
             next;
-        } elsif (/\A\s*extern\s+(SDL_DEPRECATED\s+|)(SDLMAIN_|SDL_)?DECLSPEC/) {  # a function declaration without a doxygen comment?
+        } elsif (/\A\s*extern\s+(?:$deprecatedsym\s+|)$declspecsym/) {  # a function declaration without a doxygen comment?
             $symtype = 1;   # function declaration
             @templines = ();
             $decl = $_;
             $str = '';
             $has_doxygen = 0;
-        } elsif (/\A\s*SDL_FORCE_INLINE/) {  # a (forced-inline) function declaration without a doxygen comment?
+        } elsif (/\A\s*$forceinlinesym/) {  # a (forced-inline) function declaration without a doxygen comment?
             $symtype = 1;   # function declaration
             @templines = ();
             $decl = $_;
@@ -1150,9 +1247,9 @@ sub generate_quickref {
                 $lineno++ if defined $decl;
                 $decl = '' if not defined $decl;
                 chomp($decl);
-                if ($decl =~ /\A\s*extern\s+(SDL_DEPRECATED\s+|)(SDLMAIN_|SDL_)?DECLSPEC/) {
+                if ($decl =~ /\A\s*extern\s+(?:$deprecatedsym\s+|)$declspecsym/) {
                     $symtype = 1;   # function declaration
-                } elsif ($decl =~ /\A\s*SDL_FORCE_INLINE/) {
+                } elsif ($decl =~ /\A\s*$forceinlinesym/) {
                     $symtype = 1;   # (forced-inline) function declaration
                 } elsif ($decl =~ /\A\s*\#\s*define\s+/) {
                     $symtype = 2;   # macro
@@ -1189,7 +1286,7 @@ sub generate_quickref {
             }
             $headercategorydocs{$current_wiki_category} = $sym;
         } elsif ($symtype == 1) {  # a function
-            my $is_forced_inline = ($decl =~ /\A\s*SDL_FORCE_INLINE/);
+            my $is_forced_inline = ($decl =~ /\A\s*$forceinlinesym/);
 
             if ($is_forced_inline) {
                 if (not $decl =~ /\)\s*(\{.*|)\s*\Z/) {
@@ -1226,14 +1323,14 @@ sub generate_quickref {
 
             my $paramsstr = undef;
 
-            if (!$is_forced_inline && $decl =~ /\A\s*extern\s+(SDL_DEPRECATED\s+|)(SDLMAIN_|SDL_)?DECLSPEC\w*\s+(const\s+|)(unsigned\s+|)(.*?)([\*\s]+)(\*?)\s*SDLCALL\s+(.*?)\s*\((.*?)\);/) {
-                $sym = $8;
-                $rettype = "$3$4$5$6";
-                $paramsstr = $9;
-             } elsif ($is_forced_inline && $decl =~ /\A\s*SDL_FORCE_INLINE\s+(SDL_DEPRECATED\s+|)(const\s+|)(unsigned\s+|)(.*?)([\*\s]+)(.*?)\s*\((.*?)\);/) {
+            if (!$is_forced_inline && $decl =~ /\A\s*extern\s+(?:$deprecatedsym\s+|)$declspecsym\w*\s+(const\s+|)(unsigned\s+|)(.*?)([\*\s]+)(\*?)\s*$callconvsym\s+(.*?)\s*\((.*?)\);/) {
                 $sym = $6;
-                $rettype = "$2$3$4$5";
+                $rettype = "$1$2$3$4$5";
                 $paramsstr = $7;
+             } elsif ($is_forced_inline && $decl =~ /\A\s*$forceinlinesym\s+(?:$deprecatedsym\s+|)(const\s+|)(unsigned\s+|)(.*?)([\*\s]+)(.*?)\s*\((.*?)\);/) {
+                $sym = $5;
+                $rettype = "$1$2$3$4";
+                $paramsstr = $6;
              } else {
                 #print "Found doxygen but no function sig:\n$str\n\n";
                 foreach (@templines) {
@@ -1299,7 +1396,7 @@ sub generate_quickref {
 
                         $decl = $_;
                         $temp = $decl;
-                        $temp =~ s/\Aextern\s+(SDL_DEPRECATED\s+|)(SDLMAIN_|SDL_)?DECLSPEC\w*\s+(.*?)\s+(\*?)SDLCALL\s+/$3$4 /;
+                        $temp =~ s/\Aextern\s+(?:$deprecatedsym\s+|)$declspecsym\w*\s+(.*?)\s+(\*?)$callconvsym\s+/$1$2 /;
                         $shrink_length = length($decl) - length($temp);
                         $decl = $temp;
                     } else {
@@ -1343,7 +1440,7 @@ sub generate_quickref {
                     # update strings now that we know everything pending is to be applied to this declaration. Add pending blank lines and the new text.
 
                     # At Sam's request, don't list property defines with functions. (See #9440)
-                    my $is_property = /\A\s*\#\s*define\s+SDL_PROP_/;
+                    my $is_property = (defined $apipropertyregex) ? /$apipropertyregex/ : 0;
                     if (!$is_property) {
                         if ($blank_lines > 0) {
                             while ($blank_lines > 0) {
@@ -1364,7 +1461,7 @@ sub generate_quickref {
             }
             $decl .= $additional_decl;
         } elsif ($symtype == 2) {  # a macro
-            if ($decl =~ /\A\s*\#\s*define\s+(.*?)(\(.*?\)|)\s+/) {
+            if ($decl =~ /\A\s*\#\s*define\s+(.*?)(\(.*?\)|)(\s+|\Z)/) {
                 $sym = $1;
             } else {
                 #print "Found doxygen but no macro:\n$str\n\n";
@@ -2060,18 +2157,15 @@ sub generate_quickref {
     }
 
     if (defined $readmepath) {
-        if ( -d $wikireadmepath ) {
-            mkdir($readmepath);  # just in case
-            opendir(DH, $wikireadmepath) or die("Can't opendir '$wikireadmepath': $!\n");
-            while (readdir(DH)) {
-                my $dent = $_;
-                if ($dent =~ /\A(.*?)\.md\Z/) {  # we only bridge Markdown files here.
-                    next if $1 eq 'FrontPage';
-                    filecopy("$wikireadmepath/$dent", "$readmepath/README-$dent", "\n");
-                }
+        mkdir($readmepath);  # just in case
+        opendir(DH, $wikipath) or die("Can't opendir '$wikipath': $!\n");
+        while (readdir(DH)) {
+            my $dent = $_;
+            if ($dent =~ /\A(README|INTRO)\-.*?\.md\Z/) {  # we only bridge Markdown files here that start with "README-" or "INTRO-".
+                filecopy("$wikipath/$dent", "$readmepath/$dent", "\n");
             }
-            closedir(DH);
         }
+        closedir(DH);
     }
 
 } elsif ($copy_direction == -1) { # --copy-to-wiki
@@ -2177,10 +2271,10 @@ sub generate_quickref {
 
                 $desc =~ s/[\s\n]+\Z//ms;
 
-                if (0) {  # !!! FIXME: disabled because it's not currently suitable for general use, but for manually inspecting the output, it can be useful.
-                    if (($desc =~ /\A[A-Z]/) && (not $desc =~ /\ASDL_/)) {
-                        print STDERR "WARNING: $sym\'s '\\param $arg' text starts with a capital letter: '$desc'. Fixing.\n";
-                        $desc = lcfirst($desc);
+                if (0) {
+                    if (($desc =~ /\A[a-z]/) && (not $desc =~ /$apiprefixregex/)) {
+                        print STDERR "WARNING: $sym\'s '\\param $arg' text starts with a lowercase letter: '$desc'. Fixing.\n";
+                        $desc = ucfirst($desc);
                     }
                 }
 
@@ -2224,8 +2318,8 @@ sub generate_quickref {
                 }
                 $desc =~ s/[\s\n]+\Z//ms;
 
-                if (0) {  # !!! FIXME: disabled because it's not currently suitable for general use, but for manually inspecting the output, it can be useful.
-                    if (($desc =~ /\A[A-Z]/) && (not $desc =~ /\ASDL_/)) {
+                if (0) {
+                    if (($desc =~ /\A[A-Z]/) && (not $desc =~ /$apiprefixregex/)) {
                         print STDERR "WARNING: $sym\'s '\\returns' text starts with a capital letter: '$desc'. Fixing.\n";
                         $desc = lcfirst($desc);
                     }
@@ -2369,7 +2463,7 @@ sub generate_quickref {
                 } else {
                     die("Unexpected symbol type $symtype!");
                 }
-                my $str = "This $symtypename is available since SDL 3.0.0.";
+                my $str = "This $symtypename is available since $projectshortname 3.0.0.";
                 $sections{'Version'} = wordwrap(wikify($wikitype, $str)) . "\n";
             }
         }
@@ -2676,31 +2770,27 @@ sub generate_quickref {
     # Write out READMEs...
     if (defined $readmepath) {
         if ( -d $readmepath ) {
-            mkdir($wikireadmepath);  # just in case
+            mkdir($wikipath);  # just in case
             opendir(DH, $readmepath) or die("Can't opendir '$readmepath': $!\n");
             while (my $d = readdir(DH)) {
                 my $dent = $d;
-                if ($dent =~ /\AREADME\-(.*?\.md)\Z/) {  # we only bridge Markdown files here.
-                    my $wikifname = $1;
-                    next if $wikifname eq 'FrontPage.md';
-                    filecopy("$readmepath/$dent", "$wikireadmepath/$wikifname", "\n");
+                if ($dent =~ /\A(README|INTRO)\-.*?\.md\Z/) {  # we only bridge Markdown files here that start with "README-" or "INTRO".
+                    filecopy("$readmepath/$dent", "$wikipath/$dent", "\n");
                 }
             }
             closedir(DH);
 
             my @pages = ();
-            opendir(DH, $wikireadmepath) or die("Can't opendir '$wikireadmepath': $!\n");
+            opendir(DH, $wikipath) or die("Can't opendir '$wikipath': $!\n");
             while (my $d = readdir(DH)) {
                 my $dent = $d;
-                if ($dent =~ /\A(.*?)\.(mediawiki|md)\Z/) {
-                    my $wikiname = $1;
-                    next if $wikiname eq 'FrontPage';
-                    push @pages, $wikiname;
+                if ($dent =~ /\A((README|INTRO)\-.*?)\.md\Z/) {
+                    push @pages, $1;
                 }
             }
             closedir(DH);
 
-            open(FH, '>', "$wikireadmepath/FrontPage.md") or die("Can't open '$wikireadmepath/FrontPage.md': $!\n");
+            open(FH, '>', "$wikipath/READMEs.md") or die("Can't open '$wikipath/READMEs.md': $!\n");
             print FH "# All READMEs available here\n\n";
             foreach (sort @pages) {
                 my $wikiname = $_;
@@ -2715,6 +2805,11 @@ sub generate_quickref {
         generate_quickref(\%briefs, "$wikipath/QuickReference.md", 0);
         generate_quickref(\%briefs, "$wikipath/QuickReferenceNoUnicode.md", 1);
     }
+
+    if ($envvarenabled and defined $envvarsymregex and defined $envvarsymreplace) {
+        generate_envvar_wiki_page(\%briefs, "$wikipath/EnvironmentVariables.md");
+    }
+
 } elsif ($copy_direction == -2) { # --copy-to-manpages
     # This only takes from the wiki data, since it has sections we omit from the headers, like code examples.
 
@@ -2765,7 +2860,6 @@ sub generate_quickref {
         my $wikitype = $wikitypes{$sym};
         my $sectionsref = $wikisyms{$sym};
         my $remarks = $sectionsref->{'Remarks'};
-        my $params = $sectionsref->{'Function Parameters'};
         my $returns = $sectionsref->{'Return Value'};
         my $version = $sectionsref->{'Version'};
         my $threadsafety = $sectionsref->{'Thread Safety'};
@@ -2773,6 +2867,23 @@ sub generate_quickref {
         my $examples = $sectionsref->{'Code Examples'};
         my $deprecated = $sectionsref->{'Deprecated'};
         my $headerfile = $manpageheaderfiletext;
+
+        my $params = undef;
+
+        if ($symtype == -1) {  # category documentation block.
+            # nothing to be done here.
+        } elsif (($symtype == 1) || (($symtype == 5))) {  # we'll assume a typedef (5) with a \param is a function pointer typedef.
+            $params = $sectionsref->{'Function Parameters'};
+        } elsif ($symtype == 2) {
+            $params = $sectionsref->{'Macro Parameters'};
+        } elsif ($symtype == 3) {
+            $params = $sectionsref->{'Fields'};
+        } elsif ($symtype == 4) {
+            $params = $sectionsref->{'Values'};
+        } else {
+            die("Unexpected symtype $symtype");
+        }
+
         $headerfile =~ s/\%fname\%/$headersymslocation{$sym}/g;
         $headerfile .= "\n";
 
@@ -2817,7 +2928,7 @@ sub generate_quickref {
         $str .= ".\\\" Please report issues in this manpage's content at:\n";
         $str .= ".\\\"   $bugreporturl\n";
         $str .= ".\\\" Please report issues in the generation of this manpage from the wiki at:\n";
-        $str .= ".\\\"   https://github.com/libsdl-org/SDL/issues/new?title=Misgenerated%20manpage%20for%20$sym\n";
+        $str .= ".\\\"   https://github.com/libsdl-org/SDL/issues/new?title=Misgenerated%20manpage%20for%20$sym\n";  # !!! FIXME: if this becomes a problem for other projects, we'll generalize this.
         $str .= ".\\\" $projectshortname can be found at $projecturl\n";
 
         # Define a .URL macro. The "www.tmac" thing decides if we're using GNU roff (which has a .URL macro already), and if so, overrides the macro we just created.
@@ -2839,18 +2950,22 @@ sub generate_quickref {
             $str .= dewikify($wikitype, $deprecated) . "\n";
         }
 
+        my $incfile = $mainincludefname;
         if (defined $headerfile) {
-            $str .= ".SH HEADER FILE\n";
-            $str .= dewikify($wikitype, $headerfile) . "\n";
+            if($headerfile =~ /Defined in (.*)/) {
+                $incfile = $1;
+            }
         }
 
         $str .= ".SH SYNOPSIS\n";
         $str .= ".nf\n";
-        $str .= ".B #include \\(dq$mainincludefname\\(dq\n";
+        $str .= ".B #include <$incfile>\n";
         $str .= ".PP\n";
 
         my @decllines = split /\n/, $decl;
         foreach (@decllines) {
+            $_ =~ s/\\/\\(rs/g;  # fix multiline macro defs
+            $_ =~ s/"/\\(dq/g;
             $str .= ".BI \"$_\n";
         }
         $str .= ".fi\n";
@@ -2938,8 +3053,13 @@ sub generate_quickref {
         }
 
         if (defined $returns) {
+            # Check for md link in return type: ([SDL_Renderer](SDL_Renderer) *)
+            # This would've prevented the next regex from working properly (it'd leave " *)")
+            $returns =~ s/\A\(\[.*?\]\((.*?)\)/\($1/ms;
+            # Chop datatype in parentheses off the front.
+            $returns =~ s/\A\(.*?\) //;
+
             $returns = dewikify($wikitype, $returns);
-            $returns =~ s/\A\(.*?\)\s*//;  # Chop datatype in parentheses off the front.
             $str .= ".SH RETURN VALUE\n";
             $str .= "$returns\n";
         }
@@ -2975,6 +3095,8 @@ sub generate_quickref {
                 s/\A\/*//;
                 s/\A\.BR\s+//;  # dewikify added this, but we want to handle it.
                 s/\A\.I\s+//;  # dewikify added this, but we want to handle it.
+                s/\A\.PP\s*//;  # dewikify added this, but we want to handle it.
+                s/\\\(bu//;  # dewikify added this, but we want to handle it.
                 s/\A\s*[\:\*\-]\s*//;
                 s/\A\s+//;
                 s/\s+\Z//;
diff --git a/cmake/PrivateSdlFunctions.cmake b/cmake/PrivateSdlFunctions.cmake
index e6965fa..8d71ede 100644
--- a/cmake/PrivateSdlFunctions.cmake
+++ b/cmake/PrivateSdlFunctions.cmake
@@ -310,7 +310,7 @@ function(sdl_add_warning_options TARGET)
     if(MSVC)
         target_compile_options(${TARGET} PRIVATE /W2)
     else()
-        target_compile_options(${TARGET} PRIVATE -Wall -Wextra)
+        target_compile_options(${TARGET} PRIVATE -Wall -Wextra -Wno-unused-parameter)
     endif()
     if(ARGS_WARNING_AS_ERROR)
         if(MSVC)
@@ -323,9 +323,9 @@ endfunction()
 
 function(sdl_no_deprecated_errors TARGET)
     check_c_compiler_flag(-Wno-error=deprecated-declarations HAVE_WNO_ERROR_DEPRECATED_DECLARATIONS)
-        if(HAVE_WNO_ERROR_DEPRECATED_DECLARATIONS)
-    target_compile_options(${TARGET} PRIVATE "-Wno-error=deprecated-declarations")
-endif()
+    if(HAVE_WNO_ERROR_DEPRECATED_DECLARATIONS)
+        target_compile_options(${TARGET} PRIVATE "-Wno-error=deprecated-declarations")
+    endif()
 endfunction()
 
 function(sdl_get_git_revision_hash VARNAME)
diff --git a/cmake/sdlcpu.cmake b/cmake/sdlcpu.cmake
index d63a57f..a27e732 100644
--- a/cmake/sdlcpu.cmake
+++ b/cmake/sdlcpu.cmake
@@ -1,18 +1,18 @@
 function(SDL_DetectTargetCPUArchitectures DETECTED_ARCHS)
 
-  set(known_archs EMSCRIPTEN ARM32 ARM64 ARM64EC LOONGARCH64 POWERPC32 POWERPC64 X86 X64)
+  set(known_archs EMSCRIPTEN ARM32 ARM64 ARM64EC LOONGARCH64 POWERPC32 POWERPC64 RISCV32 RISCV64 X86 X64)
 
   if(APPLE AND CMAKE_OSX_ARCHITECTURES)
     foreach(known_arch IN LISTS known_archs)
-      set(SDL_CPU_${known_arch} "0")
+      set(SDL_CPU_${known_arch} "0" PARENT_SCOPE)
     endforeach()
     set(detected_archs)
     foreach(osx_arch IN LISTS CMAKE_OSX_ARCHITECTURES)
       if(osx_arch STREQUAL "x86_64")
-        set(SDL_CPU_X64 "1")
+        set(SDL_CPU_X64 "1" PARENT_SCOPE)
         list(APPEND detected_archs "X64")
       elseif(osx_arch STREQUAL "arm64")
-        set(SDL_CPU_ARM64 "1")
+        set(SDL_CPU_ARM64 "1" PARENT_SCOPE)
         list(APPEND detected_archs "ARM64")
       endif()
     endforeach()
@@ -39,6 +39,8 @@ function(SDL_DetectTargetCPUArchitectures DETECTED_ARCHS)
   set(arch_check_LOONGARCH64 "defined(__loongarch64)")
   set(arch_check_POWERPC32 "(defined(__PPC__) || defined(__powerpc__)) && !defined(__powerpc64__)")
   set(arch_check_POWERPC64 "defined(__PPC64__) || defined(__powerpc64__)")
+  set(arch_check_RISCV32 "defined(__riscv) && defined(__riscv_xlen) && __riscv_xlen == 32")
+  set(arch_check_RISCV64 "defined(__riscv) && defined(__riscv_xlen) && __riscv_xlen == 64")
   set(arch_check_X86 "defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) ||defined( __i386) || defined(_M_IX86)")
   set(arch_check_X64 "(defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64)) && !defined(_M_ARM64EC)")
 
@@ -61,8 +63,8 @@ const char *arch_${known_arch} = \"INFO<${known_arch}=\" ARCH_${known_arch} \">\
 
   set(src_arch_detect "${src_vars}
 int main(int argc, char *argv[]) {
-  (void)argv;
   int result = 0;
+  (void)argv;
 ${src_main}
   return result;
 }")