| // |
| // 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); |
| } |