| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085 | /* -*- mode: c; tab-width: 4; c-basic-offset: 4; c-file-style: "linux" -*- *///// Copyright (c) 2009-2011, Wei Mingzhi <whistler_wmz@users.sf.net>.// Copyright (c) 2011-2017, SDLPAL development team.// All rights reserved.//// This file is part of SDLPAL.//// SDLPAL is free software: you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation, either version 3 of the License, or// (at your option) any later version.//// This program 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 General Public License for more details.//// You should have received a copy of the GNU General Public License// along with this program.  If not, see <http://www.gnu.org/licenses/>.//#include "main.h"#include <math.h>volatile PALINPUTSTATE   g_InputState;#if PAL_HAS_JOYSTICKSstatic SDL_Joystick     *g_pJoy = NULL;#endif#if !SDL_VERSION_ATLEAST(2,0,0)# define SDLK_KP_1     SDLK_KP1# define SDLK_KP_2     SDLK_KP2# define SDLK_KP_3     SDLK_KP3# define SDLK_KP_4     SDLK_KP4# define SDLK_KP_5     SDLK_KP5# define SDLK_KP_6     SDLK_KP6# define SDLK_KP_7     SDLK_KP7# define SDLK_KP_8     SDLK_KP8# define SDLK_KP_9     SDLK_KP9# define SDLK_KP_0     SDLK_KP0# define SDL_JoystickNameForIndex SDL_JoystickName#endifBOOL                     g_fUseJoystick = TRUE;static void _default_init_filter() {}static int _default_input_event_filter(const SDL_Event *event, volatile PALINPUTSTATE *state) { return 0; }static void _default_input_shutdown_filter() {}static void (*input_init_filter)() = _default_init_filter;static int (*input_event_filter)(const SDL_Event *, volatile PALINPUTSTATE *) = _default_input_event_filter;static void (*input_shutdown_filter)() = _default_input_shutdown_filter;static const int g_KeyMap[][2] = {   { SDLK_UP,        kKeyUp },   { SDLK_KP_8,      kKeyUp },   { SDLK_DOWN,      kKeyDown },   { SDLK_KP_2,      kKeyDown },   { SDLK_LEFT,      kKeyLeft },   { SDLK_KP_4,      kKeyLeft },   { SDLK_RIGHT,     kKeyRight },   { SDLK_KP_6,      kKeyRight },   { SDLK_ESCAPE,    kKeyMenu },   { SDLK_INSERT,    kKeyMenu },   { SDLK_LALT,      kKeyMenu },   { SDLK_RALT,      kKeyMenu },   { SDLK_KP_0,      kKeyMenu },   { SDLK_RETURN,    kKeySearch },   { SDLK_SPACE,     kKeySearch },   { SDLK_KP_ENTER,  kKeySearch },   { SDLK_LCTRL,     kKeySearch },   { SDLK_PAGEUP,    kKeyPgUp },   { SDLK_KP_9,      kKeyPgUp },   { SDLK_PAGEDOWN,  kKeyPgDn },   { SDLK_KP_3,      kKeyPgDn },   { SDLK_HOME,      kKeyHome },   { SDLK_END,       kKeyEnd },   { SDLK_r,         kKeyRepeat },   { SDLK_a,         kKeyAuto },   { SDLK_d,         kKeyDefend },   { SDLK_e,         kKeyUseItem },   { SDLK_w,         kKeyThrowItem },   { SDLK_q,         kKeyFlee },   { SDLK_f,         kKeyForce }};static INTPAL_ConvertKey(   INT      keySym)/*++  Purpose:    Convert SDL key code to our internal key code.  Parameters:    [IN]  keySym - SDL key code.  Return value:    Internal key code.--*/{   int i;   for (i = 0; i < sizeof(g_KeyMap) / sizeof(g_KeyMap[0]); i++)   {      if (g_KeyMap[i][0] == keySym)      {         return g_KeyMap[i][1];      }   }   return kKeyNone;}static VOIDPAL_KeyboardEventFilter(   const SDL_Event       *lpEvent)/*++  Purpose:    Handle keyboard events.  Parameters:    [IN]  lpEvent - pointer to the event.  Return value:    None.--*/{   int   key;   switch (lpEvent->type)   {   case SDL_KEYDOWN:      //      // Pressed a key      //      if (lpEvent->key.keysym.mod & KMOD_ALT)      {         if (lpEvent->key.keysym.sym == SDLK_RETURN)         {            //            // Pressed Alt+Enter (toggle fullscreen)...            //            VIDEO_ToggleFullscreen();            return;         }         else if (lpEvent->key.keysym.sym == SDLK_F4)         {            //            // Pressed Alt+F4 (Exit program)...            //            PAL_Shutdown(0);         }      }      key = PAL_ConvertKey(lpEvent->key.keysym.sym);      switch (key)      {      case kKeyUp:         if (gpGlobals->fInBattle || g_InputState.dir != kDirNorth)         {            g_InputState.prevdir = (gpGlobals->fInBattle ? kDirUnknown : g_InputState.dir);            g_InputState.dir = kDirNorth;            g_InputState.dwKeyPress |= kKeyUp;         }         break;      case kKeyDown:         if (gpGlobals->fInBattle || g_InputState.dir != kDirSouth)         {            g_InputState.prevdir = (gpGlobals->fInBattle ? kDirUnknown : g_InputState.dir);            g_InputState.dir = kDirSouth;            g_InputState.dwKeyPress |= kKeyDown;         }         break;      case kKeyLeft:         if (gpGlobals->fInBattle || g_InputState.dir != kDirWest)         {            g_InputState.prevdir = (gpGlobals->fInBattle ? kDirUnknown : g_InputState.dir);            g_InputState.dir = kDirWest;            g_InputState.dwKeyPress |= kKeyLeft;         }         break;     case kKeyRight:         if (gpGlobals->fInBattle || g_InputState.dir != kDirEast)         {            g_InputState.prevdir = (gpGlobals->fInBattle ? kDirUnknown : g_InputState.dir);            g_InputState.dir = kDirEast;            g_InputState.dwKeyPress |= kKeyRight;         }         break;      case SDLK_HASH: //# for mobile device      case SDLK_p:         VIDEO_SaveScreenshot();         break;      default:         g_InputState.dwKeyPress |= key;         break;      }      break;   case SDL_KEYUP:      //      // Released a key      //      key = PAL_ConvertKey(lpEvent->key.keysym.sym);      switch (key)      {      case kKeyUp:         if (g_InputState.dir == kDirNorth)         {            g_InputState.dir = g_InputState.prevdir;         }         g_InputState.prevdir = kDirUnknown;         break;      case kKeyDown:         if (g_InputState.dir == kDirSouth)         {            g_InputState.dir = g_InputState.prevdir;         }         g_InputState.prevdir = kDirUnknown;         break;      case kKeyLeft:         if (g_InputState.dir == kDirWest)         {            g_InputState.dir = g_InputState.prevdir;         }         g_InputState.prevdir = kDirUnknown;         break;      case kKeyRight:         if (g_InputState.dir == kDirEast)         {            g_InputState.dir = g_InputState.prevdir;         }         g_InputState.prevdir = kDirUnknown;         break;      default:         break;      }      break;   }}static VOIDPAL_MouseEventFilter(   const SDL_Event *lpEvent)/*++  Purpose:    Handle mouse events.  Parameters:    [IN]  lpEvent - pointer to the event.  Return value:    None.--*/{#if PAL_HAS_MOUSE   static short hitTest = 0; // Double click detect;      const SDL_VideoInfo *vi;   double       screenWidth, gridWidth;   double       screenHeight, gridHeight;   double       mx, my;   double       thumbx;   double       thumby;   INT          gridIndex;   BOOL         isLeftMouseDBClick = FALSE;   BOOL         isLeftMouseClick = FALSE;   BOOL         isRightMouseClick = FALSE;   static INT   lastReleaseButtonTime, lastPressButtonTime, betweenTime;   static INT   lastPressx = 0;   static INT   lastPressy = 0;   static INT   lastReleasex = 0;   static INT   lastReleasey = 0;   if (lpEvent->type!= SDL_MOUSEBUTTONDOWN && lpEvent->type != SDL_MOUSEBUTTONUP)      return;   vi = SDL_GetVideoInfo();   screenWidth = vi->current_w;   screenHeight = vi->current_h;   gridWidth = screenWidth / 3;   gridHeight = screenHeight / 3;   mx = lpEvent->button.x;   my = lpEvent->button.y;   thumbx = ceil(mx / gridWidth);   thumby = floor(my / gridHeight);   gridIndex = thumbx + thumby * 3 - 1;      switch (lpEvent->type)   {   case SDL_MOUSEBUTTONDOWN:      lastPressButtonTime = SDL_GetTicks();      lastPressx = lpEvent->button.x;      lastPressy = lpEvent->button.y;      switch (gridIndex)      {      case 2:         g_InputState.prevdir = g_InputState.dir;         g_InputState.dir = kDirNorth;         break;      case 6:         g_InputState.prevdir = g_InputState.dir;         g_InputState.dir = kDirSouth;         break;      case 0:         g_InputState.prevdir = g_InputState.dir;         g_InputState.dir = kDirWest;         break;      case 8:         g_InputState.prevdir = g_InputState.dir;         g_InputState.dir = kDirEast;         break;      case 1:        //g_InputState.prevdir = g_InputState.dir;        //g_InputState.dir = kDirNorth;         g_InputState.dwKeyPress |= kKeyUp;         break;      case 7:        //g_InputState.prevdir = g_InputState.dir;        //g_InputState.dir = kDirSouth;          g_InputState.dwKeyPress |= kKeyDown;         break;      case 3:        //g_InputState.prevdir = g_InputState.dir;        //g_InputState.dir = kDirWest;        g_InputState.dwKeyPress |= kKeyLeft;         break;      case 5:         //g_InputState.prevdir = g_InputState.dir;         //g_InputState.dir = kDirEast;         g_InputState.dwKeyPress |= kKeyRight;         break;      }      break;   case SDL_MOUSEBUTTONUP:      lastReleaseButtonTime = SDL_GetTicks();      lastReleasex = lpEvent->button.x;      lastReleasey = lpEvent->button.y;      hitTest ++;      if (abs(lastPressx - lastReleasex) < 25 &&                     abs(lastPressy - lastReleasey) < 25)      {        betweenTime = lastReleaseButtonTime - lastPressButtonTime;        if (betweenTime >500)        {           isRightMouseClick = TRUE;        }        else if (betweenTime >=0)        {           if((betweenTime < 100) && (hitTest >= 2))           {              isLeftMouseClick = TRUE;                hitTest = 0;             }           else           {                isLeftMouseClick = TRUE;              if(betweenTime > 100)              {                 hitTest = 0;              }                         }        }      }      switch (gridIndex)      {      case 2:        if( isLeftMouseDBClick )       {          AUDIO_IncreaseVolume();          break;       }      case 6:      case 0:        if( isLeftMouseDBClick )       {          AUDIO_DecreaseVolume();          break;       }      case 7:         if (isRightMouseClick) //repeat attack         {            g_InputState.dwKeyPress |= kKeyRepeat;            break;         }      case 8:         g_InputState.dir = kDirUnknown;         g_InputState.prevdir = kDirUnknown;         break;      case 1:        if( isRightMouseClick )       {          g_InputState.dwKeyPress |= kKeyForce;       }        break;      case 3:        if( isRightMouseClick )       {          g_InputState.dwKeyPress |= kKeyAuto;       }        break;      case 5:        if( isRightMouseClick )       {          g_InputState.dwKeyPress |= kKeyDefend;       }       break;      case 4:      if (isRightMouseClick) // menu      {         g_InputState.dwKeyPress |= kKeyMenu;      }      else if (isLeftMouseClick) // search      {         g_InputState.dwKeyPress |= kKeySearch;      }              break;      }      break;   }#endif}static VOIDPAL_JoystickEventFilter(   const SDL_Event       *lpEvent)/*++  Purpose:    Handle joystick events.  Parameters:    [IN]  lpEvent - pointer to the event.  Return value:    None.--*/{#if PAL_HAS_JOYSTICKS   switch (lpEvent->type)   {   case SDL_JOYAXISMOTION:      //      // Moved an axis on joystick      //      switch (lpEvent->jaxis.axis)      {      case 0:         //         // X axis         //         if (lpEvent->jaxis.value > 20000)         {            if (g_InputState.dir != kDirEast)            {               g_InputState.dwKeyPress |= kKeyRight;            }            g_InputState.prevdir = g_InputState.dir;            g_InputState.dir = kDirEast;         }         else if (lpEvent->jaxis.value < -20000)         {            if (g_InputState.dir != kDirWest)            {               g_InputState.dwKeyPress |= kKeyLeft;            }            g_InputState.prevdir = g_InputState.dir;            g_InputState.dir = kDirWest;         }         else         {            if (g_InputState.prevdir != kDirEast &&               g_InputState.prevdir != kDirWest)            {               g_InputState.dir = g_InputState.prevdir;            }            g_InputState.prevdir = kDirUnknown;         }         break;      case 1:         //         // Y axis         //         if (lpEvent->jaxis.value > 20000)         {            if (g_InputState.dir != kDirSouth)            {               g_InputState.dwKeyPress |= kKeyDown;            }            g_InputState.prevdir = g_InputState.dir;            g_InputState.dir = kDirSouth;         }         else if (lpEvent->jaxis.value < -20000)         {            if (g_InputState.dir != kDirNorth)            {               g_InputState.dwKeyPress |= kKeyUp;            }            g_InputState.prevdir = g_InputState.dir;            g_InputState.dir = kDirNorth;         }         else         {            if (g_InputState.prevdir != kDirNorth &&               g_InputState.prevdir != kDirSouth)            {               g_InputState.dir = g_InputState.prevdir;            }            g_InputState.prevdir = kDirUnknown;         }         break;      }      break;   case SDL_JOYBUTTONDOWN:      //      // Pressed the joystick button      //      switch (lpEvent->jbutton.button & 1)      {      case 0:         g_InputState.dwKeyPress |= kKeyMenu;         break;      case 1:         g_InputState.dwKeyPress |= kKeySearch;         break;      }      break;   }#endif}#if PAL_HAS_TOUCH#define  TOUCH_NONE     0#define    TOUCH_UP      1#define    TOUCH_DOWN      2#define    TOUCH_LEFT      3#define    TOUCH_RIGHT   4#define    TOUCH_BUTTON1   5#define    TOUCH_BUTTON2   6#define  TOUCH_BUTTON3  7#define  TOUCH_BUTTON4  8static float gfTouchXMin = 0.0f;static float gfTouchXMax = 1.0f;static float gfTouchYMin = 0.0f;static float gfTouchYMax = 1.0f;VOIDPAL_SetTouchBounds(   DWORD dwScreenWidth,   DWORD dwScreenHeight,   SDL_Rect renderRect){   gfTouchXMin = (float)renderRect.x / dwScreenWidth;   gfTouchXMax = (float)(renderRect.x + renderRect.w) / dwScreenWidth;   gfTouchYMin = (float)renderRect.y / dwScreenHeight;   gfTouchYMax = (float)(renderRect.y + renderRect.h) / dwScreenHeight;}static intPAL_GetTouchArea(   float X,   float Y){   if (X < gfTouchXMin || X > gfTouchXMax || Y < 0.5f || Y > gfTouchYMax)   {      //      // Upper area or cropped area      //      return TOUCH_NONE;   }   else   {      X = (X - gfTouchXMin) / (gfTouchXMax - gfTouchXMin);	  Y = (Y - gfTouchYMin) / (gfTouchYMax - gfTouchYMin);   }   if (X < 1.0f / 3)   {      if (Y - 0.5f < (1.0f / 6 - fabsf(X - 1.0f / 3 / 2)) * (0.5f / (1.0f / 3)))      {         return TOUCH_UP;      }      else if (Y - 0.75f > fabsf(X - 1.0f / 3 / 2) * (0.5f / (1.0f / 3)))      {         return TOUCH_DOWN;      }      else if (X < 1.0f / 3 / 2 && fabsf(Y - 0.75f) < 0.25f - X * (0.5f / (1.0f / 3)))      {         return TOUCH_LEFT;      }      else      {         return TOUCH_RIGHT;      }   }   else if (X > 1.0f - 1.0f / 3)   {      if (X < 1.0f - (1.0f / 3 / 2))      {         if (Y < 0.75f)         {            return TOUCH_BUTTON1;         }         else         {            return TOUCH_BUTTON3;         }      }      else      {         if (Y < 0.75f)         {            return TOUCH_BUTTON2;         }         else         {            return TOUCH_BUTTON4;         }      }   }   else    {      return TOUCH_NONE;   }}static VOIDPAL_SetTouchAction(  int area){   switch (area)   {   case TOUCH_UP:      g_InputState.dir = kDirNorth;      g_InputState.dwKeyPress |= kKeyUp;      break;   case TOUCH_DOWN:      g_InputState.dir = kDirSouth;      g_InputState.dwKeyPress |= kKeyDown;      break;   case TOUCH_LEFT:      g_InputState.dir = kDirWest;      g_InputState.dwKeyPress |= kKeyLeft;      break;   case TOUCH_RIGHT:      g_InputState.dir = kDirEast;      g_InputState.dwKeyPress |= kKeyRight;      break;   case TOUCH_BUTTON1:      if (gpGlobals->fInBattle)      {         g_InputState.dwKeyPress |= kKeyRepeat;      }      else      {         g_InputState.dwKeyPress |= kKeyForce;      }      break;   case TOUCH_BUTTON2:      g_InputState.dwKeyPress |= kKeyMenu;      break;   case TOUCH_BUTTON3:      g_InputState.dwKeyPress |= kKeyUseItem;      break;   case TOUCH_BUTTON4:      g_InputState.dwKeyPress |= kKeySearch;      break;   }}static VOIDPAL_UnsetTouchAction(  int area){   switch (area)   {   case TOUCH_UP:   case TOUCH_DOWN:   case TOUCH_LEFT:   case TOUCH_RIGHT:      g_InputState.dir = kDirUnknown;      break;   }}#endifstatic VOIDPAL_TouchEventFilter(   const SDL_Event *lpEvent)/*++  Purpose:    Handle touch events.  Parameters:    [IN]  lpEvent - pointer to the event.  Return value:    None.--*/{#if PAL_HAS_TOUCH   static SDL_TouchID finger1 = -1, finger2 = -1;   static int prev_touch1 = TOUCH_NONE;   static int prev_touch2 = TOUCH_NONE;   switch (lpEvent->type)   {   case SDL_FINGERDOWN:     if (finger1 == -1)     {        int area = PAL_GetTouchArea(lpEvent->tfinger.x, lpEvent->tfinger.y);        finger1 = lpEvent->tfinger.fingerId;        prev_touch1 = area;        PAL_SetTouchAction(area);     }     else if (finger2 == -1)     {        int area = PAL_GetTouchArea(lpEvent->tfinger.x, lpEvent->tfinger.y);        finger2 = lpEvent->tfinger.fingerId;        prev_touch2 = area;        PAL_SetTouchAction(area);     }     break;   case SDL_FINGERUP:     if (lpEvent->tfinger.fingerId == finger1)     {        PAL_UnsetTouchAction(prev_touch1);        finger1 = -1;        prev_touch1 = TOUCH_NONE;     }     else if (lpEvent->tfinger.fingerId == finger2)     {        PAL_UnsetTouchAction(prev_touch2);        finger2 = -1;        prev_touch2 = TOUCH_NONE;     }     break;   case SDL_FINGERMOTION:      if (lpEvent->tfinger.fingerId == finger1)      {         int area = PAL_GetTouchArea(lpEvent->tfinger.x, lpEvent->tfinger.y);         if (prev_touch1 != area && area != TOUCH_NONE)         {            PAL_UnsetTouchAction(prev_touch1);            prev_touch1 = area;            PAL_SetTouchAction(area);         }      }      else if (lpEvent->tfinger.fingerId == finger2)      {         int area = PAL_GetTouchArea(lpEvent->tfinger.x, lpEvent->tfinger.y);         if (prev_touch2 != area && area != TOUCH_NONE)         {            PAL_UnsetTouchAction(prev_touch2);            prev_touch2 = area;            PAL_SetTouchAction(area);         }      }      break;   }#endif}static int SDLCALLPAL_EventFilter(   const SDL_Event       *lpEvent)/*++  Purpose:    SDL event filter function. A filter to process all events.  Parameters:    [IN]  lpEvent - pointer to the event.  Return value:    1 = the event will be added to the internal queue.    0 = the event will be dropped from the queue.--*/{   switch (lpEvent->type)   {#if SDL_VERSION_ATLEAST(2,0,0)   case SDL_WINDOWEVENT:      if (lpEvent->window.event == SDL_WINDOWEVENT_RESIZED)      {         //         // resized the window         //         VIDEO_Resize(lpEvent->window.data1, lpEvent->window.data2);      }      break;   case SDL_APP_WILLENTERBACKGROUND:      g_bRenderPaused = TRUE;      break;   case SDL_APP_DIDENTERFOREGROUND:      g_bRenderPaused = FALSE;      VIDEO_UpdateScreen(NULL);      break;#else   case SDL_VIDEORESIZE:      //      // resized the window      //      VIDEO_Resize(lpEvent->resize.w, lpEvent->resize.h);      break;#endif   case SDL_QUIT:      //      // clicked on the close button of the window. Quit immediately.      //      PAL_Shutdown(0);   }   PAL_KeyboardEventFilter(lpEvent);   PAL_MouseEventFilter(lpEvent);   PAL_JoystickEventFilter(lpEvent);   PAL_TouchEventFilter(lpEvent);   //   // All events are handled here; don't put anything to the internal queue   //   return 0;}VOIDPAL_ClearKeyState(   VOID)/*++  Purpose:    Clear the record of pressed keys.  Parameters:    None.  Return value:    None.--*/{   g_InputState.dwKeyPress = 0;}VOIDPAL_InitInput(   VOID)/*++  Purpose:    Initialize the input subsystem.  Parameters:    None.  Return value:    None.--*/{   memset((void *)&g_InputState, 0, sizeof(g_InputState));   g_InputState.dir = kDirUnknown;   g_InputState.prevdir = kDirUnknown;   //   // Check for joystick   //#if PAL_HAS_JOYSTICKS   if (SDL_NumJoysticks() > 0 && g_fUseJoystick)   {      int i;	  for (i = 0; i < SDL_NumJoysticks(); i++)      {         if (PAL_IS_VALID_JOYSTICK(SDL_JoystickNameForIndex(i)))         {            g_pJoy = SDL_JoystickOpen(i);            break;         }      }      if (g_pJoy != NULL)      {         SDL_JoystickEventState(SDL_ENABLE);      }   }#endif#ifdef PAL_ALLOW_KEYREPEAT   SDL_EnableKeyRepeat(0, 0);#endif   input_init_filter();}VOIDPAL_ShutdownInput(   VOID)/*++  Purpose:    Shutdown the input subsystem.  Parameters:    None.  Return value:    None.--*/{#if PAL_HAS_JOYSTICKS   if (g_pJoy != NULL)   {      SDL_JoystickClose(g_pJoy);      g_pJoy = NULL;   }#endif   input_shutdown_filter();}VOIDPAL_ProcessEvent(   VOID)/*++  Purpose:    Process all events.  Parameters:    None.  Return value:    None.--*/{   while (PAL_PollEvent(NULL));}intPAL_PollEvent(   SDL_Event *event)/*++  Purpose:    Poll and process one event.  Parameters:    [OUT] event - Events polled from SDL.  Return value:    Return value of PAL_PollEvent.--*/{   SDL_Event evt;   int ret = SDL_PollEvent(&evt);   if (ret != 0 && !input_event_filter(&evt, &g_InputState))   {      PAL_EventFilter(&evt);   }   if (event != NULL)   {      *event = evt;   }   return ret;}VOIDPAL_RegisterInputFilter(   void (*init_filter)(),   int (*event_filter)(const SDL_Event *, volatile PALINPUTSTATE *),   void (*shutdown_filter)())/*++  Purpose:    Register caller-defined input event filter.  Parameters:    [IN] init_filter - Filter that will be called inside PAL_InitInput	[IN] event_filter - Filter that will be called inside PAL_PollEvent, 	                    return non-zero value from this filter disables						further internal event processing.	[IN] shutdown_filter - Filter that will be called inside PAL_ShutdownInput	Passing NULL to either parameter means the caller does not provide such filter.  Return value:    None.--*/{	if (init_filter)		input_init_filter = init_filter;	if (event_filter)		input_event_filter = event_filter;	if (shutdown_filter)		input_shutdown_filter = shutdown_filter;}
 |