Sleep in PRX

Discuss the development of new homebrew software, tools and libraries.

Moderators: cheriff, TyRaNiD

Post Reply
angelo
Posts: 168
Joined: Wed Aug 29, 2007 9:34 pm

Sleep in PRX

Post by angelo »

When my plugin is enabled, my PSP doesn't enter "Sleep" mode. Nor does the backlight switch off after a few minutes.

Is there a line that must be entered so this can happen?

Code: Select all

#include <pspkernel.h>
#include <pspdebug.h>
#include <pspctrl.h>
#include <pspdisplay.h>
#include <string.h>
#include <pspiofilemgr.h>
#include <stdio.h>
#include "pikey.h"
#include "apihook.h"
#include "blit.h"
#include "mapkey.h"

PSP_MODULE_INFO&#40;"piKey", 0x1000, 1, 1&#41;;

/*****************************************************************************/
/* Write debug info to SIO?                                                  */
/*                                                                           */
/* 0 &#58; off                                                                   */
/* 1 &#58; major events                                                          */
/* 2 &#58; debug events                                                          */
/* 3 &#58; extra debug                                                           */
/*****************************************************************************/
#define DEBUG_OUTPUT 0
/*****************************************************************************/
/* Internal structs                                                          */
/*****************************************************************************/
typedef struct input_info &#123;
  struct input_info *prev;
  struct input_info *next;
  SceUID             moduleID;
  char               moduleName&#91;64&#93;;
&#125; INPUT_INFO;
typedef struct output_info &#123;
  struct output_info *prev;
  struct output_info *next;
  SceUID              moduleID;
  char                moduleName&#91;64&#93;;
&#125; OUTPUT_INFO;
void DumpMem&#40;void *addr,unsigned size,int num&#41;;
typedef int &#40;*DRIVERINFOFUNC&#41;&#40;DRIVERINFO *&#41;;
/*****************************************************************************/
/* Internal vars                                                             */
/*****************************************************************************/
int terminate = 0;
int numInputs;
int numOutputs;
SceUID pikeythread;
INPUT_INFO  *inputs;
OUTPUT_INFO *outputs;
#define QUEUE_SIZE 256
#define MAX_PATH_SIZE 256
/*****************************************************************************/
/* Cyclic buffer of max-size QUEUE_SIZE for input chars.                     */
/*****************************************************************************/
wchar inBuffer&#91;QUEUE_SIZE&#93;;
int queueInputPos;
int queueOutputPos;
int queueSize;
SceUID queueSema;
SceUID gExclusiveThread;
/*****************************************************************************/
/* Keyboard info display callbacks                                           */
/*****************************************************************************/
#define MAX_DISPLAY_CALLBACKS 10
SceUID gDisplayCallbacks&#91;MAX_DISPLAY_CALLBACKS&#93;;
int    gNumDisplayCallbacks = 0;
SceUID callbackSema;
/*****************************************************************************/
/* Status indication                                                         */
/*****************************************************************************/
// num of vblanks to display status text for. &#40;3 secs&#41;
#define STATUS_DISPLAY_TIME 180
#define SCREEN_TEXT_WIDTH  80
static char centredText&#91;SCREEN_TEXT_WIDTH + 1&#93;;
int gStatusCycles = 0;
int gStatusText   = 0;
int statusMessages = 1;
int sioDebug = 0;
int disabledIn150 = 0;
/*****************************************************************************/
/* VSH mode?                                                                 */
/*****************************************************************************/
int gvshmode = 0;
/*****************************************************************************/
/* Keypress mode vars                                                        */
/*****************************************************************************/
int  gOldKeyMode = PIKEY_KEYMODE_KEYPRESS;
int  gKeyMode = PIKEY_KEYMODE_KEYPRESS;
char gPressMap&#91;KEY_MAX&#93;;
char gHoldMap&#91;KEY_MAX&#93;;
int  numPressed = 0;
int  altHeld = 0;
int  ctrlHeld = 0;
int  shiftHeld = 0;
/*****************************************************************************/
/* Key repeat metrics - measured in vblanks                                  */
/*                                                                           */
/* REPEAT_TIME&#58; time to first repeat                                         */
/* REPEAT_PERIOD&#58; time between repeats once started.                         */
/*****************************************************************************/
#define REPEAT_TIME   30
#define REPEAT_PERIOD 10
// measured in microseconds - time to simulate a keyhold event when sent a
// character in keypress mode.
#define FAKE_KEYPRESS_HOLD 80000
// length of a vblank frame &#40;60hz&#41; in microseconds.
#define FRAME_LENGTH_USECS 16667
/*****************************************************************************/
/* ESCAPE key timeout hack                                                   */
/*****************************************************************************/
int gEscTimer = -1;
// number of vblanks to wait for further chars in a vt100 escape sequence
#define ESC_TIMEOUT 6
/*****************************************************************************/
/* Config file control vars.                                                 */
/*****************************************************************************/
#define MAX_CONFIGS 10
char * configData&#91;MAX_CONFIGS&#93;;
char * configOffset&#91;MAX_CONFIGS&#93;;
int    numConfigs = 0;
char cfgtoken&#91;64&#93;;
char cfgvalue&#91;128&#93;;
/*****************************************************************************/
/* Disabled plugins list                                                     */
/*****************************************************************************/
#define MAX_DISABLED_PLUGINS 10
char disabledPlugins&#91;MAX_DISABLED_PLUGINS&#93;&#91;128&#93;;
int  numDisabledPlugins = 0;
/*****************************************************************************/
/*                                                                           */
/* Code starts here                                                          */
/*                                                                           */
/*****************************************************************************/
void sioPrint&#40;const char *str&#41;
&#123;
#if DEBUG_OUTPUT!=0
  if &#40;sioDebug&#41;
  &#123;
    while &#40;*str&#41;
    &#123;
      pspDebugSioPutchar&#40;*str&#41;;
      str++;
    &#125;
  &#125;
#endif
&#125;
void sioPrintHex&#40;char ch&#41;
&#123;
#if DEBUG_OUTPUT!=0
  if &#40;sioDebug&#41;
  &#123;
    static char * hexmap="0123456789ABCDEF";
    pspDebugSioPutchar&#40; hexmap&#91; &#40;ch >> 4&#41; & 0x0F &#93; &#41;;
    pspDebugSioPutchar&#40; hexmap&#91; &#40;ch & 0x0F&#41; &#93; &#41;;
  &#125;
#endif
&#125;
void sioPrintWord&#40;u32 data&#41;
&#123;
#if DEBUG_OUTPUT!=0
  if &#40;sioDebug&#41;
  &#123;
    sioPrintHex&#40; &#40;data >> 24&#41; & 0x000000FFL &#41;;
    sioPrintHex&#40; &#40;data >> 16&#41; & 0x000000FFL &#41;;
    sioPrintHex&#40; &#40;data >>  8&#41; & 0x000000FFL &#41;;
    sioPrintHex&#40; data & 0x000000FFL &#41;;
  &#125;
#endif
&#125;
int waitForModule&#40;const char * modname&#41;
&#123;
  int cycles = 0;
#if DEBUG_OUTPUT>=2
  sioPrint&#40;"Wait for module "&#41;;
  sioPrint&#40;modname&#41;;
  sioPrint&#40;"&#58; "&#41;;
#endif
  while &#40;sceKernelFindModuleByName&#40;modname&#41; == NULL&#41;
  &#123;
#if DEBUG_OUTPUT>=2
    pspDebugSioPutchar&#40;'.'&#41;;
#endif
    if &#40;cycles++ > 10&#41;
    &#123;
#if DEBUG_OUTPUT>=2
      sioPrint&#40;"TIMEOUT.\r\n"&#41;;
#endif
			// give up waiting after 5 secs
      return 0;
    &#125;
    sceKernelDelayThread&#40;500000&#41;;
  &#125;
#if DEBUG_OUTPUT>=2
  sioPrint&#40;"OK.\r\n"&#41;;
#endif
  return 1;
&#125;
static struct SceLibraryEntryTable *_libsFindLibrary&#40;SceUID uid, const char *library&#41;
&#123;
  struct SceLibraryEntryTable *entry;
  SceModule *pMod;
  void *entTab;
  int entLen;
  pMod = sceKernelFindModuleByUID&#40;uid&#41;;
  if&#40;pMod != NULL&#41;
  &#123;
    int i = 0;
    entTab = pMod->ent_top;
    entLen = pMod->ent_size;
    while&#40;i < entLen&#41;
    &#123;
      entry = &#40;struct SceLibraryEntryTable *&#41; &#40;entTab + i&#41;;
      if&#40;&#40;entry->libname&#41; && &#40;strcmp&#40;entry->libname, library&#41; == 0&#41;&#41;
      &#123;
        return entry;
      &#125;
      else if&#40;!entry->libname && !library&#41;
      &#123;
        return entry;
      &#125;
      i += &#40;entry->len * 4&#41;;
    &#125;
  &#125;
  return NULL;
&#125;
static void* libsFindExportAddrByNid&#40;SceUID uid, const char *library, u32 nid&#41;
&#123;
  u32 *addr = NULL;
  SceLibraryEntryTable *entry = _libsFindLibrary&#40;uid, library&#41;;
  if&#40;entry&#41;
  &#123;
    int count;
    int total;
    unsigned int *vars;
    total = entry->stubcount + entry->vstubcount;
    vars = entry->entrytable;
    if&#40;entry->stubcount > 0&#41;
    &#123;
      for&#40;count = 0; count < entry->stubcount; count++&#41;
      &#123;
        if&#40;vars&#91;count&#93; == nid&#41;
        &#123;
          return &#40;void*&#41;&#40;*&#40;&#40;void**&#41;&#40;&vars&#91;count+total&#93;&#41;&#41;&#41;;
        &#125;
      &#125;
    &#125;
  &#125;
  return addr;
&#125;
SceUID load_module&#40;const char *path, int flags, int type&#41; &#123;
  SceKernelLMOption option;
  SceUID mpid;
	/* If the type is 0, then load the module in the kernel partition, otherwise load it
  in the user partition. */
  if &#40;type == 0&#41; mpid = 1;
  else mpid = 2;
  memset&#40;&option, 0, sizeof&#40;option&#41;&#41;;
  option.size = sizeof&#40;option&#41;;
  option.mpidtext = mpid;
  option.mpiddata = mpid;
  option.position = 0;
  option.access = 1;
  return sceKernelLoadModule&#40;path, flags, type > 0 ? &option &#58; NULL&#41;;
&#125;
void centre_string&#40;const char *text&#41;
&#123;
  centredText&#91;SCREEN_TEXT_WIDTH&#93; = '\0';
  memset&#40;centredText, ' ', SCREEN_TEXT_WIDTH&#41;;
  int length = strlen&#40;text&#41;;
  int offset;
	// Cope with over-length string
  if &#40;length > SCREEN_TEXT_WIDTH&#41;
  &#123;
    length = SCREEN_TEXT_WIDTH;
    offset = 0;
  &#125;
  else
  &#123;
		// Note &#58; calculation relies on max width being even to avoid overflow
    offset = &#40;SCREEN_TEXT_WIDTH - length&#41; / 2;
  &#125;
  int ii;
  for &#40;ii = 0; ii < length; ii++&#41;
  &#123;
    centredText&#91;offset++&#93; = text&#91;ii&#93;;
  &#125;
&#125;
void displayStatusText&#40;const char *text&#41;
&#123;
  if &#40;statusMessages&#41;
  &#123;
    centre_string&#40;text&#41;;
    gStatusCycles = 0;
    gStatusText   = 1;
#if DEBUG_OUTPUT>=2
    sioPrint&#40;"Show status text&#58; "&#41;;
    sioPrint&#40;text&#41;;
    sioPrint&#40;"\r\n"&#41;;
#endif
  &#125;
&#125;
void InitVars&#40;void&#41;
&#123;
	// empty queues, driver counts
  numInputs  = 0;
  numOutputs = 0;
  inputs = NULL;
  outputs = NULL;
  queueInputPos = 0;
  queueOutputPos = 0;
  queueSize = 0;
  ctrlHeld = 0;
  altHeld = 0;
  shiftHeld = 0;
  gExclusiveThread = 0;
  queueSema    = sceKernelCreateSema&#40;"pikey_q",     0, 1, 1, NULL&#41;;
  callbackSema = sceKernelCreateSema&#40;"pikey_calls", 0, 1, 1, NULL&#41;;
  gNumDisplayCallbacks = 0;
  gStatusText = 0;
  gStatusCycles = 0;
	// default to keypress mode, no keys currently pressed
  gKeyMode = PIKEY_KEYMODE_KEYPRESS;
  memset&#40;gPressMap, 0, sizeof&#40;gPressMap&#41;&#41;;
  numPressed = 0;
  numConfigs = 0;
  int ii;
  for &#40;ii = 0; ii < MAX_CONFIGS; ii++&#41;
  &#123;
    configData&#91;ii&#93; = NULL;
    configOffset&#91;ii&#93; = NULL;
  &#125;
  initApiHooks&#40;&#41;;
&#125;

/*****************************************************************************/
/* Simple malloc clone.  The block ID is stored just before the returned 		 */
/* pointer.  																																 */
/*****************************************************************************/
void *myMalloc&#40;size_t size&#41;
&#123;
  SceUID memID = sceKernelAllocPartitionMemory&#40;1, "", 0, size + 4, NULL&#41;;
  if &#40;memID < 0&#41;
  &#123;
#if DEBUG_OUTPUT>=2
    sioPrint&#40;"sKAPM failed&#58; "&#41;;
    sioPrintWord&#40;memID&#41;;
    sioPrint&#40;"\r\n"&#41;;
#endif
    return NULL;
  &#125;
  unsigned int * lptr = sceKernelGetBlockHeadAddr&#40;memID&#41;;
  *lptr = memID;
#if DEBUG_OUTPUT>=3
  sioPrint&#40;"alloced blockID&#58; "&#41;;
  sioPrintWord&#40;memID&#41;;
  sioPrint&#40;" size "&#41;;
  sioPrintWord&#40;size&#41;;
  sioPrint&#40;"\r\n"&#41;;
#endif
  return &#40;lptr+1&#41;;
&#125;
/*****************************************************************************/
/* Corresponding Free func for myMalloc                                      */
/*****************************************************************************/
void myFree&#40;void *blockaddr&#41;
&#123;
  SceUID blockid = *&#40;&#40;&#40;SceUID*&#41;blockaddr&#41; - 1&#41;;
#if DEBUG_OUTPUT>=3
  sioPrint&#40;"Free blockID&#58; "&#41;;
  sioPrintWord&#40;blockid&#41;;
  sioPrint&#40;"\r\n"&#41;;
#endif
  int rc = sceKernelFreePartitionMemory&#40;blockid&#41;;
  if &#40;rc < 0&#41;
  &#123;
    sioPrint&#40;"Error freeing block at "&#41;;
    sioPrintWord&#40;&#40;int&#41;blockaddr&#41;;
    sioPrint&#40;" err="&#41;;
    sioPrintWord&#40;rc&#41;;
    sioPrint&#40;"\r\n"&#41;;
  &#125;
&#125;
SceIoDirent dirent;
void LoadDrivers&#40;char *path, int isInput&#41;
&#123;
  int status;
#if DEBUG_OUTPUT>=2
  sioPrint&#40;"Load drivers from "&#41;;
  sioPrint&#40;path&#41;;
  sioPrint&#40;"\r\n"&#41;;
#endif
	// iterate through "path/", loading all the prxs
  memset&#40;&dirent, 0, sizeof&#40;SceIoDirent&#41;&#41;;
  SceUID lid = sceIoDopen&#40;path&#41;;
  if &#40;lid >= 0&#41;
  &#123;
    int lrc = 0;
    do &#123;
      lrc = sceIoDread&#40;lid, &dirent&#41;;
      if &#40;lrc > 0&#41; &#123;
        if &#40;strstr&#40;dirent.d_name, ".prx"&#41; || strstr&#40;dirent.d_name, ".PRX"&#41;&#41; &#123;
          char namebuff&#91;MAX_PATH_SIZE&#93;;
          strncpy&#40;namebuff, path, MAX_PATH_SIZE&#41;;
          strcat&#40;namebuff, "/"&#41;;
          strcat&#40;namebuff, dirent.d_name&#41;;
#if DEBUG_OUTPUT>=2
          sioPrint&#40;namebuff&#41;;
          sioPrint&#40;"&#58;\r\n"&#41;;
#endif
          int disabled = 0;
          int ii;
          for &#40;ii = 0; ii < numDisabledPlugins; ii++&#41;
          &#123;
#if DEBUG_OUTPUT>=3
            sioPrint&#40;"Check &#58; '"&#41;;
            sioPrint&#40;disabledPlugins&#91;ii&#93;&#41;;
            sioPrint&#40;"'\r\n"&#41;;
#endif
            if &#40;strcmp&#40;dirent.d_name, disabledPlugins&#91;ii&#93;&#41; == 0&#41;
            &#123;
#if DEBUG_OUTPUT>=1
              sioPrint&#40;"Skip "&#41;;
              sioPrint&#40;dirent.d_name&#41;;
              sioPrint&#40;" - disabled module\r\n"&#41;;
#endif
              disabled = 1;
            &#125;
          &#125;
          if &#40;disabled&#41;
          &#123;
            continue;
          &#125;
          SceUID modid = load_module&#40;namebuff, 0, 0&#41;;
          if &#40;modid < 0&#41;
          &#123;
#if DEBUG_OUTPUT>=1
            sioPrint&#40;"Load module failed&#58; 0x"&#41;;
            sioPrintWord&#40;modid&#41;;
            sioPrint&#40;"\r\n"&#41;;
#endif
          &#125;
          int done = 0;
          while &#40;!done&#41;
          &#123;
            int lrc2 = sceKernelStartModule&#40;modid, 0, NULL, &status, NULL&#41;;
            if &#40;lrc2 < 0&#41;
            &#123;
#if DEBUG_OUTPUT>=1
              sioPrint&#40;"Start module failed&#58; 0x"&#41;;
              sioPrintWord&#40;lrc2&#41;;
#endif
              if &#40;lrc2 == 0x8002013CL&#41;
              &#123;
#if DEBUG_OUTPUT>=1
                sioPrint&#40;"...sleep and retry...\r\n"&#41;;
#endif
                sceKernelDelayThread&#40;500000&#41;;
              &#125;
              else
              &#123;
#if DEBUG_OUTPUT>=1
                sioPrint&#40;"...Give up.\r\n"&#41;;
#endif
                done = 1;
              &#125;
            &#125;
            else
            &#123;
#if DEBUG_OUTPUT>=2
              sioPrint&#40;"Started.\r\n"&#41;;
#endif
              done = 1;
						// Now we started the module successfully, call its driverinfo func.
              DRIVERINFOFUNC info_func = NULL;
              DRIVERINFO ldriverinfo;
              memset&#40;&ldriverinfo, 0, sizeof&#40;ldriverinfo&#41;&#41;;
              SceKernelModuleInfo lmodinfo;
              memset&#40;&lmodinfo, 0, sizeof&#40;lmodinfo&#41;&#41;;
              lmodinfo.size = sizeof&#40;lmodinfo&#41;;
              int lmodinforc = sceKernelQueryModuleInfo&#40;modid, &lmodinfo&#41;;
              if &#40;lmodinforc == 0&#41;
              &#123;
                info_func = libsFindExportAddrByNid&#40;modid, lmodinfo.name, 0x79EC1E42&#41;;
#if DEBUG_OUTPUT>=1
                if &#40;info_func == NULL&#41;
                &#123;
                  sioPrint&#40;"Failed to get driver info export.\r\n"&#41;;
                  sioPrint&#40;"Was using name&#58; '"&#41;;
                  sioPrint&#40;lmodinfo.name&#41;;
                  sioPrint&#40;"'\r\n"&#41;;
                &#125;
#endif
                ldriverinfo.apiVersion = PIKEY_THIS_VERSION;
              &#125;
#if DEBUG_OUTPUT>=1
              else
              &#123;
                sioPrint&#40;"Failed to get module info. RC="&#41;;
                sioPrintWord&#40;lmodinforc&#41;;
                sioPrint&#40;"\r\n"&#41;;
              &#125;
#endif
              if &#40;&#40;info_func == NULL&#41; || &#40;info_func&#40;&ldriverinfo&#41; != PIKEY_SUCCESS&#41;&#41;
              &#123;
								// XXX should unload the driver here.
#if DEBUG_OUTPUT>=1
                sioPrint&#40;"Would unload plugin here.\r\n"&#41;;
#endif
              &#125;
              else
              &#123;
#if DEBUG_OUTPUT>=1
								// output some info about the driver
                sioPrint&#40;"Successfully loaded driver '"&#41;;
                sioPrint&#40;ldriverinfo.driverName&#41;;
                sioPrint&#40;"' version "&#41;;
                sioPrint&#40;ldriverinfo.driverVersion&#41;;
                sioPrint&#40;"\r\n"&#41;;
#endif
              &#125;
              /***************************************************************/
              /* Save some info in our linked list of driver info.           */
              /***************************************************************/
              if &#40;isInput&#41;
              &#123;
                INPUT_INFO *linfo = &#40;INPUT_INFO*&#41;myMalloc&#40;sizeof&#40;INPUT_INFO&#41;&#41;;
                if &#40;!linfo&#41;
                &#123;
                  sioPrint&#40;"malloc failed\r\n"&#41;;
                &#125;
                else
                &#123;
                  linfo->next = NULL;
                  if &#40;inputs == NULL&#41;
                  &#123;
                    linfo->prev = NULL;
                    inputs = linfo;
                  &#125;
                  else
                  &#123;
                    INPUT_INFO *lptr = inputs;
                    while &#40;lptr->next&#41;
                    &#123;
                      lptr = lptr->next;
                    &#125;
                    linfo->prev = lptr;
                    lptr->next = linfo;
                  &#125;
                  linfo->moduleID = modid;
                  strcpy&#40;linfo->moduleName, ldriverinfo.driverName&#41;;
                &#125;
                numInputs++;
              &#125;
              else
              &#123;
                OUTPUT_INFO *linfo = &#40;OUTPUT_INFO*&#41;myMalloc&#40;sizeof&#40;OUTPUT_INFO&#41;&#41;;
                if &#40;!linfo&#41;
                &#123;
                  sioPrint&#40;"malloc failed\r\n"&#41;;
                &#125;
                else
                &#123;
                  linfo->next = NULL;
                  if &#40;outputs == NULL&#41;
                  &#123;
                    linfo->prev = NULL;
                    outputs = linfo;
                  &#125;
                  else
                  &#123;
                    OUTPUT_INFO *lptr = outputs;
                    while &#40;lptr->next&#41;
                    &#123;
                      lptr = lptr->next;
                    &#125;
                    linfo->prev = lptr;
                    lptr->next = linfo;
                  &#125;

                  linfo->moduleID = modid;
                  strcpy&#40;linfo->moduleName, ldriverinfo.driverName&#41;;
                &#125;
                numOutputs++;
              &#125;
            &#125;
          &#125;
        &#125;
      &#125;
    &#125;
    while &#40;lrc > 0&#41;;
    sceIoDclose&#40;lid&#41;;
  &#125;
&#125;

#if DEBUG_OUTPUT>=1
void DumpMem&#40;void *addr,unsigned size,int num&#41;//DELME
&#123;
  char buf&#91;270&#93;;
  SceUID fd;
  sprintf&#40;buf,"ms0&#58;/%d_%08X_%08X.bin",num,&#40;int&#41;addr,size&#41;;
  fd = sceIoOpen&#40;buf,PSP_O_WRONLY|PSP_O_CREAT|PSP_O_TRUNC,0777&#41;;
  if&#40;fd < 0&#41;
    return;
  sceIoWrite&#40;fd,addr,size&#41;;
  sceIoClose&#40;fd&#41;;
&#125;
#endif
int main_thread&#40;SceSize args, void *argp&#41;
&#123;
  unsigned int disableHotkey = 0;
  unsigned int oskstate = 0;
  displayStatusText&#40;"Starting"&#41;;
	// Read the config file for user-defined control map
  int cfg = configOpen&#40;"ms0&#58;/seplugins/pikey/pikeyconfig.txt"&#41;;
  if &#40;cfg >= 0&#41;
  &#123;
    while &#40;configRead&#40;cfg, cfgtoken, cfgvalue&#41;&#41;
    &#123;
      if &#40;strcmp&#40;cfgtoken, "SIO DEBUG"&#41; == 0&#41;
      &#123;
        sioDebug = &#40;&#40;cfgvalue&#91;0&#93; == 'Y'&#41; || &#40;cfgvalue&#91;0&#93; == 'y'&#41;&#41;;
      &#125;
      else if &#40;strcmp&#40;cfgtoken, "STATUS MESSAGES"&#41; == 0&#41;
      &#123;
        statusMessages = &#40;&#40;cfgvalue&#91;0&#93; == 'Y'&#41; || &#40;cfgvalue&#91;0&#93; == 'y'&#41;&#41;;
      &#125;
      else if &#40;strcmp&#40;cfgtoken, "DISABLED IN 150"&#41; == 0&#41;
      &#123;
        disabledIn150 = &#40;&#40;cfgvalue&#91;0&#93; == 'Y'&#41; || &#40;cfgvalue&#91;0&#93; == 'y'&#41;&#41;;
      &#125;
      else if &#40;strcmp&#40;cfgtoken, "DISABLED PLUGINS"&#41; == 0&#41;
      &#123;
				// split the CSV list, save in disabledPlugins&#91;&#93;
        char *lptr = cfgvalue;
        char *ldestptr = disabledPlugins&#91;0&#93;;
        numDisabledPlugins = 0;
        while &#40;*lptr != '\0'&#41;
        &#123;
          if &#40;*lptr == ' '&#41;
          &#123;
						// skip spaces
          &#125;
          else if &#40;*lptr == ','&#41;
          &#123;
            *ldestptr = '\0';
            if &#40;numDisabledPlugins++ > MAX_DISABLED_PLUGINS&#41;
            &#123;
							// too many disabled plugins
              break;
            &#125;
            else
            &#123;
              ldestptr = disabledPlugins&#91;numDisabledPlugins&#93;;
            &#125;
          &#125;
          else
          &#123;
            *ldestptr++ = *lptr;
          &#125;
          lptr++;
        &#125;
				// if we read any data, then adjust the disabledplugins count
				// &#40;since we didn't increment it at the end of the string, just the
				// last comma&#41;
        if &#40;ldestptr != disabledPlugins&#91;0&#93;&#41;
        &#123;
          numDisabledPlugins ++;
        &#125;
      &#125;
      else if &#40;strcmp&#40;cfgtoken, "DISABLE HOTKEY"&#41; == 0&#41;
      &#123;
        if &#40;&#40;cfgvalue&#91;0&#93; == '0'&#41; &&
             &#40;&#40;cfgvalue&#91;1&#93; == 'x'&#41; ||
             &#40;cfgvalue&#91;1&#93; == 'X'&#41;&#41;&#41;
        &#123;
          unsigned int lhex = 0;
          char *lptr = &&#40;cfgvalue&#91;2&#93;&#41;;
          while &#40;&#40;*lptr != '\0'&#41; && &#40;*lptr != ' '&#41;&#41;
          &#123;
						// This is a hex number, decode it.
            char lhexdigit = &#40;*lptr&#41; - '0';
            if &#40;lhexdigit > 9&#41;
            &#123;
              lhexdigit -= &#40;'A' - '9' - 1&#41;;
            &#125;
            if &#40;lhexdigit > 15&#41;
            &#123;
              lhexdigit -= &#40;'a' - 'A'&#41;;
            &#125;
            if &#40;lhexdigit < 0&#41;
            &#123;
              lhexdigit = 0;
            &#125;
            lhex <<= 4;
            lhex += lhexdigit;
            lptr++;
          &#125;
          disableHotkey = lhex;
        &#125;
      &#125;
    &#125;
    configClose&#40;cfg&#41;;
  &#125;
  displayStatusText&#40;"line 806"&#41;;
	// If we're in v1.50 firmware, and we're disabled for this mode, then exit.
  if &#40;disabledIn150&#41;
  &#123;
    if &#40;sceKernelDevkitVersion&#40;&#41; == 0x01050001L&#41;
    &#123;
      sceKernelExitDeleteThread&#40;0&#41;;
    &#125;
  &#125;
	// If there's a disable hotkey, and it's pressed, then exit.
  if &#40;disableHotkey != 0&#41;
  &#123;
    SceCtrlData lpad;
    sceCtrlPeekBufferPositive&#40;&lpad, 1&#41;;
    if &#40;&#40;lpad.Buttons & disableHotkey&#41; == disableHotkey&#41;
    &#123;
      sceKernelExitDeleteThread&#40;0&#41;;
    &#125;
  &#125;
  if &#40;sioDebug&#41;
  &#123;
    pspDebugSioInit&#40;&#41;;
    sioPrint&#40;"\r\n\r\n"&#41;;
#if DEBUG_OUTPUT>=2
    pspDebugSioInstallKprintf&#40;&#41;;
    pspDebugSioEnableKprintf&#40;&#41;;
#endif
    sioPrint&#40;"  -----------------------\r\n"&#41;;
    sioPrint&#40;"  -- piKey is starting --\r\n"&#41;;
    sioPrint&#40;"  -----------------------\r\n"&#41;;
#if DEBUG_OUTPUT >= 3
    int ii;
    for &#40;ii = 1; ii <= 6; ii++&#41;
    &#123;
      sioPrint&#40;"\r\nFreemem&#58; "&#41;;
      sioPrintHex&#40;ii&#41;;
      sioPrint&#40;" = "&#41;;
      SceSize mem = sceKernelPartitionMaxFreeMemSize&#40;ii&#41;;
      sioPrintWord&#40;mem&#41;;
    &#125;
#endif
  &#125;
  InitVars&#40;&#41;;
  gvshmode = waitForModule&#40;"sceVshBridge_Driver"&#41;;
  if &#40;gvshmode&#41;
  &#123;
    sioPrint&#40;"Detected VSH mode\r\n"&#41;;
		// sleep here to avoid corrupting the startup animation
    sceKernelDelayThread&#40;4 * 1000 * 1000&#41;;
  &#125;
  else
  &#123;
    sioPrint&#40;"Detected GAME mode\r\n"&#41;;
  &#125;
  LoadDrivers&#40;"ms0&#58;/seplugins/pikey/inputdrivers", 1&#41;;
  LoadDrivers&#40;"ms0&#58;/seplugins/pikey/outputdrivers", 0&#41;;
  sioPrint&#40;"--- All plugins loaded ---\r\n"&#41;;
  displayStatusText&#40;"piKey is now ACTIVE"&#41;;
  while &#40;!terminate&#41;
  &#123;
    sceKernelDelayThread&#40;100&#41;;  // Yields in a way that waitvblank does not
		                            // Without this tiny sleep, we won't exit
		                            // cleanly during module tidy-up.
    sceDisplayWaitVblankStart&#40;&#41;;
		// If we have something to display, display it
    if &#40;gStatusText&#41;
    &#123;
      font_init&#40;&#41;;
      if &#40;++gStatusCycles > STATUS_DISPLAY_TIME&#41;
      &#123;
        gStatusCycles = 0;
        gStatusText   = 0;
        memset&#40;centredText, ' ', sizeof&#40;centredText&#41;&#41;;
        blit_string&#40;0, 0, centredText, 1&#41;;
      &#125;
      else
      &#123;
        blit_string&#40;0, 0, centredText, 1&#41;;
      &#125;
    &#125;
		// check for held keys.  We only need to do this for stream mode, with
		// some keys held.
    if &#40;&#40;gKeyMode == PIKEY_KEYMODE_VT100STREAM&#41; &&
         &#40;numPressed > 0&#41;&#41;
    &#123;
      int ii;
      for &#40;ii=0; ii < KEY_MAX; ii++&#41;
      &#123;
        if &#40;gPressMap&#91;ii&#93;&#41;
        &#123;
          if &#40;&#40;gHoldMap&#91;ii&#93;&#41;++ > REPEAT_TIME + REPEAT_PERIOD&#41;
          &#123;
            gHoldMap&#91;ii&#93; = REPEAT_TIME;

            unsigned char str&#91;16&#93;;
            if &#40;mapScanToVT100&#40;ii, ctrlHeld, shiftHeld, altHeld, str&#41;&#41;
            &#123;
              unsigned char *lptr = str;
              while &#40;*lptr&#41;
              &#123;
                putNextChar&#40;*lptr&#41;;
                lptr++;
              &#125;
            &#125;
          &#125;
        &#125;
      &#125;
    &#125;
		// Wait until we are activated with the NUM lock key.
    if &#40;gPressMap&#91;KEY_NUMLOCK&#93;&#41;
    &#123;
			// check if JUST pressed
      if &#40;!&#40;oskstate & 32&#41;&#41;
      &#123;
        oskstate |= 32; // NumLock pressed
        oskstate ^= 4;
        if &#40;oskstate & 4&#41;
        &#123;
          sioPrint&#40;"NUM lock is ON\r\n"&#41;;
          displayStatusText&#40;"NUM lock is ON"&#41;;
        &#125;
        else
        &#123;
          sioPrint&#40;"NUM lock is OFF\r\n"&#41;;
          displayStatusText&#40;"NUM lock is OFF"&#41;;
        &#125;
      &#125;
    &#125;
    else
      oskstate &= ~32; // NumLock not pressed
		// Wait until we are activated with the scroll lock key.
    if &#40;gPressMap&#91;KEY_SCROLLLOCK&#93;&#41;
    &#123;
			// check if JUST pressed
      if &#40;!&#40;oskstate & 16&#41;&#41;
      &#123;
        oskstate |= 16; // ScrollLock pressed
        oskstate ^= 2;
        if &#40;oskstate & 2&#41;
        &#123;
          sioPrint&#40;"SCROLL lock is ON\r\n"&#41;;
          displayStatusText&#40;"SCROLL lock is ON"&#41;;
        &#125;
        else
        &#123;
          sioPrint&#40;"SCROLL lock is OFF\r\n"&#41;;
          displayStatusText&#40;"SCROLL lock is OFF"&#41;;
        &#125;
      &#125;
    &#125;
    else
      oskstate &= ~16; // ScrollLock not pressed
		// Wait until we are activated with the CAPS lock key.
    if &#40;gPressMap&#91;KEY_CAPSLOCK&#93;&#41;
    &#123;
			// check if JUST pressed
      if &#40;!&#40;oskstate & 8&#41;&#41;
      &#123;
        oskstate |= 8; // CapsLock pressed
        oskstate ^= 1;
        if &#40;oskstate & 1&#41;
        &#123;
          sioPrint&#40;"CAPS lock is ON\r\n"&#41;;
          displayStatusText&#40;"CAPS lock is ON"&#41;;
        &#125;
        else
        &#123;
          sioPrint&#40;"CAPS lock is OFF\r\n"&#41;;
          displayStatusText&#40;"CAPS lock is OFF"&#41;;
        &#125;
      &#125;
    &#125;
    else
      oskstate &= ~8; // CapsLock not pressed
		// if we have an ESC charinput, in keypress mode, check whether we
		// need to time it out into a simple ESC.
    if &#40;&#40;gKeyMode == PIKEY_KEYMODE_KEYPRESS&#41; &&
         &#40;gEscTimer != -1&#41;&#41;
    &#123;
      gEscTimer++;
      if &#40;&#40;gEscTimer > ESC_TIMEOUT&#41; &&
           !&#40;gPressMap&#91;KEY_ESC&#93;&#41;&#41;
      &#123;
				// we have a lone escape key.  Press it...
        resetMapkey&#40;&#41;;
        sendKeyPress&#40;KEY_ESC, 1, 0, 0, 0&#41;;
      &#125;
      else if &#40;gEscTimer > &#40;ESC_TIMEOUT +&#40;FAKE_KEYPRESS_HOLD / FRAME_LENGTH_USECS&#41;&#41;&#41;
      &#123;
				// ... and release it after another timeout
        sendKeyPress&#40;KEY_ESC, 0, 0, 0, 0&#41;;
        gEscTimer = -1;
      &#125;
    &#125;
  &#125;
  sioPrint&#40;"--- Exiting piKey ---\r\n"&#41;;
  sceKernelExitDeleteThread&#40;0&#41;;
  return 0;
&#125;

int main&#40;void&#41; &#123;
  int rc = 0;
  /***************************************************************************/
  /* Create main thread.                                                     */
  /***************************************************************************/
  pikeythread = sceKernelCreateThread&#40;"pikey", main_thread, 16, 0x1000, 0, NULL&#41;;
  if &#40;pikeythread >= 0&#41;
  &#123;
    rc = sceKernelStartThread&#40;pikeythread, 0, NULL&#41;;
    if &#40;rc < 0&#41;
    &#123;
      return -2;
    &#125;
  &#125;
  else
  &#123;
    return -1;
  &#125;
  return 0;
&#125;
void throwAwayInput&#40;int numchars&#41;
&#123;
  while &#40;numchars--&#41;
  &#123;
    if &#40;numCharsAvailable&#40;&#41; > 0&#41;  getNextChar&#40;&#41;;
  &#125;
&#125;
void putNextCharW&#40;wchar inChar&#41; &#123;
  if &#40;gKeyMode == PIKEY_KEYMODE_VT100STREAM&#41;
  &#123;
		// block until queue is not full
    int lfull = 1;
    while &#40;lfull&#41; &#123;
      sceKernelWaitSema&#40;queueSema, 1, 0&#41;;
      if &#40;queueSize != QUEUE_SIZE&#41; lfull = 0;
      else &#123;
        sceKernelSignalSema&#40;queueSema, 1&#41;;
        sceKernelDelayThread&#40;5000&#41;;
      &#125;
    &#125;
		// add to Q
    inBuffer&#91;queueInputPos&#93; = inChar;
    queueInputPos = &#40;queueInputPos + 1&#41; % QUEUE_SIZE;
    queueSize ++;
		// release Q sema
    sceKernelSignalSema&#40;queueSema, 1&#41;;
  &#125;
  else
  &#123;
		// for keypress mode, we need to simulate a keypress and release
    int shift, ctrl, alt;
    int lscan = mapVT100toScan&#40;inChar, &ctrl, &alt, &shift&#41;;
#if DEBUG_OUTPUT>=3
    sioPrint&#40;"vt100&#58; "&#41;;
    sioPrintHex&#40;inChar&#41;;
    sioPrint&#40;"=> "&#41;;
    sioPrintWord&#40;lscan&#41;;
    sioPrint&#40;" &#58; "&#41;;
    sioPrintHex&#40;&#40;ctrl << 2&#41; | &#40;alt << 1&#41; | &#40;shift&#41;&#41;;
    sioPrint&#40;"\r\n"&#41;;
#endif
		// special hack to ESC chars - timeout a single ESC after a short time,
		// if it isn't followed by any other chars.
    if &#40;inChar == 0x1b&#41;
    &#123;
      gEscTimer = 0;
    &#125;
    if &#40;lscan != -1&#41;
    &#123;
      gEscTimer = -1;
      sendKeyPress&#40;lscan, 1, shift, ctrl, alt&#41;;
      sceKernelDelayThread&#40;FAKE_KEYPRESS_HOLD&#41;;
      sendKeyPress&#40;lscan, 0, shift, ctrl, alt&#41;;
    &#125;
  &#125;
&#125;
void putNextChar&#40;char inChar&#41; &#123;
	// we do a conversion to wchar then call the putwchar func.
	// XXX &#58; for now assume a very simple conversion
  putNextCharW&#40;&#40;wchar&#41;inChar&#41;;
&#125;
int numCharsAvailable&#40;void&#41; &#123;
  int lreturn = 0;
	// only valid in stream mode
  if &#40;gKeyMode == PIKEY_KEYMODE_VT100STREAM&#41;
  &#123;
    sceKernelWaitSema&#40;queueSema, 1, 0&#41;;
    lreturn = queueSize;
   	// return 0 if there's an exclusive thread and we're not it
    if &#40;gExclusiveThread != 0&#41;
    &#123;
      if &#40;sceKernelGetThreadId&#40;&#41; != gExclusiveThread&#41;
      &#123;
        lreturn = 0;
      &#125;
    &#125;
    sceKernelSignalSema&#40;queueSema, 1&#41;;
  &#125;
  else
  &#123;
    lreturn = PIKEY_ERROR_WRONG_MODE;
  &#125;
  return&#40;lreturn&#41;;
&#125;
/*****************************************************************************/
/* Returns PIKEY_SUCCESS if there are n chars available, copies them to      */
/* buffer                                                                    */
/*****************************************************************************/
int peekNChars&#40;int n, wchar *buffer&#41; &#123;
  int ii;
	// only valid in stream mode
  if &#40;gKeyMode != PIKEY_KEYMODE_VT100STREAM&#41;
  &#123;
    return PIKEY_ERROR_WRONG_MODE;
  &#125;
  sceKernelWaitSema&#40;queueSema, 1, 0&#41;;
  int lqueueSize = queueSize;
	// override queue size if there's an exclusive thread and we're not it
  if &#40;gExclusiveThread != 0&#41;
  &#123;
    if &#40;sceKernelGetThreadId&#40;&#41; != gExclusiveThread&#41;
    &#123;
      lqueueSize = 0;
    &#125;
  &#125;
  if &#40;lqueueSize < n&#41;
  &#123;
    sceKernelSignalSema&#40;queueSema, 1&#41;;
    return PIKEY_ERROR_NOT_ENOUGH_CHARS;
  &#125;
	// copy to buffer
  for &#40;ii = 0; ii < n; ii++&#41;
  &#123;
    buffer&#91;ii&#93; = inBuffer&#91;&#40;queueOutputPos + ii&#41; % QUEUE_SIZE&#93;;
  &#125;
  sceKernelSignalSema&#40;queueSema, 1&#41;;
  return&#40;PIKEY_SUCCESS&#41;;
&#125;
void _flushBuffer&#40;void&#41;
&#123;
  queueInputPos = 0;
  queueOutputPos = 0;
  queueSize = 0;
  resetMapkey&#40;&#41;;
  gEscTimer = -1;
&#125;
void flushBuffer&#40;void&#41; &#123;
  sceKernelWaitSema&#40;queueSema, 1, 0&#41;;
	// do nothing if there's an exclusive thread and we're not it
  if &#40;&#40;gExclusiveThread == 0&#41;	|| &#40;sceKernelGetThreadId&#40;&#41; == gExclusiveThread&#41;&#41;
  &#123;
    _flushBuffer&#40;&#41;;
  &#125;
  sceKernelSignalSema&#40;queueSema, 1&#41;;
&#125;
int requestExclusive&#40;void&#41;
&#123;
  SceUID lthid = sceKernelGetThreadId&#40;&#41;;
  int lreturn;
	// semaphore this using the standard queue semaphore
  sceKernelWaitSema&#40;queueSema, 1, 0&#41;;
  /***************************************************************************/
  /* Return success if we already have exclusivity.                          */
  /***************************************************************************/
  if &#40;gExclusiveThread == lthid&#41;
  &#123;
    lreturn = 1;
  &#125;
  /***************************************************************************/
  /* Return failure if someone else already has exclusivity                  */
  /***************************************************************************/
  else if &#40;gExclusiveThread != 0&#41;
  &#123;
    lreturn = 0;
  &#125;
  else
  &#123;
    gExclusiveThread = lthid;
    gOldKeyMode = gKeyMode;
    lreturn = 1;
  &#125;
  sceKernelSignalSema&#40;queueSema, 1&#41;;
  return lreturn;
&#125;
void releaseExclusive&#40;void&#41;
&#123;
  SceUID lthid = sceKernelGetThreadId&#40;&#41;;
  sceKernelWaitSema&#40;queueSema, 1, 0&#41;;
	// Can only release exclusive if we are the exclusive thread
  if &#40;lthid == gExclusiveThread&#41;
  &#123;
    gExclusiveThread = 0;
    _setMode&#40;gOldKeyMode&#41;;
  &#125;
  sceKernelSignalSema&#40;queueSema, 1&#41;;
&#125;
// mode&#58; 0&#58; keypress  1&#58; stream
int setMode&#40;int mode&#41;
&#123;
  int lreturn = PIKEY_SUCCESS;
	// change mode only if exclusive
  SceUID lthid = sceKernelGetThreadId&#40;&#41;;
  sceKernelWaitSema&#40;queueSema, 1, 0&#41;;
	// Can only change mode if we are the exclusive thread
  if &#40;lthid == gExclusiveThread&#41;
  &#123;
    _setMode&#40;mode&#41;;
  &#125;
  else
  &#123;
    lreturn = PIKEY_ERROR_NOT_EXCLUSIVE;
  &#125;
  sceKernelSignalSema&#40;queueSema, 1&#41;;
  return lreturn;
&#125;
void _setMode&#40;int mode&#41;
&#123;
  gKeyMode = mode;
  _flushKeyPresses&#40;&#41;;
  _flushBuffer&#40;&#41;;
&#125;
int	getMode&#40;void&#41;
&#123;
  return gKeyMode;
&#125;
int isKeyPressed&#40;int keycode&#41;
&#123;
  sceKernelWaitSema&#40;queueSema, 1, 0&#41;;
  int lreturn = gPressMap&#91;keycode&#93;;
	// return 0 if there's an exclusive thread and we're not it
  if &#40;gExclusiveThread != 0&#41;
  &#123;
    if &#40;sceKernelGetThreadId&#40;&#41; != gExclusiveThread&#41;
    &#123;
      lreturn = 0;
    &#125;
  &#125;
  sceKernelSignalSema&#40;queueSema, 1&#41;;
  return lreturn;
&#125;
void getMetaKeys&#40;int *alt, int *ctrl, int *shift&#41;
&#123;
  sceKernelWaitSema&#40;queueSema, 1, 0&#41;;
	// meaningless when not in keypress mode.
  if &#40;gKeyMode == PIKEY_KEYMODE_KEYPRESS&#41;
  &#123;
    *alt = altHeld;
    *ctrl = ctrlHeld;
    *shift = shiftHeld;
  &#125;
  else
  &#123;
    *alt = 0;
    *ctrl = 0;
    *shift = 0;
  &#125;
  sceKernelSignalSema&#40;queueSema, 1&#41;;
&#125;
void _flushKeyPresses&#40;&#41;
&#123;
  memset&#40;gPressMap, 0, sizeof&#40;gPressMap&#41;&#41;;
  memset&#40;gHoldMap, 0, sizeof&#40;gHoldMap&#41;&#41;;
  numPressed = 0;
  gEscTimer = -1;
  resetMapkey&#40;&#41;;
&#125;
void flushKeyPresses&#40;&#41;
&#123;
  sceKernelWaitSema&#40;queueSema, 1, 0&#41;;
	// don't allow flush if there's an exclusive thread and we're not it
  if &#40;&#40;gExclusiveThread == 0&#41; ||
       &#40;sceKernelGetThreadId&#40;&#41; == gExclusiveThread&#41;&#41;
  &#123;
    _flushKeyPresses&#40;&#41;;
  &#125;
  sceKernelSignalSema&#40;queueSema, 1&#41;;
&#125;
int numKeysPressed&#40;&#41;
&#123;
  int lreturn = 0;
  sceKernelWaitSema&#40;queueSema, 1, 0&#41;;
	// don't allow read if there's an exclusive thread and we're not it
  if &#40;&#40;gExclusiveThread == 0&#41; ||
       &#40;sceKernelGetThreadId&#40;&#41; == gExclusiveThread&#41;&#41;
  &#123;
    lreturn = numPressed;
  &#125;
  sceKernelSignalSema&#40;queueSema, 1&#41;;
  return lreturn;
&#125;
void sendKeyPress&#40;int keycode, int pressed, int shift, int ctrl, int alt&#41;
&#123;
  if &#40;pressed&#41;
  &#123;
    if &#40;!gPressMap&#91;keycode&#93;&#41;
    &#123;
			// only if wasn't already pressed &#40;repeat issue&#41;
      gHoldMap&#91;keycode&#93; = 0;
      gPressMap&#91;keycode&#93; = 1;
      numPressed ++;
    &#125;
#if DEBUG_OUTPUT >= 3
    sioPrint&#40;"Keypress&#58; "&#41;;
    sioPrintWord&#40;keycode&#41;;
    sioPrint&#40;" "&#41;;
    sioPrintWord&#40;&#40;shift << 2&#41; | &#40;ctrl << 1&#41; | alt&#41;;
    sioPrint&#40;"\r\n"&#41;;
#endif
    if &#40;gKeyMode != PIKEY_KEYMODE_KEYPRESS&#41;
    &#123;
			// We're in stream mode, so we need to be a bit fancier.
			// If this is a press event, we just add the relevant char to the
			// buffer.
			// XXX &#58; this needs a mapping to allow for capslock etc.
      unsigned char str&#91;16&#93;;
      if &#40;mapScanToVT100&#40;keycode, ctrlHeld, shiftHeld, altHeld, str&#41;&#41;
      &#123;
        unsigned char *lptr = str;
        while &#40;*lptr&#41;
        &#123;
          putNextChar&#40;*lptr&#41;;
          lptr++;
        &#125;
      &#125;
    &#125;
    shiftHeld = shift;
    ctrlHeld  = ctrl;
    altHeld   = alt;
  &#125;
  else
  &#123;
		// currently libpspirkeyb seems bugged for keycodes on at least the
		// Targus.  It sends a huge stream of released presses, so we need to
		// filter for those keys that are actually pressed.
    if &#40;gPressMap&#91;keycode&#93;&#41;
    &#123;
#if DEBUG_OUTPUT >= 3
      sioPrint&#40;"Keyrelease&#58; "&#41;;
      sioPrintWord&#40;keycode&#41;;
      sioPrint&#40;" "&#41;;
      sioPrintWord&#40;&#40;shift << 2&#41; | &#40;ctrl << 1&#41; | alt&#41;;
      sioPrint&#40;"\r\n"&#41;;
#endif
      gPressMap&#91;keycode&#93; = 0;
      numPressed --;
      shiftHeld = shift;
      ctrlHeld  = ctrl;
      altHeld   = alt;
    &#125;
  &#125;

&#125;
wchar getNextCharW&#40;void&#41; &#123;
  // block until queue is not empty
  int lempty = 1;
  SceUID lthid = sceKernelGetThreadId&#40;&#41;;
  while &#40;lempty&#41; &#123;
    sceKernelWaitSema&#40;queueSema, 1, 0&#41;;
		// pretend empty if there's an exclusive thread and we are not it
    if &#40;&#40;gExclusiveThread == 0&#41; || &#40;lthid == gExclusiveThread&#41;&#41;
    &#123;
      if &#40;queueSize != 0&#41;
      &#123;
        lempty = 0;
      &#125;
    &#125;
    if &#40;lempty&#41;
    &#123;
      sceKernelSignalSema&#40;queueSema, 1&#41;;
      sceKernelDelayThread&#40;5000&#41;;
    &#125;
  &#125;
  wchar lchar = inBuffer&#91;queueOutputPos&#93;;
  queueOutputPos = &#40;queueOutputPos + 1&#41; % QUEUE_SIZE;
  queueSize --;
  sceKernelSignalSema&#40;queueSema, 1&#41;;
  return&#40;lchar&#41;;
&#125;
char getNextChar&#40;void&#41; &#123;
	// we call getNextCharW, then convert to 8bit
  wchar lchar = getNextCharW&#40;&#41;;
	// XXX&#58; naive conversion func.
  return &#40;&#40;char&#41; lchar&#41;;
&#125;
int registerDisplayCallback&#40;SceUID hookid&#41;
&#123;
  int lreturn = 0;
  sceKernelWaitSema&#40;callbackSema, 1, 0&#41;;
  if &#40;gNumDisplayCallbacks < MAX_DISPLAY_CALLBACKS&#41;
  &#123;
    gDisplayCallbacks&#91;gNumDisplayCallbacks++&#93; = hookid;
    lreturn = 1;
#if DEBUG_INFO>=2
    sioPrint&#40;"Registered display callback #"&#41;;
    sioPrintHex&#40;gNumDisplayCallbackss&#41;;
    sioPrint&#40;" - ID "&#41;;
    sioPrintWord&#40;hookid&#41;;
    sioPrint&#40;"\r\n"&#41;;
#endif
  &#125;
  sceKernelSignalSema&#40;callbackSema, 1&#41;;
  return lreturn;
&#125;
void drawKeyboardInfo&#40;DRAWINFO *argument&#41;
&#123;
  int ii;
  sceKernelWaitSema&#40;callbackSema, 1, 0&#41;;
  for &#40;ii = 0; ii < gNumDisplayCallbacks; ii++&#41;
  &#123;
    sceKernelNotifyCallback&#40;gDisplayCallbacks&#91;ii&#93;, &#40;int&#41;argument&#41;;
  &#125;
  sceKernelSignalSema&#40;callbackSema, 1&#41;;
&#125;
int isVshMode&#40;&#41;
&#123;
  return gvshmode;
&#125;
int configOpen&#40;const char *filename&#41;
&#123;
  int lreturn;
  sceKernelWaitSema&#40;queueSema, 1, 0&#41;;
#if DEBUG_OUTPUT>=3
  sioPrint&#40;"configOpen&#58; "&#41;;
  sioPrint&#40;filename&#41;;
  sioPrint&#40;"\r\n"&#41;;
#endif
  if &#40;numConfigs == MAX_CONFIGS&#41;
  &#123;
    sceKernelSignalSema&#40;queueSema, 1&#41;;
    return PIKEY_ERROR_TOO_MANY_CONFIGS;
  &#125;
  int fh = sceIoOpen&#40;filename, PSP_O_RDONLY, 0777&#41;;
  if &#40;fh < 0&#41;
  &#123;
    sceKernelSignalSema&#40;queueSema, 1&#41;;
    return fh;
  &#125;
  int len = sceIoLseek32&#40;fh, 0, SEEK_END&#41;;
  int rc = sceIoLseek32&#40;fh, 0, SEEK_SET&#41;;
  if &#40;rc < 0&#41;
  &#123;
    sioPrint&#40;"Error on file seek&#58; "&#41;;
    sioPrintWord&#40;rc&#41;;
    sioPrint&#40;"\r\n"&#41;;
    sceKernelSignalSema&#40;queueSema, 1&#41;;
    return rc;
  &#125;
	// allocate mem to hold the file
  configData&#91;numConfigs&#93; = myMalloc&#40;len + 1&#41;;
  if &#40;configData&#91;numConfigs&#93; == NULL&#41;
  &#123;
    sceKernelSignalSema&#40;queueSema, 1&#41;;
    return PIKEY_ERROR_NO_MEM;
  &#125;
	// set progress pointer to the start of the file data
  configOffset&#91;numConfigs&#93; = configData&#91;numConfigs&#93;;
#if DEBUG_OUTPUT>=3
  sioPrint&#40;"Read the file\r\n"&#41;;
#endif
	// read the file into mem, null-terminate it.
  rc = sceIoRead&#40;fh, configData&#91;numConfigs&#93;, len&#41;;
  if &#40;rc < len&#41;
  &#123;
    sceKernelSignalSema&#40;queueSema, 1&#41;;
    return PIKEY_ERROR_MISC;
  &#125;
  configData&#91;numConfigs&#93;&#91;len&#93; = '\0';
#if DEBUG_OUTPUT>=3
  sioPrint&#40;"Close the file\r\n"&#41;;
#endif
  sceIoClose&#40;fh&#41;;
  lreturn = numConfigs++;
  sceKernelSignalSema&#40;queueSema, 1&#41;;
  return lreturn;
&#125;
int configRead&#40;int cfgHandle, char *token, char *value&#41;
&#123;
  int lreturn = 0;
  sceKernelWaitSema&#40;queueSema, 1, 0&#41;;
	// valid handle?
  if &#40;&#40;cfgHandle >= numConfigs&#41; ||
       &#40;cfgHandle < 0&#41; ||
       &#40;configData&#91;cfgHandle&#93; == NULL&#41; ||
       &#40;configOffset&#91;cfgHandle&#93; == NULL&#41;&#41;
  &#123;
    sioPrint&#40;"configRead&#58; Invalid config handle\r\n"&#41;;
    lreturn = 0;
  &#125;
  else
  &#123;
		// We start from configOffset, and seek to an EOL or EOF
    int   ldone = 0;
    char *lendptr;
    char *lequalptr;
    while &#40;!ldone&#41;
    &#123;
      lendptr = configOffset&#91;cfgHandle&#93;;
      lequalptr = NULL;
      while &#40;&#40;*lendptr&#41; && &#40;*lendptr != '\n'&#41;&#41;
      &#123;
        if &#40;*lendptr == '='&#41;
        &#123;
          lequalptr = lendptr;
        &#125;
        lendptr++;
      &#125;
      if &#40;&#40;configOffset&#91;cfgHandle&#93;&#91;0&#93; == '#'&#41; &&
           &#40;*lendptr != '\0'&#41;&#41;
      &#123;
				// this is a comment, and we're not at EOF.  Read the next line.
        configOffset&#91;cfgHandle&#93; = lendptr + 1;
      &#125;
      else
      &#123;
        ldone = 1;
      &#125;
    &#125;
    char *linestart = configOffset&#91;cfgHandle&#93;;
		// found an equals sign that's not in the first column?
    if &#40;lequalptr == NULL&#41;
    &#123;
      lreturn = 0;
    &#125;
    else
    &#123;
			// scan back through whitespace from the equals
      char *ltokenend = lequalptr - 1;
      while &#40;&#40;ltokenend > linestart&#41; &&
              &#40;&#40;*ltokenend == ' '&#41; ||
              &#40;*ltokenend == '\t'&#41;&#41;&#41;
      &#123;
        ltokenend--;
      &#125;
			// token length too small or too big?
      int toklen = ltokenend - linestart + 1;
      if &#40;&#40;toklen == 0&#41; || &#40;toklen > 63&#41;&#41;
      &#123;
        lreturn = 0;
      &#125;
      else
      &#123;
        strncpy&#40;token, linestart, toklen&#41;;
        token&#91;toklen&#93; = '\0';
				// ... and now the value.  Scan forward from the equals, through
				// any whitespace.
        char *lvalstart = lequalptr + 1;
        while &#40;&#40;lvalstart < lendptr&#41; &&
                &#40;&#40;*lvalstart == ' '&#41; ||
                &#40;*lvalstart == '\t'&#41;&#41;&#41;
        &#123;
          lvalstart++;
        &#125;
        int lvallen = lendptr - lvalstart;
				// value too large?
        if &#40;lvallen > 127&#41;
        &#123;
          lreturn = 0;
        &#125;
				// we allow an empty value, but it is a special case &#40;might be -ve length&#41;
        else if &#40;lvallen < 0&#41;
        &#123;
          value&#91;0&#93; = '\0';
          lreturn = 1;
        &#125;
        else
        &#123;
          strncpy&#40;value, lvalstart, lvallen&#41;;
          value&#91;lvallen&#93; = '\0';
					// tweak for CRLF files - remove the CR if present.
          if &#40;&#40;lvallen >= 1&#41; &&
               &#40;value&#91;lvallen-1&#93; == '\r'&#41;&#41;
          &#123;
            value&#91;lvallen-1&#93; = '\0';
          &#125;
					// all done, and we got a value of some sort.  SUCCESS!
          lreturn = 1;
        &#125;
      &#125;
    &#125;
		// advance the config offset appropriately, ready for the next call.
    if &#40;*lendptr == '\0'&#41;
    &#123;
			// EOF
      configOffset&#91;cfgHandle&#93; = NULL;
    &#125;
    else
    &#123;
			// EOL
      configOffset&#91;cfgHandle&#93; = lendptr + 1;
    &#125;
  &#125;
  sceKernelSignalSema&#40;queueSema, 1&#41;;
  return lreturn;
&#125;
void configClose&#40;int cfgHandle&#41;
&#123;
  sceKernelWaitSema&#40;queueSema, 1, 0&#41;;
  if &#40;&#40;cfgHandle >= numConfigs&#41; ||
       &#40;cfgHandle < 0&#41; ||
       &#40;configData&#91;cfgHandle&#93; == NULL&#41;&#41;
  &#123;
    sioPrint&#40;"Attempt to close bad config handle&#58; "&#41;;
    sioPrintWord&#40;cfgHandle&#41;;
    sioPrint&#40;"\r\n"&#41;;
    sceKernelSignalSema&#40;queueSema, 1&#41;;
    return;
  &#125;
#if DEBUG_OUTPUT>=3
  sioPrint&#40;"Valid handle\r\n"&#41;;
#endif
	// We don't do anything about freeing the config slot here,
	// since the cost of one slot is just 8 bytes, and we expect that MAX_CONFIGS
	// is plenty for all the config files we will be expected to process during
	// the lifetime of the PRX invocation.
	// free the mem
  myFree&#40;configData&#91;cfgHandle&#93;&#41;;
  configData&#91;cfgHandle&#93; = NULL;
  configOffset&#91;cfgHandle&#93; = NULL;
  sceKernelSignalSema&#40;queueSema, 1&#41;;
&#125;
int module_start&#40;SceSize args, void *argp&#41; __attribute__&#40;&#40;alias&#40;"_start"&#41;&#41;&#41;;
int _start&#40;SceSize args, void *argp&#41;
&#123;
  main&#40;&#41;;
  return 0;
&#125;
/*****************************************************************************/
/* Shut everything down - kill the main thread, unload the modules.          */
/*****************************************************************************/
void terminateAll&#40;&#41;
&#123;
  terminate = 1;
	// this seems to fail to unload most of the time, or just crashes in
	// GAME150 mode - probably not worth running it, for the time being.
#if 0
  if &#40;inputs != NULL&#41;
  &#123;
  INPUT_INFO *lptr = inputs;
  while &#40;lptr&#41;
  &#123;
  int status, rc;
#if DEBUG_OUTPUT>=2
  sioPrint&#40;"Shutdown module &#40;"&#41;;
               sioPrintWord&#40;lptr->moduleID&#41;;
               sioPrint&#40;"&#41;..."&#41;;
#endif
               rc = sceKernelStopModule&#40;lptr->moduleID, 0, NULL, &status, NULL&#41;;
               if &#40;rc < 0&#41;
           &#123;
#if DEBUG_OUTPUT>=2
               sioPrint&#40;"Failed to stop module&#58; "&#41;;
               sioPrint&#40;lptr->moduleName&#41;;
               sioPrint&#40;"&#58; "&#41;;
               sioPrintWord&#40;rc&#41;;
#endif
&#125;
               else
           &#123;
               rc = sceKernelUnloadModule&#40;lptr->moduleID&#41;;
               if &#40;rc < 0&#41;
           &#123;
#if DEBUG_OUTPUT>=2
               sioPrint&#40;"Failed to unload module&#58; "&#41;;
               sioPrint&#40;lptr->moduleName&#41;;
               sioPrint&#40;"&#58; "&#41;;
               sioPrintWord&#40;rc&#41;;
#endif
&#125;
&#125;
               sioPrint&#40;"\r\n"&#41;;
               lptr = lptr->next;
&#125;
&#125;

               if &#40;outputs != NULL&#41;
           &#123;
               OUTPUT_INFO *lptr = outputs;
               while &#40;lptr&#41;
           &#123;
               int status, rc;
#if DEBUG_OUTPUT>=2
               sioPrint&#40;"Shutdown module &#40;"&#41;;
               sioPrintWord&#40;lptr->moduleID&#41;;
               sioPrint&#40;"&#41;..."&#41;;
#endif
               rc = sceKernelStopModule&#40;lptr->moduleID, 0, NULL, &status, NULL&#41;;
               if &#40;rc < 0&#41;
           &#123;
#if DEBUG_OUTPUT>=2
               sioPrint&#40;"Failed to stop module&#58; "&#41;;
               sioPrint&#40;lptr->moduleName&#41;;
               sioPrint&#40;"&#58; "&#41;;
               sioPrintWord&#40;rc&#41;;
#endif
&#125;
               else
           &#123;
               rc = sceKernelUnloadModule&#40;lptr->moduleID&#41;;
               if &#40;rc < 0&#41;
           &#123;
#if DEBUG_OUTPUT>=2
               sioPrint&#40;"Failed to unload module&#58; "&#41;;
               sioPrint&#40;lptr->moduleName&#41;;
               sioPrint&#40;"&#58; "&#41;;
               sioPrintWord&#40;rc&#41;;
#endif
&#125;
&#125;
               sioPrint&#40;"\r\n"&#41;;

               lptr = lptr->next;
&#125;
&#125;
#endif
               sceKernelWaitThreadEnd&#40;pikeythread, 0&#41;;
&#125;
/*****************************************************************************/
/* This is apparently never called when running as a VSH OE plugin.          */
/*****************************************************************************/
int module_stop&#40;SceSize args, void *argp&#41;
&#123;
#if DEBUG_OUTPUT>=2
  sioPrint&#40;"---SHUTDOWN---\r\n"&#41;;
#endif
  terminateAll&#40;&#41;;
  return 0;
&#125;
int module_bootstart&#40;&#41;
&#123;
#if DEBUG_OUTPUT>=2
  sioPrint&#40;"---bootstart---\r\n"&#41;;
#endif
  return 0;
&#125;
/*****************************************************************************/
/* This gets called immediately before the transition from VSH to GAME mode  */
/* &#40;and presumably vice-versa&#41;.                                              */
/*                                                                           */
/* We don't do anything with this call in piKey.                             */
/*****************************************************************************/
int module_reboot_before&#40;&#41;
&#123;
#if DEBUG_OUTPUT>=2
  sioPrint&#40;"---boot before---\r\n"&#41;;
#endif
  return 0;
&#125;
/*****************************************************************************/
/* Called 3 times during reboot to GAME mode. arg1 is 3, 2, 1 in the 3       */
/* calls, respectively.                                                      */
/*****************************************************************************/
int module_reboot_phase&#40;int arg1&#41;
&#123;
#if DEBUG_OUTPUT>=2
  sioPrint&#40;"---boot phase--- "&#41;;
  sioPrintHex&#40;arg1&#41;;
  sioPrint&#40;"\r\n"&#41;;
#endif
  if &#40;arg1 == 3&#41;  // first reboot phase
  &#123;
    terminateAll&#40;&#41;;
  &#125;
  return 0;
&#125;
#if 0
Lessons from this crash&#58;
  - if a vsh kernel thread does not properly yield &#40;must be delaythread,
		waitvblank does not count&#41; then it will not be killed during the reboot
		to game mode.
  - you can detect the transition via the little-known module_reboot_* funcs
		and therefore end a thread loop that doesn't yield.
#endif
User avatar
jean
Posts: 489
Joined: Sat Jan 05, 2008 2:44 am

Post by jean »

It's not "sleep"....it's a freeze, and it's a pikey well known problem. As I PMed you, if you can live without SIO output, just turn off every debug flag you can find.
PS: not a good idea to post that huge code...people gets frightened and doesn't answer! ;)
angelo
Posts: 168
Joined: Wed Aug 29, 2007 9:34 pm

OK

Post by angelo »

OK,

I just posted it so people have a better idea!

Since piKey is made up of about 9 PRX's, do I have to remove dubugging flags on all six?
User avatar
jean
Posts: 489
Joined: Sat Jan 05, 2008 2:44 am

Post by jean »

If you take a look to the code you'll figure out by yourself: in "main" pikey plugin the SIO debug functions are defined as proper functions or empty functions depending on the debug flag, so if it's disabled, other modules can safely call it and nothing will happen. i.e you can disable debug only in pikey.c, but it's more elegant not to leave code around calling empty functions....
angelo
Posts: 168
Joined: Wed Aug 29, 2007 9:34 pm

OK

Post by angelo »

Oh I see!

Thanks!
User avatar
jean
Posts: 489
Joined: Sat Jan 05, 2008 2:44 am

Post by jean »

just to save you the time to face the other difficulties like k1 stuff etc, here's my version of pikey working on 3.xx (so on slim&lite, too...)
[[link removed]]

jean
Last edited by jean on Thu Jul 17, 2008 6:15 pm, edited 1 time in total.
angelo
Posts: 168
Joined: Wed Aug 29, 2007 9:34 pm

Burst bubble

Post by angelo »

Sorry to pop your buble jean, but it fails in 3.XX mode. :(

Works fine in VSH and Game150 mode too.

What's the change log? Maybe you and I can swap bit of each others sources so we can improve it!

I've noticed you're using an out of date PSPIRKeyB as this one does repeat it's keys when keys are held down.

Is SIO fixed and that all?

It'll be great to have you on our team as neither of us have a SIO keyboard!
User avatar
jean
Posts: 489
Joined: Sat Jan 05, 2008 2:44 am

Post by jean »

SIO is still working in read only mode (when i'll have time i'll try to fix this..promised :) ) and to be honest i only tested VSH mode (that's what i most care about, with my openKeyboard project). This means prx is functional (no changes in usermode NIDs for ctrl hooks)....if you're trying "3.xx game mode" with the same old included sample, then nobody will be surprised it's not working: all measures needed to port old samples to new 3.xx fw are to be taken into account. So, if you code something of your own following the guidelines stated in a bunch of posts here around, it should work (i cannot swear upon old homebrews using old pikey...that's a "client side" issue). I made a quick snapshot of the project as it is on my harddrive, and -to be honest- i'm not interested in a repack with installer and examples for noobs/users at the moment (still work-in-progress...).

PS: see
http://forums.ps2dev.org/viewtopic.php? ... =pikey+mod
for a list of changes...
angelo
Posts: 168
Joined: Wed Aug 29, 2007 9:34 pm

Oh OK

Post by angelo »

Ok cool.

So only the SIO PRX has been altered. That's cool.

As for all the "Noobz extras", all of that I'm dealing with. Docs, keymaps, tutorials...

It's nice to see that SIO is up and running. What I'll do it this. I'll keep working on piKey for IR and clear up the bugs on that and when SIO is done in the OSK, drop me a PM and I will add the fixed SIO driver to the source and compile it all.

piKey so far is deffinantly usable but not 100% perfect.

Great to see somebody who has work on this!

Angelo
User avatar
jean
Posts: 489
Joined: Sat Jan 05, 2008 2:44 am

Re: Oh OK

Post by jean »

angelo wrote: So only the SIO PRX has been altered.
....nope.... read carefully the post i linked (or alternatively the whole sourcecode :p).
angelo
Posts: 168
Joined: Wed Aug 29, 2007 9:34 pm

Can't see

Post by angelo »

I cannot see what else has been altered that isn't SIO releated...

It's probably because I'm tired or I'm going BLIND!

Also, I think porting the Sample to 3.XX cannot be done yet.

IRDA fails in 3.XX. How are IRDA users (most of piKey users) going to overcome this?
Post Reply