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