SDL for Windows CE: a few patches

Hi *!

I’ve just ported a POWDER roguelike ( http://www.zincland.com/powder/ ) to
Windows CE (PDAs, Windows Mobile/Pocket PC). To do that, I had to get libsdl
working. Thanks for the awesome project files, it built without a hitch.

Nevertheless, I’ve found quite a few bugs in Windows CE (GAPI) SDL
implementation, which I’ve solved and now present as a serie of patches.

I’ll carefully annotate them. Please accept them into the main source tree
since without them SDL isn’t really working on Windows CE (I wonder why nobody
fixed them before, btw: why isn’t SDL popular as a way to develop Windows CE
games? Where are no ports?)

These changes can’t be considered flawless, but they can be considered working
because I’ve yet to hear complains about things I fixed and POWDER build for
Windows CE is now considered stable.

Note: my comments start with !!, delete them before applying.

diff -bru clean\SDL-1.2.13/src/video/gapi/SDL_gapivideo.c
SDL-1.2.13/src/video/gapi/SDL_gapivideo.c
— clean\SDL-1.2.13/src/video/gapi/SDL_gapivideo.c Sun Dec 30 16:48:00
2007
+++ SDL-1.2.13/src/video/gapi/SDL_gapivideo.c Thu Jul 24 19:38:54 2008
@@ -661,18 +661,18 @@
}

    /* detect user landscape mode */
  •   if( (width > height) && (GetSystemMetrics(SM_CXSCREEN) < 
    

GetSystemMetrics(SM_CYSCREEN)))

  •   if( (width > height) && (gapi->gxProperties.cxWidth < 
    

gapi->gxProperties.cyHeight))
gapi->userOrientation = SDL_ORIENTATION_RIGHT;

    /* shall we apply hires fix? for example when we do not use hires 

resource */
gapi->hiresFix = 0;
if( gapi->userOrientation == SDL_ORIENTATION_RIGHT )
{

  •           if( (width > GetSystemMetrics(SM_CYSCREEN)) || (height > 
    

GetSystemMetrics(SM_CXSCREEN)))

  •           if( (width > gapi->gxProperties.cyHeight) || (height > 
    

gapi->gxProperties.cxWidth))
gapi->hiresFix = 1;
} else

  •           if( (width > GetSystemMetrics(SM_CXSCREEN)) || (height > 
    

GetSystemMetrics(SM_CYSCREEN)))

  •                   if( !((width == GetSystemMetrics(SM_CYSCREEN)) && 
    

(height == GetSystemMetrics(SM_CXSCREEN)))) // user portrait, device landscape

  •           if( (width > gapi->gxProperties.cxWidth) || (height > 
    

gapi->gxProperties.cyHeight))

  •                   if( !((width == gapi->gxProperties.cyHeight) && (height 
    

== gapi->gxProperties.cxWidth))) // user portrait, device landscape
gapi->hiresFix = 1;

    switch( gapi->userOrientation )

!! It used to query system metrics which return dimensions according to screen
orientation, which can really be portrait, left landscape or right landscape.
!! This is presumably incorrect because we couldn’t care less about user mode
dimensions - all we want are the GAPI framebuffer dimensions, which only match
user dimensions in one of possible orientations.
!! There’s a fair dose of cargo cult programming involved in this fix, but it
used to work only in one orientation (portrait for PDAs, where frame-buffer
have same orientation as user screen), and now it works on all orientations.
@@ -742,12 +742,15 @@
WIN_FlushMessageQueue();

    /* Open GAPI display */
  •   if( !gapi->useVga && this->hidden->useGXOpenDisplay )
    
  •   if( !gapi->useVga && this->hidden->useGXOpenDisplay 
    

&& !this->hidden->alreadyGXOpened )

  •   {
    
  •           this->hidden->alreadyGXOpened = 1;
              if( !gapi->gxFunc.GXOpenDisplay(SDL_Window, GX_FULLSCREEN) )
              {
                      SDL_SetError("Couldn't initialize GAPI");
                      return(NULL);
              }
    
  •   }
    

#if REPORT_VIDEO_INFO
printf(“Video properties:\n”);
@@ -757,6 +760,9 @@
printf(“x pitch: %d\n”, gapi->gxProperties.cbxPitch);
printf(“y pitch: %d\n”, gapi->gxProperties.cbyPitch);
printf(“gapi flags: 0x%x\n”, gapi->gxProperties.ffFormat);

  •   printf("gapi user orientation: %d\n", gapi->userOrientation);
    
  •   printf("gapi orientation: %d\n", gapi->gapiOrientation);+
    
      if( !gapi->useVga && this->hidden->useGXOpenDisplay && 
    

gapi->needUpdate)
{
diff -bru clean\SDL-1.2.13/src/video/gapi/SDL_gapivideo.h
SDL-1.2.13/src/video/gapi/SDL_gapivideo.h
!! Previous version used to call gapi->gxFunc.GXOpenDisplay each time the video
mode would be changed. You shouldn’t, because this call has a meaning “Lock the
GAPI framebuffer, designate it as busy”, so the second call will fail (it is
already locked/busy).
!! Testing might not find that because most programs set up the video mode only
once, but POWDER does this once in a while, so it crashed when in 320x240 mode
(640x480 mode doesn’t use that code, it worked fine).
— clean\SDL-1.2.13/src/video/gapi/SDL_gapivideo.h Sun Dec 30 16:48:00
2007
+++ SDL-1.2.13/src/video/gapi/SDL_gapivideo.h Thu Jul 24 18:41:26 2008
@@ -137,6 +137,7 @@
char hiresFix; // using hires mode without defining hires resource
// --------------
int useGXOpenDisplay; /* use GXOpenDispplay */

  •   int alreadyGXOpened;
    
    int w, h;
    enum SDL_ScreenOrientation gapiOrientation;

diff -bru clean\SDL-1.2.13/src/video/wincommon/SDL_sysevents.c
SDL-1.2.13/src/video/wincommon/SDL_sysevents.c
— clean\SDL-1.2.13/src/video/wincommon/SDL_sysevents.c Sun Dec 30
16:48:02 2007
!! This is a flag variable, see the previous comment
+++ SDL-1.2.13/src/video/wincommon/SDL_sysevents.c Thu Jul 24 18:45:14
2008
@@ -160,10 +160,22 @@
#endif */
}
break;

  •           // FIXME: Older version used just SDL_VideoSurface->(w, h)
    
  •           // w and h are "clipped" while x and y are "raw", which caused
    
  •           // x in former and y in latter case to be clipped in a wrong 
    

direction,

  •           // thus offsetting the coordinate on 2 x clip pixels
    
  •           //     (like, 128 for 640 -> 512 clipping).
    
  •           // We will now try to extract and use raw values.
    
  •           // The way to do that RIGHT is do (orientation-dependent) 
    

clipping before

  •           // doing this transform, but it's hardly possible.
    
  •           // SEE SDL_mouse.c /ClipOffset to understand these 
    

calculations.
case SDL_ORIENTATION_RIGHT:
if (!SDL_VideoSurface)
break;

  •                   rotatedX = SDL_VideoSurface->w - *y;
    
  •                   rotatedX = (2 * 
    

((SDL_VideoSurface->offset%SDL_VideoSurface->pitch)/

  •                           SDL_VideoSurface->format->BytesPerPixel))
    
  •                           + SDL_VideoSurface->w - *y;
                      rotatedY = *x;
                      *x = rotatedX;
                      *y = rotatedY;
    

@@ -172,7 +184,8 @@
if (!SDL_VideoSurface)
break;
rotatedX = *y;

  •                   rotatedY = SDL_VideoSurface->h - *x;
    
  •                   rotatedY = (2 * 
    

(SDL_VideoSurface->offset/SDL_VideoSurface->pitch))

  •                           + SDL_VideoSurface->h - *x;
                      *x = rotatedX;
                      *y = rotatedY;
                      break;
    

!! That’s the trickest part, hence the long comment.
GAPI would really support only 320x240 or 640x480 mode, if application
requested the different screen size (as POWDER did, wishing 256x192), then SDL
is going to grab the first mode that fits the requested, and pad the screen
with black bars (as they do with wide-screen films).
!! It would also get, say, 240x320 mode, and to turn it into 256x192 it would
need to rotate mouse clicks.
!! It worked, but one bug slipped through: it would receive mouse clicks
unpadded, then rotate them, and then pad the black bars. The problem is: rotate
is done by GAPI driver while padding is done by SDL core. SDL core doesn’t know
anything about rotating, so it would pad one of dimensions incorrectly.

I understand that some of my claims (or code) might seem unbacked, but you can
always grab the POWDER binary, compile your own libsdl with one or more of
those fixes turned off, and see how weird it would misbehave. I can even supply
you with those custom builds of libsdl if you don’t want to set up the build
environment for windows ce, you’ll just need a PDA or a smartphone with it.

I plan to take care of SDL on Windows CE as long as I maintain the POWDER port.