diff options
| author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:29:09 -0800 |
|---|---|---|
| committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:29:09 -0800 |
| commit | 52d4c30ca52320ec92d1d1ddc8db3f07f69c4f98 (patch) | |
| tree | 2eb01432fa3da41550dd622d29fb672509122b51 /simulator/app/PhoneWindow.cpp | |
| parent | d4aee0c0caa00aa02d4c50ed28151591ac0456b5 (diff) | |
| download | android_development-52d4c30ca52320ec92d1d1ddc8db3f07f69c4f98.tar.gz android_development-52d4c30ca52320ec92d1d1ddc8db3f07f69c4f98.tar.bz2 android_development-52d4c30ca52320ec92d1d1ddc8db3f07f69c4f98.zip | |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'simulator/app/PhoneWindow.cpp')
| -rw-r--r-- | simulator/app/PhoneWindow.cpp | 1051 |
1 files changed, 1051 insertions, 0 deletions
diff --git a/simulator/app/PhoneWindow.cpp b/simulator/app/PhoneWindow.cpp new file mode 100644 index 000000000..60a98090c --- /dev/null +++ b/simulator/app/PhoneWindow.cpp @@ -0,0 +1,1051 @@ +// +// 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); +} |
