1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 package javax.microedition.lcdui;
27
28 import java.util.Timer;
29 import java.util.TimerTask;
30
31 import javax.microedition.lcdui.game.GameCanvas;
32 import javax.microedition.midlet.MIDlet;
33
34 import org.microemu.CommandManager;
35 import org.microemu.DisplayAccess;
36 import org.microemu.GameCanvasKeyAccess;
37 import org.microemu.MIDletBridge;
38 import org.microemu.device.DeviceFactory;
39 import org.microemu.device.ui.DisplayableUI;
40
41 import edu.emory.mathcs.backport.java.util.concurrent.BlockingQueue;
42 import edu.emory.mathcs.backport.java.util.concurrent.LinkedBlockingQueue;
43 import edu.emory.mathcs.backport.java.util.concurrent.Semaphore;
44
45 public class Display {
46
47 public static final int LIST_ELEMENT = 1;
48
49 public static final int CHOICE_GROUP_ELEMENT = 2;
50
51 public static final int ALERT = 3;
52
53 public static final int COLOR_BACKGROUND = 0;
54
55 public static final int COLOR_FOREGROUND = 1;
56
57 public static final int COLOR_HIGHLIGHTED_BACKGROUND = 2;
58
59 public static final int COLOR_HIGHLIGHTED_FOREGROUND = 3;
60
61 public static final int COLOR_BORDER = 4;
62
63 public static final int COLOR_HIGHLIGHTED_BORDER = 5;
64
65
66
67
68 private Displayable current = null;
69
70 private DisplayAccessor accessor = null;
71
72 private static final String EVENT_DISPATCHER_NAME = "event-thread";
73
74 private final EventDispatcher eventDispatcher = new EventDispatcher();
75
76 private final class GaugePaintTask implements Runnable {
77
78 public void run() {
79 if (current != null) {
80 if (current instanceof Alert) {
81 Gauge gauge = ((Alert) current).indicator;
82 if (gauge != null && gauge.hasIndefiniteRange() && gauge.getValue() == Gauge.CONTINUOUS_RUNNING) {
83 gauge.updateIndefiniteFrame();
84 }
85 } else if (current instanceof Form) {
86 Item[] items = ((Form) current).items;
87 for (int i = 0; i < items.length; i++) {
88 Item it = items[i];
89 if (it != null && it instanceof Gauge) {
90 Gauge gauge = (Gauge) it;
91
92 if (gauge.hasIndefiniteRange() && gauge.getValue() == Gauge.CONTINUOUS_RUNNING) {
93 gauge.updateIndefiniteFrame();
94 }
95 }
96 }
97 }
98 }
99 }
100 }
101
102
103
104
105
106 private final class TickerPaintTask implements Runnable {
107
108 public void run() {
109 if (current != null) {
110 Ticker ticker = current.getTicker();
111 if (ticker != null) {
112 synchronized (ticker) {
113 if (ticker.resetTextPosTo != -1) {
114 ticker.textPos = ticker.resetTextPosTo;
115 ticker.resetTextPosTo = -1;
116 }
117 ticker.textPos -= Ticker.PAINT_MOVE;
118 }
119 repaint(current, 0, 0, current.getWidth(), current.getHeight());
120 }
121 }
122 }
123 }
124
125
126
127
128
129
130
131 private void putInQueue(Runnable runnable) {
132 try {
133 eventQueue.put(runnable);
134 } catch (InterruptedException exception) {
135
136 exception.printStackTrace();
137 }
138 }
139
140
141
142
143
144
145
146
147
148
149 private final class KeyEvent implements Runnable {
150
151 static final short KEY_PRESSED = 0;
152
153 static final short KEY_RELEASED = 1;
154
155 static final short KEY_REPEATED = 2;
156
157 private short type;
158
159 private int keyCode;
160
161 KeyEvent(short type, int keyCode) {
162 this.type = type;
163 this.keyCode = keyCode;
164 }
165
166 public void run() {
167 switch (type) {
168 case KEY_PRESSED:
169 if (current != null) {
170 current.keyPressed(keyCode);
171 }
172 break;
173
174 case KEY_RELEASED:
175 if (current != null) {
176 current.keyReleased(keyCode);
177 }
178 break;
179
180 case KEY_REPEATED:
181 if (current != null) {
182 current.keyRepeated(keyCode);
183 }
184 break;
185 }
186 }
187 }
188
189 private final class PointerEvent implements Runnable {
190
191 static final short POINTER_PRESSED = 0;
192
193 static final short POINTER_RELEASED = 1;
194
195 static final short POINTER_DRAGGED = 2;
196
197 private short type;
198
199 private int x;
200
201 private int y;
202
203 PointerEvent(short type, int x, int y) {
204 this.type = type;
205 this.x = x;
206 this.y = y;
207 }
208
209 public void run() {
210 switch (type) {
211 case POINTER_PRESSED:
212 if (current != null) {
213 current.pointerPressed(x, y);
214 }
215 break;
216 case POINTER_RELEASED:
217 if (current != null) {
218 current.pointerReleased(x, y);
219 }
220 break;
221 case POINTER_DRAGGED:
222 if (current != null) {
223 current.pointerDragged(x, y);
224 }
225 break;
226 }
227 }
228 }
229
230 private final class ShowHideNotifyEvent implements Runnable {
231
232 static final short SHOW_NOTIFY = 0;
233
234 static final short HIDE_NOTIFY = 1;
235
236 private short type;
237
238 private Displayable current;
239
240 private Displayable nextDisplayable;
241
242 ShowHideNotifyEvent(short type, Displayable current, Displayable nextDisplayable) {
243 this.type = type;
244 this.current = current;
245 this.nextDisplayable = nextDisplayable;
246 }
247
248 public void run() {
249 switch (type) {
250 case SHOW_NOTIFY:
251 if (current != null) {
252 putInQueue(new ShowHideNotifyEvent(ShowHideNotifyEvent.HIDE_NOTIFY, current, nextDisplayable));
253 }
254
255 if (nextDisplayable instanceof Alert) {
256 setCurrent((Alert) nextDisplayable, current);
257 return;
258 }
259
260
261
262
263
264
265
266
267
268
269 nextDisplayable.showNotify(Display.this);
270 Display.this.current = nextDisplayable;
271
272 setScrollUp(false);
273 setScrollDown(false);
274 updateCommands();
275 nextDisplayable.repaint();
276
277 break;
278 case HIDE_NOTIFY:
279 current.hideNotify(Display.this);
280 break;
281 }
282 }
283 }
284
285 private class DisplayAccessor implements DisplayAccess {
286
287 Display display;
288
289 DisplayAccessor(Display d) {
290
291 display = d;
292 }
293
294 public void commandAction(Command c, Displayable d) {
295 if (c.equals(CommandManager.CMD_SCREEN_UP)) {
296 if (d != null && d instanceof Screen) {
297 ((Screen) d).scroll(Canvas.UP);
298 }
299 } else if (c.equals(CommandManager.CMD_SCREEN_DOWN)) {
300 if (d != null && d instanceof Screen) {
301 ((Screen) d).scroll(Canvas.DOWN);
302 }
303 } else if (c.isRegularCommand()) {
304 if (d == null) {
305 return;
306 }
307 CommandListener listener = d.getCommandListener();
308 if (listener == null) {
309 return;
310 }
311 listener.commandAction(c, d);
312 } else {
313
314 Item item = c.getFocusedItem();
315
316 ItemCommandListener listener = item.getItemCommandListener();
317 if (listener == null) {
318 return;
319 }
320 listener.commandAction(c.getOriginalCommand(), item);
321 }
322 }
323
324 public Display getDisplay() {
325 return display;
326 }
327
328
329 private void processGameCanvasKeyEvent(GameCanvas c, int k, boolean press) {
330
331
332
333
334
335 GameCanvasKeyAccess access = MIDletBridge.getMIDletAccess().getGameCanvasKeyAccess();
336 int gameCode = c.getGameAction(k);
337 boolean suppress = false;
338 if (gameCode != 0) {
339
340 if (press)
341 access.recordKeyPressed(c, gameCode);
342 else
343 access.recordKeyReleased(c, gameCode);
344 suppress = access.suppressedKeyEvents(c);
345 }
346 if (!suppress) {
347 if (press) {
348 putInQueue(new KeyEvent(KeyEvent.KEY_PRESSED, k));
349 } else {
350 putInQueue(new KeyEvent(KeyEvent.KEY_RELEASED, k));
351 }
352 }
353 }
354
355
356
357
358
359 public void keyPressed(int keyCode) {
360
361 if (current != null && current instanceof GameCanvas) {
362 processGameCanvasKeyEvent((GameCanvas) current, keyCode, true);
363 } else {
364 putInQueue(new KeyEvent(KeyEvent.KEY_PRESSED, keyCode));
365 }
366 }
367
368 public void keyRepeated(int keyCode) {
369 putInQueue(new KeyEvent(KeyEvent.KEY_REPEATED, keyCode));
370 }
371
372 public void keyReleased(int keyCode) {
373
374 if (current != null && current instanceof GameCanvas) {
375 processGameCanvasKeyEvent((GameCanvas) current, keyCode, false);
376 } else {
377 putInQueue(new KeyEvent(KeyEvent.KEY_RELEASED, keyCode));
378 }
379 }
380
381 public void pointerPressed(int x, int y) {
382 if (current != null) {
383 putInQueue(new PointerEvent(PointerEvent.POINTER_PRESSED, x, y));
384 }
385 }
386
387 public void pointerReleased(int x, int y) {
388 if (current != null) {
389 putInQueue(new PointerEvent(PointerEvent.POINTER_RELEASED, x, y));
390 }
391 }
392
393 public void pointerDragged(int x, int y) {
394
395 if (current != null) {
396 putInQueue(new PointerEvent(PointerEvent.POINTER_DRAGGED, x, y));
397 }
398 }
399
400 public void paint(Graphics g) {
401
402 if (current != null) {
403 try {
404 current.paint(g);
405 } catch (Throwable th) {
406 th.printStackTrace();
407 }
408 g.translate(-g.getTranslateX(), -g.getTranslateY());
409 }
410 }
411
412 public Displayable getCurrent() {
413 return getDisplay().getCurrent();
414 }
415
416 public DisplayableUI getCurrentUI() {
417 Displayable current = getCurrent();
418 if (current == null) {
419 return null;
420 } else {
421 return current.ui;
422 }
423 }
424
425 public boolean isFullScreenMode() {
426 Displayable current = getCurrent();
427
428 if (current instanceof Canvas) {
429 return ((Canvas) current).fullScreenMode;
430 } else {
431 return false;
432 }
433 }
434
435 public void serviceRepaints() {
436 getDisplay().serviceRepaints();
437 }
438
439 public void setCurrent(Displayable d) {
440 getDisplay().setCurrent(d);
441 }
442
443 public void sizeChanged(int width, int height) {
444 if (current != null) {
445 current.sizeChanged(width, height);
446 updateCommands();
447 }
448 }
449
450 public void updateCommands() {
451 getDisplay().updateCommands();
452 }
453
454 public void clean() {
455 if (current != null) {
456 current.hideNotify();
457 }
458 eventDispatcher.cancel();
459 timer.cancel();
460 }
461 }
462
463 private class AlertTimeout implements Runnable {
464
465 int time;
466
467 AlertTimeout(int time) {
468 this.time = time;
469 }
470
471 public void run() {
472 try {
473 Thread.sleep(time);
474 } catch (InterruptedException ex) {
475 ex.printStackTrace();
476 }
477
478 Displayable d = current;
479 if (d != null && d instanceof Alert) {
480 Alert alert = (Alert) d;
481 if (alert.time != Alert.FOREVER) {
482 alert.getCommandListener().commandAction((Command) alert.getCommands().get(0), alert);
483 }
484 }
485 }
486 }
487
488 private final Semaphore serviceRepaintSemaphore = new Semaphore(0);
489
490 private final class PaintTask implements Runnable {
491
492 private int x = -1, y = -1, width = -1, height = -1;
493
494 PaintTask(int x, int y, int width, int height) {
495 this.x = x;
496 this.y = y;
497 this.width = width;
498 this.height = height;
499 }
500
501 public void run() {
502 DeviceFactory.getDevice().getDeviceDisplay().repaint(x, y, width, height);
503 }
504
505
506
507
508
509
510 public final void merge(PaintTask task) {
511 int xMax = x + width;
512 int yMax = y + height;
513
514 this.x = Math.min(this.x, task.x);
515 xMax = Math.max(xMax, task.x + task.width);
516
517 this.y = Math.min(this.y, task.y);
518 yMax = Math.max(yMax, task.y + task.height);
519
520 this.width = xMax - x;
521 this.height = yMax - y;
522 }
523
524 }
525
526
527
528
529
530
531
532
533 private final BlockingQueue eventQueue = new LinkedBlockingQueue();
534
535
536
537
538
539
540
541
542 private final class EventDispatcher implements Runnable {
543
544 private Thread thread;
545
546 private volatile boolean cancelled = false;
547
548 public void run() {
549
550 while (!cancelled) {
551 try {
552 Runnable runnable = (Runnable) eventQueue.take();
553
554 if (runnable instanceof PaintTask) {
555
556 PaintTask paint = (PaintTask) runnable;
557
558 while (eventQueue.peek() != null && eventQueue.peek() instanceof PaintTask) {
559 paint.merge((PaintTask) eventQueue.take());
560 }
561
562 }
563 runnable.run();
564
565 } catch (InterruptedException exception) {
566
567 }
568 }
569 }
570
571
572
573
574 public final void cancel() {
575 this.cancelled = true;
576 thread.interrupt();
577 }
578
579 }
580
581 private final Timer timer = new Timer();
582
583
584
585
586
587
588
589
590 private final class RunnableWrapper extends TimerTask {
591
592 private final Runnable runnable;
593
594 RunnableWrapper(Runnable runnable) {
595 this.runnable = runnable;
596 }
597
598 public void run() {
599 putInQueue(runnable);
600 }
601
602 }
603
604 Display() {
605
606 accessor = new DisplayAccessor(this);
607
608 startEventDispatcher();
609
610 timer.scheduleAtFixedRate(new RunnableWrapper(new TickerPaintTask()), 0, Ticker.PAINT_TIMEOUT);
611
612 timer.scheduleAtFixedRate(new RunnableWrapper(new GaugePaintTask()), 0, Ticker.PAINT_TIMEOUT);
613 }
614
615 private final void startEventDispatcher() {
616 eventDispatcher.thread = new Thread(eventDispatcher, EVENT_DISPATCHER_NAME);
617 eventDispatcher.thread.setDaemon(true);
618 eventDispatcher.thread.start();
619 }
620
621 public void callSerially(Runnable runnable) {
622 putInQueue(runnable);
623 }
624
625 public int numAlphaLevels() {
626 return DeviceFactory.getDevice().getDeviceDisplay().numAlphaLevels();
627 }
628
629 public int numColors() {
630 return DeviceFactory.getDevice().getDeviceDisplay().numColors();
631 }
632
633 public boolean flashBacklight(int duration) {
634
635 return false;
636 }
637
638 public static Display getDisplay(MIDlet m) {
639 Display result;
640
641 if (MIDletBridge.getMIDletAccess(m).getDisplayAccess() == null) {
642 result = new Display();
643 MIDletBridge.getMIDletAccess(m).setDisplayAccess(result.accessor);
644 } else {
645 result = MIDletBridge.getMIDletAccess(m).getDisplayAccess().getDisplay();
646 }
647
648 return result;
649 }
650
651 public int getColor(int colorSpecifier) {
652
653 switch (colorSpecifier) {
654 case COLOR_BACKGROUND:
655 case COLOR_HIGHLIGHTED_FOREGROUND:
656 case COLOR_HIGHLIGHTED_BORDER:
657 return 0xFFFFFF;
658 default:
659 return 0x000000;
660 }
661 }
662
663 public int getBorderStyle(boolean highlighted) {
664
665 return highlighted ? Graphics.DOTTED : Graphics.SOLID;
666 }
667
668 public int getBestImageWidth(int imageType) {
669
670 return 0;
671 }
672
673 public int getBestImageHeight(int imageType) {
674
675
676 return 0;
677 }
678
679 public Displayable getCurrent() {
680 return current;
681 }
682
683 public boolean isColor() {
684 return DeviceFactory.getDevice().getDeviceDisplay().isColor();
685 }
686
687 public void setCurrent(Displayable nextDisplayable) {
688 if (nextDisplayable != null) {
689 putInQueue(new ShowHideNotifyEvent(ShowHideNotifyEvent.SHOW_NOTIFY, current, nextDisplayable));
690 }
691 }
692
693 public void setCurrent(Alert alert, Displayable nextDisplayable) {
694
695
696 Alert.nextDisplayable = nextDisplayable;
697
698 current = alert;
699
700 current.showNotify(this);
701 updateCommands();
702 current.repaint();
703
704 if (alert.getTimeout() != Alert.FOREVER) {
705 AlertTimeout at = new AlertTimeout(alert.getTimeout());
706 Thread t = new Thread(at);
707 t.start();
708 }
709 }
710
711 public void setCurrentItem(Item item) {
712
713 }
714
715 public boolean vibrate(int duration) {
716 return DeviceFactory.getDevice().vibrate(duration);
717 }
718
719
720 void clearAlert() {
721 setCurrent(Alert.nextDisplayable);
722 }
723
724 static int getGameAction(int keyCode) {
725 return DeviceFactory.getDevice().getInputMethod().getGameAction(keyCode);
726 }
727
728 static int getKeyCode(int gameAction) {
729 return DeviceFactory.getDevice().getInputMethod().getKeyCode(gameAction);
730 }
731
732 static String getKeyName(int keyCode) throws IllegalArgumentException {
733 return DeviceFactory.getDevice().getInputMethod().getKeyName(keyCode);
734 }
735
736 boolean isShown(Displayable d) {
737 if (current == null || current != d) {
738 return false;
739 } else {
740 return true;
741 }
742 }
743
744 void repaint(Displayable d, int x, int y, int width, int height) {
745 if (current == d) {
746 putInQueue(new PaintTask(x, y, width, height));
747 }
748 }
749
750 void serviceRepaints() {
751
752
753
754
755
756
757 if (EVENT_DISPATCHER_NAME.equals(Thread.currentThread().getName())) {
758 DeviceFactory.getDevice().getDeviceDisplay().repaint(0, 0, current.getWidth(), current.getHeight());
759 return;
760 }
761
762
763 putInQueue(new Runnable() {
764
765 public void run() {
766 serviceRepaintSemaphore.release();
767 }
768
769 });
770
771 try {
772 serviceRepaintSemaphore.acquire();
773 } catch (InterruptedException exception) {
774
775 exception.printStackTrace();
776 }
777 }
778
779 void setScrollDown(boolean state) {
780 DeviceFactory.getDevice().getDeviceDisplay().setScrollDown(state);
781 }
782
783 void setScrollUp(boolean state) {
784 DeviceFactory.getDevice().getDeviceDisplay().setScrollUp(state);
785 }
786
787 void updateCommands() {
788 if (current == null) {
789 CommandManager.getInstance().updateCommands(null);
790 } else {
791 CommandManager.getInstance().updateCommands(current.getCommands());
792 }
793 repaint(current, 0, 0, current.getWidth(), current.getHeight());
794 }
795
796 }