view src/thread/win32/win_ce_semaphore.c @ 563:04dcaf3da918

Massive Quartz input enhancements from Darrell Walisser. His email: Enclosed is a patch that addresses the following: --Various minor cleanups. Removed dead/obsolete code, made some style cleanups --Mouse Events Now keep track of what button(s) were pressed so we know when to send the mouse up event. This fixes the case where the mouse is dragged outside of the game window and released (in which case we want to send the mouse up event even though the mouse is outside the game window). --Input Grabbing Here is my take on the grabbing situation, which is the basis for the new implementation. There are 3 grab states, ungrabbed (UG), visible (VG), and invisible (IG). Both VG and IG keep the mouse constrained to the window and produce relative motion events. In VG the cursor is visible (duh), in IG it is not. In VG, absolute motion events also work. There are 6 actions that can affect grabbing: 1. Set Fullscreen/Window (F/W). In fullscreen, a visible grab should do nothing. However, a fullscreen visible grab can be treated just like a windowed visible grab, which is what I have done to help simplify things. 2. Cursor hide/show (H/S). If the cursor is hidden when grabbing, the grab is an invisible grab. If the cursor is visible, the grab should just constrain the mouse to the window. 3. Input grab/ungrab(G/U). If grabbed, the cursor should be confined to the window as should the keyboard input. On Mac OS X, the keyboard input is implicitly grabbed by confining the cursor, except for command-tab which can switch away from the application. Should the window come to the foreground if the application is deactivated and grab input is called? This isn't necessary in this implementation because the grab state will be asserted upon activation. Using my notation, these are all the cases that need to be handled (state + action = new state). UG+U = UG UG+G = VG or IG, if cursor is visible or not UG+H = UG UG+S = UG VG+U = UG VG+G = VG VG+H = IG VG+S = VG IG+U = UG IG+G = IG IG+H = IG IG+S = VG The cases that result in the same state can be ignored in the code, which cuts it down to just 5 cases. Another issue is what happens when the app loses/gains input focus from deactivate/activate or iconify/deiconify. I think that if input focus is ever lost (outside of SDL's control), the grab state should be suspended and the cursor should become visible and active again. When regained, the cursor should reappear in its original location and/or grab state. This way, when reactivating the cursor is still in the same position as before so apps shouldn't get confused when the next motion event comes in. This is what I've done in this patch.
author Ryan C. Gordon <icculus@icculus.org>
date Fri, 27 Dec 2002 20:52:41 +0000
parents 13ee9f4834ea
children 86d0d01290ea
line wrap: on
line source

/* win_ce_semaphore.c

   Copyright (c) 1998, Johnson M. Hart
   (with corrections 2001 by Rainer Loritz)
   Permission is granted for any and all use providing that this
   copyright is properly acknowledged.
   There are no assurances of suitability for any use whatsoever.

   WINDOWS CE: There is a collection of Windows CE functions to simulate
   semaphores using only a mutex and an event. As Windows CE events cannot
   be named, these simulated semaphores cannot be named either.

   Implementation notes:
   1. All required internal data structures are allocated on the process's heap.
   2. Where appropriate, a new error code is returned (see the header
      file), or, if the error is a Win32 error, that code is unchanged.
   3. Notice the new handle type "SYNCHHANDLE" that has handles, counters,
      and other information. This structure will grow as new objects are added
      to this set; some members are specific to only one or two of the objects.
   4. Mutexes are used for critical sections. These could be replaced with
      CRITICAL_SECTION objects but then this would give up the time out
      capability.
   5. The implementation shows several interesting aspects of synchronization, some
      of which are specific to Win32 and some of which are general. These are pointed
      out in the comments as appropriate.
   6. The wait function emulates WaitForSingleObject only. An emulation of
      WaitForMultipleObjects is much harder to implement outside the kernel,
      and it is not clear how to handle a mixture of WCE semaphores and normal
      events and mutexes. */

#include <windows.h>
#include "win_ce_semaphore.h"

static SYNCHHANDLE CleanUp (SYNCHHANDLE hSynch, DWORD Flags);

SYNCHHANDLE CreateSemaphoreCE (

   LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,  /* pointer to security attributes */
      LONG lInitialCount,   /* initial count */
      LONG lMaximumCount,   /* maximum count */
      LPCTSTR lpName )

/* Semaphore for use with Windows CE that does not support them directly.
   Requires a counter, a mutex to protect the counter, and an
   autoreset event.

   Here are the rules that must always hold between the autoreset event
   and the mutex (any violation of these rules by the CE semaphore functions
   will, in all likelihood, result in a defect):
    1. No thread can set, pulse, or reset the event,
       nor can it access any part of the SYNCHHANDLE structure,
       without first gaining ownership of the mutex.
       BUT, a thread can wait on the event without owning the mutex
       (this is clearly necessary or else the event could never be set).
    2. The event is in a signaled state if and only if the current semaphore
       count ("CurCount") is greater than zero.
    3. The semaphore count is always >= 0 and <= the maximum count */

{
   SYNCHHANDLE hSynch = NULL, result = NULL;

   __try
	{
      if (lInitialCount > lMaximumCount || lMaximumCount < 0 || lInitialCount < 0) 
	  {
              /* Bad parameters */
         SetLastError (SYNCH_ERROR);
         __leave;
      }

      hSynch = HeapAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY, SYNCH_HANDLE_SIZE);
      if (hSynch == NULL) __leave;

      hSynch->MaxCount = lMaximumCount;
      hSynch->CurCount = lInitialCount;
      hSynch->lpName = lpName;

      hSynch->hMutex = CreateMutex (lpSemaphoreAttributes, FALSE, NULL);

      WaitForSingleObject (hSynch->hMutex, INFINITE);
      /*  Create the event. It is initially signaled if and only if the
          initial count is > 0 */
      hSynch->hEvent = CreateEvent (lpSemaphoreAttributes, FALSE, 
              lInitialCount > 0, NULL);
      ReleaseMutex (hSynch->hMutex);
      hSynch->hSemph = NULL;
   }
   __finally
   {
       /* Return with the handle, or, if there was any error, return
        a null after closing any open handles and freeing any allocated memory. */
      result=CleanUp(hSynch, 6 /* An event and a mutex, but no semaphore. */);
   }

   return result;
}

BOOL ReleaseSemaphoreCE (SYNCHHANDLE hSemCE, LONG cReleaseCount, LPLONG lpPreviousCount)
/* Windows CE equivalent to ReleaseSemaphore. */
{
   BOOL Result = TRUE;

   /* Gain access to the object to assure that the release count
      would not cause the total count to exceed the maximum. */

   __try 
   {
      WaitForSingleObject (hSemCE->hMutex, INFINITE);
	  /* reply only if asked to */	
	  if (lpPreviousCount!=NULL)
		 *lpPreviousCount = hSemCE->CurCount;
      if (hSemCE->CurCount + cReleaseCount > hSemCE->MaxCount || cReleaseCount <= 0)
	  {
         SetLastError (SYNCH_ERROR);
         Result = FALSE;
         __leave;
      }
      hSemCE->CurCount += cReleaseCount;

      /*  Set the autoreset event, releasing exactly one waiting thread, now or
          in the future.  */

      SetEvent (hSemCE->hEvent);
   }
   __finally
   {
      ReleaseMutex (hSemCE->hMutex);
   }

   return Result;
}

DWORD WaitForSemaphoreCE (SYNCHHANDLE hSemCE, DWORD dwMilliseconds)
   /* Windows CE semaphore equivalent of WaitForSingleObject. */
{
   DWORD WaitResult;

   WaitResult = WaitForSingleObject (hSemCE->hMutex, dwMilliseconds);
   if (WaitResult != WAIT_OBJECT_0 && WaitResult != WAIT_ABANDONED_0) return WaitResult;
   while (hSemCE->CurCount <= 0) 
   { 

      /* The count is 0, and the thread must wait on the event (which, by
         the rules, is currently reset) for semaphore resources to become
         available. First, of course, the mutex must be released so that another
         thread will be capable of setting the event. */

      ReleaseMutex (hSemCE->hMutex);

      /*  Wait for the event to be signaled, indicating a semaphore state change.
          The event is autoreset and signaled with a SetEvent (not PulseEvent)
          so exactly one waiting thread (whether or not there is currently
          a waiting thread) is released as a result of the SetEvent. */

      WaitResult = WaitForSingleObject (hSemCE->hEvent, dwMilliseconds);
      if (WaitResult != WAIT_OBJECT_0) return WaitResult;

      /*  This is where the properties of setting of an autoreset event is critical
          to assure that, even if the semaphore state changes between the
          preceding Wait and the next, and even if NO threads are waiting
          on the event at the time of the SetEvent, at least one thread
          will be released. 
          Pulsing a manual reset event would appear to work, but it would have
          a defect which could appear if the semaphore state changed between
          the two waits. */

      WaitResult = WaitForSingleObject (hSemCE->hMutex, dwMilliseconds);
      if (WaitResult != WAIT_OBJECT_0 && WaitResult != WAIT_ABANDONED_0) return WaitResult;

   }
   /* The count is not zero and this thread owns the mutex.  */

   hSemCE->CurCount--;
   /* The event is now unsignaled, BUT, the semaphore count may not be
      zero, in which case the event should be signaled again
      before releasing the mutex. */

   if (hSemCE->CurCount > 0) SetEvent (hSemCE->hEvent);
   ReleaseMutex (hSemCE->hMutex);
   return WaitResult;
}

BOOL CloseSynchHandle (SYNCHHANDLE hSynch)
/* Close a synchronization handle. 
   Improvement: Test for a valid handle before dereferencing the handle. */
{
   BOOL Result = TRUE;
   if (hSynch->hEvent != NULL) Result = Result && CloseHandle (hSynch->hEvent);
   if (hSynch->hMutex != NULL) Result = Result && CloseHandle (hSynch->hMutex);
   if (hSynch->hSemph != NULL) Result = Result && CloseHandle (hSynch->hSemph);
   HeapFree (GetProcessHeap (), 0, hSynch);
   return (Result);
}

static SYNCHHANDLE CleanUp (SYNCHHANDLE hSynch, DWORD Flags)
{ /* Prepare to return from a create of a synchronization handle.
     If there was any failure, free any allocated resources.
     "Flags" indicates which Win32 objects are required in the 
     synchronization handle. */

   BOOL ok = TRUE;

   if (hSynch == NULL) return NULL;
   if (Flags & 4 == 1 && hSynch->hEvent == NULL) ok = FALSE;
   if (Flags & 2 == 1 && hSynch->hMutex == NULL) ok = FALSE;
   if (Flags & 1 == 1 && hSynch->hEvent == NULL) ok = FALSE;
   if (!ok) 
   {
      CloseSynchHandle (hSynch);
      return NULL;
   }
   /* Everything worked */
   return hSynch;
}