| // | 
 | // Copyright 2005 The Android Open Source Project | 
 | // | 
 | // Displays the phone image and handles user input. | 
 | // | 
 |  | 
 | // For compilers that support precompilation, include "wx/wx.h". | 
 | #include "wx/wxprec.h" | 
 |  | 
 | // Otherwise, include all standard headers | 
 | #ifndef WX_PRECOMP | 
 | # include "wx/wx.h" | 
 | #endif | 
 | #include "wx/image.h"   // needed for Windows build | 
 | #include "wx/dcbuffer.h" | 
 |  | 
 | #include "LinuxKeys.h" | 
 | #include "PhoneWindow.h" | 
 | #include "DeviceWindow.h" | 
 | #include "PhoneData.h" | 
 | #include "PhoneCollection.h" | 
 | #include "MainFrame.h" | 
 | #include "MyApp.h" | 
 |  | 
 | using namespace android; | 
 |  | 
 | BEGIN_EVENT_TABLE(PhoneWindow, wxWindow)    // NOT wxDialog | 
 |     EVT_ACTIVATE(PhoneWindow::OnActivate) | 
 |     //EVT_ACTIVATE_APP(PhoneWindow::OnActivate) | 
 |     EVT_CLOSE(PhoneWindow::OnClose) | 
 |     EVT_MOVE(PhoneWindow::OnMove) | 
 |     EVT_ERASE_BACKGROUND(PhoneWindow::OnErase) | 
 |     EVT_PAINT(PhoneWindow::OnPaint) | 
 |  | 
 |     EVT_KEY_DOWN(PhoneWindow::OnKeyDown) | 
 |     EVT_KEY_UP(PhoneWindow::OnKeyUp) | 
 |     EVT_LEFT_DOWN(PhoneWindow::OnMouseLeftDown) | 
 |     EVT_LEFT_DCLICK(PhoneWindow::OnMouseLeftDown) | 
 |     EVT_LEFT_UP(PhoneWindow::OnMouseLeftUp) | 
 |     EVT_RIGHT_DOWN(PhoneWindow::OnMouseRightDown) | 
 |     EVT_RIGHT_DCLICK(PhoneWindow::OnMouseRightDown) | 
 |     EVT_RIGHT_UP(PhoneWindow::OnMouseRightUp) | 
 |     EVT_MOTION(PhoneWindow::OnMouseMotion) | 
 |     EVT_LEAVE_WINDOW(PhoneWindow::OnMouseLeaveWindow) | 
 |     EVT_TIMER(kVibrateTimerId, PhoneWindow::OnTimer) | 
 | END_EVENT_TABLE() | 
 |  | 
 |  | 
 | /* | 
 |  * Create a new PhoneWindow.  This should be a child of the main frame. | 
 |  */ | 
 | PhoneWindow::PhoneWindow(wxWindow* parent, const wxPoint& posn) | 
 |     : wxDialog(parent, wxID_ANY, wxT("Device"), posn, wxDefaultSize, | 
 |         wxDEFAULT_DIALOG_STYLE), | 
 |       mpMOHViewIndex(-1), | 
 |       mpMOHButton(NULL), | 
 |       mMouseKeySent(kKeyCodeUnknown), | 
 |       mpViewInfo(NULL), | 
 |       mNumViewInfo(0), | 
 |       mpDeviceWindow(NULL), | 
 |       mNumDeviceWindows(0), | 
 |       mPhoneModel(-1), | 
 |       mCurrentMode(wxT("(unknown)")), | 
 |       mPlacementChecked(false), | 
 |       mpParent((MainFrame*)parent), | 
 |       mTimer(this, kVibrateTimerId), | 
 |       mTrackingTouch(false) | 
 | { | 
 |     SetBackgroundColour(*wxLIGHT_GREY); | 
 |     SetBackgroundStyle(wxBG_STYLE_CUSTOM); | 
 |  | 
 |     //SetCursor(wxCursor(wxCURSOR_HAND));     // a bit distracting (pg.276) | 
 | } | 
 |  | 
 | /* | 
 |  * Destroy everything we own. | 
 |  * | 
 |  * This might be called well after we've been closed and another | 
 |  * PhoneWindow has been created, because wxWidgets likes to defer things. | 
 |  */ | 
 | PhoneWindow::~PhoneWindow(void) | 
 | { | 
 |     //printf("--- ~PhoneWindow %p\n", this); | 
 |     delete[] mpViewInfo; | 
 |     if (mpDeviceWindow != NULL) { | 
 |         for (int i = 0; i < mNumDeviceWindows; i++) { | 
 |             /* make sure they don't try to use our member */ | 
 |             mpDeviceWindow[i]->DeviceManagerClosing(); | 
 |             /* make sure the child window gets destroyed -- not necessary? */ | 
 |             mpDeviceWindow[i]->Destroy(); | 
 |         } | 
 |  | 
 |         /* delete our array of pointers */ | 
 |         delete[] mpDeviceWindow; | 
 |     } | 
 | } | 
 |  | 
 | /* | 
 |  * Check for an updated runtime when window becomes active | 
 |  */ | 
 | void PhoneWindow::OnActivate(wxActivateEvent& event) | 
 | { | 
 |     /* | 
 |      * DO NOT do this.  Under Windows, it causes the parent window to get | 
 |      * an activate event, which causes our parent to get the focus.  With | 
 |      * this bit of code active it is impossible for the phone window to | 
 |      * receive user input. | 
 |      */ | 
 |     //GetParent()->AddPendingEvent(event); | 
 |  | 
 |     // If we are being deactivated, go ahead and send key up events so that the | 
 |     // runtime doesn't think we are holding down the key. Issue #685750 | 
 |     if (!event.GetActive()) { | 
 |         ListIter iter; | 
 |         for (iter = mPressedKeys.begin(); iter != mPressedKeys.end(); ) { | 
 |             KeyCode keyCode = (*iter).GetKeyCode(); | 
 |             GetDeviceManager()->SendKeyEvent(keyCode, false); | 
 |             iter = mPressedKeys.erase(iter); | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | /* | 
 |  * Close the phone window. | 
 |  */ | 
 | void PhoneWindow::OnClose(wxCloseEvent& event) | 
 | { | 
 |     //printf("--- PhoneWindow::OnClose %p\n", this); | 
 | #if 0 | 
 |     if (mDeviceManager.IsRunning() && !mDeviceManager.IsKillable()) { | 
 |         printf("Sim: refusing to close window on external runtime\n"); | 
 |         event.Veto(); | 
 |         return; | 
 |     } | 
 | #endif | 
 |  | 
 |     wxRect rect = GetRect(); | 
 |     printf("Sim: Closing phone window (posn=(%d,%d))\n", rect.x, rect.y); | 
 |  | 
 |     /* notify others */ | 
 |     mpParent->PhoneWindowClosing(rect.x, rect.y); | 
 |     mDeviceManager.WindowsClosing(); | 
 |  | 
 |     /* end it all */ | 
 |     Destroy(); | 
 | } | 
 |  | 
 | /* | 
 |  * Prep the PhoneWindow to display a specific phone model.  Pass in the | 
 |  * model index. | 
 |  * | 
 |  * This gets called whenever the display changes.  This could be a new | 
 |  * device with identical characteristics, or a different mode for the same | 
 |  * device. | 
 |  * | 
 |  * The window can be re-used so long as the display characteristics are | 
 |  * the same.  If the display characteristics are different, we have to | 
 |  * restart the device. | 
 |  */ | 
 | bool PhoneWindow::Setup(int phoneIdx) | 
 | { | 
 |     wxString fileName; | 
 |     PhoneCollection* pCollection = PhoneCollection::GetInstance(); | 
 |  | 
 |     if (phoneIdx < 0 || phoneIdx >= pCollection->GetPhoneCount()) { | 
 |         fprintf(stderr, "Bogus phone index %d\n", phoneIdx); | 
 |         return false; | 
 |     } | 
 |  | 
 |     /* | 
 |      * Clear these out so that failure here is noticeable to caller.  We | 
 |      * regenerate the ViewInfo array every time, because the set of Views | 
 |      * is different for every Mode. | 
 |      */ | 
 |     delete[] mpViewInfo; | 
 |     mpViewInfo = NULL; | 
 |     mNumViewInfo = -1; | 
 |  | 
 |     PhoneData* pPhoneData; | 
 |     PhoneMode* pPhoneMode; | 
 |     PhoneView* pPhoneView; | 
 |  | 
 |     pPhoneData = pCollection->GetPhoneData(phoneIdx); | 
 |  | 
 |     pPhoneMode = pPhoneData->GetPhoneMode(GetCurrentMode().ToAscii()); | 
 |     if (pPhoneMode == NULL) { | 
 |         fprintf(stderr, "current mode (%s) not known\n", | 
 |             (const char*) GetCurrentMode().ToAscii()); | 
 |         return false; | 
 |     } | 
 |  | 
 |     int numViews = pPhoneMode->GetNumViews(); | 
 |     if (numViews == 0) { | 
 |         fprintf(stderr, "Phone %d mode %s has no views\n", | 
 |             phoneIdx, pPhoneMode->GetName()); | 
 |         return false; | 
 |     } | 
 |  | 
 |     const int kBorder = 2; | 
 |     int i; | 
 |     int maxHeight = 0; | 
 |     int fullWidth = kBorder; | 
 |     ViewInfo* pViewInfo; | 
 |  | 
 |     pViewInfo = new ViewInfo[numViews]; | 
 |  | 
 |     /* figure out individual and overall dimensions */ | 
 |     for (i = 0; i < numViews; i++) { | 
 |         pPhoneView = pPhoneMode->GetPhoneView(i); | 
 |         if (pPhoneView == NULL) { | 
 |             fprintf(stderr, "view %d not found\n", i); | 
 |             return false; | 
 |         } | 
 |  | 
 |         if (!GetDimensions(pPhoneData, pPhoneView, &pViewInfo[i])) | 
 |             return false; | 
 |  | 
 |         if (maxHeight < pViewInfo[i].GetHeight()) | 
 |             maxHeight = pViewInfo[i].GetHeight(); | 
 |         fullWidth += pViewInfo[i].GetWidth() + kBorder; | 
 |     } | 
 |  | 
 |     /* create the device windows if we don't already have them */ | 
 |     if (mpDeviceWindow == NULL) { | 
 |         mNumDeviceWindows = pPhoneData->GetNumDisplays(); | 
 |         mpDeviceWindow = new DeviceWindow*[mNumDeviceWindows]; | 
 |         if (mpDeviceWindow == NULL) | 
 |             return false; | 
 |  | 
 |         for (i = 0; i < mNumDeviceWindows; i++) { | 
 |             mpDeviceWindow[i] = new DeviceWindow(this, &mDeviceManager); | 
 |         } | 
 |     } else { | 
 |         assert(pPhoneData->GetNumDisplays() == mNumDeviceWindows); | 
 |     } | 
 |  | 
 |     /* | 
 |      * Position device windows within their views, taking into account | 
 |      * border areas. | 
 |      */ | 
 |     int shift = kBorder; | 
 |     for (i = 0; i < numViews; i++) { | 
 |         int displayIdx; | 
 |         PhoneDisplay* pPhoneDisplay; | 
 |  | 
 |         displayIdx = pViewInfo[i].GetDisplayIndex(); | 
 |         pPhoneDisplay = pPhoneData->GetPhoneDisplay(displayIdx); | 
 |         //printf("View %d: display %d\n", i, displayIdx); | 
 |  | 
 |         pViewInfo[i].SetX(shift); | 
 |         pViewInfo[i].SetY(kBorder); | 
 |  | 
 |         mpDeviceWindow[displayIdx]->SetSize( | 
 |             pViewInfo[i].GetX() + pViewInfo[i].GetDisplayX(), | 
 |             pViewInfo[i].GetY() + pViewInfo[i].GetDisplayY(), | 
 |             pPhoneDisplay->GetWidth(), pPhoneDisplay->GetHeight()); | 
 |  | 
 |         // incr by width of view | 
 |         shift += pViewInfo[i].GetWidth() + kBorder; | 
 |     } | 
 |  | 
 |     /* configure the device manager if it's not already running */ | 
 |     if (!mDeviceManager.IsInitialized()) { | 
 |         mDeviceManager.Init(pPhoneData->GetNumDisplays(), mpParent); | 
 |  | 
 |         for (i = 0; i < pPhoneData->GetNumDisplays(); i++) { | 
 |             PhoneDisplay* pPhoneDisplay; | 
 |             bool res; | 
 |  | 
 |             pPhoneDisplay = pPhoneData->GetPhoneDisplay(i); | 
 |  | 
 |             res = mDeviceManager.SetDisplayConfig(i, mpDeviceWindow[i], | 
 |                 pPhoneDisplay->GetWidth(), pPhoneDisplay->GetHeight(), | 
 |                 pPhoneDisplay->GetFormat(), pPhoneDisplay->GetRefresh()); | 
 |             if (!res) { | 
 |                 fprintf(stderr, "Sim: ERROR: could not configure device mgr\n"); | 
 |                 return false; | 
 |             } | 
 |         } | 
 |         const char *kmap = pPhoneData->GetPhoneKeyboard(0)->getKeyMap(); | 
 |         mDeviceManager.SetKeyboardConfig(kmap); | 
 |     } else { | 
 |         assert(pPhoneData->GetNumDisplays() == mDeviceManager.GetNumDisplays()); | 
 |     } | 
 |  | 
 |     /* | 
 |      * Success.  Finish up. | 
 |      */ | 
 |     mPhoneModel = phoneIdx; | 
 |     mpViewInfo = pViewInfo; | 
 |     mNumViewInfo = numViews; | 
 |  | 
 |     /* set up our window */ | 
 |     SetClientSize(fullWidth, maxHeight + kBorder * 2); | 
 |     SetBackgroundColour(*wxLIGHT_GREY); | 
 |     //SetBackgroundColour(*wxBLUE); | 
 |     SetTitle(wxString::FromAscii(pPhoneData->GetTitle())); | 
 |  | 
 |     SetFocus();     // set keyboard input focus | 
 |  | 
 |     return true; | 
 | } | 
 |  | 
 | /* | 
 |  * The device table has been reloaded.  We need to throw out any pointers | 
 |  * we had into it and possibly reload some stuff. | 
 |  */ | 
 | void PhoneWindow::DevicesRescanned(void) | 
 | { | 
 |     mpMOHButton = NULL; | 
 |     mpMOHViewIndex = -1; | 
 |  | 
 |     /* | 
 |      * Re-evaluate phone definition.  There is an implicit assumption | 
 |      * that the re-scanned version is compatible with the previous | 
 |      * version (i.e. it still exists and has the same screen size). | 
 |      * | 
 |      * We're also currently assuming that no phone definitions have been | 
 |      * added or removed, which is bad -- we should get the new index for | 
 |      * for phone by searching for it by name. | 
 |      * | 
 |      * TODO: don't make these assumptions. | 
 |      */ | 
 |     Setup(mPhoneModel); | 
 | } | 
 |  | 
 | /* | 
 |  * Check the initial placement of the window.  We get one of these messages | 
 |  * when the window is first placed, and every time it's moved thereafter. | 
 |  * | 
 |  * Right now we're just trying to make sure wxWidgets doesn't shove it off | 
 |  * the top of the screen under Linux.  Might want to change this to | 
 |  * remember the previous placement and put the window back. | 
 |  */ | 
 | void PhoneWindow::OnMove(wxMoveEvent& event) | 
 | { | 
 |     if (mPlacementChecked) | 
 |         return; | 
 |  | 
 |     wxPoint point; | 
 |     point = event.GetPosition(); | 
 |     if (point.y < 0) { | 
 |         printf("Sim: window is at (%d,%d), adjusting\n", point.x, point.y); | 
 |         point.y = 0; | 
 |         Move(point); | 
 |     } | 
 |  | 
 |     mPlacementChecked = true; | 
 | } | 
 |  | 
 | /* | 
 |  * Figure out the dimensions required to contain the specified view. | 
 |  * | 
 |  * This is usually the size of the background image, but if we can't | 
 |  * load it or it's too small just create a trivial window. | 
 |  */ | 
 | bool PhoneWindow::GetDimensions(PhoneData* pPhoneData, PhoneView* pPhoneView, | 
 |     ViewInfo* pInfo) | 
 | { | 
 |     PhoneDisplay* pPhoneDisplay; | 
 |     int xoff=0, yoff=0, width, height; | 
 |     int displayIdx; | 
 |  | 
 |     displayIdx = pPhoneData->GetPhoneDisplayIndex(pPhoneView->GetDisplayName()); | 
 |     if (displayIdx < 0) | 
 |         return false; | 
 |  | 
 |     pPhoneDisplay = pPhoneData->GetPhoneDisplay(displayIdx); | 
 |     if (pPhoneDisplay == NULL) { | 
 |         fprintf(stderr, "display '%s' not found in device '%s'\n", | 
 |             pPhoneView->GetDisplayName(), pPhoneData->GetName()); | 
 |         return false; | 
 |     } | 
 |  | 
 |     // load images for this phone | 
 |     (void) pPhoneView->LoadResources(); | 
 |  | 
 |     width = height = 0; | 
 |  | 
 |     // by convention, the background bitmap is the first image in the list | 
 |     if (pPhoneView->GetBkgImageCount() > 0) { | 
 |         wxBitmap* pBitmap = pPhoneView->GetBkgImage(0)->GetBitmap(); | 
 |         if (pBitmap != NULL) { | 
 |             // size window to match bitmap | 
 |             xoff = pPhoneView->GetXOffset(); | 
 |             yoff = pPhoneView->GetYOffset(); | 
 |             width = pBitmap->GetWidth(); | 
 |             height = pBitmap->GetHeight(); | 
 |         } | 
 |     } | 
 |  | 
 |     // no bitmap, or bitmap is smaller than display | 
 |     if (width < pPhoneDisplay->GetWidth() || | 
 |         height < pPhoneDisplay->GetHeight()) | 
 |     { | 
 |         // create window to just hold display | 
 |         xoff = yoff = 0; | 
 |         width = pPhoneDisplay->GetWidth(); | 
 |         height = pPhoneDisplay->GetHeight(); | 
 |     } | 
 |     if (width <= 0 || height <= 0) { | 
 |         fprintf(stderr, "ERROR: couldn't determine display size\n"); | 
 |         return false; | 
 |     } | 
 |  | 
 |     pInfo->SetX(0); | 
 |     pInfo->SetY(0);             // another function determines these | 
 |     pInfo->SetDisplayX(xoff); | 
 |     pInfo->SetDisplayY(yoff); | 
 |     pInfo->SetWidth(width); | 
 |     pInfo->SetHeight(height); | 
 |     pInfo->SetDisplayIndex(displayIdx); | 
 |  | 
 |     //printf("xoff=%d yoff=%d width=%d height=%d index=%d\n", | 
 |     //    pInfo->GetDisplayX(), pInfo->GetDisplayY(), | 
 |     //    pInfo->GetWidth(), pInfo->GetHeight(), pInfo->GetDisplayIndex()); | 
 |  | 
 |     return true; | 
 | } | 
 |  | 
 | /* | 
 |  * Return PhoneData pointer for the current phone model. | 
 |  */ | 
 | PhoneData* PhoneWindow::GetPhoneData(void) const | 
 | { | 
 |     PhoneCollection* pCollection = PhoneCollection::GetInstance(); | 
 |     return pCollection->GetPhoneData(mPhoneModel); | 
 | } | 
 |  | 
 | /* | 
 |  * Convert a wxWidgets key code into a device key code. | 
 |  * | 
 |  * Someday we may want to make this configurable. | 
 |  * | 
 |  * NOTE: we need to create a mapping between simulator key and desired | 
 |  * function.  The "return" key should always mean "select", whether | 
 |  * it's a "select" button or pressing in on the d-pad.  Ditto for | 
 |  * the arrow keys, whether we have a joystick, d-pad, or four buttons. | 
 |  * Each key here should have a set of things that it could possibly be, | 
 |  * and we match it up with the set of buttons actually defined for the | 
 |  * phone.  [for convenience, need to ensure that buttons need not have | 
 |  * an associated image] | 
 |  */ | 
 | int PhoneWindow::ConvertKeyCode(int wxKeyCode) const | 
 | { | 
 |     switch (wxKeyCode) { | 
 |     case WXK_NUMPAD_INSERT: | 
 |     case WXK_NUMPAD0: | 
 |     case '0':                   return KEY_0; | 
 |     case WXK_NUMPAD_HOME: | 
 |     case WXK_NUMPAD1: | 
 |     case '1':                   return KEY_1; | 
 |     case WXK_NUMPAD_UP: | 
 |     case WXK_NUMPAD2: | 
 |     case '2':                   return KEY_2; | 
 |     case WXK_NUMPAD_PRIOR: | 
 |     case WXK_NUMPAD3: | 
 |     case '3':                   return KEY_3; | 
 |     case WXK_NUMPAD_LEFT: | 
 |     case WXK_NUMPAD4: | 
 |     case '4':                   return KEY_4; | 
 |     case WXK_NUMPAD_BEGIN: | 
 |     case WXK_NUMPAD5: | 
 |     case '5':                   return KEY_5; | 
 |     case WXK_NUMPAD_RIGHT: | 
 |     case WXK_NUMPAD6: | 
 |     case '6':                   return KEY_6; | 
 |     case WXK_NUMPAD_END: | 
 |     case WXK_NUMPAD7: | 
 |     case '7':                   return KEY_7; | 
 |     case WXK_NUMPAD_DOWN: | 
 |     case WXK_NUMPAD8: | 
 |     case '8':                   return KEY_8; | 
 |     case WXK_NUMPAD_NEXT: | 
 |     case WXK_NUMPAD9: | 
 |     case '9':                   return KEY_9; | 
 |     case WXK_NUMPAD_MULTIPLY:   return KEY_SWITCHVIDEOMODE; //kKeyCodeStar; | 
 |     case WXK_LEFT:              return KEY_LEFT; | 
 |     case WXK_RIGHT:             return KEY_RIGHT; | 
 |     case WXK_UP:                return KEY_UP; | 
 |     case WXK_DOWN:              return KEY_DOWN; | 
 |     case WXK_NUMPAD_ENTER:      return KEY_REPLY; //kKeyCodeDpadCenter; | 
 |     case WXK_HOME:              return KEY_HOME; | 
 |     case WXK_PRIOR: | 
 |     case WXK_PAGEUP:            return KEY_MENU; //kKeyCodeSoftLeft; | 
 |     case WXK_NEXT: | 
 |     case WXK_PAGEDOWN:          return KEY_KBDILLUMUP; //kKeyCodeSoftRight; | 
 |     case WXK_DELETE:             | 
 |     case WXK_BACK:              return KEY_BACKSPACE; //kKeyCodeDel; | 
 |     case WXK_ESCAPE: | 
 |     case WXK_END:               return KEY_BACK; //kKeyCodeBack; | 
 |     case WXK_NUMPAD_DELETE: | 
 |     case WXK_NUMPAD_DECIMAL:    return KEY_KBDILLUMTOGGLE; //kKeyCodePound; | 
 |     case WXK_SPACE:             return KEY_SPACE; //kKeyCodeSpace; | 
 |     case WXK_RETURN:            return KEY_ENTER; //kKeyCodeNewline; | 
 |     case WXK_F3:                return KEY_F3; //kKeyCodeCall; | 
 |     case WXK_F4:                return KEY_F4; //kKeyCodeEndCall; | 
 |     case WXK_NUMPAD_ADD: | 
 |     case WXK_F5:                return KEY_VOLUMEUP; | 
 |     case WXK_NUMPAD_SUBTRACT: | 
 |     case WXK_F6:                return KEY_VOLUMEDOWN; | 
 |     case WXK_F7:                return KEY_POWER; | 
 |     case WXK_F8:                return KEY_CAMERA; | 
 |     case 'A':                   return KEY_A; | 
 |     case 'B':                   return KEY_B; | 
 |     case 'C':                   return KEY_C; | 
 |     case 'D':                   return KEY_D; | 
 |     case 'E':                   return KEY_E; | 
 |     case 'F':                   return KEY_F; | 
 |     case 'G':                   return KEY_G; | 
 |     case 'H':                   return KEY_H; | 
 |     case 'I':                   return KEY_I; | 
 |     case 'J':                   return KEY_J; | 
 |     case 'K':                   return KEY_K; | 
 |     case 'L':                   return KEY_L; | 
 |     case 'M':                   return KEY_M; | 
 |     case 'N':                   return KEY_N; | 
 |     case 'O':                   return KEY_O; | 
 |     case 'P':                   return KEY_P; | 
 |     case 'Q':                   return KEY_Q; | 
 |     case 'R':                   return KEY_R; | 
 |     case 'S':                   return KEY_S; | 
 |     case 'T':                   return KEY_T; | 
 |     case 'U':                   return KEY_U; | 
 |     case 'V':                   return KEY_V; | 
 |     case 'W':                   return KEY_W; | 
 |     case 'X':                   return KEY_X; | 
 |     case 'Y':                   return KEY_Y; | 
 |     case 'Z':                   return KEY_Z; | 
 |     case ',':                   return KEY_COMMA; | 
 |     case '.':                   return KEY_DOT; | 
 |     case '<':                   return KEY_COMMA; | 
 |     case '>':                   return KEY_DOT; | 
 |     case '`':                   return KEY_GREEN; /*KEY_GRAVE;*/ | 
 |     case '-':                   return KEY_MINUS; | 
 |     case '=':                   return KEY_EQUAL; | 
 |     case '[':                   return KEY_LEFTBRACE; | 
 |     case ']':                   return KEY_RIGHTBRACE; | 
 |     case '\\':                  return KEY_BACKSLASH; | 
 |     case ';':                   return KEY_SEMICOLON; | 
 |     case '\'':                  return KEY_APOSTROPHE; | 
 |     case '/':                   return KEY_SLASH; | 
 |     case WXK_SHIFT:             return KEY_LEFTSHIFT; | 
 |     case WXK_CONTROL: | 
 |     case WXK_ALT:               return KEY_LEFTALT; | 
 |     case WXK_TAB:               return KEY_TAB; | 
 |     // don't show "ignoring key" message for these | 
 |     case WXK_MENU: | 
 |         break; | 
 |     default: | 
 |         printf("(ignoring key %d)\n", wxKeyCode); | 
 |         break; | 
 |     } | 
 |  | 
 |     return kKeyCodeUnknown; | 
 | } | 
 |  | 
 |  | 
 | /* | 
 |  * Keyboard handling.  These get converted into Android-defined key | 
 |  * constants here. | 
 |  * | 
 |  * NOTE: would be nice to handle menu keyboard accelerators here. | 
 |  * Simply stuffing the key events into MainFrame with AddPendingEvent | 
 |  * didn't seem to do the trick. | 
 |  */ | 
 | void PhoneWindow::OnKeyDown(wxKeyEvent& event) | 
 | { | 
 |     KeyCode keyCode; | 
 |  | 
 |     keyCode = (KeyCode) ConvertKeyCode(event.GetKeyCode()); | 
 |     if (keyCode != kKeyCodeUnknown) { | 
 |         if (!IsKeyPressed(keyCode)) { | 
 |             //printf("PW: down: key %d\n", keyCode); | 
 |             GetDeviceManager()->SendKeyEvent(keyCode, true); | 
 |             AddPressedKey(keyCode); | 
 |         } | 
 |     } else { | 
 |         //printf("PW: down: %d\n", event.GetKeyCode()); | 
 |         event.Skip();       // not handled by us | 
 |     } | 
 | } | 
 |  | 
 | /* | 
 |  * Pass key-up events to runtime. | 
 |  */ | 
 | void PhoneWindow::OnKeyUp(wxKeyEvent& event) | 
 | { | 
 |     KeyCode keyCode; | 
 |  | 
 |     keyCode = (KeyCode) ConvertKeyCode(event.GetKeyCode()); | 
 |     if (keyCode != kKeyCodeUnknown) { | 
 |         // Send the key event if we already have this key pressed. | 
 |         if (IsKeyPressed(keyCode)) { | 
 |             //printf("PW:   up: key %d\n", keyCode); | 
 |             GetDeviceManager()->SendKeyEvent(keyCode, false); | 
 |             RemovePressedKey(keyCode); | 
 |         } | 
 |     } else { | 
 |         //printf("PW:   up: %d\n", event.GetKeyCode()); | 
 |         event.Skip();       // not handled by us | 
 |     } | 
 | } | 
 |  | 
 | /* | 
 |  * Mouse handling. | 
 |  * | 
 |  * Unlike more conventional button tracking, we highlight on mouse-over | 
 |  * and send the key on mouse-down.  This behavior may be confusing for | 
 |  * people expecting standard behavior, but it allows us to simulate the | 
 |  * effect of holding a key down. | 
 |  * | 
 |  * We want to catch both "down" and "double click" events; otherwise | 
 |  * fast clicking results in a lot of discarded events. | 
 |  */ | 
 | void PhoneWindow::OnMouseLeftDown(wxMouseEvent& event) | 
 | { | 
 |     if (mpMOHButton != NULL) { | 
 |         //printf("PW: left down\n"); | 
 |         KeyCode keyCode = mpMOHButton->GetKeyCode(); | 
 |         GetDeviceManager()->SendKeyEvent(keyCode, true); | 
 |         mMouseKeySent = keyCode; | 
 |         AddPressedKey(keyCode); | 
 |     } else { | 
 |         int screenX, screenY; | 
 |  | 
 |         if (GetTouchPosition(event, &screenX, &screenY)) { | 
 |             //printf("TOUCH at %d,%d\n", screenX, screenY); | 
 |             mTrackingTouch = true; | 
 |             mTouchX = screenX; | 
 |             mTouchY = screenY; | 
 |             GetDeviceManager()->SendTouchEvent(Simulator::kTouchDown, | 
 |                 mTouchX, mTouchY); | 
 |         } else { | 
 |             //printf("(ignoring left click)\n"); | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | /* | 
 |  * Left button has been released.  Do something clever. | 
 |  * | 
 |  * On some platforms we will lose this if the mouse leaves the window. | 
 |  */ | 
 | void PhoneWindow::OnMouseLeftUp(wxMouseEvent& WXUNUSED(event)) | 
 | { | 
 |     if (mMouseKeySent != kKeyCodeUnknown) { | 
 |         //printf("PW: left up\n"); | 
 |         GetDeviceManager()->SendKeyEvent(mMouseKeySent, false); | 
 |         RemovePressedKey(mMouseKeySent); | 
 |     } else { | 
 |         if (mTrackingTouch) { | 
 |             //printf("TOUCH release (last was %d,%d)\n", mTouchX, mTouchY); | 
 |             mTrackingTouch = false; | 
 |             GetDeviceManager()->SendTouchEvent(Simulator::kTouchUp, | 
 |                 mTouchX, mTouchY); | 
 |         } else { | 
 |             //printf("(ignoring left-up)\n"); | 
 |         } | 
 |     } | 
 |     mMouseKeySent = kKeyCodeUnknown; | 
 | } | 
 |  | 
 | void PhoneWindow::OnMouseRightDown(wxMouseEvent& event) | 
 | { | 
 |     //printf("(ignoring right-down)\n"); | 
 | } | 
 | void PhoneWindow::OnMouseRightUp(wxMouseEvent& event) | 
 | { | 
 |     //printf("(ignoring right-up)\n"); | 
 | } | 
 |  | 
 | /* | 
 |  * Track mouse motion so we can do mouse-over button highlighting. | 
 |  */ | 
 | void PhoneWindow::OnMouseMotion(wxMouseEvent& event) | 
 | { | 
 |     /* | 
 |      * If the mouse motion event occurred inside the device window, | 
 |      * we treat it differently than mouse movement over the picture of | 
 |      * the device. | 
 |      */ | 
 |     if (event.GetEventObject() == mpDeviceWindow[0]) { | 
 |         if (mpMOHViewIndex >= 0) { | 
 |             /* can happen if the mouse moves fast enough */ | 
 |             //printf("Mouse now in dev window, clearing button highlight\n"); | 
 |             mpMOHViewIndex = -1; | 
 |             mpMOHButton = NULL; | 
 |             Refresh(); | 
 |         } | 
 |  | 
 |         if (!event.LeftIsDown() && event.RightIsDown()) { | 
 |             /* right-button movement */ | 
 |             //printf("(ignoring right-drag)\n"); | 
 |             return; | 
 |         } | 
 |  | 
 |         //printf("moveto: %d,%d\n", event.m_x, event.m_y); | 
 |  | 
 |         int screenX, screenY; | 
 |         if (mTrackingTouch) { | 
 |             if (GetTouchPosition(event, &screenX, &screenY)) { | 
 |                 //printf("TOUCH moved to %d,%d\n", screenX, screenY); | 
 |                 mTouchX = screenX; | 
 |                 mTouchY = screenY; | 
 |                 GetDeviceManager()->SendTouchEvent(Simulator::kTouchDrag, | 
 |                     mTouchX, mTouchY); | 
 |             } else { | 
 |                 //printf("TOUCH moved off screen\n"); | 
 |             } | 
 |         } | 
 |  | 
 |         return; | 
 |     } | 
 |  | 
 |     PhoneData* pPhoneData = GetPhoneData(); | 
 |     if (pPhoneData == NULL) | 
 |         return; | 
 |  | 
 |     /* | 
 |      * Check to see if we're on top of a button.  If our "on top of | 
 |      * something" state has changed, force a redraw. | 
 |      * | 
 |      * We have to run through the list of Views and check all of the | 
 |      * buttons in each. | 
 |      */ | 
 |     PhoneMode* pMode = pPhoneData->GetPhoneMode(GetCurrentMode().ToAscii()); | 
 |     if (pMode == NULL) | 
 |         return; | 
 |  | 
 |     int viewIndex = -1; | 
 |     PhoneButton* pHighlight = NULL; | 
 |     int i; | 
 |  | 
 |     for (i = pMode->GetNumViews()-1; i >= 0; i--) { | 
 |         PhoneView* pView = pMode->GetPhoneView(i); | 
 |         assert(pView != NULL); | 
 |  | 
 |         /* convert from window-relative to view-relative */ | 
 |         pHighlight = pView->FindButtonHit(event.m_x - mpViewInfo[i].GetX(), | 
 |                                           event.m_y - mpViewInfo[i].GetY()); | 
 |         if (pHighlight != NULL) { | 
 |             viewIndex = i; | 
 |             break; | 
 |         } | 
 |     } | 
 |  | 
 |     if (viewIndex == mpMOHViewIndex && pHighlight == mpMOHButton) { | 
 |         /* still hovering over same button */ | 
 |     } else { | 
 |         /* mouse has moved, possibly to a new button */ | 
 |  | 
 |         mpMOHViewIndex = viewIndex; | 
 |         mpMOHButton = pHighlight; | 
 |  | 
 |         /* force refresh */ | 
 |         Refresh(); | 
 |     } | 
 | } | 
 |  | 
 | /* | 
 |  * Mouse has left the building.  All keys and mouse buttons up. | 
 |  * | 
 |  * We get one of these if the mouse moves over a child window, such as | 
 |  * our DeviceWindow, so it is not the case that we no longer receive | 
 |  * key input after getting this event. | 
 |  */ | 
 | void PhoneWindow::OnMouseLeaveWindow(wxMouseEvent& WXUNUSED(event)) | 
 | { | 
 |     //printf("--- mouse is GONE\n"); | 
 |     ClearPressedKeys(); | 
 | } | 
 |  | 
 | /* | 
 |  * Determine device touch screen (x,y) based on window position. | 
 |  * | 
 |  * Returns "true" if the click corresponds to a location on the display. | 
 |  * | 
 |  * TODO: should return display index as well -- currently this only | 
 |  * supports touch on the main display. | 
 |  */ | 
 | bool PhoneWindow::GetTouchPosition(const wxMouseEvent& event, int* pScreenX, | 
 |     int* pScreenY) | 
 | { | 
 |     /* | 
 |      * If the click came from our device window, treat it as a touch. | 
 |      */ | 
 |     if (event.GetEventObject() != mpDeviceWindow[0]) | 
 |         return false; | 
 |  | 
 |     *pScreenX = event.m_x; | 
 |     *pScreenY = event.m_y; | 
 |     return true; | 
 | } | 
 |  | 
 | /* | 
 |  * We don't want to erase the background now, because it causes flicker | 
 |  * under Windows. | 
 |  */ | 
 | void PhoneWindow::OnErase(wxEraseEvent& WXUNUSED(event)) | 
 | { | 
 |     //printf("erase\n"); | 
 | } | 
 |  | 
 | /* | 
 |  * Paint the phone and any highlighted buttons. | 
 |  * | 
 |  * The device output is drawn by DeviceWindow. | 
 |  */ | 
 | void PhoneWindow::OnPaint(wxPaintEvent& WXUNUSED(event)) | 
 | { | 
 |     int view; | 
 |  | 
 |     /* | 
 |      * Under Mac OS X, the parent window is redrawn every time the child | 
 |      * window is redrawn.  This causes poor performance in the simulator. | 
 |      * If we're being asked to update a region that corresponds exactly | 
 |      * to one of the device output windows, skip the redraw. | 
 |      */ | 
 |     assert(mpViewInfo != NULL); | 
 |     for (view = 0; view < mNumViewInfo; view++) { | 
 |         int displayIndex; | 
 |  | 
 |         displayIndex = mpViewInfo[view].GetDisplayIndex(); | 
 |         assert(displayIndex >= 0); | 
 |         DeviceWindow* pDeviceWindow = mpDeviceWindow[displayIndex]; | 
 |         assert(pDeviceWindow != NULL); | 
 |  | 
 |         wxRect displayRect = pDeviceWindow->GetRect(); | 
 |         wxRect updateRect = GetUpdateClientRect(); | 
 |  | 
 |         if (displayRect == updateRect) { | 
 |             //printf("(skipping redraw)\n"); | 
 |             return; | 
 |         } | 
 |     } | 
 |  | 
 |     wxBufferedPaintDC dc(this); | 
 |  | 
 |     /* | 
 |      * Erase the background to the currently-specified background color. | 
 |      */ | 
 |     wxColour backColor = GetBackgroundColour(); | 
 |     dc.SetBrush(wxBrush(backColor)); | 
 |     dc.SetPen(wxPen(backColor, 1)); | 
 |     wxRect windowRect(wxPoint(0, 0), GetClientSize()); | 
 |     dc.DrawRectangle(windowRect); | 
 |  | 
 |     PhoneData* pPhoneData = GetPhoneData(); | 
 |     if (pPhoneData == NULL) { | 
 |         fprintf(stderr, "OnPaint: no phone data\n"); | 
 |         return; | 
 |     } | 
 |  | 
 |     PhoneMode* pPhoneMode; | 
 |     PhoneView* pPhoneView; | 
 |     int numImages; | 
 |  | 
 |     pPhoneMode = pPhoneData->GetPhoneMode(GetCurrentMode().ToAscii()); | 
 |     if (pPhoneMode == NULL) { | 
 |         fprintf(stderr, "current mode (%s) not known\n", | 
 |             (const char*) GetCurrentMode().ToAscii()); | 
 |         return; | 
 |     } | 
 |  | 
 |     for (view = 0; view < pPhoneMode->GetNumViews(); view++) { | 
 |         pPhoneView = pPhoneMode->GetPhoneView(view); | 
 |         if (pPhoneView == NULL) { | 
 |             fprintf(stderr, "view %d not found\n", view); | 
 |             return; | 
 |         } | 
 |  | 
 |         /* draw background image and "button patches" */ | 
 |         numImages = pPhoneView->GetBkgImageCount(); | 
 |         for (int i = 0; i < numImages; i++) { | 
 |             const LoadableImage* pLimg = pPhoneView->GetBkgImage(i); | 
 |             wxBitmap* pBitmap = pLimg->GetBitmap(); | 
 |             if (pBitmap != NULL) | 
 |                 dc.DrawBitmap(*pBitmap, | 
 |                     mpViewInfo[view].GetX() + pLimg->GetX(), | 
 |                     mpViewInfo[view].GetY() + pLimg->GetY(), | 
 |                     TRUE); | 
 |         } | 
 |     } | 
 |  | 
 |  | 
 |     /* | 
 |      * Draw button mouse-over highlight. | 
 |      * | 
 |      * Currently we don't do anything different when the button is held down. | 
 |      */ | 
 |     if (mpMOHViewIndex >= 0 && mpMOHButton != NULL) { | 
 |         // button must have graphic, or hit-testing wouldn't have worked | 
 |         assert(mpMOHButton->GetHighlightedBitmap() != NULL); | 
 |         dc.DrawBitmap(*mpMOHButton->GetHighlightedBitmap(), | 
 |             mpViewInfo[mpMOHViewIndex].GetX() + mpMOHButton->GetX(), | 
 |             mpViewInfo[mpMOHViewIndex].GetY() + mpMOHButton->GetY(), | 
 |             TRUE); | 
 |     } | 
 |  | 
 |     /* | 
 |      * Highlight pressed keys.  We want to do this in all views, because | 
 |      * some buttons on the side of the phone might be visible in more | 
 |      * than one view. | 
 |      */ | 
 |     for (view = 0; view < pPhoneMode->GetNumViews(); view++) { | 
 |         pPhoneView = pPhoneMode->GetPhoneView(view); | 
 |         assert(pPhoneView != NULL); | 
 |  | 
 |         ListIter iter; | 
 |         for (iter = mPressedKeys.begin(); iter != mPressedKeys.end(); ++iter) { | 
 |             KeyCode keyCode; | 
 |             PhoneButton* pButton; | 
 |  | 
 |             keyCode = (*iter).GetKeyCode(); | 
 |             pButton = pPhoneView->FindButtonByKey(keyCode); | 
 |             if (pButton != NULL) { | 
 |                 wxBitmap* pBitmap = pButton->GetSelectedBitmap(); | 
 |                 if (pBitmap != NULL) { | 
 |                     dc.DrawBitmap(*pBitmap, | 
 |                         mpViewInfo[view].GetX() + pButton->GetX(), | 
 |                         mpViewInfo[view].GetY() + pButton->GetY(), | 
 |                         TRUE); | 
 |                 } | 
 |             } | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 |  | 
 | /* | 
 |  * Press a key on the device. | 
 |  * | 
 |  * Schedules a screen refresh if the set of held-down keys changes. | 
 |  */ | 
 | void PhoneWindow::AddPressedKey(KeyCode keyCode) | 
 | { | 
 |     /* | 
 |      * See if the key is already down.  This usually means that the key | 
 |      * repeat has kicked into gear.  It could also mean that we | 
 |      * missed the key-up event, or the user has hit the same device | 
 |      * key with both mouse and keyboard.  Either way, we don't add it | 
 |      * a second time.  This way, if we did lose a key-up somehow, they | 
 |      * can "clear" the stuck key by hitting it again. | 
 |      */ | 
 |     if (keyCode == kKeyCodeUnknown) { | 
 |         //printf("--- not adding kKeyCodeUnknown!\n"); | 
 |         return; | 
 |     } | 
 |  | 
 |     ListIter iter; | 
 |     for (iter = mPressedKeys.begin(); iter != mPressedKeys.end(); ++iter) { | 
 |         if ((*iter).GetKeyCode() == keyCode) | 
 |             break; | 
 |     } | 
 |     if (iter == mPressedKeys.end()) { | 
 |         KeyInfo newInfo; | 
 |         newInfo.SetKeyCode(keyCode); | 
 |         mPressedKeys.push_back(newInfo); | 
 |         //printf("---  added down=%d\n", keyCode); | 
 |         Refresh();      // redraw w/ highlight | 
 |     } else { | 
 |         //printf("---  already have down=%d\n", keyCode); | 
 |     } | 
 | } | 
 |  | 
 | /* | 
 |  * Release a key on the device. | 
 |  * | 
 |  * Schedules a screen refresh if the set of held-down keys changes. | 
 |  */ | 
 | void PhoneWindow::RemovePressedKey(KeyCode keyCode) | 
 | { | 
 |     /* | 
 |      * Release the key.  If it's not in the list, we either missed a | 
 |      * key-down event, or the user used both mouse and keyboard and we | 
 |      * removed the key when the first device went up. | 
 |      */ | 
 |     ListIter iter; | 
 |     for (iter = mPressedKeys.begin(); iter != mPressedKeys.end(); ++iter) { | 
 |         if ((*iter).GetKeyCode() == keyCode) { | 
 |             mPressedKeys.erase(iter); | 
 |             //printf("---  removing down=%d\n", keyCode); | 
 |             Refresh();      // redraw w/o highlight | 
 |             break; | 
 |         } | 
 |     } | 
 |     if (iter == mPressedKeys.end()) { | 
 |         //printf("---  didn't find down=%d\n", keyCode); | 
 |     } | 
 | } | 
 |  | 
 | /* | 
 |  * Clear the set of keys that we think are being held down. | 
 |  */ | 
 | void PhoneWindow::ClearPressedKeys(void) | 
 | { | 
 |     //printf("--- All keys up (count=%d)\n", mPressedKeys.size()); | 
 |  | 
 |     if (!mPressedKeys.empty()) { | 
 |         ListIter iter = mPressedKeys.begin(); | 
 |         while (iter != mPressedKeys.end()) { | 
 |             KeyCode keyCode = (*iter).GetKeyCode(); | 
 |             GetDeviceManager()->SendKeyEvent(keyCode, false); | 
 |             iter = mPressedKeys.erase(iter); | 
 |         } | 
 |         Refresh(); | 
 |     } | 
 | } | 
 |  | 
 | /* | 
 |  * Returns "true" if the specified key is currently pressed. | 
 |  */ | 
 | bool PhoneWindow::IsKeyPressed(KeyCode keyCode) | 
 | { | 
 |     ListIter iter; | 
 |     for (iter = mPressedKeys.begin(); iter != mPressedKeys.end(); ++iter) { | 
 |         if ((*iter).GetKeyCode() == keyCode) | 
 |             return true; | 
 |     } | 
 |     return false; | 
 | } | 
 |  | 
 | void PhoneWindow::Vibrate(int vibrateOn) | 
 | { | 
 |     wxRect rect = GetRect(); | 
 |     if(vibrateOn) | 
 |     { | 
 |         mVibrateX = 0; | 
 |         mTimer.Start(25);      // arg is delay in ms | 
 |         Move(rect.x-2,rect.y); | 
 |     } | 
 |     else if(mTimer.IsRunning()) | 
 |     { | 
 |         mTimer.Stop(); | 
 |         if(mVibrateX&1) | 
 |             Move(rect.x-2,rect.y); | 
 |         else | 
 |             Move(rect.x+2,rect.y); | 
 |     } | 
 | } | 
 |  | 
 | void PhoneWindow::OnTimer(wxTimerEvent& event) | 
 | { | 
 |     wxRect rect = GetRect(); | 
 |     mVibrateX++; | 
 |     if(mVibrateX&1) | 
 |         Move(rect.x+4,rect.y); | 
 |     else | 
 |         Move(rect.x-4,rect.y); | 
 | } |