• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
BEGIN_EVENT_TABLE(PhoneWindow,wxWindow)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(kKeyCodeUnknown),
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  */
~PhoneWindow(void)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  */
OnActivate(wxActivateEvent & event)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             KeyCode keyCode = (*iter).GetKeyCode();
117             GetDeviceManager()->SendKeyEvent(keyCode, false);
118             iter = mPressedKeys.erase(iter);
119         }
120     }
121 }
122 
123 /*
124  * Close the phone window.
125  */
OnClose(wxCloseEvent & event)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  */
Setup(int phoneIdx)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  */
DevicesRescanned(void)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  */
OnMove(wxMoveEvent & event)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  */
GetDimensions(PhoneData * pPhoneData,PhoneView * pPhoneView,ViewInfo * pInfo)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  */
GetPhoneData(void) const424 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  */
ConvertKeyCode(int wxKeyCode) const444 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; //kKeyCodeStar;
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; //kKeyCodeDpadCenter;
483     case WXK_HOME:              return KEY_HOME;
484     case WXK_PRIOR:
485     case WXK_PAGEUP:            return KEY_MENU; //kKeyCodeSoftLeft;
486     case WXK_NEXT:
487     case WXK_PAGEDOWN:          return KEY_KBDILLUMUP; //kKeyCodeSoftRight;
488     case WXK_DELETE:
489     case WXK_BACK:              return KEY_BACKSPACE; //kKeyCodeDel;
490     case WXK_ESCAPE:
491     case WXK_END:               return KEY_BACK; //kKeyCodeBack;
492     case WXK_NUMPAD_DELETE:
493     case WXK_NUMPAD_DECIMAL:    return KEY_KBDILLUMTOGGLE; //kKeyCodePound;
494     case WXK_SPACE:             return KEY_SPACE; //kKeyCodeSpace;
495     case WXK_RETURN:            return KEY_ENTER; //kKeyCodeNewline;
496     case WXK_F3:                return KEY_F3; //kKeyCodeCall;
497     case WXK_F4:                return KEY_F4; //kKeyCodeEndCall;
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 kKeyCodeUnknown;
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  */
OnKeyDown(wxKeyEvent & event)567 void PhoneWindow::OnKeyDown(wxKeyEvent& event)
568 {
569     KeyCode keyCode;
570 
571     keyCode = (KeyCode) ConvertKeyCode(event.GetKeyCode());
572     if (keyCode != kKeyCodeUnknown) {
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  */
OnKeyUp(wxKeyEvent & event)587 void PhoneWindow::OnKeyUp(wxKeyEvent& event)
588 {
589     KeyCode keyCode;
590 
591     keyCode = (KeyCode) ConvertKeyCode(event.GetKeyCode());
592     if (keyCode != kKeyCodeUnknown) {
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  */
OnMouseLeftDown(wxMouseEvent & event)616 void PhoneWindow::OnMouseLeftDown(wxMouseEvent& event)
617 {
618     if (mpMOHButton != NULL) {
619         //printf("PW: left down\n");
620         KeyCode 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  */
OnMouseLeftUp(wxMouseEvent & WXUNUSED (event))645 void PhoneWindow::OnMouseLeftUp(wxMouseEvent& WXUNUSED(event))
646 {
647     if (mMouseKeySent != kKeyCodeUnknown) {
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 = kKeyCodeUnknown;
662 }
663 
OnMouseRightDown(wxMouseEvent & event)664 void PhoneWindow::OnMouseRightDown(wxMouseEvent& event)
665 {
666     //printf("(ignoring right-down)\n");
667 }
OnMouseRightUp(wxMouseEvent & event)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  */
OnMouseMotion(wxMouseEvent & event)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  */
OnMouseLeaveWindow(wxMouseEvent & WXUNUSED (event))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  */
GetTouchPosition(const wxMouseEvent & event,int * pScreenX,int * pScreenY)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  */
OnErase(wxEraseEvent & WXUNUSED (event))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  */
OnPaint(wxPaintEvent & WXUNUSED (event))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             KeyCode 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  */
AddPressedKey(KeyCode keyCode)936 void PhoneWindow::AddPressedKey(KeyCode 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 == kKeyCodeUnknown) {
947         //printf("--- not adding kKeyCodeUnknown!\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  */
RemovePressedKey(KeyCode keyCode)972 void PhoneWindow::RemovePressedKey(KeyCode 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  */
ClearPressedKeys(void)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             KeyCode 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  */
IsKeyPressed(KeyCode keyCode)1014 bool PhoneWindow::IsKeyPressed(KeyCode 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 
Vibrate(int vibrateOn)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 
OnTimer(wxTimerEvent & event)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