Windows CE

Sunday, May 10, 2009

Multiple Key detection + PXA27x + Wince 6.0

Recently i had a chance to look into a keypad problem.We are using a development board having PXA270 processor.For a single key press we were getting the proper scan code, but when multiple key were pressed, we were still getting the 1st pressed key scan code.Second Key was getting ignored.

So,I checked the xllp code for keypad.

\WINCE600\PLATFORM\COMMON\SRC\SOC\PXA27X_MS_V1\XLLP\SOURCE\xllp_keypad.c

There i found that in function XllpKeyPadConfigure , IGNORE_MULTIPLE_KEY_PRESS bit was set in keypad control register. I cleared that bit and checked the scan code.Now i was able to get some scan code when the second key was pressed,without lifting the 1st key.It means Multiple key press was not ignored now.

But still the second key scan code was not proper.The scan code we were getting was not what we were expecting.So i checked function ReadScanCodeAutomatically.

There i found a comment for the part of the code when numOfKeysPressed > 1

// these keys are the "minor keys", the ones that needs top right and bottom left of the
// cooresponding 4 keys surrounding them to trigger the correct key. Doing a binary search
// there are 5 keys, the middle key reads 0x8, the first key reads 0x2 and the last reads 0x20.
// this needs to be done for each row.  This will be encorporated into a routine for the next
// upgrade of keypad.

I couldn't understand this what does this mean.But one thing was sure that the logic for determining the scan code for multikey press was not according to the PXA27x.

I rewrote the function as follows :

   1: XLLP_BOOL_T ReadScanCodeAutomatically(XLLP_KEYPAD_REGS *v_pKeyPadRegs,XLLP_UINT8_T *key)
   2: {
   3:    
   4:         /**
   5:             Check for Single Key press
   6:         **/
   7: if(numOfKeysPressed > 1)
   8:         {
   9:             c0 = v_pKeyPadRegs->kpAutoScanMultiKeyPress0 & 0xFF;
  10:             c1 = ((v_pKeyPadRegs->kpAutoScanMultiKeyPress0 >> 16) & 0xFF);
  11:             c2 = v_pKeyPadRegs->kpAutoScanMultiKeyPress1 & 0xFF;
  12:             c3 = ((v_pKeyPadRegs->kpAutoScanMultiKeyPress1 >> 16) & 0xFF);
  13:             c4 = v_pKeyPadRegs->kpAutoScanMultiKeyPress2 & 0xFF;
  14:             c5 = ((v_pKeyPadRegs->kpAutoScanMultiKeyPress2 >> 16) & 0xFF);
  15:             c6 = v_pKeyPadRegs->kpAutoScanMultiKeyPress3 & 0xFF;
  16:             c7 = ((v_pKeyPadRegs->kpAutoScanMultiKeyPress3 >> 16) & 0xFF);
  17:  
  18:             
  19:         if(c0!=0)
  20:         {
  21:             ReadMultipleKeyScanCode(c0,0,RowBit,Col);
  22:         }
  23:         if(c1!=0)
  24:         {
  25:             ReadMultipleKeyScanCode(c1,1,RowBit,Col);
  26:         }
  27:         if(c2!=0)
  28:         {
  29:             ReadMultipleKeyScanCode(c2,2,RowBit,Col);
  30:         }
  31:         if(c3!=0)
  32:         {
  33:             ReadMultipleKeyScanCode(c3,3,RowBit,Col);
  34:         }
  35:         if(c4!=0)
  36:         {
  37:             ReadMultipleKeyScanCode(c4,4,RowBit,Col);
  38:         }
  39:         if(c5!=0)
  40:         {
  41:             ReadMultipleKeyScanCode(c5,5,RowBit,Col);
  42:         }
  43:         if(c6!=0)
  44:         {
  45:             ReadMultipleKeyScanCode(c6,6,RowBit,Col);
  46:         }
  47:         if(c7!=0)
  48:         {
  49:             ReadMultipleKeyScanCode(c7,7,RowBit,Col);
  50:         }
  51:  
  52:         key1 = (unsigned char) (( RowBit[0] <<>
  53:         key2 = (unsigned char) (( RowBit[1] <<>
  54:        
  55:         if(key1 == PrevKey)
  56:         {
  57:             *key = key2 ;
  58:         }
  59:         else
  60:             *key = key1 ;
  61:  
  62:     }
  63:         else
  64:             *key = NO_KEY;
  65:  
  66:       retval = XLLP_TRUE;
  67:     }
  68:     NKDbgPrintfW(L"ReadScanCodeAutomatically<\r\n");
  69:     PrevKey = *key ;
  70:     return(retval);
  71: }
  72:  
  73: //---------------------------------------------------------------------------------------------------------------
  74: // Function: ReadMultipleKeyScanCode
  75: // Purpose:  This functions reads the Multiple key pressed scan code from the KeyPad controller triggered by setting of the ASACT bit
  76: //           in the KeyPad Control register. If there is a valid key detected then it returns the scan code.
  77: // Returns:  success/failure.
  78: //---------------------------------------------------------------------------------------------------------------
  79: XLLP_BOOL_T ReadMultipleKeyScanCode(XLLP_UINT32_T KPASMKPRegister,XLLP_UINT32_T col, XLLP_UINT32_T RowBit[] ,XLLP_UINT32_T Col[])
  80: {
  81:     int i = 0;
  82:     while (i <>
  83:     {
  84:         if((KPASMKPRegister & 0x01) == 0x01 )
  85:         {
  86:             if(RowBit[0] == 0)
  87:             {
  88:                 RowBit[0] = i ;
  89:                 Col[0]  = col ;
  90:             }
  91:             else
  92:             {
  93:                 RowBit[1] = i ;
  94:                 Col[1]  = col ;
  95:             }
  96:             NKDbgPrintfW(L"XLLP_NEW:ReadMultipleKeyScanCode bit no  %d>\r\n",i);
  97:         }
  98:  
  99:         KPASMKPRegister = KPASMKPRegister >> 1 ;
 100:         i++ ;
 101:  
 102:     }
 103:      return(XLLP_TRUE);
 104: }
 105:  

And it worked fine. I was getting the second key scan code properly.

Note : There is a limitation in above code.It can only read two key press properly.More than 2 keys pressed simultaneously cant be recognized, for example Ctrl+Alt+Del   ;) But this is not required for our project..

Sunday, April 19, 2009

Use of SHBrowseForFolder() in wince 6.0


Hi.. A another less documented API is MSDN is use of SHBrowseForFolder() in wince 6.0
Several people have trouble getting to use SHBrowseForFolder().
Here is the code snippet which will create a Folder selection dialog in Wince 6.0
   1:     TCHAR psz_result[MAX_PATH];
2: LPMALLOC p_malloc = 0;
   3:     LPITEMIDLIST pidl;
4: BROWSEINFO bi;
   5:  
6: #ifdef UNDER_CE
   7:     #   define SHGetMalloc MySHGetMalloc
   8:     #   define SHBrowseForFolder MySHBrowseForFolder
   9:     #   define SHGetPathFromIDList MySHGetPathFromIDList
  10:  
  11:  
12: HMODULE ceshell_dll = LoadLibrary( _T("ceshell") );
  13:         if( !ceshell_dll ) return;
  14:  
  15:         HRESULT (WINAPI *SHGetMalloc)(LPMALLOC *) =
  16:             (HRESULT (WINAPI *)(LPMALLOC *))
  17:             GetProcAddress( ceshell_dll, _T("SHGetMalloc") );
  18:         LPITEMIDLIST (WINAPI *SHBrowseForFolder)(LPBROWSEINFO) =
  19:             (LPITEMIDLIST (WINAPI *)(LPBROWSEINFO))
  20:             GetProcAddress( ceshell_dll, _T("SHBrowseForFolder") );
  21:         BOOL (WINAPI *SHGetPathFromIDList)(LPCITEMIDLIST, LPTSTR) =
  22:             (BOOL (WINAPI *)(LPCITEMIDLIST, LPTSTR))
  23:             GetProcAddress( ceshell_dll, _T("SHGetPathFromIDList") );
  24:  
  25:         if( !SHGetMalloc || !SHBrowseForFolder || !SHGetPathFromIDList )
  26:         {
  27:             _tprintf( _T( "couldn't load SHBrowseForFolder API") );
  28:             FreeLibrary( ceshell_dll );
  29:             return;
  30:         }
  31:     #endif
  32:  
  33:         if( !SUCCEEDED( SHGetMalloc(&p_malloc) ) ) goto error;
  34:  
  35:         memset( &bi, 0, sizeof(BROWSEINFO) );
36: bi.hwndOwner = NULL;
  37:         bi.lpszTitle = L"Select Folder to backup.";
  38:         bi.pszDisplayName = psz_result;
  39:         bi.ulFlags = BIF_EDITBOX;
  40:     #ifndef UNDER_CE
  41:         bi.ulFlags |= BIF_USENEWUI;
42: #endif
  43:  
  44:         if( (pidl = SHBrowseForFolder( &bi ) ) )
  45:             {
  46:                 SHGetPathFromIDList( pidl, psz_result );
  47:                 
48: p_malloc->Free( pidl );
  49:             }
  50:         _tprintf( _T( "%s"),psz_result );
  51:          error:
  52:  
  53:  
  54:  
  55:  
56: #ifdef UNDER_CE
  57:             FreeLibrary( ceshell_dll );
  58:         #endif

Saturday, April 11, 2009

Minimize / Hide Applications on startup

Hi Guys..
 In my last post I discussed about how to Add an MFC application into tray Icon. The sample application can be Minimized to tray icon, without even shown in task bar when HIDE menu Item is clicked. And again can be relaunched when SHOW menu in tray icon is clicked.
 But one problem is there. We can not start the Sample MFC application as a tray icon from the  startup.Even a call to  ShowWindow(SW_HIDE) from the  OnInitDialog() function, will not work.
Well here is a trick of doing this, by modifying the initinstance() function of the App class.
The Normal code for creating the dialog in InitInstance() looks like

CDialogBasedDlg dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();


Edit this code as follows :

CDialogBasedDlg dlg;
if(dlg.Create( CDialogBasedDlg::IDD ))
 {
dlg.ShowWindow( SW_HIDE );
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.RunModalLoop();
}
And you are done.. Integrating it with previous sample application will result in a Dialog based application starting as a trayicon, Always Minimized and will not appear in System tray. ;))

Adding System Tray Icon in Wince

Adding Tray Icon in status area, Using Visual C++ (MFC), is not well documenetd in MSDN.
After many attempts, i have been able to add an ICON to the status area.
Lets see the steps required for this in a MFC Visual C++ Application.

Shell_NotifyIcon -- This function sends a message to the system to add, modify, or delete an icon from the taskbar status area.

WINSHELLAPI BOOL WINAPI Shell_NotifyIcon(
  DWORD dwMessage, 
  PNOTIFYICONDATA pnid 
);

The first parameter to Shell_NotifyIcon is either of NIM_ADD, NIM_DELETE, or NIM_MODIFY. The second parameter will be a pointer to NOTIFYICONDATA variable.
The NOTIFYICONDATA structure variable contains all the information regarding the icon to be placed on the status area.

For NOTIFYICONDATA structure check :

I have discussed few members here :

  • hWnd - Handle to the window that receives notification messages associated with an icon in the taskbar status area. 
In MFC applications it can be retrieved by calling this->GetSafeHWnd( ) from main dialog of the application in Dialog based application, and from CMainFrame class in SDI or MDI applications.
  • hIcon - Handle to the icon to be added, modified, or deleted. If the Icon is imported to the project, handle can be obtained by calling LoadIcon API.
  • uCallbackMessage - Application-defined message identifier. The system uses this identifier to send notifications to the window identified in hWnd. These notifications are sent when a mouse event occurs in the bounding rectangle of the icon, or when the icon is selected or activated with the keyboard.
The identifier can be defined a numeric value starting from WM_USER+1 to WM_USER+64k which is not previously assigned for other such identifiers. The application (Dialog based, SDI or MDI) receives ON_MESSAGE with first parameter as the identifier declared above.


Sample Application : 

1.Create a new Dialog Based Application using MFC wizard and name it as TrayIcon.

2.Open the file for CTrayIconDlg, the trayicondlg.cpp file and add the following at the end of preprocessor directives

define TRAYICON WM_USER+100
define ICONNUMBER 4001

3. From Resource View , insert an icon to the project and with id as ID_TRAYICON.
Also add a menu to the project and name it as ID_TRAYMENU. 
Using Menu Designer create a menu item status and add HIDE and SHOW menu in its drop-down menu 
Set the id for the menu item as ID_SHOW and ID_HIDE.
From the ClassWizard,add message handlers for each of them for COMMAND message.
The function that handles them will be OnShow() and OnHide(). 

4. Add a member function to CTrayIconDlg class that will react to the messages send by the system to the application regarding the status area icon.
Define it as  
void OnTrayIconMessage(WPARAM wParam, LPARAM lParam)

Edit the code of the function to add these 

POINT pt;
GetCursorPos(&pt);
// When Mouse Left button is clicked or Tapped in touch screen devices
if( (UINT)lParam == WM_LBUTTONDOWN)
{                                                                                     
CMenu menu;
menu.LoadMenu(ID_TRAYMENU);
CMenu *pContextMenu = menu.GetSubMenu(0);
pContextMenu->TrackPopupMenu( TPM_LEFTALIGN,
pt.x,pt.y,AfxGetMainWnd() );
}

5. Check for tag BEGIN_MESSAGE_MAP and END_MESSAGE_MAP and insert the following in between these two:

ON_MESSAGE(TRAYICON, OnTrayIconMessage) // Mapping the message identifier to the callback function

it should look like :

BEGIN_MESSAGE_MAP(CTrayIconDlg, CDialog)
//{{AFX_MSG_MAP(CTrayIconDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_CLOSE()
ON_WM_DESTROY()
ON_MESSAGE(TRAYICON, OnTrayIconMessage) //message identifier
ON_COMMAND(ID_SHOW, OnShow) //Menu message identifier for SHOW
ON_COMMAND(ID_HIDE, OnHide) //Menu message identifier for HIDE
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

6. To add the icon when the application starts, place the following code to OnInitDialog function after the TODO tag

NOTIFYICONDATA nid;
nid.cbSize = sizeof(NOTIFYICONDATA); // Size of Structure
nid.hWnd=this->GetSafeHwnd(); // Application Handle
strcpy(nid.szTip, "My Tray Icon App");
nid.uCallbackMessage=TRAYICON; // message identifier - Callback function mapped to this
nid.uFlags=NIF_MESSAGE|NIF_ICON|NIF_TIP;
nid.hIcon=AfxGetApp( )->LoadIcon(ID_TRAYICON); // ICON to be displayed
nid.uID=ICONNUMBER;  
Shell_NotifyIcon(NIM_ADD,&nid); // NIM_ADD to add the icon

If yours is SDI or MDI application, add the above code to OnCreate function of CMainFrame class.

7. To delete the icon from status area when the application gets destroyed ,add a handler for WM_DESTROY message using ClassWizard..
 The function name will be OnDestroy.
Add the following code

NOTIFYICONDATA nid;
nid.cbSize=sizeof( NOTIFYICONDATA );
nid.hWnd=this->GetSafeHwnd();
nid.uID=ICONNUMBER;
Shell_NotifyIcon( NIM_DELETE , &nid ); // delete the Icon

8. To Show and Hide the Application when Menu Item SHOW and HIDE is clicked edit the following functions 

void CTrayIconDlg::OnStatusHide()
{
// TODO: Add your command handler code here
ShowWindow(SW_HIDE);
}

void CTrayIconDlg::OnStatusShow()
{
// TODO: Add your command handler code here
ShowWindow(SW_SHOWNORMAL);
}

In this way we can add and delete(Modify)the icon to the status area,and menu items can be displayed when clicked on tray icons.