auto import from //depot/cupcake/@135843
diff --git a/simulator/app/PhoneWindow.cpp b/simulator/app/PhoneWindow.cpp
new file mode 100644
index 0000000..60a9809
--- /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);
+}