view src/video/qnxgf/SDL_gf_input.c @ 3109:7b3a09fb9c8b

Support for HID devices (mice and keyboards only for now) has been added
author Mike Gorchak <lestat@i.com.ua>
date Tue, 28 Apr 2009 04:33:30 +0000
parents
children d7174e9f65ce
line wrap: on
line source

/*
    SDL - Simple DirectMedia Layer
    Copyright (C) 1997-2009 Sam Lantinga

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

    Sam Lantinga
    slouken@libsdl.org

    QNX Graphics Framework SDL driver
    Copyright (C) 2009 Mike Gorchak
    (mike@malva.ua, lestat@i.com.ua)
*/

#include "SDL_gf_input.h"

#include "SDL_config.h"
#include "SDL_events.h"
#include "../../events/SDL_mouse_c.h"
#include "../../events/SDL_keyboard_c.h"

/* Include QNX HIDDI definitions */
#include "SDL_hiddi_keyboard.h"
#include "SDL_hiddi_mouse.h"
#include "SDL_hiddi_joystick.h"

/* Mouse related functions */
SDL_Cursor* gf_createcursor(SDL_Surface* surface, int hot_x, int hot_y);
int  gf_showcursor(SDL_Cursor* cursor);
void gf_movecursor(SDL_Cursor* cursor);
void gf_freecursor(SDL_Cursor* cursor);
void gf_warpmouse(SDL_Mouse* mouse, SDL_WindowID windowID, int x, int y);
void gf_freemouse(SDL_Mouse* mouse);

/* HIDDI interacting functions */
static int32_t hiddi_connect_devices();
static int32_t hiddi_disconnect_devices();

int32_t gf_addinputdevices(_THIS)
{
   SDL_VideoData* gfdata=(SDL_VideoData*)_this->driverdata;
   SDL_DisplayData* didata;
   struct SDL_Mouse gf_mouse;
   SDL_Keyboard gf_keyboard;
   SDLKey keymap[SDL_NUM_SCANCODES];
   SDL_MouseData* mdata;
   uint32_t it;

   for (it=0; it<_this->num_displays; it++)
   {
      /* Clear SDL mouse structure */
      SDL_memset(&gf_mouse, 0x00, sizeof(struct SDL_Mouse));

      /* Allocate SDL_MouseData structure */
      mdata=(SDL_MouseData*)SDL_calloc(1, sizeof(SDL_MouseData));
      if (mdata==NULL)
      {
         SDL_OutOfMemory();
         return -1;
      }

      /* Mark this mouse with ID 0 */
      gf_mouse.id=it;
      gf_mouse.driverdata=(void*)mdata;
      gf_mouse.CreateCursor=gf_createcursor;
      gf_mouse.ShowCursor=gf_showcursor;
      gf_mouse.MoveCursor=gf_movecursor;
      gf_mouse.FreeCursor=gf_freecursor;
      gf_mouse.WarpMouse=gf_warpmouse;
      gf_mouse.FreeMouse=gf_freemouse;

      /* Get display data */
      didata=(SDL_DisplayData*)_this->displays[it].driverdata;

      /* Store SDL_DisplayData pointer in the mouse driver internals */
      mdata->didata=didata;

      /* Set cursor pos to 0,0 to avoid cursor disappearing in some drivers */
      gf_cursor_set_pos(didata->display, 0, 0, 0);

      /* Register mouse cursor in SDL */
      SDL_AddMouse(&gf_mouse, "GF mouse cursor", 0, 0, 1);
   }

   /* Keyboard could be one only */
   SDL_zero(gf_keyboard);
   SDL_AddKeyboard(&gf_keyboard, -1);

   /* Add scancode to key mapping, HIDDI uses USB HID codes, so */
   /* map will be exact one-to-one */
   SDL_GetDefaultKeymap(keymap);
   SDL_SetKeymap(0, 0, keymap, SDL_NUM_SCANCODES);

   /* Connect to HID server and enumerate all input devices */
   hiddi_connect_devices();

   return 0;
}

int32_t gf_delinputdevices(_THIS)
{
   /* Disconnect from HID server and release input devices */
   hiddi_disconnect_devices();

   /* Delete keyboard */
   SDL_KeyboardQuit();

   /* Destroy all of the mice */
   SDL_MouseQuit();
}

/*****************************************************************************/
/* GF Mouse related functions                                                */
/*****************************************************************************/
SDL_Cursor* gf_createcursor(SDL_Surface* surface, int hot_x, int hot_y)
{
   gf_cursor_t* internal_cursor;
   SDL_Cursor*  sdl_cursor;
   uint8_t*     image0=NULL;
   uint8_t*     image1=NULL;
   uint32_t     it;
   uint32_t     jt;
   uint32_t     shape_color;

   /* SDL converts monochrome cursor shape to 32bpp cursor shape      */
   /* and we must convert it back to monochrome, this routine handles */
   /* 24/32bpp surfaces only                                          */
   if ((surface->format->BitsPerPixel!=32) && (surface->format->BitsPerPixel!=24))
   {
      SDL_SetError("GF: Cursor shape is not 24/32bpp.");
      return NULL;
   }

   /* Since GF is not checking data, we must check */
   if ((surface->w==0) || (surface->h==0))
   {
      SDL_SetError("GF: Cursor shape dimensions are zero");
      return NULL;
   }

   /* Allocate memory for the internal cursor format */
   internal_cursor=(gf_cursor_t*)SDL_calloc(1, sizeof(gf_cursor_t));
   if (internal_cursor==NULL)
   {
      SDL_OutOfMemory();
      return NULL;
   }

   /* Allocate memory for the SDL cursor */
   sdl_cursor=(SDL_Cursor*)SDL_calloc(1, sizeof(SDL_Cursor));
   if (sdl_cursor==NULL)
   {
      SDL_free(internal_cursor);
      SDL_OutOfMemory();
      return NULL;
   }

   /* Allocate two monochrome images */
   image0=(uint8_t*)SDL_calloc(1, ((surface->w+7)/8)*surface->h);
   if (image0==NULL)
   {
      SDL_free(sdl_cursor);
      SDL_free(internal_cursor);
      SDL_OutOfMemory();
      return NULL;
   }
   image1=(uint8_t*)SDL_calloc(1, ((surface->w+7)>>3)*surface->h);
   if (image1==NULL)
   {
      SDL_free(image0);
      SDL_free(sdl_cursor);
      SDL_free(internal_cursor);
      SDL_OutOfMemory();
      return NULL;
   }

   /* Set driverdata as GF cursor format */
   sdl_cursor->driverdata=(void*)internal_cursor;
   internal_cursor->type=GF_CURSOR_BITMAP;
   internal_cursor->hotspot.x=hot_x;
   internal_cursor->hotspot.y=hot_y;
   internal_cursor->cursor.bitmap.w=surface->w;
   internal_cursor->cursor.bitmap.h=surface->h;
   internal_cursor->cursor.bitmap.color0=SDL_GF_MOUSE_COLOR_BLACK;
   internal_cursor->cursor.bitmap.color1=SDL_GF_MOUSE_COLOR_WHITE;

   /* Setup cursor shape images */
   internal_cursor->cursor.bitmap.stride=((surface->w+7)>>3);
   internal_cursor->cursor.bitmap.image0=image0;
   internal_cursor->cursor.bitmap.image1=image1;

   /* Convert cursor from 32 bpp */
   for (jt=0; jt<surface->h; jt++)
   {
      for (it=0; it<surface->w; it++)
      {
         shape_color=*((uint32_t*)((uint8_t*)surface->pixels+jt*surface->pitch+it*surface->format->BytesPerPixel));
         switch(shape_color)
         {
            case SDL_GF_MOUSE_COLOR_BLACK:
                 {
                    *(image0+jt*(internal_cursor->cursor.bitmap.stride)+(it>>3))|=0x80>>(it%8);
                    *(image1+jt*(internal_cursor->cursor.bitmap.stride)+(it>>3))&=~(0x80>>(it%8));
                 }
                 break;
            case SDL_GF_MOUSE_COLOR_WHITE:
                 {
                    *(image0+jt*(internal_cursor->cursor.bitmap.stride)+(it>>3))&=~(0x80>>(it%8));
                    *(image1+jt*(internal_cursor->cursor.bitmap.stride)+(it>>3))|=0x80>>(it%8);
                 }
                 break;
            case SDL_GF_MOUSE_COLOR_TRANS:
                 {
                    *(image0+jt*(internal_cursor->cursor.bitmap.stride)+(it>>3))&=~(0x80>>(it%8));
                    *(image1+jt*(internal_cursor->cursor.bitmap.stride)+(it>>3))&=~(0x80>>(it%8));
                 }
                 break;
            default:
                 {
                    /* The same as transparent color, must not happen */
                    *(image0+jt*(internal_cursor->cursor.bitmap.stride)+(it>>3))&=~(0x80>>(it%8));
                    *(image1+jt*(internal_cursor->cursor.bitmap.stride)+(it>>3))&=~(0x80>>(it%8));
                 }
                 break;
         }
      }
   }

   return sdl_cursor;
}

int gf_showcursor(SDL_Cursor* cursor)
{
   SDL_VideoDisplay* display;
   SDL_DisplayData*  didata;
   SDL_Window*  window;
   SDL_WindowID window_id;
   gf_cursor_t* internal_cursor;
   int32_t status;

   /* Get current window id */
   window_id=SDL_GetFocusWindow();
   if (window_id<=0)
   {
      SDL_MouseData* mdata=NULL;

      /* If there is no current window, then someone calls this function */
      /* to set global mouse settings during SDL initialization          */
      if (cursor!=NULL)
      {
         mdata=(SDL_MouseData*)cursor->mouse->driverdata;
         didata=(SDL_DisplayData*)mdata->didata;
      }
      else
      {
         /* We can't get SDL_DisplayData at this point, return fake success */
         return 0;
      }
   }
   else
   {
      /* Sanity checks */
      window=SDL_GetWindowFromID(window_id);
      if (window!=NULL)
      {
         display=SDL_GetDisplayFromWindow(window);
         if (display!=NULL)
         {
            didata=(SDL_DisplayData*)display->driverdata;
            if (didata==NULL)
            {
               return -1;
            }
         }
         else
         {
            return -1;
         }
      }
      else
      {
         return -1;
      }
   }

   /* Check if we need to set new shape or disable cursor shape */
   if (cursor!=NULL)
   {
      /* Retrieve GF cursor shape */
      internal_cursor=(gf_cursor_t*)cursor->driverdata;
      if (internal_cursor==NULL)
      {
         SDL_SetError("GF: Internal cursor data is absent");
         return -1;
      }
      if ((internal_cursor->cursor.bitmap.image0==NULL) ||
          (internal_cursor->cursor.bitmap.image1==NULL))
      {
         SDL_SetError("GF: Cursor shape is absent");
         return -1;
      }

      /* Store last shown cursor to display data */
      didata->cursor.type=internal_cursor->type;
      didata->cursor.hotspot.x=internal_cursor->hotspot.x;
      didata->cursor.hotspot.y=internal_cursor->hotspot.y;
      if (internal_cursor->cursor.bitmap.w>SDL_VIDEO_GF_MAX_CURSOR_SIZE)
      {
         didata->cursor.cursor.bitmap.w=SDL_VIDEO_GF_MAX_CURSOR_SIZE;
      }
      else
      {
         didata->cursor.cursor.bitmap.w=internal_cursor->cursor.bitmap.w;
      }

      if (didata->cursor.cursor.bitmap.h>SDL_VIDEO_GF_MAX_CURSOR_SIZE)
      {
         didata->cursor.cursor.bitmap.h=SDL_VIDEO_GF_MAX_CURSOR_SIZE;
      }
      else
      {
         didata->cursor.cursor.bitmap.h=internal_cursor->cursor.bitmap.h;
      }

      didata->cursor.cursor.bitmap.color0=internal_cursor->cursor.bitmap.color0;
      didata->cursor.cursor.bitmap.color1=internal_cursor->cursor.bitmap.color1;
      didata->cursor.cursor.bitmap.stride=internal_cursor->cursor.bitmap.stride;
      SDL_memcpy(didata->cursor.cursor.bitmap.image0,
                internal_cursor->cursor.bitmap.image0,
                ((internal_cursor->cursor.bitmap.w+7)/(sizeof(uint8_t)*8))*internal_cursor->cursor.bitmap.h);
      SDL_memcpy(didata->cursor.cursor.bitmap.image1,
                internal_cursor->cursor.bitmap.image1,
                ((internal_cursor->cursor.bitmap.w+7)/(sizeof(uint8_t)*8))*internal_cursor->cursor.bitmap.h);

      /* Setup cursor shape */
      status=gf_cursor_set(didata->display, 0, internal_cursor);
      if (status!=GF_ERR_OK)
      {
         if (status!=GF_ERR_NOSUPPORT)
         {
            SDL_SetError("GF: Can't set hardware cursor shape");
            return -1;
         }
      }

      /* Enable just set cursor */
      status=gf_cursor_enable(didata->display, 0);
      if (status!=GF_ERR_OK)
      {
         if (status!=GF_ERR_NOSUPPORT)
         {
            SDL_SetError("GF: Can't enable hardware cursor");
            return -1;
         }
      }

      /* Set cursor visible */
      didata->cursor_visible=SDL_TRUE;
   }
   else
   {
      /* SDL requests to disable cursor */
      status=gf_cursor_disable(didata->display, 0);
      if (status!=GF_ERR_OK)
      {
         if (status!=GF_ERR_NOSUPPORT)
         {
            SDL_SetError("GF: Can't disable hardware cursor");
            return -1;
         }
      }

      /* Set cursor invisible */
      didata->cursor_visible=SDL_FALSE;
   }

   /* New cursor shape is set */
   return 0;
}

void gf_movecursor(SDL_Cursor* cursor)
{
   SDL_VideoDisplay* display;
   SDL_DisplayData*  didata;
   SDL_Window*  window;
   SDL_WindowID window_id;
   int32_t status;
   uint32_t xmax;
   uint32_t ymax;

   /* Get current window id */
   window_id=SDL_GetFocusWindow();
   if (window_id<=0)
   {
      didata=(SDL_DisplayData*)cursor->mouse->driverdata;
   }
   else
   {
      /* Sanity checks */
      window=SDL_GetWindowFromID(window_id);
      if (window!=NULL)
      {
         display=SDL_GetDisplayFromWindow(window);
         if (display!=NULL)
         {
            didata=(SDL_DisplayData*)display->driverdata;
            if (didata==NULL)
            {
               return;
            }
         }
         else
         {
            return;
         }
      }
      else
      {
         return;
      }
   }

   /* Add checks for out of screen bounds position */
   if (cursor->mouse->x<0)
   {
      cursor->mouse->x=0;
   }
   if (cursor->mouse->y<0)
   {
      cursor->mouse->y=0;
   }

   /* Get window size to clamp maximum coordinates */
   SDL_GetWindowSize(window_id, &xmax, &ymax);
   if (cursor->mouse->x>=xmax)
   {
      cursor->mouse->x=xmax-1;
   }
   if (cursor->mouse->y>=ymax)
   {
      cursor->mouse->y=ymax-1;
   }

   status=gf_cursor_set_pos(didata->display, 0, cursor->mouse->x, cursor->mouse->y);
   if (status!=GF_ERR_OK)
   {
      if (status!=GF_ERR_NOSUPPORT)
      {
         SDL_SetError("GF: Can't set hardware cursor position");
         return;
      }
   }
}

void gf_freecursor(SDL_Cursor* cursor)
{
   gf_cursor_t* internal_cursor;

   if (cursor!=NULL)
   {
      internal_cursor=(gf_cursor_t*)cursor->driverdata;
      if (internal_cursor!=NULL)
      {
         if (internal_cursor->cursor.bitmap.image0!=NULL)
         {
            SDL_free((uint8_t*)internal_cursor->cursor.bitmap.image0);
         }
         if (internal_cursor->cursor.bitmap.image1!=NULL)
         {
            SDL_free((uint8_t*)internal_cursor->cursor.bitmap.image1);
         }
         SDL_free(internal_cursor);
      }
   }
}

void gf_warpmouse(SDL_Mouse* mouse, SDL_WindowID windowID, int x, int y)
{
   SDL_VideoDisplay* display;
   SDL_DisplayData*  didata;
   SDL_Window* window;
   uint32_t xmax;
   uint32_t ymax;
   int32_t  status;

   /* Sanity checks */
   window=SDL_GetWindowFromID(windowID);
   if (window!=NULL)
   {
      display=SDL_GetDisplayFromWindow(window);
      if (display!=NULL)
      {
         didata=(SDL_DisplayData*)display->driverdata;
         if (didata==NULL)
         {
            return;
         }
      }
      else
      {
         return;
      }
   }
   else
   {
      return;
   }

   /* Add checks for out of screen bounds position */
   if (x<0)
   {
      x=0;
   }
   if (y<0)
   {
      y=0;
   }

   /* Get window size to clamp maximum coordinates */
   SDL_GetWindowSize(windowID, &xmax, &ymax);
   if (x>=xmax)
   {
      x=xmax-1;
   }
   if (y>=ymax)
   {
      y=ymax-1;
   }

   status=gf_cursor_set_pos(didata->display, 0, x, y);
   if (status!=GF_ERR_OK)
   {
      if (status!=GF_ERR_NOSUPPORT)
      {
         SDL_SetError("GF: Can't set hardware cursor position");
         return;
      }
   }
}

void gf_freemouse(SDL_Mouse* mouse)
{
   if (mouse->driverdata==NULL)
   {
      return;
   }

   /* Mouse framework doesn't deletes automatically our driverdata */
   SDL_free(mouse->driverdata);
   mouse->driverdata=NULL;

   return;
}

/*****************************************************************************/
/* HIDDI handlers code                                                       */
/*****************************************************************************/
static key_packet key_last_state[SDL_HIDDI_MAX_DEVICES];

static void hiddi_keyboard_handler(uint32_t devno, uint8_t* report_data, uint32_t report_len)
{
   key_packet* packet;
   uint32_t    it;
   uint32_t    jt;

   packet=(key_packet*)report_data;

   /* Check for special states */
   switch (report_len)
   {
      case 8:   /* 8 bytes of report length */
           {
              for (it=0; it<6; it++)
              {
                 /* Check for keyboard overflow, when it can't handle */
                 /* many simultaneous pressed keys */
                 if (packet->codes[it]==HIDDI_KEY_OVERFLOW)
                 {
                    return;
                 }
              }
           }
           break;
      default:
           {
              /* Do not process unknown reports */
              return;
           }
           break;
   }

   /* Check if modifier key was pressed */
   if (packet->modifiers!=key_last_state[devno].modifiers)
   {
      if (((packet->modifiers & HIDDI_MKEY_LEFT_CTRL)==HIDDI_MKEY_LEFT_CTRL) &&
         (key_last_state[devno].modifiers & HIDDI_MKEY_LEFT_CTRL)==0)
      {
         /* Left Ctrl key was pressed */
         SDL_SendKeyboardKey(0, SDL_PRESSED, SDL_SCANCODE_LCTRL);
      }
      if (((packet->modifiers & HIDDI_MKEY_LEFT_CTRL)==0) &&
         (key_last_state[devno].modifiers & HIDDI_MKEY_LEFT_CTRL)==HIDDI_MKEY_LEFT_CTRL)
      {
         /* Left Ctrl key was released */
         SDL_SendKeyboardKey(0, SDL_RELEASED, SDL_SCANCODE_LCTRL);
      }
      if (((packet->modifiers & HIDDI_MKEY_LEFT_SHIFT)==HIDDI_MKEY_LEFT_SHIFT) &&
         (key_last_state[devno].modifiers & HIDDI_MKEY_LEFT_SHIFT)==0)
      {
         /* Left Shift key was pressed */
         SDL_SendKeyboardKey(0, SDL_PRESSED, SDL_SCANCODE_LSHIFT);
      }
      if (((packet->modifiers & HIDDI_MKEY_LEFT_SHIFT)==0) &&
         (key_last_state[devno].modifiers & HIDDI_MKEY_LEFT_SHIFT)==HIDDI_MKEY_LEFT_SHIFT)
      {
         /* Left Shift key was released */
         SDL_SendKeyboardKey(0, SDL_RELEASED, SDL_SCANCODE_LSHIFT);
      }
      if (((packet->modifiers & HIDDI_MKEY_LEFT_ALT)==HIDDI_MKEY_LEFT_ALT) &&
         (key_last_state[devno].modifiers & HIDDI_MKEY_LEFT_ALT)==0)
      {
         /* Left Alt key was pressed */
         SDL_SendKeyboardKey(0, SDL_PRESSED, SDL_SCANCODE_LALT);
      }
      if (((packet->modifiers & HIDDI_MKEY_LEFT_ALT)==0) &&
         (key_last_state[devno].modifiers & HIDDI_MKEY_LEFT_ALT)==HIDDI_MKEY_LEFT_ALT)
      {
         /* Left Alt key was released */
         SDL_SendKeyboardKey(0, SDL_RELEASED, SDL_SCANCODE_LALT);
      }
      if (((packet->modifiers & HIDDI_MKEY_LEFT_WFLAG)==HIDDI_MKEY_LEFT_WFLAG) &&
         (key_last_state[devno].modifiers & HIDDI_MKEY_LEFT_WFLAG)==0)
      {
         /* Left Windows flag key was pressed */
         SDL_SendKeyboardKey(0, SDL_PRESSED, SDL_SCANCODE_LGUI);
      }
      if (((packet->modifiers & HIDDI_MKEY_LEFT_WFLAG)==0) &&
         (key_last_state[devno].modifiers & HIDDI_MKEY_LEFT_WFLAG)==HIDDI_MKEY_LEFT_WFLAG)
      {
         /* Left Windows flag key was released */
         SDL_SendKeyboardKey(0, SDL_RELEASED, SDL_SCANCODE_LGUI);
      }
      if (((packet->modifiers & HIDDI_MKEY_RIGHT_CTRL)==HIDDI_MKEY_RIGHT_CTRL) &&
         (key_last_state[devno].modifiers & HIDDI_MKEY_RIGHT_CTRL)==0)
      {
         /* Right Ctrl key was pressed */
         SDL_SendKeyboardKey(0, SDL_PRESSED, SDL_SCANCODE_RCTRL);
      }
      if (((packet->modifiers & HIDDI_MKEY_RIGHT_CTRL)==0) &&
         (key_last_state[devno].modifiers & HIDDI_MKEY_RIGHT_CTRL)==HIDDI_MKEY_RIGHT_CTRL)
      {
         /* Right Ctrl key was released */
         SDL_SendKeyboardKey(0, SDL_RELEASED, SDL_SCANCODE_RCTRL);
      }
      if (((packet->modifiers & HIDDI_MKEY_RIGHT_SHIFT)==HIDDI_MKEY_RIGHT_SHIFT) &&
         (key_last_state[devno].modifiers & HIDDI_MKEY_RIGHT_SHIFT)==0)
      {
         /* Right Shift key was pressed */
         SDL_SendKeyboardKey(0, SDL_PRESSED, SDL_SCANCODE_RSHIFT);
      }
      if (((packet->modifiers & HIDDI_MKEY_RIGHT_SHIFT)==0) &&
         (key_last_state[devno].modifiers & HIDDI_MKEY_RIGHT_SHIFT)==HIDDI_MKEY_RIGHT_SHIFT)
      {
         /* Right Shift key was released */
         SDL_SendKeyboardKey(0, SDL_RELEASED, SDL_SCANCODE_RSHIFT);
      }
      if (((packet->modifiers & HIDDI_MKEY_RIGHT_ALT)==HIDDI_MKEY_RIGHT_ALT) &&
         (key_last_state[devno].modifiers & HIDDI_MKEY_RIGHT_ALT)==0)
      {
         /* Right Alt key was pressed */
         SDL_SendKeyboardKey(0, SDL_PRESSED, SDL_SCANCODE_RALT);
      }
      if (((packet->modifiers & HIDDI_MKEY_RIGHT_ALT)==0) &&
         (key_last_state[devno].modifiers & HIDDI_MKEY_RIGHT_ALT)==HIDDI_MKEY_RIGHT_ALT)
      {
         /* Right Alt key was released */
         SDL_SendKeyboardKey(0, SDL_RELEASED, SDL_SCANCODE_RALT);
      }
      if (((packet->modifiers & HIDDI_MKEY_RIGHT_WFLAG)==HIDDI_MKEY_RIGHT_WFLAG) &&
         (key_last_state[devno].modifiers & HIDDI_MKEY_RIGHT_WFLAG)==0)
      {
         /* Right Windows flag key was pressed */
         SDL_SendKeyboardKey(0, SDL_PRESSED, SDL_SCANCODE_RGUI);
      }
      if (((packet->modifiers & HIDDI_MKEY_RIGHT_WFLAG)==0) &&
         (key_last_state[devno].modifiers & HIDDI_MKEY_RIGHT_WFLAG)==HIDDI_MKEY_RIGHT_WFLAG)
      {
         /* Right Windows flag key was released */
         SDL_SendKeyboardKey(0, SDL_RELEASED, SDL_SCANCODE_RGUI);
      }
   }

   /* Check each key in the press/release buffer */
   switch (report_len)
   {
      case 8:   /* 8 bytes of report length */
           {
              /* Check if at least one key was unpressed */
              for (it=0; it<6; it++)
              {
                 if (key_last_state[devno].codes[it]==HIDDI_KEY_UNPRESSED)
                 {
                    /* if stored keycode is zero, find another */
                    continue;
                 }
                 for (jt=0; jt<6; jt++)
                 {
                    /* Find stored keycode in the current pressed codes */
                    if (packet->codes[jt]==key_last_state[devno].codes[it])
                    {
                       /* If found then particular key state is not changed */
                       break;
                    }
                 }

                 /* Check if pressed key can't longer be found */
                 if (jt==6)
                 {
                    SDL_SendKeyboardKey(0, SDL_RELEASED, key_last_state[devno].codes[it]);
                 }
              }

              /* Check if at least one new key was pressed */
              for (it=0; it<6; it++)
              {
                 if (packet->codes[it]==HIDDI_KEY_UNPRESSED)
                 {
                    continue;
                 }
                 for (jt=0; jt<6; jt++)
                 {
                    /* Find new keycode it the array of old pressed keys */
                    if (packet->codes[it]==key_last_state[devno].codes[jt])
                    {
                       break;
                    }
                 }

                 /* Check if new key was pressed */
                 if (jt==6)
                 {
                    SDL_SendKeyboardKey(0, SDL_PRESSED, packet->codes[it]);
                 }
              }
           }
      default:  /* unknown keyboard report type */
           {
              /* Ignore all unknown reports */
           }
           break;
   }

   /* Store last state */
   key_last_state[devno]=*packet;
}

static uint32_t mouse_last_state_button[SDL_HIDDI_MAX_DEVICES];
static uint32_t collect_reports=0;

static void hiddi_mouse_handler(uint32_t devno, uint8_t* report_data, uint32_t report_len)
{
   uint32_t it;
   uint32_t sdlbutton;

   /* We do not want to collect stored events */
   if (collect_reports==0)
   {
      return;
   }

   /* Check for special states */
   switch (report_len)
   {
      case 8:   /* 8 bytes of report length, usually multi-button USB mice */
           {
              mouse_packet8* packet;
              packet=(mouse_packet8*)report_data;

              /* Send motion event if motion really was */
              if ((packet->horizontal_precision!=0) || (packet->vertical_precision!=0))
              {
                 SDL_SendMouseMotion(0, 1, packet->horizontal_precision, packet->vertical_precision, 0);
              }

              /* Send mouse button press/release events */
              if (mouse_last_state_button[devno]!=packet->buttons)
              {
                 /* Cycle all buttons status */
                 for (it=0; it<8; it++)
                 {
                    /* convert hiddi button id to sdl button id */
                    switch(it)
                    {
                       case 0:
                            {
                               sdlbutton=SDL_BUTTON_LEFT;
                            }
                            break;
                       case 1:
                            {
                               sdlbutton=SDL_BUTTON_RIGHT;
                            }
                            break;
                       case 2:
                            {
                               sdlbutton=SDL_BUTTON_MIDDLE;
                            }
                            break;
                       default:
                            {
                               sdlbutton=it+1;
                            }
                            break;
                    }

                    /* Button pressed */
                    if (((packet->buttons & (0x01<<it))==(0x01<<it)) &&
                        ((mouse_last_state_button[devno] & (0x01<<it))==0x00))
                    {
                       SDL_SendMouseButton(0, SDL_PRESSED, sdlbutton);
                    }
                    /* Button released */
                    if (((packet->buttons & (0x01<<it))==0x00) &&
                        ((mouse_last_state_button[devno] & (0x01<<it))==(0x01<<it)))
                    {
                       SDL_SendMouseButton(0, SDL_RELEASED, sdlbutton);
                    }
                 }
                 mouse_last_state_button[devno]=packet->buttons;
              }

              /* Send mouse wheel events */
              if (packet->wheel!=0)
              {
                 /* Send vertical wheel event only */
                 SDL_SendMouseWheel(0, 0, packet->wheel);
              }
           }
           break;
      case 4:   /* 4 bytes of report length, usually PS/2 mice */
           {
              mouse_packet4* packet;
              packet=(mouse_packet4*)report_data;

              /* Send motion event if motion really was */
              if ((packet->horizontal!=0) || (packet->vertical!=0))
              {
                 SDL_SendMouseMotion(0, 1, packet->horizontal, packet->vertical, 0);
              }

              /* Send mouse button press/release events */
              if (mouse_last_state_button[devno]!=packet->buttons)
              {
                 /* Cycle all buttons status */
                 for (it=0; it<8; it++)
                 {
                    /* convert hiddi button id to sdl button id */
                    switch(it)
                    {
                       case 0:
                            {
                               sdlbutton=SDL_BUTTON_LEFT;
                            }
                            break;
                       case 1:
                            {
                               sdlbutton=SDL_BUTTON_RIGHT;
                            }
                            break;
                       case 2:
                            {
                               sdlbutton=SDL_BUTTON_MIDDLE;
                            }
                            break;
                       default:
                            {
                               sdlbutton=it+1;
                            }
                            break;
                    }

                    /* Button pressed */
                    if (((packet->buttons & (0x01<<it))==(0x01<<it)) &&
                        ((mouse_last_state_button[devno] & (0x01<<it))==0x00))
                    {
                       SDL_SendMouseButton(0, SDL_PRESSED, sdlbutton);
                    }
                    /* Button released */
                    if (((packet->buttons & (0x01<<it))==0x00) &&
                        ((mouse_last_state_button[devno] & (0x01<<it))==(0x01<<it)))
                    {
                       SDL_SendMouseButton(0, SDL_RELEASED, sdlbutton);
                    }
                 }
                 mouse_last_state_button[devno]=packet->buttons;
              }

              /* Send mouse wheel events */
              if (packet->wheel!=0)
              {
                 /* Send vertical wheel event only */
                 SDL_SendMouseWheel(0, 0, packet->wheel);
              }
           }
           break;
   }
}

/*****************************************************************************/
/* HIDDI interacting code                                                    */
/*****************************************************************************/
static hidd_device_ident_t hiddevice=
{
   HIDD_CONNECT_WILDCARD,  /* vendor id:  any */
   HIDD_CONNECT_WILDCARD,  /* product id: any */
   HIDD_CONNECT_WILDCARD,  /* version:    any */
};

static hidd_connect_parm_t hidparams={NULL, HID_VERSION, HIDD_VERSION, 0, 0, &hiddevice, NULL, 0};

static void hiddi_insertion(struct hidd_connection* connection, hidd_device_instance_t* device_instance);
static void hiddi_removal(struct hidd_connection* connection, hidd_device_instance_t* instance);
static void hiddi_report(struct hidd_connection* connection, struct hidd_report* report, void* report_data, uint32_t report_len, uint32_t flags, void* user);

static hidd_funcs_t hidfuncs={_HIDDI_NFUNCS, hiddi_insertion, hiddi_removal, hiddi_report, NULL};

/* HID handle, singletone */
struct hidd_connection* connection=NULL;

/* SDL detected input device types, singletone */
static uint32_t sdl_input_devices[SDL_HIDDI_MAX_DEVICES];

static int hiddi_register_for_reports(struct hidd_collection* col, hidd_device_instance_t* device_instance)
{
   int it;
   uint16_t num_col;
   struct hidd_collection** hidd_collections;
   struct hidd_report_instance* report_instance;
   struct hidd_report* report;
   int status=0;
   hidview_device_t* device;

   for (it=0 ; it<10 && !status; it++)
   {
      status=hidd_get_report_instance(col, it, HID_INPUT_REPORT, &report_instance);
      if (status==EOK)
      {
         status=hidd_report_attach(connection, device_instance, report_instance, 0, sizeof(hidview_device_t), &report);
         if (status==EOK)
         {
            device=hidd_report_extra(report);
            device->report=report;
            device->instance=report_instance;
         }
      }
   }
   hidd_get_collections(NULL, col, &hidd_collections, &num_col);

   for (it=0; it<num_col; it++)
   {
      hiddi_register_for_reports(hidd_collections[it], device_instance);
   }

   return EOK;
}

static void hiddi_insertion(struct hidd_connection* connection, hidd_device_instance_t* device_instance)
{
   uint32_t it;
   struct hidd_collection** hidd_collections;
   uint16_t num_col;

   /* get root level collections */
   hidd_get_collections(device_instance, NULL, &hidd_collections, &num_col);
   for (it=0; it<num_col; it++)
   {
      hiddi_register_for_reports(hidd_collections[it], device_instance);
   }
}

static void hiddi_removal(struct hidd_connection* connection, hidd_device_instance_t* instance)
{
   hidd_reports_detach(connection, instance);
}

static void hiddi_report(struct hidd_connection* connection, hidd_report_t* report, void* report_data, uint32_t report_len, uint32_t flags, void* user)
{
   if (report->dev_inst->devno>=SDL_HIDDI_MAX_DEVICES)
   {
      /* Unknown HID device, with devno number out of supported range */
      return;
   }

   /* Check device type which generates event */
   switch (sdl_input_devices[report->dev_inst->devno])
   {
      case SDL_GF_HIDDI_NONE:
           {
              /* We do not handle other devices type*/
              return;
           }
           break;
      case SDL_GF_HIDDI_MOUSE:
           {
              /* Call mouse handler */
              hiddi_mouse_handler(report->dev_inst->devno, report_data, report_len);
           }
           break;
      case SDL_GF_HIDDI_KEYBOARD:
           {
              /* Call keyboard handler */
              hiddi_keyboard_handler(report->dev_inst->devno, report_data, report_len);
           }
           break;
      case SDL_GF_HIDDI_JOYSTICK:
           {
              /* Call joystick handler */
           }
           break;
   }
}

static hiddi_get_device_type(uint8_t* report_data, uint16_t report_length)
{
   hid_byte_t  byte;
   uint16_t    usage_page=0;
   uint16_t    usage=0;
   uint16_t    data=0;

   while (report_length && !(usage_page && usage))
   {
      if (hidp_analyse_byte(*report_data, &byte))
      {
         /* Error in parser, do nothing */
      }
      data=hidp_get_data((report_data+1), &byte);
      switch (byte.HIDB_Type)
      {
         case HID_TYPE_GLOBAL:
              if (!usage_page && byte.HIDB_Tag==HID_GLOBAL_USAGE_PAGE)
              {
                 usage_page=data;
              }
              break;
         case HID_TYPE_LOCAL:
              if (!usage && byte.HIDB_Tag==HID_LOCAL_USAGE)
              {
                 usage=data;
              }
              break;
      }
      report_data+=byte.HIDB_Length+1;
      report_length-=byte.HIDB_Length+1;
   }

   switch (usage_page)
   {
      case HIDD_PAGE_DESKTOP:
           {
              switch (usage)
              {
                 case HIDD_USAGE_MOUSE:
                      {
                         return SDL_GF_HIDDI_MOUSE;
                      }
                      break;
                 case HIDD_USAGE_JOYSTICK:
                      {
                         return SDL_GF_HIDDI_JOYSTICK;
                      }
                      break;
                 case HIDD_USAGE_KEYBOARD:
                      {
                         return SDL_GF_HIDDI_KEYBOARD;
                      }
                      break;
              }
           }
           break;
      case HIDD_PAGE_DIGITIZER:
           {
              /* Do not handle digitizers */
           }
           break;
      case HIDD_PAGE_CONSUMER:
           {
              /* Do not handle consumer input devices */
           }
           break;
   }

   return SDL_GF_HIDDI_NONE;
}

static int32_t hiddi_connect_devices()
{
   int32_t  status;
   uint32_t it;
   uint8_t* report_data;
   uint16_t report_length;
   hidd_device_instance_t instance;

   /* Cleanup initial keys and mice state */
   SDL_memset(key_last_state, 0x00, sizeof(key_packet)*SDL_HIDDI_MAX_DEVICES);
   SDL_memset(mouse_last_state_button, 0x00, sizeof(uint32_t)*SDL_HIDDI_MAX_DEVICES);

   status=hidd_connect(&hidparams, &connection);
   if (status!=EOK)
   {
      return -1;
   }

   for (it=0; it<SDL_HIDDI_MAX_DEVICES; it++)
   {
      /* Get device instance */
      status=hidd_get_device_instance(connection, it, &instance);
      if (status!=EOK)
      {
         continue;
      }

      status=hidd_get_report_desc(connection, &instance, &report_data, &report_length);
      if (status!=EOK)
      {
         continue;
      }

      status=hiddi_get_device_type(report_data, report_length);
      sdl_input_devices[it]=status;

      free(report_data);
   }

   /* Disconnect from HID server */
   status=hidd_disconnect(connection);
   if (status!=EOK)
   {
      return -1;
   }

   /* Add handlers for HID devices */
   hidparams.funcs=&hidfuncs;

   status=hidd_connect(&hidparams, &connection);
   if (status!=EOK)
   {
      return -1;
   }

   return 0;
}

static int32_t hiddi_disconnect_devices()
{
   int32_t status;

   hiddi_disable_mouse();

   /* Disconnect from HID server */
   status=hidd_disconnect(connection);
   if (status!=EOK)
   {
      return -1;
   }
}

void hiddi_enable_mouse()
{
   collect_reports=1;
}

void hiddi_disable_mouse()
{
   collect_reports=0;
}