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