view src/timer/os2/SDL_systimer.c @ 1192:54aa9aa32327

To: sdl@libsdl.org From: Christian Walther <cwalther@gmx.ch> Date: Fri, 18 Nov 2005 23:39:02 +0100 Subject: [SDL] Mouse position bugs on Mac OS X The attached patch fixes a few bugs in SDL related to the mouse position in windowed mode on Mac OS X, reproduced using the attached minimal test program - at least here on 10.3.9, with SDL CVS from today. Could anyone test whether the bugs exist and are fixed by the patch on 10.2 and 10.4? 1. When using OpenGL, the vertical mouse positions obtained through events or SDL_GetMouseState() are off by one. 2. When using OpenGL, SDL_WarpMouse() inverts the y coordinate. 3. Clicks on the topmost pixel row of the window are not recognized. 1 and 2 do not occur in non-OpenGL mode, while 3 does. All three only occur in windowed mode, not in fullscreen. The cause for 1 and 3 is that in Cocoa, "the location of the mouse" seems to be defined as "the location of the top left corner of the mouse pointer's hot pixel" (this is not documented, it's just what I found out here), which together with the fact that Cocoa's usual y coordinates start at the bottom and increase upwards means that the y coordinate of the mouse runs from 1 to h, not from 0 to h-1, in a window of height h. If it does work on 10.2 and 10.4 (I'll try to test it as soon as I can, but at the moment all I have at hand is 10.3.9), can this be applied to the CVS? -Christian To: sdl@libsdl.org From: Christian Walther <cwalther@gmx.ch> Date: Mon, 28 Nov 2005 10:41:51 +0100 Subject: [SDL] Re: Mouse position bugs on Mac OS X I wrote: > I'll try to test it as soon as I can, but at the moment all I have at hand is 10.3.9 So, here are the results of my tests (with patched and unpatched frameworks compiled with Xcode 1.5 (gcc 3.3) on 10.3.9): On 10.1.5, my test program doesn't run because of "Undefined symbols: SDL undefined reference to _CGMainDisplayID expected to be defined in Carbon". I guess not supporting 10.1 was a deliberate decision then and that's OK with me. On 10.2.8, 10.3.9, and 10.4.0, the bugs exist as described in my original post and are fixed by my patch. That is, there is no difference between pre/post 10.3 and the patched version works correctly in all combinations of GL/non-GL and windowed/fullscreen. I therefore recommend the patch for inclusion. -Christian
author Ryan C. Gordon <icculus@icculus.org>
date Mon, 28 Nov 2005 13:58:26 +0000
parents 173c063d4f55
children c9b51268668f
line wrap: on
line source

/*
    SDL - Simple DirectMedia Layer
    Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002  Sam Lantinga

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

    Sam Lantinga
    slouken@libsdl.org
*/

#ifdef SAVE_RCSID
static char rcsid =
 "@(#) $Id$";
#endif

#define INCL_DOSMISC
#define INCL_DOSERRORS
#define INCL_DOSSEMAPHORES
#define INCL_DOSDATETIME
#define INCL_DOSPROCESS
#define INCL_DOSPROFILE
#define INCL_DOSEXCEPTIONS
#include <os2.h>

#include "SDL_thread.h"
#include "SDL_timer.h"
#include "SDL_timer_c.h"
#include "SDL_error.h"


#define TIME_WRAP_VALUE (~(DWORD)0)

/* The first high-resolution ticks value of the application */
static long long hires_start_ticks;
/* The number of ticks per second of the high-resolution performance counter */
static ULONG hires_ticks_per_second;

void SDL_StartTicks(void)
{
        DosTmrQueryFreq(&hires_ticks_per_second);
        DosTmrQueryTime((PQWORD)&hires_start_ticks);
}

DECLSPEC Uint32 SDLCALL SDL_GetTicks(void)
{
        long long hires_now;
        ULONG ticks = ticks;

        DosTmrQueryTime((PQWORD)&hires_now);
/*
        hires_now -= hires_start_ticks;
        hires_now *= 1000;
        hires_now /= hires_ticks_per_second;
*/
        /* inline asm to avoid runtime inclusion */
        _asm {
           push edx
           push eax
           mov eax, dword ptr hires_now
           mov edx, dword ptr hires_now+4
           sub eax, dword ptr hires_start_ticks
           sbb edx, dword ptr hires_start_ticks+4
           mov ebx,1000
           mov ecx,edx
           mul ebx
           push eax
           push edx
           mov eax,ecx
           mul ebx
           pop eax
           add edx,eax
           pop eax
           mov ebx, dword ptr hires_ticks_per_second
           div ebx
           mov dword ptr ticks, eax
           pop edx
           pop eax
        }

        return ticks;

}

/* High resolution sleep, originally made by Ilya Zakharevich */
DECLSPEC void SDLCALL SDL_Delay(Uint32 ms)
{
  /* This is similar to DosSleep(), but has 8ms granularity in time-critical
     threads even on Warp3. */
  HEV     hevEvent1     = 0;   /* Event semaphore handle    */
  HTIMER  htimerEvent1  = 0;   /* Timer handle              */
  APIRET  rc            = NO_ERROR;  /* Return code               */
  int ret = 1;
  ULONG priority = 0, nesting;   /* Shut down the warnings */
  PPIB pib;
  PTIB tib;
  char *e = NULL;
  APIRET badrc;
  int switch_priority = 50;

  DosCreateEventSem(NULL,      /* Unnamed */
                    &hevEvent1,  /* Handle of semaphore returned */
                    DC_SEM_SHARED, /* Shared needed for DosAsyncTimer */
                    FALSE);      /* Semaphore is in RESET state  */

  if (ms >= switch_priority)
    switch_priority = 0;
  if (switch_priority)
  {
    if (DosGetInfoBlocks(&tib, &pib)!=NO_ERROR)
      switch_priority = 0;
    else
    {
 /* In Warp3, to switch scheduling to 8ms step, one needs to do 
    DosAsyncTimer() in time-critical thread.  On laters versions,
    more and more cases of wait-for-something are covered.

    It turns out that on Warp3fp42 it is the priority at the time
    of DosAsyncTimer() which matters.  Let's hope that this works
    with later versions too...  XXXX
  */
      priority = (tib->tib_ptib2->tib2_ulpri);
      if ((priority & 0xFF00) == 0x0300) /* already time-critical */
        switch_priority = 0;
 /* Make us time-critical.  Just modifying TIB is not enough... */
 /* tib->tib_ptib2->tib2_ulpri = 0x0300;*/
 /* We do not want to run at high priority if a signal causes us
    to longjmp() out of this section... */
      if (DosEnterMustComplete(&nesting))
        switch_priority = 0;
      else
        DosSetPriority(PRTYS_THREAD, PRTYC_TIMECRITICAL, 0, 0);
    }
  }

  if ((badrc = DosAsyncTimer(ms,
        (HSEM) hevEvent1, /* Semaphore to post        */
        &htimerEvent1))) /* Timer handler (returned) */
    e = "DosAsyncTimer";

  if (switch_priority && tib->tib_ptib2->tib2_ulpri == 0x0300)
  {
 /* Nobody switched priority while we slept...  Ignore errors... */
 /* tib->tib_ptib2->tib2_ulpri = priority; */ /* Get back... */
    if (!(rc = DosSetPriority(PRTYS_THREAD, (priority>>8) & 0xFF, 0, 0)))
      rc = DosSetPriority(PRTYS_THREAD, 0, priority & 0xFF, 0);
  }
  if (switch_priority)
    rc = DosExitMustComplete(&nesting); /* Ignore errors */

  /* The actual blocking call is made with "normal" priority.  This way we
     should not bother with DosSleep(0) etc. to compensate for us interrupting
     higher-priority threads.  The goal is to prohibit the system spending too
     much time halt()ing, not to run us "no matter what". */
  if (!e)     /* Wait for AsyncTimer event */
    badrc = DosWaitEventSem(hevEvent1, SEM_INDEFINITE_WAIT);

  if (e) ;    /* Do nothing */
  else if (badrc == ERROR_INTERRUPT)
    ret = 0;
  else if (badrc)
    e = "DosWaitEventSem";
  if ((rc = DosCloseEventSem(hevEvent1)) && !e) { /* Get rid of semaphore */
    e = "DosCloseEventSem";
    badrc = rc;
  }
  if (e)
  {
    SDL_SetError("[SDL_Delay] : Had error in %s(), rc is 0x%x\n", e, badrc);
  }
}

/* Data to handle a single periodic alarm */
static int timer_alive = 0;
static SDL_Thread *timer = NULL;

static int RunTimer(void *unused)
{
        DosSetPriority(PRTYS_THREAD, PRTYC_TIMECRITICAL, 0, 0);
        while ( timer_alive ) {
                if ( SDL_timer_running ) {
                        SDL_ThreadedTimerCheck();
                }
                SDL_Delay(10);
        }
        return(0);
}

/* This is only called if the event thread is not running */
int SDL_SYS_TimerInit(void)
{
        timer_alive = 1;
        timer = SDL_CreateThread(RunTimer, NULL);
        if ( timer == NULL )
                return(-1);
        return(SDL_SetTimerThreaded(1));
}

void SDL_SYS_TimerQuit(void)
{
        timer_alive = 0;
        if ( timer ) {
                SDL_WaitThread(timer, NULL);
                timer = NULL;
        }
}

int SDL_SYS_StartTimer(void)
{
        SDL_SetError("Internal logic error: OS/2 uses threaded timer");
        return(-1);
}

void SDL_SYS_StopTimer(void)
{
        return;
}