Aprovechando el experimento realizado en el artículo Bluetooth Remote Sensor de esta misma sección, es trivial terminar de adaptarlo para poder usar el móvil como un ratón inalámbrico (en este caso un Nokia N95 pero debería servir para cualquier Symbian S60 que disponga de un acelerómetro y Bluetooth).
La aplicación práctica de esta aplicación se puede extender a la susticución de un ratón, de un joystick, de un gamepad y como no, de un teclado.
No obstante, las características físicas del acelerómetro (con el que se toma el movimiento del móvil) y su teclado, limitan esta aplicación práctica aunque quedará especialmente indicado para aplicaciones con un control suave del movimiento, básicamente juegos claro, pero dentro de ellos aquellos como:
Habíamos quedado que enviábamos las coordenadas de aceleración codificadas como {ejeX};{ejeY};{ejeZ}{CR} pues ahora sólo tenemos que codificarlo como M{ejeX};{ejeY};{ejeZ}{CR} y cuando sea un comando de tecla como LD{keyCode}{CR} cuando sea Down y LU{keyCode}{CR} cuando sea Up.
Capturar una pulsación de tecla en Symbian significará implementar (o modificar) el método OfferKeyEventL y tan sólo añadiremos nuestro comando de tecla a la lista de mensajes (éstos se enviarán inmediatamente, pues las coordenadas de aceleración se envian varias decenas de veces por segundo) de la siguiente forma:
if( aType == EEventKeyUp || aType == EEventKeyDown ) { keyBuffer.Append( _L("L") ); // indicamos pulsación de tecla keyBuffer.Append( aType == EEventKeyDown ? _L("D") : _L("U") ); // indicamos arriba keyBuffer.AppendNum( aKeyEvent.iScanCode ); // indicamos arriba keyBuffer.Append( _L("\r") ); // fin de comando return true; }
Para optimizar la batería del móvil, lo primero que debe hacerse es desactivar cualquier salida por la pantalla del móvil (puesto que no la usamos para nada). Esto (tras trastear un poco, eso sí en la tan "amigable" documentación de Symbian) se resuelve sin dificultad con las siguientes líneas:
... // En "algún" sitio una vez conectado con el PC y puesto en modo "dispositivo", hacemos: // Hacemos que el control del log no sea visible (ahora no contenemos ningún control visible): iLog->MakeVisible( EFalse ); // Hacemos que nuestro contenedor ocupe toda la pantalla: SetExtentToWholeScreen(); ... // En el método <i>Draw</i>, para dejarlo todo "negro", añadimos: gc.SetBrushStyle( CGraphicsContext::ESolidBrush ); gc.SetBrushColor( KRgbBlack ); gc.Clear();
Es obvio diferenciar entre los comandos de movimiento y los de teclado, sólo queda ver de qué forma se envían los movimientos al ratón y las pulsaciones al teclado. Para ésto, una sencilla clase nos encapsula las llamadas a las funciones keybd_event y mouse_event. Sí, ya se, estas dos funciones han sido reemplazadas por la función SendInputs pero que carajo, llamarlas desde C# es más simple (no tienen tipos complejos) y funcionan exactamente igual de bien. El código es revelador...
class KeyAndMouse { [DllImport( "user32.dll" )] private unsafe static extern void keybd_event( byte bVk, // 1 - 254 byte bScan, // no usado int dwFlags, ulong* dwExtraInfo ); public enum KeyList { VK_BACK = 0x08, VK_TAB = 0x09, VK_CLEAR = 0x0C, VK_RETURN = 0x0D, VK_SHIFT = 0x10, VK_CONTROL = 0x11, VK_MENU = 0x12, VK_PAUSE = 0x13, VK_CAPITAL = 0x14, VK_KANA = 0x15, VK_HANGEUL = 0x15, VK_HANGUL = 0x15, VK_JUNJA = 0x17, VK_FINAL = 0x18, VK_HANJA = 0x19, VK_KANJI = 0x19, VK_ESCAPE = 0x1B, VK_CONVERT = 0x1C, VK_NONCONVERT = 0x1D, VK_ACCEPT = 0x1E, VK_MODECHANGE = 0x1F, VK_SPACE = 0x20, VK_PRIOR = 0x21, VK_NEXT = 0x22, VK_END = 0x23, VK_HOME = 0x24, VK_LEFT = 0x25, VK_UP = 0x26, VK_RIGHT = 0x27, VK_DOWN = 0x28, VK_SELECT = 0x29, VK_PRINT = 0x2A, VK_EXECUTE = 0x2B, VK_SNAPSHOT = 0x2C, VK_INSERT = 0x2D, VK_DELETE = 0x2E, VK_HELP = 0x2F, VK_LWIN = 0x5B, VK_RWIN = 0x5C, VK_APPS = 0x5D, VK_SLEEP = 0x5F, VK_NUMPAD0 = 0x60, VK_NUMPAD1 = 0x61, VK_NUMPAD2 = 0x62, VK_NUMPAD3 = 0x63, VK_NUMPAD4 = 0x64, VK_NUMPAD5 = 0x65, VK_NUMPAD6 = 0x66, VK_NUMPAD7 = 0x67, VK_NUMPAD8 = 0x68, VK_NUMPAD9 = 0x69, VK_MULTIPLY = 0x6A, VK_ADD = 0x6B, VK_SEPARATOR = 0x6C, VK_SUBTRACT = 0x6D, VK_DECIMAL = 0x6E, VK_DIVIDE = 0x6F, VK_F1 = 0x70, VK_F2 = 0x71, VK_F3 = 0x72, VK_F4 = 0x73, VK_F5 = 0x74, VK_F6 = 0x75, VK_F7 = 0x76, VK_F8 = 0x77, VK_F9 = 0x78, VK_F10 = 0x79, VK_F11 = 0x7A, VK_F12 = 0x7B, VK_F13 = 0x7C, VK_F14 = 0x7D, VK_F15 = 0x7E, VK_F16 = 0x7F, VK_F17 = 0x80, VK_F18 = 0x81, VK_F19 = 0x82, VK_F20 = 0x83, VK_F21 = 0x84, VK_F22 = 0x85, VK_F23 = 0x86, VK_F24 = 0x87, VK_NUMLOCK = 0x90, VK_SCROLL = 0x91, VK_OEM_NEC_EQUAL = 0x92, VK_OEM_FJ_JISHO = 0x92, VK_OEM_FJ_MASSHOU = 0x93, VK_OEM_FJ_TOUROKU = 0x94, VK_OEM_FJ_LOYA = 0x95, VK_OEM_FJ_ROYA = 0x96 } private const int KEYEVENTF_EXTENDEDKEY = 0x0001; // If specified, the scan code was preceded by a prefix byte having the value 0xE0 (224). private const int KEYEVENTF_KEYUP = 0x0002; // If specified, the key is being released. If not specified, the key is being depressed. [DllImport( "user32.dll" )] private unsafe static extern void mouse_event( int dwFlags, int dx, int dy, int dwData, ulong* dwExtraInfo ); private const int MOUSEEVENTF_MOVE = 0x0001; /* mouse move */ private const int MOUSEEVENTF_LEFTDOWN = 0x0002; /* left button down */ private const int MOUSEEVENTF_LEFTUP = 0x0004; /* left button up */ private const int MOUSEEVENTF_RIGHTDOWN = 0x0008; /* right button down */ private const int MOUSEEVENTF_RIGHTUP = 0x0010; /* right button up */ private const int MOUSEEVENTF_MIDDLEDOWN = 0x0020; /* middle button down */ private const int MOUSEEVENTF_MIDDLEUP = 0x0040; /* middle button up */ private const int MOUSEEVENTF_XDOWN = 0x0080; /* x button down */ private const int MOUSEEVENTF_XUP = 0x0100; /* x button down */ private const int MOUSEEVENTF_WHEEL = 0x0800; /* wheel button rolled */ private const int MOUSEEVENTF_VIRTUALDESK = 0x4000; /* map to entire virtual desktop */ private const int MOUSEEVENTF_ABSOLUTE = 0x8000; /* absolute move */ private const int XBUTTON1 = 0x0001; private const int XBUTTON2 = 0x0002; private const int WHEEL_DELTA = 120; public static void MouseMove( int dx, int dy ) { unsafe { mouse_event( MOUSEEVENTF_MOVE, dx, dy, 0, null ); } } public static void MouseTo( int x, int y ) { unsafe { mouse_event( MOUSEEVENTF_MOVE + MOUSEEVENTF_ABSOLUTE, x, y, 0, null ); } } public static void MouseDown( bool left, bool right, bool middle, bool xbutton1, bool xbutton2 ) { unsafe { mouse_event( ( left ? MOUSEEVENTF_LEFTDOWN : 0 ) + ( right ? MOUSEEVENTF_RIGHTDOWN : 0 ) + ( middle ? MOUSEEVENTF_MIDDLEDOWN : 0 ) + ( xbutton1 | xbutton2 ? MOUSEEVENTF_XDOWN : 0 ) , 0, 0, ( xbutton1 ? XBUTTON1 : 0 ) + ( xbutton2 ? XBUTTON2 : 0 ) , null ); } } public static void MouseUp( bool left, bool right, bool middle, bool xbutton1, bool xbutton2 ) { unsafe { mouse_event( ( left ? MOUSEEVENTF_LEFTUP : 0 ) + ( right ? MOUSEEVENTF_RIGHTUP : 0 ) + ( middle ? MOUSEEVENTF_MIDDLEUP : 0 ) + ( xbutton1 | xbutton2 ? MOUSEEVENTF_XUP : 0 ) , 0, 0, ( xbutton1 ? XBUTTON1 : 0 ) + ( xbutton2 ? XBUTTON2 : 0 ) , null ); } } public static void MouseWheelClick() { unsafe { mouse_event( MOUSEEVENTF_WHEEL, 0, 0, WHEEL_DELTA, null ); } } public static void MouseWheelForward() { unsafe { mouse_event( MOUSEEVENTF_WHEEL, 0, 0, +1, null ); } } public static void MouseWheelBackward() { unsafe { mouse_event( MOUSEEVENTF_WHEEL, 0, 0, -1, null ); } } public static void KeyDown( byte key ) { unsafe { keybd_event( key, 0, 0, null ); } } public static void KeyDown( KeyList key ) { unsafe { keybd_event( (byte) key, 0, KEYEVENTF_EXTENDEDKEY, null ); } } public static void KeyUp( byte key ) { unsafe { keybd_event( key, 0, KEYEVENTF_KEYUP, null ); } } public static void KeyUp( KeyList key ) { unsafe { keybd_event( (byte) key, 0, KEYEVENTF_EXTENDEDKEY + KEYEVENTF_KEYUP, null ); } } }
Ningún otro aspecto del código merece atención. Por lo demás, no hace falta que instales nada en el PC, sólo necesitas los archivos S60SensorBt.exe y InTheHand.Net.Personal.dll. Para el móvil sólo tienes que instalar el BluetoothRemoteSensor.sisx. Estos archivos los puedes bajar de aquí INSTALABLES.
Cómo ejecutar el programa
Por lo demás, déjalo minimizado y actuará como cualquier ratón o joystick y te permitirá controlar cualquier juego o aplicación.
Si quieres el código fuente, sólo tienes que pedírmelo a la dirección de correo.