Forum

Input untangled

Discuss programming topics for the various GPL'd game engine sources.

Moderator: InsideQC Admins

Input untangled

Postby Baker » Thu May 02, 2013 6:47 am

Quake's input code is a quantum entangled mess. I have no idea why I bother untangling the mess --- well it was probably rage against the machine.

The short versions goes like this:

Code: Select all
void IN_Think (in_think_type action)
{
   switch (action)
   {
   case in_initialize:
      Cmd_AddCommand ("force_centerview", Force_CenterView_f);
      IN_Local_Keyboard_Startup (); // Stupid stickey keys
      in_initialized = true;
      break;

   case in_startup:
      if (!in_initialized)
         IN_Think (in_initialize);
      IN_Think (in_acquire_mouse);
      IN_Think (in_acquire_keyboard);
      break;

   case in_acquire_mouse:
      if (mouse_acquired == false)
      {
         IN_Local_Mouse_Clip_Cursor (window_rect); // GET New clip region + ClipCursor (&window_rect); + center_x
         IN_Local_Acquire_Mouse (); // ShowCursor (FALSE); + SetCapture (wplat.mainwindow);
         IN_Think (in_clear_mouse_movement_accum);
         IN_Think (in_center_mouse_cursor);         
      }
      mouse_acquired = true;
      break;

   case in_center_mouse_cursor:
      IN_Local_Mouse_Cursor (window_center_x, window_center_y); //SetCursorPos (window_center_x, window_center_y);
      break;

   case in_update_mouse_region:
      IN_Think (in_clear_mouse_accum);
      IN_Local_Update_Mouse_Region (&mouseregion);
      break;

   case in_clear_mouse_movement_accum:
      mouse_accum_x = mouse_accum_y = 0;
      break;

   case in_release_mouse_buttons:
      Key_Release_Mouse_Buttons ();
      break;

   case in_unacquire_mouse:
      IN_Think (in_release_mouse_buttons);      // Fire -aliases
      mouse_old_button_state = 0;               // Now clear the buttons
      IN_Think (in_clear_mouse_movement_accum);   // Now clear the movement
      if (mouse_acquired == false)
      {
         IN_Local_Release_Mouse (); // ClipCursor (NULL); ReleaseCapture (); ShowCursor (TRUE);
      }
      mouse_acquired = false;
      break;

   case in_unacquire_keyboard:
      IN_Keyboard_Release_Keys (); // Key ups
      IN_Local_Keyboard_Unacquire (); // Stupid Windows key
      break;

   case in_acquire_keyboard:
      IN_Local_Keyboard_Acquire (); // Stupid Windows key
      break;

   case in_unacquire_all:   // Lost focus
      IN_Think (in_unacquire_mouse);
      IN_Think (in_unacquire_keyboard); // Stupid windows key
      break;

   case in_acquire_all: // Got focus
      IN_Think (in_acquire_keyboard); // Stupid windows key
      IN_Think (in_acquire_mouse);
      break;

   case in_shutdown:
      IN_Think (in_unaquire_all); // About same deal.
      IN_Local_Keyboard_Restore (); // Stupid Stickey keys on Windows
      break;

}

The slight longer version is multiplatform involving ClipCursor and friends and other crapola I'm somewhat tired of reading at this point.. Quake's input code is truly terrible --- my only motivation was someone has to win and someone has to lose --- and although the battle is a Pyrrhic victory --- to do it right there has to be a loser to the battle and it's not gonna be me.

P.S: To clean up this mess I also had to rewrite all the co-conspirator code = all the video. Go figure --- it was easier to rewrite ALL OF IT, then clean it up. Seriously, the video code almost rivals the suck of the input code and that's saying something.
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
User avatar
Baker
 
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Input untangled

Postby hogsy » Thu May 02, 2013 3:21 pm

I've found a lot of Quake's code to be considerably messy while working on OpenKatana and I agree sometimes it's easier to just rewrite all of it than to try cleaning it up. Despite the time it can take it seems there's usually more long-term benefits that way as you can then better document the code you write and it means you have to spend less time reading over the code to understand it which then in-turn obviously means you can make changes much more efficiently.

Just a shame it can sometimes take so much time and effort, but it's usually worth it... Usually :lol:
User avatar
hogsy
 
Posts: 198
Joined: Wed Aug 03, 2011 3:44 pm
Location: UK

Re: Input untangled

Postby Baker » Fri May 03, 2013 7:23 am

hogsy wrote:it seems there's usually more long-term benefits ...
Just a shame it can sometimes take so much time and effort, but it's usually worth it... Usually :lol:
I hope it becomes worth it :D I wasn't wanting to do this at all. But after trying it out, I am bothered less by this side quest. But really I wanted to be doing something else than the input code --- but I don't think I'll need to worry about the input code in the future now.

Revision 2: Far more streamlined than even the above + short. Modelled in part from MH's ideas of one place where input is handled.

Code: Select all
#include "quakedef.h"

#define NUM_MOUSE_BUTTONS 5
#define MRECT_PRINT(_x) _x.left, _x.top, _x.right, _x.bottom, _x.center_x, _x.center_y
enum { GET_IT = 1, LOSE_IT = 2 };

keypair_t input_state_text [] =
{
   KEYVALUE (input_none),
   KEYVALUE (input_have_keyboard),
   KEYVALUE (input_have_mouse_keyboard),
NULL, 0 };  // NULL termination

static inp_info_t inp;

static void Force_CenterView_f (void) { cl.viewangles[PITCH] = 0; }

static void IN_Info_f (void)
{
   Con_Printf ("IN Info ...\n");
   Con_Printf ("current_state: %s\n", Keypair_String (input_state_text, inp.current_state) );
   Con_Printf ("initialized: %i\n", inp.initialized);
   Con_Printf ("have_mouse: %i\n", inp.have_mouse);
   Con_Printf ("have_keyboard: %i\n", inp.have_keyboard);
   Con_Printf ("mouse_clip_screen_rect: (%i, %i)-(%i, %i) center: %i, %i\n", MRECT_PRINT(inp.mouse_clip_screen_rect) );
   Con_Printf ("mouse_accum_x: %i\n", inp.mouse_accum_x);
   Con_Printf ("mouse_accum_y: %i\n", inp.mouse_accum_y);
   Con_Printf ("mouse_old_button_state: %i\n", inp.mouse_old_button_state);
}

void Input_Think (void)
{
   input_state_t   newstate = (inp.initialized && vid.ActiveApp && !vid.Minimized) ? input_have_keyboard : input_none;

   // newstate upgrades from should have "keyboard" to should have "mouse"
   // If the key_dest is game or we are binding keys in the menu
   if (newstate == input_have_keyboard && key_dest == key_game || (key_dest == key_menu && m_keys_bind_grab) )
      newstate = input_have_mouse_keyboard;
      
   if (newstate != inp.current_state)
   { // New state.
      char   mouse_action   = ( newstate == input_have_mouse_keyboard && inp.have_mouse == false) ? GET_IT :  (( newstate != input_have_mouse_keyboard && inp.have_mouse == true) ? LOSE_IT : 0);
      char   keyboard_action = ( newstate != input_none && inp.have_keyboard == false) ? GET_IT :  (( newstate == input_none && inp.have_keyboard == true) ? LOSE_IT : 0);

      switch (mouse_action)
      {
      case GET_IT:
         // Sticky keys, Window key disabled
         IN_Local_Keyboard_Disable_Annoying_Keys (true);
         inp.have_keyboard = true;
         break;

      case LOSE_IT:
         // Key ups
         Key_Release_Keys ();
#pragma message ("Note we still need our key ups when entering the console")
         // Sticky keys, Window key reenabled
         IN_Local_Keyboard_Disable_Annoying_Keys (false);
         inp.have_keyboard = false;
         break;
      }

      switch (mouse_action)
      {
      case GET_IT:
         // Load window screen coords to mouse_clip_screen_rect
         // And clip the mouse cursor to that area
         IN_Local_Update_Mouse_Clip_Region (&inp.mouse_clip_screen_rect);

         // Hide the mouse cursor and attach it
         IN_Local_Capture_Mouse (true);

         // Center the mouse on-screen
         IN_Local_Mouse_Cursor_SetPos (inp.mouse_clip_screen_rect.center_x, inp.mouse_clip_screen_rect.center_y); //SetCursorPos (window_center_x, window_center_y);
         
         // Clear movement accumulation
         inp.mouse_accum_x = inp.mouse_accum_y = 0
            ;
         inp.have_mouse = true;
         break;

      case LOSE_IT:
         // Release mouse buttons so "-alias" get triggered
         Key_Release_Mouse_Buttons ();

         // Unclip the mouse
         IN_Local_Unclip_Mouse_Movement ();

         // Release the mouse and show the cursor
         IN_Local_Capture_Mouse (false);

         // Clear movement accumulation and buttons
         inp.mouse_accum_x = inp.mouse_accum_y = inp.mouse_old_button_state = 0;

         inp.have_mouse = false;
         break;
      }
      inp.current_state = newstate;
   }
   // End of function
}

void Input_Mouse_Button_Event (int mstate)
{
   if (inp.have_mouse /*|| (key_dest == key_menu && m_keys_bind_grab)*/ )
   {  // perform button actions
      int i;
      for (i = 0 ; i < NUM_MOUSE_BUTTONS ; i ++)
      {
         int button_bit = (1 << i);
         qboolean button_pressed  =  (mstate & button_bit) && !(inp.mouse_old_button_state & button_bit);
         qboolean button_released = !(mstate & button_bit) &&  (inp.mouse_old_button_state & button_bit);

         if (button_pressed || button_released)
            Key_Event (K_MOUSE1 + i, button_pressed ? true : false);
      }
      inp.mouse_old_button_state = mstate;
   }
}

void Input_Mouse_Accumulate (void)
{
   int new_mouse_x, new_mouse_y;
#pragma message ("Need a clip region check here")   
   if (inp.have_mouse == false)
      return;

   IN_Local_Mouse_Cursor_GetPos (&new_mouse_x, &new_mouse_y); // GetCursorPos (&current_pos);

   inp.mouse_accum_x += new_mouse_x - inp.mouse_clip_screen_rect.center_x;
   inp.mouse_accum_y += new_mouse_y - inp.mouse_clip_screen_rect.center_y;

   // Re-center the mouse cursor
   IN_Local_Mouse_Cursor_SetPos (inp.mouse_clip_screen_rect.center_x, inp.mouse_clip_screen_rect.center_y);
}

void Input_Mouse_Move (usercmd_t *cmd)
{
   Input_Mouse_Accumulate ();

   if (inp.mouse_accum_x || inp.mouse_accum_y)
   {
      int   mouse_x = inp.mouse_accum_x *= sensitivity.value;
      int mouse_y = inp.mouse_accum_y *= sensitivity.value;
   // add mouse X/Y movement to cmd
      if ( (in_strafe.state & 1) || (lookstrafe.value && (in_mlook.state & 1) ))
         cmd->sidemove += m_side.value * mouse_x;
      else cl.viewangles[YAW] -= m_yaw.value * mouse_x;

      if (in_mlook.state & 1)
         V_StopPitchDrift ();

      if ( (in_mlook.state & 1) && !(in_strafe.state & 1))
      {
         cl.viewangles[PITCH] += m_pitch.value * mouse_y;
         cl.viewangles[PITCH] = CLAMP (cl_minpitch.value, cl.viewangles[PITCH], cl_maxpitch.value);
      }
      else
      {
         if ((in_strafe.state & 1) && cl.noclip_anglehack)
            cmd->upmove -= m_forward.value * mouse_y;
         else cmd->forwardmove -= m_forward.value * mouse_y;
      }
      inp.mouse_accum_x = inp.mouse_accum_y = 0;
   }
}

void Input_Initialized (void)
{
   Cmd_AddCommand ("in_info", IN_Info_f);
   Cmd_AddCommand ("force_centerview", Force_CenterView_f);
   inp.initialized = true;
}

void Input_Shutdown (void)
{
   inp.initialized = false;
   Input_Think (); // Will shut everything off
}
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
User avatar
Baker
 
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Input untangled

Postby Baker » Fri May 03, 2013 11:18 am

Final answer: Tested, debugged, short, only checks once per frame (like what MH did in DirectQ). All the IN_HideMouse stuff all over the source code is gone.

Nightmares -1.

Code: Select all
#include "quakedef.h"

typedef enum
{
   input_none,
   input_have_keyboard,
   input_have_mouse_keyboard,
} input_state_t;

typedef struct
{
   input_state_t   current_state;
   qboolean      initialized, have_mouse, have_keyboard;

// Internals
   mrect_t         mouse_clip_screen_rect;
   int            mouse_accum_x, mouse_accum_y;
   int            mouse_old_button_state;
} inp_info_t;


#define NUM_MOUSE_BUTTONS 5
#define MRECT_PRINT(_x) _x.left, _x.top, _x.right, _x.bottom, _x.center_x, _x.center_y
enum { GET_IT = 1, LOSE_IT = 2 };

keypair_t input_state_text [] =
{
   KEYVALUE (input_none),
   KEYVALUE (input_have_keyboard),
   KEYVALUE (input_have_mouse_keyboard),
NULL, 0 };  // NULL termination

static inp_info_t inp;

static void Force_CenterView_f (void) { cl.viewangles[PITCH] = 0; }

static void IN_Info_f (void)
{
   Con_Printf ("IN Info ...\n");
   Con_Printf ("current_state: %s\n", Keypair_String (input_state_text, inp.current_state) );
   Con_Printf ("initialized: %i\n", inp.initialized);
   Con_Printf ("have_mouse: %i\n", inp.have_mouse);
   Con_Printf ("have_keyboard: %i\n", inp.have_keyboard);
   Con_Printf ("mouse_clip_screen_rect: (%i, %i)-(%i, %i) center: %i, %i\n", MRECT_PRINT(inp.mouse_clip_screen_rect) );
   Con_Printf ("mouse_accum_x: %i\n", inp.mouse_accum_x);
   Con_Printf ("mouse_accum_y: %i\n", inp.mouse_accum_y);
   Con_Printf ("mouse_old_button_state: %i\n", inp.mouse_old_button_state);
}

void Input_Think (void)
{
   input_state_t   newstate = (inp.initialized && vid.ActiveApp && !vid.Minimized && !vid.in_setmode) ? input_have_keyboard : input_none;

   // newstate upgrades from should have "keyboard" to should have "mouse"
   // If the key_dest is game or we are binding keys in the menu
   if (newstate == input_have_keyboard && (key_dest == key_game || (key_dest == key_menu && m_keys_bind_grab)) )
      newstate = input_have_mouse_keyboard;
      
//   Con_Printf ("current_state: %s (init %i active %i mini %i)\n", Keypair_String (input_state_text, inp.current_state),
//      inp.initialized, vid.ActiveApp, vid.Minimized);

   if (newstate != inp.current_state)
   { // New state.
      char   mouse_action   = ( newstate == input_have_mouse_keyboard && inp.have_mouse == false) ? GET_IT :  (( newstate != input_have_mouse_keyboard && inp.have_mouse == true) ? LOSE_IT : 0);
      char   keyboard_action = ( newstate != input_none && inp.have_keyboard == false) ? GET_IT :  (( newstate == input_none && inp.have_keyboard == true) ? LOSE_IT : 0);

//      Con_Printf ("State change\n");
      switch (keyboard_action)
      {
      case GET_IT:
         // Sticky keys, Window key disabled
         IN_Local_Keyboard_Disable_Annoying_Keys (true);
         inp.have_keyboard = true;
         break;

      case LOSE_IT:
         // Key ups
         Key_Release_Keys ();
#pragma message ("Note we still need our key ups when entering the console")
         // Sticky keys, Window key reenabled
         IN_Local_Keyboard_Disable_Annoying_Keys (false);
         inp.have_keyboard = false;
         break;
      }

      switch (mouse_action)
      {
      case GET_IT:
         // Load window screen coords to mouse_clip_screen_rect
         // And clip the mouse cursor to that area
         IN_Local_Update_Mouse_Clip_Region_Think (&inp.mouse_clip_screen_rect);

         // Hide the mouse cursor and attach it
         IN_Local_Capture_Mouse (true);

         // Center the mouse on-screen
         IN_Local_Mouse_Cursor_SetPos (inp.mouse_clip_screen_rect.center_x, inp.mouse_clip_screen_rect.center_y); //SetCursorPos (window_center_x, window_center_y);
         
         // Clear movement accumulation
         inp.mouse_accum_x = inp.mouse_accum_y = 0
            ;
         inp.have_mouse = true;
         break;

      case LOSE_IT:
         // Release mouse buttons so "-alias" get triggered.  Even the keyboard keys.
         // As if we lose the mouse, usually this is from entering the console.
         // The exceptions to this are weird and uninteresting (like changing a mouse cvar via a bind)
         Key_Release_Keys ();

         // Release the mouse and show the cursor.  Also unclips mouse.
         IN_Local_Capture_Mouse (false);

         // Clear movement accumulation and buttons
         inp.mouse_accum_x = inp.mouse_accum_y = inp.mouse_old_button_state = 0;

         inp.have_mouse = false;
         break;
      }
      inp.current_state = newstate;
   }

   if (inp.have_mouse && IN_Local_Update_Mouse_Clip_Region_Think (&inp.mouse_clip_screen_rect) == true)
   {
      // Re-center the mouse cursor and clear mouse accumulation
      IN_Local_Mouse_Cursor_SetPos (inp.mouse_clip_screen_rect.center_x, inp.mouse_clip_screen_rect.center_y);
      inp.mouse_accum_x = inp.mouse_accum_y = 0;
   }

   // End of function
}


void Input_Mouse_Button_Event (int mstate)
{
   if (inp.have_mouse /*|| (key_dest == key_menu && m_keys_bind_grab)*/ )
   {  // perform button actions
      int i;
      for (i = 0 ; i < NUM_MOUSE_BUTTONS ; i ++)
      {
         int button_bit = (1 << i);
         qboolean button_pressed  =  (mstate & button_bit) && !(inp.mouse_old_button_state & button_bit);
         qboolean button_released = !(mstate & button_bit) &&  (inp.mouse_old_button_state & button_bit);

         if (button_pressed || button_released)
            Key_Event (K_MOUSE1 + i, button_pressed ? true : false);
      }
      inp.mouse_old_button_state = mstate;
   }
}

void Input_Mouse_Accumulate (void)
{
   int new_mouse_x, new_mouse_y;
#pragma message ("Need a clip region check here")

   Input_Think ();
   if (inp.have_mouse == false)
      return;

   IN_Local_Mouse_Cursor_GetPos (&new_mouse_x, &new_mouse_y); // GetCursorPos (&current_pos);

   inp.mouse_accum_x += new_mouse_x - inp.mouse_clip_screen_rect.center_x;
   inp.mouse_accum_y += new_mouse_y - inp.mouse_clip_screen_rect.center_y;

   // Re-center the mouse cursor
   IN_Local_Mouse_Cursor_SetPos (inp.mouse_clip_screen_rect.center_x, inp.mouse_clip_screen_rect.center_y);
}

void Input_Mouse_Move (usercmd_t *cmd)
{
   Input_Mouse_Accumulate ();

   if (inp.mouse_accum_x || inp.mouse_accum_y)
   {
      int   mouse_x = inp.mouse_accum_x *= sensitivity.value;
      int mouse_y = inp.mouse_accum_y *= sensitivity.value;
   // add mouse X/Y movement to cmd
      if ( (in_strafe.state & 1) || (lookstrafe.value && (in_mlook.state & 1) ))
         cmd->sidemove += m_side.value * mouse_x;
      else cl.viewangles[YAW] -= m_yaw.value * mouse_x;

      if (in_mlook.state & 1)
         V_StopPitchDrift ();

      if ( (in_mlook.state & 1) && !(in_strafe.state & 1))
      {
         cl.viewangles[PITCH] += m_pitch.value * mouse_y;
         cl.viewangles[PITCH] = CLAMP (cl_minpitch.value, cl.viewangles[PITCH], cl_maxpitch.value);
      }
      else
      {
         if ((in_strafe.state & 1) && cl.noclip_anglehack)
            cmd->upmove -= m_forward.value * mouse_y;
         else cmd->forwardmove -= m_forward.value * mouse_y;
      }
      inp.mouse_accum_x = inp.mouse_accum_y = 0;
   }
}

void Input_Initialize (void)
{
   Cmd_AddCommand ("in_info", IN_Info_f);
   Cmd_AddCommand ("force_centerview", Force_CenterView_f);
   inp.initialized = true;
}

void Input_Shutdown (void)
{
   inp.initialized = false;
   Input_Think (); // Will shut everything off
}


Header look like:

Code: Select all
typedef struct mrect_s
{
   int            left, right, bottom, top;
   int            center_x, center_y;
   int            width, height;
} mrect_t;

void Input_Initialize (void);
void Input_Shutdown (void);
void Input_Think (void);
void Input_Mouse_Move (usercmd_t *cmd);
void Input_Mouse_Accumulate (void);
void Input_Mouse_Button_Event (int mstate);

// Platform
void IN_Local_Capture_Mouse (qboolean bDoCapture);
void IN_Local_Keyboard_Disable_Annoying_Keys (qboolean bDoDisable);
void IN_Local_Mouse_Cursor_SetPos (int x, int y);
void IN_Local_Mouse_Cursor_GetPos (int *x, int *y);
qboolean IN_Local_Update_Mouse_Clip_Region_Think (mrect_t* mouseregion);


in_win.c looks like:

Code: Select all
void IN_Local_Capture_Mouse (qboolean bDoCapture)
{
   static qboolean captured = false;

   if (bDoCapture && !captured)
   {
      ShowCursor (FALSE); // Hides mouse cursor
      SetCapture (mainwindow);   // Captures mouse events
      Con_DPrintf ("Mouse Captured\n");
      captured = true;
   }
   
   if (!bDoCapture && captured)
   {
      ShowCursor (TRUE); // Hides mouse cursor
      ReleaseCapture ();
      ClipCursor (NULL); // Can't hurt
      Con_DPrintf ("Mouse Released\n");
      captured = false;
   }

}


qboolean IN_Local_Update_Mouse_Clip_Region_Think (mrect_t* mouseregion)
{
   mrect_t oldregion = *mouseregion;
   WINDOWINFO windowinfo;
   windowinfo.cbSize = sizeof (WINDOWINFO);
   GetWindowInfo (mainwindow, &windowinfo);   // client_area screen coordinates

   // Fill in top left, bottom, right, center
   mouseregion->left = windowinfo.rcClient.left;
   mouseregion->right = windowinfo.rcClient.right;
   mouseregion->bottom = windowinfo.rcClient.bottom;
   mouseregion->top = windowinfo.rcClient.top;

   if (memcmp (mouseregion, &oldregion, sizeof(mrect_t) ) != 0)
   {  // Changed!   
      mouseregion->width = mouseregion->right - mouseregion->left;
      mouseregion->height = mouseregion->bottom - mouseregion->top;
      mouseregion->center_x = (mouseregion->left + mouseregion->right) / 2;
      mouseregion->center_y = (mouseregion->top + mouseregion->bottom) / 2;
      ClipCursor (&windowinfo.rcClient);
      return true;
   }
   return false;
}

void IN_Local_Mouse_Cursor_SetPos (int x, int y)
{
   SetCursorPos (x, y);
}

void IN_Local_Mouse_Cursor_GetPos (int *x, int *y)
{
   POINT current_pos;
   GetCursorPos (&current_pos);

   *x = current_pos.x;
   *y = current_pos.y;
}


STICKYKEYS StartupStickyKeys = {sizeof (STICKYKEYS), 0};
TOGGLEKEYS StartupToggleKeys = {sizeof (TOGGLEKEYS), 0};
FILTERKEYS StartupFilterKeys = {sizeof (FILTERKEYS), 0};


void AllowAccessibilityShortcutKeys (qboolean bAllowKeys)
{
   static qboolean initialized = false;

   if (!initialized)
   {   // Save the current sticky/toggle/filter key settings so they can be restored them later
      SystemParametersInfo (SPI_GETSTICKYKEYS, sizeof (STICKYKEYS), &StartupStickyKeys, 0);
      SystemParametersInfo (SPI_GETTOGGLEKEYS, sizeof (TOGGLEKEYS), &StartupToggleKeys, 0);
      SystemParametersInfo (SPI_GETFILTERKEYS, sizeof (FILTERKEYS), &StartupFilterKeys, 0);
      Con_DPrintf ("Accessibility key startup settings saved\n");
      initialized = true;
   }

   if (bAllowKeys)
   {
      // Restore StickyKeys/etc to original state
      // (note that this function is called "allow", not "enable"; if they were previously
      // disabled it will put them back that way too, it doesn't force them to be enabled.)
      SystemParametersInfo (SPI_SETSTICKYKEYS, sizeof (STICKYKEYS), &StartupStickyKeys, 0);
      SystemParametersInfo (SPI_SETTOGGLEKEYS, sizeof (TOGGLEKEYS), &StartupToggleKeys, 0);
      SystemParametersInfo (SPI_SETFILTERKEYS, sizeof (FILTERKEYS), &StartupFilterKeys, 0);

      Con_DPrintf ("Accessibility keys enabled\n");
   }
   else
   {
      // Disable StickyKeys/etc shortcuts but if the accessibility feature is on,
      // then leave the settings alone as its probably being usefully used
      STICKYKEYS skOff = StartupStickyKeys;
      TOGGLEKEYS tkOff = StartupToggleKeys;
      FILTERKEYS fkOff = StartupFilterKeys;

      if ((skOff.dwFlags & SKF_STICKYKEYSON) == 0)
      {
         // Disable the hotkey and the confirmation
         skOff.dwFlags &= ~SKF_HOTKEYACTIVE;
         skOff.dwFlags &= ~SKF_CONFIRMHOTKEY;

         SystemParametersInfo (SPI_SETSTICKYKEYS, sizeof (STICKYKEYS), &skOff, 0);
      }

      if ((tkOff.dwFlags & TKF_TOGGLEKEYSON) == 0)
      {
         // Disable the hotkey and the confirmation
         tkOff.dwFlags &= ~TKF_HOTKEYACTIVE;
         tkOff.dwFlags &= ~TKF_CONFIRMHOTKEY;

         SystemParametersInfo (SPI_SETTOGGLEKEYS, sizeof (TOGGLEKEYS), &tkOff, 0);
      }

      if ((fkOff.dwFlags & FKF_FILTERKEYSON) == 0)
      {
         // Disable the hotkey and the confirmation
         fkOff.dwFlags &= ~FKF_HOTKEYACTIVE;
         fkOff.dwFlags &= ~FKF_CONFIRMHOTKEY;

         SystemParametersInfo (SPI_SETFILTERKEYS, sizeof (FILTERKEYS), &fkOff, 0);
      }

      Con_DPrintf ("Accessibility keys disabled\n");
   }
}



LRESULT CALLBACK LLWinKeyHook(int Code, WPARAM wParam, LPARAM lParam)
{
   PKBDLLHOOKSTRUCT p;
   p = (PKBDLLHOOKSTRUCT) lParam;

   if (vid.ActiveApp)
   {
      switch(p->vkCode)
      {
      case VK_LWIN:   // Left Windows Key
      case VK_RWIN:   // Right Windows key
      case VK_APPS:    // Context Menu key

         return 1; // Ignore these keys
      }
   }

   return CallNextHookEx(NULL, Code, wParam, lParam);
}



void AllowWindowsShortcutKeys (qboolean bAllowKeys)
{
   static qboolean WinKeyHook_isActive = false;
   static HHOOK WinKeyHook;

   if (!bAllowKeys)
   {
      // Disable if not already disabled
      if (!WinKeyHook_isActive)
      {
         if (!(WinKeyHook = SetWindowsHookEx(13, LLWinKeyHook, global_hInstance, 0)))
         {
            Con_Printf("Failed to install winkey hook.\n");
            Con_Printf("Microsoft Windows NT 4.0, 2000 or XP is required.\n");
            return;
         }

         WinKeyHook_isActive = true;
         Con_DPrintf ("Windows and context menu key disabled\n");
      }
   }

   if (bAllowKeys)
   {   // Keys allowed .. stop hook
      if (WinKeyHook_isActive)
      {
         UnhookWindowsHookEx(WinKeyHook);
         WinKeyHook_isActive = false;
         Con_DPrintf ("Windows and context menu key enabled\n");
      }
   }
}

void IN_Local_Keyboard_Disable_Annoying_Keys (qboolean bDoDisable)
{
   if (bDoDisable)
   {
      AllowAccessibilityShortcutKeys (false);
      AllowWindowsShortcutKeys (false);
   }
   else
   {
      AllowAccessibilityShortcutKeys (true);
      AllowWindowsShortcutKeys (true);
   }
}

/*
=======
MapKey

Map from windows to quake keynums
=======
*/
int IN_Local_MapKey (int windowskey)
{
   static byte scantokey[128] =
   {
      0 ,   27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', K_BACKSPACE,
      9, 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', 13 ,
      K_CTRL, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`',
      K_SHIFT,'\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', K_SHIFT,'*',K_ALT,' ', 0 ,
      K_F1, K_F2, K_F3, K_F4, K_F5,K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, 0,
      K_HOME, K_UPARROW,K_PGUP,'-',K_LEFTARROW,'5',K_RIGHTARROW,'+',K_END,
      K_DOWNARROW,K_PGDN,K_INS,K_DEL,0,0, 0, K_F11, K_F12,0 , 0 , 0 , 0 , 0 , 0 , 0,
   };
   int key = (windowskey >> 16) & 255;

   if (key > 127)
      return 0;

   key = scantokey[key];
/*
   switch (key)
   {
      case K_KP_STAR:      return '*';
      case K_KP_MINUS:   return '-';
      case K_KP_5:      return '5';
      case K_KP_PLUS:      return '+';
   }
*/
   return key;
}

qboolean IN_ReadInputMessages (HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
   int button_bits = 0;

   switch (Msg)
   {
   case WM_MOUSEWHEEL:
      if ((short) HIWORD(wParam) > 0)
      {
         Key_Event(K_MWHEELUP, true);
         Key_Event(K_MWHEELUP, false);
      }
      else
      {
         Key_Event(K_MWHEELDOWN, true);
         Key_Event(K_MWHEELDOWN, false);
      }
      return true;

   case WM_LBUTTONDOWN:
   case WM_LBUTTONUP:
   case WM_RBUTTONDOWN:
   case WM_RBUTTONUP:
   case WM_MBUTTONDOWN:
   case WM_MBUTTONUP:
   case WM_MOUSEMOVE:
      if (wParam & MK_LBUTTON)   button_bits |= 1;
      if (wParam & MK_RBUTTON)   button_bits |= 2;
      if (wParam & MK_MBUTTON)   button_bits |= 4;
      if (wParam & MK_XBUTTON1)   button_bits |= 8;
      if (wParam & MK_XBUTTON2)   button_bits |= 16;
      Input_Mouse_Button_Event (button_bits);

      return true;

   case WM_KEYDOWN:
   case WM_SYSKEYDOWN:
      Key_Event (IN_Local_MapKey (lParam), true);
      return true;

   case WM_KEYUP:
   case WM_SYSKEYUP:
      Key_Event (IN_Local_MapKey(lParam), false);
      return true;

   default:
      return false;
   }
}
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
User avatar
Baker
 
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Input untangled

Postby Spike » Fri May 03, 2013 12:11 pm

dude, that's disgusting.
shift+2 should be a " char, not an @
fix it. fix it now.
before I vomit.

more seriously though, code that ignores your keymap is really insanely annoying. especially if you're an azerty user, or indeed any other european or other non-american.
Spike
 
Posts: 2892
Joined: Fri Nov 05, 2004 3:12 am
Location: UK

Re: Input untangled

Postby Baker » Fri May 03, 2013 12:58 pm

Spike wrote:dude, that's disgusting.
shift+2 should be a " char, not an @
fix it. fix it now.
before I vomit.

more seriously though, code that ignores your keymap is really insanely annoying. especially if you're an azerty user, or indeed any other european or other non-american.
I agree with that. Since it is my understanding that Quakespasm does this well, I may adopt the Quakespasm keyboard input code. I'm trying to solve things one step at a time. :D

p.s. I get the joke.
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
User avatar
Baker
 
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Input untangled

Postby Spike » Fri May 03, 2013 1:43 pm

no joke, only melodrama.
Spike
 
Posts: 2892
Joined: Fri Nov 05, 2004 3:12 am
Location: UK


Return to Engine Programming

Who is online

Users browsing this forum: No registered users and 1 guest