Home | History | Annotate | Download | only in app
      1 //
      2 // Copyright 2005 The Android Open Source Project
      3 //
      4 // Displays the phone image and handles user input.
      5 //
      6 
      7 // For compilers that support precompilation, include "wx/wx.h".
      8 #include "wx/wxprec.h"
      9 
     10 // Otherwise, include all standard headers
     11 #ifndef WX_PRECOMP
     12 # include "wx/wx.h"
     13 #endif
     14 #include "wx/image.h"   // needed for Windows build
     15 #include "wx/dcbuffer.h"
     16 
     17 #include "LinuxKeys.h"
     18 #include "PhoneWindow.h"
     19 #include "DeviceWindow.h"
     20 #include "PhoneData.h"
     21 #include "PhoneCollection.h"
     22 #include "MainFrame.h"
     23 #include "MyApp.h"
     24 
     25 using namespace android;
     26 
     27 BEGIN_EVENT_TABLE(PhoneWindow, wxWindow)    // NOT wxDialog
     28     EVT_ACTIVATE(PhoneWindow::OnActivate)
     29     //EVT_ACTIVATE_APP(PhoneWindow::OnActivate)
     30     EVT_CLOSE(PhoneWindow::OnClose)
     31     EVT_MOVE(PhoneWindow::OnMove)
     32     EVT_ERASE_BACKGROUND(PhoneWindow::OnErase)
     33     EVT_PAINT(PhoneWindow::OnPaint)
     34 
     35     EVT_KEY_DOWN(PhoneWindow::OnKeyDown)
     36     EVT_KEY_UP(PhoneWindow::OnKeyUp)
     37     EVT_LEFT_DOWN(PhoneWindow::OnMouseLeftDown)
     38     EVT_LEFT_DCLICK(PhoneWindow::OnMouseLeftDown)
     39     EVT_LEFT_UP(PhoneWindow::OnMouseLeftUp)
     40     EVT_RIGHT_DOWN(PhoneWindow::OnMouseRightDown)
     41     EVT_RIGHT_DCLICK(PhoneWindow::OnMouseRightDown)
     42     EVT_RIGHT_UP(PhoneWindow::OnMouseRightUp)
     43     EVT_MOTION(PhoneWindow::OnMouseMotion)
     44     EVT_LEAVE_WINDOW(PhoneWindow::OnMouseLeaveWindow)
     45     EVT_TIMER(kVibrateTimerId, PhoneWindow::OnTimer)
     46 END_EVENT_TABLE()
     47 
     48 
     49 /*
     50  * Create a new PhoneWindow.  This should be a child of the main frame.
     51  */
     52 PhoneWindow::PhoneWindow(wxWindow* parent, const wxPoint& posn)
     53     : wxDialog(parent, wxID_ANY, wxT("Device"), posn, wxDefaultSize,
     54         wxDEFAULT_DIALOG_STYLE),
     55       mpMOHViewIndex(-1),
     56       mpMOHButton(NULL),
     57       mMouseKeySent(AKEYCODE_UNKNOWN),
     58       mpViewInfo(NULL),
     59       mNumViewInfo(0),
     60       mpDeviceWindow(NULL),
     61       mNumDeviceWindows(0),
     62       mPhoneModel(-1),
     63       mCurrentMode(wxT("(unknown)")),
     64       mPlacementChecked(false),
     65       mpParent((MainFrame*)parent),
     66       mTimer(this, kVibrateTimerId),
     67       mTrackingTouch(false)
     68 {
     69     SetBackgroundColour(*wxLIGHT_GREY);
     70     SetBackgroundStyle(wxBG_STYLE_CUSTOM);
     71 
     72     //SetCursor(wxCursor(wxCURSOR_HAND));     // a bit distracting (pg.276)
     73 }
     74 
     75 /*
     76  * Destroy everything we own.
     77  *
     78  * This might be called well after we've been closed and another
     79  * PhoneWindow has been created, because wxWidgets likes to defer things.
     80  */
     81 PhoneWindow::~PhoneWindow(void)
     82 {
     83     //printf("--- ~PhoneWindow %p\n", this);
     84     delete[] mpViewInfo;
     85     if (mpDeviceWindow != NULL) {
     86         for (int i = 0; i < mNumDeviceWindows; i++) {
     87             /* make sure they don't try to use our member */
     88             mpDeviceWindow[i]->DeviceManagerClosing();
     89             /* make sure the child window gets destroyed -- not necessary? */
     90             mpDeviceWindow[i]->Destroy();
     91         }
     92 
     93         /* delete our array of pointers */
     94         delete[] mpDeviceWindow;
     95     }
     96 }
     97 
     98 /*
     99  * Check for an updated runtime when window becomes active
    100  */
    101 void PhoneWindow::OnActivate(wxActivateEvent& event)
    102 {
    103     /*
    104      * DO NOT do this.  Under Windows, it causes the parent window to get
    105      * an activate event, which causes our parent to get the focus.  With
    106      * this bit of code active it is impossible for the phone window to
    107      * receive user input.
    108      */
    109     //GetParent()->AddPendingEvent(event);
    110 
    111     // If we are being deactivated, go ahead and send key up events so that the
    112     // runtime doesn't think we are holding down the key. Issue #685750
    113     if (!event.GetActive()) {
    114         ListIter iter;
    115         for (iter = mPressedKeys.begin(); iter != mPressedKeys.end(); ) {
    116             int32_t keyCode = (*iter).GetKeyCode();
    117             GetDeviceManager()->SendKeyEvent(keyCode, false);
    118             iter = mPressedKeys.erase(iter);
    119         }
    120     }
    121 }
    122 
    123 /*
    124  * Close the phone window.
    125  */
    126 void PhoneWindow::OnClose(wxCloseEvent& event)
    127 {
    128     //printf("--- PhoneWindow::OnClose %p\n", this);
    129 #if 0
    130     if (mDeviceManager.IsRunning() && !mDeviceManager.IsKillable()) {
    131         printf("Sim: refusing to close window on external runtime\n");
    132         event.Veto();
    133         return;
    134     }
    135 #endif
    136 
    137     wxRect rect = GetRect();
    138     printf("Sim: Closing phone window (posn=(%d,%d))\n", rect.x, rect.y);
    139 
    140     /* notify others */
    141     mpParent->PhoneWindowClosing(rect.x, rect.y);
    142     mDeviceManager.WindowsClosing();
    143 
    144     /* end it all */
    145     Destroy();
    146 }
    147 
    148 /*
    149  * Prep the PhoneWindow to display a specific phone model.  Pass in the
    150  * model index.
    151  *
    152  * This gets called whenever the display changes.  This could be a new
    153  * device with identical characteristics, or a different mode for the same
    154  * device.
    155  *
    156  * The window can be re-used so long as the display characteristics are
    157  * the same.  If the display characteristics are different, we have to
    158  * restart the device.
    159  */
    160 bool PhoneWindow::Setup(int phoneIdx)
    161 {
    162     wxString fileName;
    163     PhoneCollection* pCollection = PhoneCollection::GetInstance();
    164 
    165     if (phoneIdx < 0 || phoneIdx >= pCollection->GetPhoneCount()) {
    166         fprintf(stderr, "Bogus phone index %d\n", phoneIdx);
    167         return false;
    168     }
    169 
    170     /*
    171      * Clear these out so that failure here is noticeable to caller.  We
    172      * regenerate the ViewInfo array every time, because the set of Views
    173      * is different for every Mode.
    174      */
    175     delete[] mpViewInfo;
    176     mpViewInfo = NULL;
    177     mNumViewInfo = -1;
    178 
    179     PhoneData* pPhoneData;
    180     PhoneMode* pPhoneMode;
    181     PhoneView* pPhoneView;
    182 
    183     pPhoneData = pCollection->GetPhoneData(phoneIdx);
    184 
    185     pPhoneMode = pPhoneData->GetPhoneMode(GetCurrentMode().ToAscii());
    186     if (pPhoneMode == NULL) {
    187         fprintf(stderr, "current mode (%s) not known\n",
    188             (const char*) GetCurrentMode().ToAscii());
    189         return false;
    190     }
    191 
    192     int numViews = pPhoneMode->GetNumViews();
    193     if (numViews == 0) {
    194         fprintf(stderr, "Phone %d mode %s has no views\n",
    195             phoneIdx, pPhoneMode->GetName());
    196         return false;
    197     }
    198 
    199     const int kBorder = 2;
    200     int i;
    201     int maxHeight = 0;
    202     int fullWidth = kBorder;
    203     ViewInfo* pViewInfo;
    204 
    205     pViewInfo = new ViewInfo[numViews];
    206 
    207     /* figure out individual and overall dimensions */
    208     for (i = 0; i < numViews; i++) {
    209         pPhoneView = pPhoneMode->GetPhoneView(i);
    210         if (pPhoneView == NULL) {
    211             fprintf(stderr, "view %d not found\n", i);
    212             return false;
    213         }
    214 
    215         if (!GetDimensions(pPhoneData, pPhoneView, &pViewInfo[i]))
    216             return false;
    217 
    218         if (maxHeight < pViewInfo[i].GetHeight())
    219             maxHeight = pViewInfo[i].GetHeight();
    220         fullWidth += pViewInfo[i].GetWidth() + kBorder;
    221     }
    222 
    223     /* create the device windows if we don't already have them */
    224     if (mpDeviceWindow == NULL) {
    225         mNumDeviceWindows = pPhoneData->GetNumDisplays();
    226         mpDeviceWindow = new DeviceWindow*[mNumDeviceWindows];
    227         if (mpDeviceWindow == NULL)
    228             return false;
    229 
    230         for (i = 0; i < mNumDeviceWindows; i++) {
    231             mpDeviceWindow[i] = new DeviceWindow(this, &mDeviceManager);
    232         }
    233     } else {
    234         assert(pPhoneData->GetNumDisplays() == mNumDeviceWindows);
    235     }
    236 
    237     /*
    238      * Position device windows within their views, taking into account
    239      * border areas.
    240      */
    241     int shift = kBorder;
    242     for (i = 0; i < numViews; i++) {
    243         int displayIdx;
    244         PhoneDisplay* pPhoneDisplay;
    245 
    246         displayIdx = pViewInfo[i].GetDisplayIndex();
    247         pPhoneDisplay = pPhoneData->GetPhoneDisplay(displayIdx);
    248         //printf("View %d: display %d\n", i, displayIdx);
    249 
    250         pViewInfo[i].SetX(shift);
    251         pViewInfo[i].SetY(kBorder);
    252 
    253         mpDeviceWindow[displayIdx]->SetSize(
    254             pViewInfo[i].GetX() + pViewInfo[i].GetDisplayX(),
    255             pViewInfo[i].GetY() + pViewInfo[i].GetDisplayY(),
    256             pPhoneDisplay->GetWidth(), pPhoneDisplay->GetHeight());
    257 
    258         // incr by width of view
    259         shift += pViewInfo[i].GetWidth() + kBorder;
    260     }
    261 
    262     /* configure the device manager if it's not already running */
    263     if (!mDeviceManager.IsInitialized()) {
    264         mDeviceManager.Init(pPhoneData->GetNumDisplays(), mpParent);
    265 
    266         for (i = 0; i < pPhoneData->GetNumDisplays(); i++) {
    267             PhoneDisplay* pPhoneDisplay;
    268             bool res;
    269 
    270             pPhoneDisplay = pPhoneData->GetPhoneDisplay(i);
    271 
    272             res = mDeviceManager.SetDisplayConfig(i, mpDeviceWindow[i],
    273                 pPhoneDisplay->GetWidth(), pPhoneDisplay->GetHeight(),
    274                 pPhoneDisplay->GetFormat(), pPhoneDisplay->GetRefresh());
    275             if (!res) {
    276                 fprintf(stderr, "Sim: ERROR: could not configure device mgr\n");
    277                 return false;
    278             }
    279         }
    280         const char *kmap = pPhoneData->GetPhoneKeyboard(0)->getKeyMap();
    281         mDeviceManager.SetKeyboardConfig(kmap);
    282     } else {
    283         assert(pPhoneData->GetNumDisplays() == mDeviceManager.GetNumDisplays());
    284     }
    285 
    286     /*
    287      * Success.  Finish up.
    288      */
    289     mPhoneModel = phoneIdx;
    290     mpViewInfo = pViewInfo;
    291     mNumViewInfo = numViews;
    292 
    293     /* set up our window */
    294     SetClientSize(fullWidth, maxHeight + kBorder * 2);
    295     SetBackgroundColour(*wxLIGHT_GREY);
    296     //SetBackgroundColour(*wxBLUE);
    297     SetTitle(wxString::FromAscii(pPhoneData->GetTitle()));
    298 
    299     SetFocus();     // set keyboard input focus
    300 
    301     return true;
    302 }
    303 
    304 /*
    305  * The device table has been reloaded.  We need to throw out any pointers
    306  * we had into it and possibly reload some stuff.
    307  */
    308 void PhoneWindow::DevicesRescanned(void)
    309 {
    310     mpMOHButton = NULL;
    311     mpMOHViewIndex = -1;
    312 
    313     /*
    314      * Re-evaluate phone definition.  There is an implicit assumption
    315      * that the re-scanned version is compatible with the previous
    316      * version (i.e. it still exists and has the same screen size).
    317      *
    318      * We're also currently assuming that no phone definitions have been
    319      * added or removed, which is bad -- we should get the new index for
    320      * for phone by searching for it by name.
    321      *
    322      * TODO: don't make these assumptions.
    323      */
    324     Setup(mPhoneModel);
    325 }
    326 
    327 /*
    328  * Check the initial placement of the window.  We get one of these messages
    329  * when the window is first placed, and every time it's moved thereafter.
    330  *
    331  * Right now we're just trying to make sure wxWidgets doesn't shove it off
    332  * the top of the screen under Linux.  Might want to change this to
    333  * remember the previous placement and put the window back.
    334  */
    335 void PhoneWindow::OnMove(wxMoveEvent& event)
    336 {
    337     if (mPlacementChecked)
    338         return;
    339 
    340     wxPoint point;
    341     point = event.GetPosition();
    342     if (point.y < 0) {
    343         printf("Sim: window is at (%d,%d), adjusting\n", point.x, point.y);
    344         point.y = 0;
    345         Move(point);
    346     }
    347 
    348     mPlacementChecked = true;
    349 }
    350 
    351 /*
    352  * Figure out the dimensions required to contain the specified view.
    353  *
    354  * This is usually the size of the background image, but if we can't
    355  * load it or it's too small just create a trivial window.
    356  */
    357 bool PhoneWindow::GetDimensions(PhoneData* pPhoneData, PhoneView* pPhoneView,
    358     ViewInfo* pInfo)
    359 {
    360     PhoneDisplay* pPhoneDisplay;
    361     int xoff=0, yoff=0, width, height;
    362     int displayIdx;
    363 
    364     displayIdx = pPhoneData->GetPhoneDisplayIndex(pPhoneView->GetDisplayName());
    365     if (displayIdx < 0)
    366         return false;
    367 
    368     pPhoneDisplay = pPhoneData->GetPhoneDisplay(displayIdx);
    369     if (pPhoneDisplay == NULL) {
    370         fprintf(stderr, "display '%s' not found in device '%s'\n",
    371             pPhoneView->GetDisplayName(), pPhoneData->GetName());
    372         return false;
    373     }
    374 
    375     // load images for this phone
    376     (void) pPhoneView->LoadResources();
    377 
    378     width = height = 0;
    379 
    380     // by convention, the background bitmap is the first image in the list
    381     if (pPhoneView->GetBkgImageCount() > 0) {
    382         wxBitmap* pBitmap = pPhoneView->GetBkgImage(0)->GetBitmap();
    383         if (pBitmap != NULL) {
    384             // size window to match bitmap
    385             xoff = pPhoneView->GetXOffset();
    386             yoff = pPhoneView->GetYOffset();
    387             width = pBitmap->GetWidth();
    388             height = pBitmap->GetHeight();
    389         }
    390     }
    391 
    392     // no bitmap, or bitmap is smaller than display
    393     if (width < pPhoneDisplay->GetWidth() ||
    394         height < pPhoneDisplay->GetHeight())
    395     {
    396         // create window to just hold display
    397         xoff = yoff = 0;
    398         width = pPhoneDisplay->GetWidth();
    399         height = pPhoneDisplay->GetHeight();
    400     }
    401     if (width <= 0 || height <= 0) {
    402         fprintf(stderr, "ERROR: couldn't determine display size\n");
    403         return false;
    404     }
    405 
    406     pInfo->SetX(0);
    407     pInfo->SetY(0);             // another function determines these
    408     pInfo->SetDisplayX(xoff);
    409     pInfo->SetDisplayY(yoff);
    410     pInfo->SetWidth(width);
    411     pInfo->SetHeight(height);
    412     pInfo->SetDisplayIndex(displayIdx);
    413 
    414     //printf("xoff=%d yoff=%d width=%d height=%d index=%d\n",
    415     //    pInfo->GetDisplayX(), pInfo->GetDisplayY(),
    416     //    pInfo->GetWidth(), pInfo->GetHeight(), pInfo->GetDisplayIndex());
    417 
    418     return true;
    419 }
    420 
    421 /*
    422  * Return PhoneData pointer for the current phone model.
    423  */
    424 PhoneData* PhoneWindow::GetPhoneData(void) const
    425 {
    426     PhoneCollection* pCollection = PhoneCollection::GetInstance();
    427     return pCollection->GetPhoneData(mPhoneModel);
    428 }
    429 
    430 /*
    431  * Convert a wxWidgets key code into a device key code.
    432  *
    433  * Someday we may want to make this configurable.
    434  *
    435  * NOTE: we need to create a mapping between simulator key and desired
    436  * function.  The "return" key should always mean "select", whether
    437  * it's a "select" button or pressing in on the d-pad.  Ditto for
    438  * the arrow keys, whether we have a joystick, d-pad, or four buttons.
    439  * Each key here should have a set of things that it could possibly be,
    440  * and we match it up with the set of buttons actually defined for the
    441  * phone.  [for convenience, need to ensure that buttons need not have
    442  * an associated image]
    443  */
    444 int PhoneWindow::ConvertKeyCode(int wxKeyCode) const
    445 {
    446     switch (wxKeyCode) {
    447     case WXK_NUMPAD_INSERT:
    448     case WXK_NUMPAD0:
    449     case '0':                   return KEY_0;
    450     case WXK_NUMPAD_HOME:
    451     case WXK_NUMPAD1:
    452     case '1':                   return KEY_1;
    453     case WXK_NUMPAD_UP:
    454     case WXK_NUMPAD2:
    455     case '2':                   return KEY_2;
    456     case WXK_NUMPAD_PRIOR:
    457     case WXK_NUMPAD3:
    458     case '3':                   return KEY_3;
    459     case WXK_NUMPAD_LEFT:
    460     case WXK_NUMPAD4:
    461     case '4':                   return KEY_4;
    462     case WXK_NUMPAD_BEGIN:
    463     case WXK_NUMPAD5:
    464     case '5':                   return KEY_5;
    465     case WXK_NUMPAD_RIGHT:
    466     case WXK_NUMPAD6:
    467     case '6':                   return KEY_6;
    468     case WXK_NUMPAD_END:
    469     case WXK_NUMPAD7:
    470     case '7':                   return KEY_7;
    471     case WXK_NUMPAD_DOWN:
    472     case WXK_NUMPAD8:
    473     case '8':                   return KEY_8;
    474     case WXK_NUMPAD_NEXT:
    475     case WXK_NUMPAD9:
    476     case '9':                   return KEY_9;
    477     case WXK_NUMPAD_MULTIPLY:   return KEY_SWITCHVIDEOMODE; //AKEYCODE_STAR;
    478     case WXK_LEFT:              return KEY_LEFT;
    479     case WXK_RIGHT:             return KEY_RIGHT;
    480     case WXK_UP:                return KEY_UP;
    481     case WXK_DOWN:              return KEY_DOWN;
    482     case WXK_NUMPAD_ENTER:      return KEY_REPLY; //AKEYCODE_DPAD_CENTER;
    483     case WXK_HOME:              return KEY_HOME;
    484     case WXK_PRIOR:
    485     case WXK_PAGEUP:            return KEY_MENU; //AKEYCODE_SOFT_LEFT;
    486     case WXK_NEXT:
    487     case WXK_PAGEDOWN:          return KEY_KBDILLUMUP; //AKEYCODE_SOFT_RIGHT;
    488     case WXK_DELETE:
    489     case WXK_BACK:              return KEY_BACKSPACE; //AKEYCODE_DEL;
    490     case WXK_ESCAPE:
    491     case WXK_END:               return KEY_BACK; //AKEYCODE_BACK;
    492     case WXK_NUMPAD_DELETE:
    493     case WXK_NUMPAD_DECIMAL:    return KEY_KBDILLUMTOGGLE; //AKEYCODE_POUND;
    494     case WXK_SPACE:             return KEY_SPACE; //AKEYCODE_SPACE;
    495     case WXK_RETURN:            return KEY_ENTER; //AKEYCODE_ENTER;
    496     case WXK_F3:                return KEY_F3; //AKEYCODE_CALL;
    497     case WXK_F4:                return KEY_F4; //AKEYCODE_END_CALL;
    498     case WXK_NUMPAD_ADD:
    499     case WXK_F5:                return KEY_VOLUMEUP;
    500     case WXK_NUMPAD_SUBTRACT:
    501     case WXK_F6:                return KEY_VOLUMEDOWN;
    502     case WXK_F7:                return KEY_POWER;
    503     case WXK_F8:                return KEY_CAMERA;
    504     case 'A':                   return KEY_A;
    505     case 'B':                   return KEY_B;
    506     case 'C':                   return KEY_C;
    507     case 'D':                   return KEY_D;
    508     case 'E':                   return KEY_E;
    509     case 'F':                   return KEY_F;
    510     case 'G':                   return KEY_G;
    511     case 'H':                   return KEY_H;
    512     case 'I':                   return KEY_I;
    513     case 'J':                   return KEY_J;
    514     case 'K':                   return KEY_K;
    515     case 'L':                   return KEY_L;
    516     case 'M':                   return KEY_M;
    517     case 'N':                   return KEY_N;
    518     case 'O':                   return KEY_O;
    519     case 'P':                   return KEY_P;
    520     case 'Q':                   return KEY_Q;
    521     case 'R':                   return KEY_R;
    522     case 'S':                   return KEY_S;
    523     case 'T':                   return KEY_T;
    524     case 'U':                   return KEY_U;
    525     case 'V':                   return KEY_V;
    526     case 'W':                   return KEY_W;
    527     case 'X':                   return KEY_X;
    528     case 'Y':                   return KEY_Y;
    529     case 'Z':                   return KEY_Z;
    530     case ',':                   return KEY_COMMA;
    531     case '.':                   return KEY_DOT;
    532     case '<':                   return KEY_COMMA;
    533     case '>':                   return KEY_DOT;
    534     case '`':                   return KEY_GREEN; /*KEY_GRAVE;*/
    535     case '-':                   return KEY_MINUS;
    536     case '=':                   return KEY_EQUAL;
    537     case '[':                   return KEY_LEFTBRACE;
    538     case ']':                   return KEY_RIGHTBRACE;
    539     case '\\':                  return KEY_BACKSLASH;
    540     case ';':                   return KEY_SEMICOLON;
    541     case '\'':                  return KEY_APOSTROPHE;
    542     case '/':                   return KEY_SLASH;
    543     case WXK_SHIFT:             return KEY_LEFTSHIFT;
    544     case WXK_CONTROL:
    545     case WXK_ALT:               return KEY_LEFTALT;
    546     case WXK_TAB:               return KEY_TAB;
    547     // don't show "ignoring key" message for these
    548     case WXK_MENU:
    549         break;
    550     default:
    551         printf("(ignoring key %d)\n", wxKeyCode);
    552         break;
    553     }
    554 
    555     return AKEYCODE_UNKNOWN;
    556 }
    557 
    558 
    559 /*
    560  * Keyboard handling.  These get converted into Android-defined key
    561  * constants here.
    562  *
    563  * NOTE: would be nice to handle menu keyboard accelerators here.
    564  * Simply stuffing the key events into MainFrame with AddPendingEvent
    565  * didn't seem to do the trick.
    566  */
    567 void PhoneWindow::OnKeyDown(wxKeyEvent& event)
    568 {
    569     int32_t keyCode;
    570 
    571     keyCode = ConvertKeyCode(event.GetKeyCode());
    572     if (keyCode != AKEYCODE_UNKNOWN) {
    573         if (!IsKeyPressed(keyCode)) {
    574             //printf("PW: down: key %d\n", keyCode);
    575             GetDeviceManager()->SendKeyEvent(keyCode, true);
    576             AddPressedKey(keyCode);
    577         }
    578     } else {
    579         //printf("PW: down: %d\n", event.GetKeyCode());
    580         event.Skip();       // not handled by us
    581     }
    582 }
    583 
    584 /*
    585  * Pass key-up events to runtime.
    586  */
    587 void PhoneWindow::OnKeyUp(wxKeyEvent& event)
    588 {
    589     int32_t keyCode;
    590 
    591     keyCode = ConvertKeyCode(event.GetKeyCode());
    592     if (keyCode != AKEYCODE_UNKNOWN) {
    593         // Send the key event if we already have this key pressed.
    594         if (IsKeyPressed(keyCode)) {
    595             //printf("PW:   up: key %d\n", keyCode);
    596             GetDeviceManager()->SendKeyEvent(keyCode, false);
    597             RemovePressedKey(keyCode);
    598         }
    599     } else {
    600         //printf("PW:   up: %d\n", event.GetKeyCode());
    601         event.Skip();       // not handled by us
    602     }
    603 }
    604 
    605 /*
    606  * Mouse handling.
    607  *
    608  * Unlike more conventional button tracking, we highlight on mouse-over
    609  * and send the key on mouse-down.  This behavior may be confusing for
    610  * people expecting standard behavior, but it allows us to simulate the
    611  * effect of holding a key down.
    612  *
    613  * We want to catch both "down" and "double click" events; otherwise
    614  * fast clicking results in a lot of discarded events.
    615  */
    616 void PhoneWindow::OnMouseLeftDown(wxMouseEvent& event)
    617 {
    618     if (mpMOHButton != NULL) {
    619         //printf("PW: left down\n");
    620         int32_t keyCode = mpMOHButton->GetKeyCode();
    621         GetDeviceManager()->SendKeyEvent(keyCode, true);
    622         mMouseKeySent = keyCode;
    623         AddPressedKey(keyCode);
    624     } else {
    625         int screenX, screenY;
    626 
    627         if (GetTouchPosition(event, &screenX, &screenY)) {
    628             //printf("TOUCH at %d,%d\n", screenX, screenY);
    629             mTrackingTouch = true;
    630             mTouchX = screenX;
    631             mTouchY = screenY;
    632             GetDeviceManager()->SendTouchEvent(Simulator::kTouchDown,
    633                 mTouchX, mTouchY);
    634         } else {
    635             //printf("(ignoring left click)\n");
    636         }
    637     }
    638 }
    639 
    640 /*
    641  * Left button has been released.  Do something clever.
    642  *
    643  * On some platforms we will lose this if the mouse leaves the window.
    644  */
    645 void PhoneWindow::OnMouseLeftUp(wxMouseEvent& WXUNUSED(event))
    646 {
    647     if (mMouseKeySent != AKEYCODE_UNKNOWN) {
    648         //printf("PW: left up\n");
    649         GetDeviceManager()->SendKeyEvent(mMouseKeySent, false);
    650         RemovePressedKey(mMouseKeySent);
    651     } else {
    652         if (mTrackingTouch) {
    653             //printf("TOUCH release (last was %d,%d)\n", mTouchX, mTouchY);
    654             mTrackingTouch = false;
    655             GetDeviceManager()->SendTouchEvent(Simulator::kTouchUp,
    656                 mTouchX, mTouchY);
    657         } else {
    658             //printf("(ignoring left-up)\n");
    659         }
    660     }
    661     mMouseKeySent = AKEYCODE_UNKNOWN;
    662 }
    663 
    664 void PhoneWindow::OnMouseRightDown(wxMouseEvent& event)
    665 {
    666     //printf("(ignoring right-down)\n");
    667 }
    668 void PhoneWindow::OnMouseRightUp(wxMouseEvent& event)
    669 {
    670     //printf("(ignoring right-up)\n");
    671 }
    672 
    673 /*
    674  * Track mouse motion so we can do mouse-over button highlighting.
    675  */
    676 void PhoneWindow::OnMouseMotion(wxMouseEvent& event)
    677 {
    678     /*
    679      * If the mouse motion event occurred inside the device window,
    680      * we treat it differently than mouse movement over the picture of
    681      * the device.
    682      */
    683     if (event.GetEventObject() == mpDeviceWindow[0]) {
    684         if (mpMOHViewIndex >= 0) {
    685             /* can happen if the mouse moves fast enough */
    686             //printf("Mouse now in dev window, clearing button highlight\n");
    687             mpMOHViewIndex = -1;
    688             mpMOHButton = NULL;
    689             Refresh();
    690         }
    691 
    692         if (!event.LeftIsDown() && event.RightIsDown()) {
    693             /* right-button movement */
    694             //printf("(ignoring right-drag)\n");
    695             return;
    696         }
    697 
    698         //printf("moveto: %d,%d\n", event.m_x, event.m_y);
    699 
    700         int screenX, screenY;
    701         if (mTrackingTouch) {
    702             if (GetTouchPosition(event, &screenX, &screenY)) {
    703                 //printf("TOUCH moved to %d,%d\n", screenX, screenY);
    704                 mTouchX = screenX;
    705                 mTouchY = screenY;
    706                 GetDeviceManager()->SendTouchEvent(Simulator::kTouchDrag,
    707                     mTouchX, mTouchY);
    708             } else {
    709                 //printf("TOUCH moved off screen\n");
    710             }
    711         }
    712 
    713         return;
    714     }
    715 
    716     PhoneData* pPhoneData = GetPhoneData();
    717     if (pPhoneData == NULL)
    718         return;
    719 
    720     /*
    721      * Check to see if we're on top of a button.  If our "on top of
    722      * something" state has changed, force a redraw.
    723      *
    724      * We have to run through the list of Views and check all of the
    725      * buttons in each.
    726      */
    727     PhoneMode* pMode = pPhoneData->GetPhoneMode(GetCurrentMode().ToAscii());
    728     if (pMode == NULL)
    729         return;
    730 
    731     int viewIndex = -1;
    732     PhoneButton* pHighlight = NULL;
    733     int i;
    734 
    735     for (i = pMode->GetNumViews()-1; i >= 0; i--) {
    736         PhoneView* pView = pMode->GetPhoneView(i);
    737         assert(pView != NULL);
    738 
    739         /* convert from window-relative to view-relative */
    740         pHighlight = pView->FindButtonHit(event.m_x - mpViewInfo[i].GetX(),
    741                                           event.m_y - mpViewInfo[i].GetY());
    742         if (pHighlight != NULL) {
    743             viewIndex = i;
    744             break;
    745         }
    746     }
    747 
    748     if (viewIndex == mpMOHViewIndex && pHighlight == mpMOHButton) {
    749         /* still hovering over same button */
    750     } else {
    751         /* mouse has moved, possibly to a new button */
    752 
    753         mpMOHViewIndex = viewIndex;
    754         mpMOHButton = pHighlight;
    755 
    756         /* force refresh */
    757         Refresh();
    758     }
    759 }
    760 
    761 /*
    762  * Mouse has left the building.  All keys and mouse buttons up.
    763  *
    764  * We get one of these if the mouse moves over a child window, such as
    765  * our DeviceWindow, so it is not the case that we no longer receive
    766  * key input after getting this event.
    767  */
    768 void PhoneWindow::OnMouseLeaveWindow(wxMouseEvent& WXUNUSED(event))
    769 {
    770     //printf("--- mouse is GONE\n");
    771     ClearPressedKeys();
    772 }
    773 
    774 /*
    775  * Determine device touch screen (x,y) based on window position.
    776  *
    777  * Returns "true" if the click corresponds to a location on the display.
    778  *
    779  * TODO: should return display index as well -- currently this only
    780  * supports touch on the main display.
    781  */
    782 bool PhoneWindow::GetTouchPosition(const wxMouseEvent& event, int* pScreenX,
    783     int* pScreenY)
    784 {
    785     /*
    786      * If the click came from our device window, treat it as a touch.
    787      */
    788     if (event.GetEventObject() != mpDeviceWindow[0])
    789         return false;
    790 
    791     *pScreenX = event.m_x;
    792     *pScreenY = event.m_y;
    793     return true;
    794 }
    795 
    796 /*
    797  * We don't want to erase the background now, because it causes flicker
    798  * under Windows.
    799  */
    800 void PhoneWindow::OnErase(wxEraseEvent& WXUNUSED(event))
    801 {
    802     //printf("erase\n");
    803 }
    804 
    805 /*
    806  * Paint the phone and any highlighted buttons.
    807  *
    808  * The device output is drawn by DeviceWindow.
    809  */
    810 void PhoneWindow::OnPaint(wxPaintEvent& WXUNUSED(event))
    811 {
    812     int view;
    813 
    814     /*
    815      * Under Mac OS X, the parent window is redrawn every time the child
    816      * window is redrawn.  This causes poor performance in the simulator.
    817      * If we're being asked to update a region that corresponds exactly
    818      * to one of the device output windows, skip the redraw.
    819      */
    820     assert(mpViewInfo != NULL);
    821     for (view = 0; view < mNumViewInfo; view++) {
    822         int displayIndex;
    823 
    824         displayIndex = mpViewInfo[view].GetDisplayIndex();
    825         assert(displayIndex >= 0);
    826         DeviceWindow* pDeviceWindow = mpDeviceWindow[displayIndex];
    827         assert(pDeviceWindow != NULL);
    828 
    829         wxRect displayRect = pDeviceWindow->GetRect();
    830         wxRect updateRect = GetUpdateClientRect();
    831 
    832         if (displayRect == updateRect) {
    833             //printf("(skipping redraw)\n");
    834             return;
    835         }
    836     }
    837 
    838     wxBufferedPaintDC dc(this);
    839 
    840     /*
    841      * Erase the background to the currently-specified background color.
    842      */
    843     wxColour backColor = GetBackgroundColour();
    844     dc.SetBrush(wxBrush(backColor));
    845     dc.SetPen(wxPen(backColor, 1));
    846     wxRect windowRect(wxPoint(0, 0), GetClientSize());
    847     dc.DrawRectangle(windowRect);
    848 
    849     PhoneData* pPhoneData = GetPhoneData();
    850     if (pPhoneData == NULL) {
    851         fprintf(stderr, "OnPaint: no phone data\n");
    852         return;
    853     }
    854 
    855     PhoneMode* pPhoneMode;
    856     PhoneView* pPhoneView;
    857     int numImages;
    858 
    859     pPhoneMode = pPhoneData->GetPhoneMode(GetCurrentMode().ToAscii());
    860     if (pPhoneMode == NULL) {
    861         fprintf(stderr, "current mode (%s) not known\n",
    862             (const char*) GetCurrentMode().ToAscii());
    863         return;
    864     }
    865 
    866     for (view = 0; view < pPhoneMode->GetNumViews(); view++) {
    867         pPhoneView = pPhoneMode->GetPhoneView(view);
    868         if (pPhoneView == NULL) {
    869             fprintf(stderr, "view %d not found\n", view);
    870             return;
    871         }
    872 
    873         /* draw background image and "button patches" */
    874         numImages = pPhoneView->GetBkgImageCount();
    875         for (int i = 0; i < numImages; i++) {
    876             const LoadableImage* pLimg = pPhoneView->GetBkgImage(i);
    877             wxBitmap* pBitmap = pLimg->GetBitmap();
    878             if (pBitmap != NULL)
    879                 dc.DrawBitmap(*pBitmap,
    880                     mpViewInfo[view].GetX() + pLimg->GetX(),
    881                     mpViewInfo[view].GetY() + pLimg->GetY(),
    882                     TRUE);
    883         }
    884     }
    885 
    886 
    887     /*
    888      * Draw button mouse-over highlight.
    889      *
    890      * Currently we don't do anything different when the button is held down.
    891      */
    892     if (mpMOHViewIndex >= 0 && mpMOHButton != NULL) {
    893         // button must have graphic, or hit-testing wouldn't have worked
    894         assert(mpMOHButton->GetHighlightedBitmap() != NULL);
    895         dc.DrawBitmap(*mpMOHButton->GetHighlightedBitmap(),
    896             mpViewInfo[mpMOHViewIndex].GetX() + mpMOHButton->GetX(),
    897             mpViewInfo[mpMOHViewIndex].GetY() + mpMOHButton->GetY(),
    898             TRUE);
    899     }
    900 
    901     /*
    902      * Highlight pressed keys.  We want to do this in all views, because
    903      * some buttons on the side of the phone might be visible in more
    904      * than one view.
    905      */
    906     for (view = 0; view < pPhoneMode->GetNumViews(); view++) {
    907         pPhoneView = pPhoneMode->GetPhoneView(view);
    908         assert(pPhoneView != NULL);
    909 
    910         ListIter iter;
    911         for (iter = mPressedKeys.begin(); iter != mPressedKeys.end(); ++iter) {
    912             int32_t keyCode;
    913             PhoneButton* pButton;
    914 
    915             keyCode = (*iter).GetKeyCode();
    916             pButton = pPhoneView->FindButtonByKey(keyCode);
    917             if (pButton != NULL) {
    918                 wxBitmap* pBitmap = pButton->GetSelectedBitmap();
    919                 if (pBitmap != NULL) {
    920                     dc.DrawBitmap(*pBitmap,
    921                         mpViewInfo[view].GetX() + pButton->GetX(),
    922                         mpViewInfo[view].GetY() + pButton->GetY(),
    923                         TRUE);
    924                 }
    925             }
    926         }
    927     }
    928 }
    929 
    930 
    931 /*
    932  * Press a key on the device.
    933  *
    934  * Schedules a screen refresh if the set of held-down keys changes.
    935  */
    936 void PhoneWindow::AddPressedKey(int32_t keyCode)
    937 {
    938     /*
    939      * See if the key is already down.  This usually means that the key
    940      * repeat has kicked into gear.  It could also mean that we
    941      * missed the key-up event, or the user has hit the same device
    942      * key with both mouse and keyboard.  Either way, we don't add it
    943      * a second time.  This way, if we did lose a key-up somehow, they
    944      * can "clear" the stuck key by hitting it again.
    945      */
    946     if (keyCode == AKEYCODE_UNKNOWN) {
    947         //printf("--- not adding AKEYCODE_UNKNOWN!\n");
    948         return;
    949     }
    950 
    951     ListIter iter;
    952     for (iter = mPressedKeys.begin(); iter != mPressedKeys.end(); ++iter) {
    953         if ((*iter).GetKeyCode() == keyCode)
    954             break;
    955     }
    956     if (iter == mPressedKeys.end()) {
    957         KeyInfo newInfo;
    958         newInfo.SetKeyCode(keyCode);
    959         mPressedKeys.push_back(newInfo);
    960         //printf("---  added down=%d\n", keyCode);
    961         Refresh();      // redraw w/ highlight
    962     } else {
    963         //printf("---  already have down=%d\n", keyCode);
    964     }
    965 }
    966 
    967 /*
    968  * Release a key on the device.
    969  *
    970  * Schedules a screen refresh if the set of held-down keys changes.
    971  */
    972 void PhoneWindow::RemovePressedKey(int32_t keyCode)
    973 {
    974     /*
    975      * Release the key.  If it's not in the list, we either missed a
    976      * key-down event, or the user used both mouse and keyboard and we
    977      * removed the key when the first device went up.
    978      */
    979     ListIter iter;
    980     for (iter = mPressedKeys.begin(); iter != mPressedKeys.end(); ++iter) {
    981         if ((*iter).GetKeyCode() == keyCode) {
    982             mPressedKeys.erase(iter);
    983             //printf("---  removing down=%d\n", keyCode);
    984             Refresh();      // redraw w/o highlight
    985             break;
    986         }
    987     }
    988     if (iter == mPressedKeys.end()) {
    989         //printf("---  didn't find down=%d\n", keyCode);
    990     }
    991 }
    992 
    993 /*
    994  * Clear the set of keys that we think are being held down.
    995  */
    996 void PhoneWindow::ClearPressedKeys(void)
    997 {
    998     //printf("--- All keys up (count=%d)\n", mPressedKeys.size());
    999 
   1000     if (!mPressedKeys.empty()) {
   1001         ListIter iter = mPressedKeys.begin();
   1002         while (iter != mPressedKeys.end()) {
   1003             int32_t keyCode = (*iter).GetKeyCode();
   1004             GetDeviceManager()->SendKeyEvent(keyCode, false);
   1005             iter = mPressedKeys.erase(iter);
   1006         }
   1007         Refresh();
   1008     }
   1009 }
   1010 
   1011 /*
   1012  * Returns "true" if the specified key is currently pressed.
   1013  */
   1014 bool PhoneWindow::IsKeyPressed(int32_t keyCode)
   1015 {
   1016     ListIter iter;
   1017     for (iter = mPressedKeys.begin(); iter != mPressedKeys.end(); ++iter) {
   1018         if ((*iter).GetKeyCode() == keyCode)
   1019             return true;
   1020     }
   1021     return false;
   1022 }
   1023 
   1024 void PhoneWindow::Vibrate(int vibrateOn)
   1025 {
   1026     wxRect rect = GetRect();
   1027     if(vibrateOn)
   1028     {
   1029         mVibrateX = 0;
   1030         mTimer.Start(25);      // arg is delay in ms
   1031         Move(rect.x-2,rect.y);
   1032     }
   1033     else if(mTimer.IsRunning())
   1034     {
   1035         mTimer.Stop();
   1036         if(mVibrateX&1)
   1037             Move(rect.x-2,rect.y);
   1038         else
   1039             Move(rect.x+2,rect.y);
   1040     }
   1041 }
   1042 
   1043 void PhoneWindow::OnTimer(wxTimerEvent& event)
   1044 {
   1045     wxRect rect = GetRect();
   1046     mVibrateX++;
   1047     if(mVibrateX&1)
   1048         Move(rect.x+4,rect.y);
   1049     else
   1050         Move(rect.x-4,rect.y);
   1051 }
   1052