/*
Реализация спецификаций CLDC версии 1.1 (JSR-139), MIDP версии 2.1 (JSR-118)
и других спецификаций для функционирования компактных приложений на языке
Java (мидлетов) в среде программного обеспечения Малик Эмулятор.
Copyright © 2016, 2019 Малик Разработчик
Это свободная программа: вы можете перераспространять ее и/или изменять
ее на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
в каком она была опубликована Фондом свободного программного обеспечения;
либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.
Эта программа распространяется в надежде, что она будет полезной,
но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
общественной лицензии GNU.
Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
вместе с этой программой. Если это не так, см.
<https://www.gnu.org/licenses/>.
*/
/* Структура экрана: отметки
* +--------------------------------+ +--------------------------------+ -- p0
* | Заголовок (title) | | Заголовок (title) |
* +--------------------------------+ +--------------------------------+ -- p1
* | Клиентская часть (client) | | Бегущая строка (ticker) |
* | | +--------------------------------+ -- p2
* | | | Клиентская часть (client) |
* | | | |
* | | | |
* | | | |
* | | | |
* | | ИЛИ | |
* | | | |
* | | | |
* | | | |
* | | | |
* | | | |
* | | | |
* | | | |
* +----------+----------+----------+ +----------+----------+----------+ -- p3
* | К1 | К2 | К3 | | К1 | К2 | К3 |
* +----------+----------+----------+ +----------+----------+----------+ -- p4
* Режим без бегущей строки Режим с бегущей строкой
* (по умолчанию) (если задана бегущая строка)
* (MODE_WITHOUT_TICKER) (MODE_WITH_TICKER)
* В полноэкранном режиме (MODE_FULL_SCREEN) отображается только клиентская
* часть, простирающаяся от отметки p0 до отметки p4, остальные элементы
* (даже если они заданы) на экране отсутствуют.
* К1 - команда левой программной клавиши,
* К2 - команда центральной клавиши выбора,
* К3 - команда правой программной клавиши.
* Команда К2 - основная команда выбора, выделяется от остальных двух
* команд (default command).
* Команды К1 и К3 - сторонние команды, выглядят одинаково (side commands).
*/
package javax.microedition.lcdui;
import java.io.*;
import java.util.*;
import javax.microedition.midlet.*;
import javax.microedition.rms.*;
import malik.emulator.application.*;
import malik.emulator.media.graphics.*;
public class Display extends Object
{
public static final int LIST_ELEMENT = 1;
public static final int CHOICE_GROUP_ELEMENT = 2;
public static final int ALERT = 3;
public static final int COLOR_BACKGROUND = 0;
public static final int COLOR_FOREGROUND = 1;
public static final int COLOR_HIGHLIGHTED_BACKGROUND = 2;
public static final int COLOR_HIGHLIGHTED_FOREGROUND = 3;
public static final int COLOR_BORDER = 4;
public static final int COLOR_HIGHLIGHTED_BORDER = 5;
static final int EVENT_KEY_PRESSED = (char) 0x0001;
static final int EVENT_KEY_REPEATED = (char) 0x0002;
static final int EVENT_KEY_RELEASED = (char) 0x0003;
static final int EVENT_POINTER_PRESSED = (char) 0x0005;
static final int EVENT_POINTER_DRAGGED = (char) 0x0006;
static final int EVENT_POINTER_RELEASED = (char) 0x0007;
static final int EVENT_WINDOW_SHOW = (char) 0x000a;
static final int EVENT_WINDOW_HIDE = (char) 0x000b;
private static final int EVENT_EMPTY = (char) 0x0000;
private static final int EVENT_CUSTOM = (char) 0x000d;
private static final int EVENT_PAINT = (char) 0x000e;
private static final int MIDLET_EVENT_QUEUE_INITIAL_SIZE = 0x21;
private static boolean SERIALIZE_REPAINT;
private static int MAXIMUM_FREQUENCY;
private static int MINIMUM_PERIOD;
static
{
setSerializeRepaint(true);
setMaximumFrequency(25);
}
public static void setSerializeRepaint(boolean serializeRepaint)
{
SERIALIZE_REPAINT = serializeRepaint;
}
public static void setMaximumFrequency(int maximumFrequency)
{
MAXIMUM_FREQUENCY = maximumFrequency = maximumFrequency < 1 ? 1 :
maximumFrequency > 100 ? 100 : maximumFrequency;
MINIMUM_PERIOD = 1000 / maximumFrequency + ((1000 % maximumFrequency) > 0 ? 1 : 0);
}
public static boolean isSerializeRepaint()
{
return SERIALIZE_REPAINT;
}
public static int getMaximumFrequency()
{
return MAXIMUM_FREQUENCY;
}
public static int getMinimumPeriod()
{
return MINIMUM_PERIOD;
}
public static Display getDisplay(MIDlet midlet)
{
if(midlet == null)
{
throw new NullPointerException("Display.getDisplay: " +
"параметр midlet равен нулевой ссылке.");
}
return MIDletProxy.getInstance().getEmulatorScreen();
}
private class SystemScreenChangeEvent extends Object
implements Runnable
{
public SystemScreenChangeEvent()
{
}
public void run()
{
/* код этого метода выполняется только в главном потоке */
Display display = Display.this;
Displayable curr = display.shownSystemScreen;
Displayable next = display.currentSystemScreen;
MIDletEventStream events = display.clientEvents;
if(display.applicationInBackground || curr == next)
{
display.shownSystemScreen = next;
return;
}
if(curr != null)
{
display.shownSystemScreen = null;
try
{
curr.eventHide();
}
catch(RuntimeException e)
{
e.printRealStackTrace();
}
}
if(events != null)
{
if(curr == null)
{
events.setEvent(EVENT_WINDOW_HIDE);
}
if(next == null)
{
events.setEvent(EVENT_WINDOW_SHOW);
}
}
if(next != null)
{
try
{
next.eventShow();
}
catch(RuntimeException e)
{
e.printRealStackTrace();
}
display.shownSystemScreen = next;
Displayable.tickerUpdate();
display.callSeriallyPaint(next);
}
}
}
private class SystemScreenRepaintEvent extends Object
implements Runnable, Rectangle, ImmediatelyRunnable
{
protected final Displayable screen;
public SystemScreenRepaintEvent(Displayable screen)
{
this.screen = screen;
}
public void run()
{
(Display.this).handlePaint(screen);
}
public int getLeft()
{
return 0;
}
public int getTop()
{
return 0;
}
public int getRight()
{
Displayable screen;
return (screen = this.screen) == null ? (Display.this).getWidth() : screen.getClientWidth();
}
public int getBottom()
{
Displayable screen;
return (screen = this.screen) == null ? (Display.this).getHeight() : screen.getClientHeight();
}
public Object argument()
{
return screen;
}
}
private class SystemScreenRepaintRegionEvent extends SystemScreenRepaintEvent
{
private int left;
private int top;
private int right;
private int bottom;
public SystemScreenRepaintRegionEvent(Displayable screen, int left, int top, int right, int bottom)
{
super(screen);
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}
public void run()
{
int l;
int t;
(Display.this).handlePaint(screen, l = left, t = top, right - l, bottom - t);
}
public int getLeft()
{
return left;
}
public int getTop()
{
return top;
}
public int getRight()
{
return right;
}
public int getBottom()
{
return bottom;
}
}
private class ClientScreenChangeEvent extends Object
implements Runnable
{
public ClientScreenChangeEvent()
{
}
public void run()
{
/* код этого метода выполняется только в потоке-обработчике событий мидлета */
Display display = Display.this;
Displayable curr = display.shownClientScreen;
Displayable next = display.currentClientScreen;
if(display.applicationInBackground || display.shownSystemScreen != null ||
display.currentSystemScreen != null || curr == next)
{
display.shownClientScreen = next;
return;
}
if(curr != null)
{
display.shownClientScreen = null;
try
{
curr.eventHide();
}
catch(RuntimeException e)
{
e.printRealStackTrace();
}
}
if(next != null)
{
try
{
next.eventShow();
}
catch(RuntimeException e)
{
e.printRealStackTrace();
}
display.shownClientScreen = next;
Displayable.tickerUpdate();
display.callSeriallyPaint(next);
}
}
}
private class ClientScreenRepaintRegionEvent extends Object
implements Runnable, Rectangle, ImmediatelyRunnable
{
private int left;
private int top;
private int right;
private int bottom;
private Displayable screen;
public ClientScreenRepaintRegionEvent(Displayable screen, int left, int top, int right, int bottom)
{
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
this.screen = screen;
}
public void run()
{
int l;
int t;
(Display.this).handlePaint(screen, l = left, t = top, right - l, bottom - t);
}
public int getLeft()
{
return left;
}
public int getTop()
{
return top;
}
public int getRight()
{
return right;
}
public int getBottom()
{
return bottom;
}
public Object argument()
{
return screen;
}
}
private class MIDletTerminationEvent extends Object
implements Runnable
{
public MIDletTerminationEvent()
{
}
public void run()
{
/* код этого метода выполняется только в потоке-обработчике событий мидлета */
Display display;
(display = Display.this).dispatcher.stopMIDlet();
display.terminateEventStreams();
}
}
private class MIDletEventStream extends Queue
implements Runnable
{
private boolean terminated;
private long[] eventsFromUser;
private Runnable[] eventsFromApp;
private Displayable[] eventsScreens;
public MIDletEventStream()
{
super(MIDLET_EVENT_QUEUE_INITIAL_SIZE);
this.eventsFromUser = new long[MIDLET_EVENT_QUEUE_INITIAL_SIZE];
this.eventsFromApp = new Runnable[MIDLET_EVENT_QUEUE_INITIAL_SIZE];
this.eventsScreens = new Displayable[MIDLET_EVENT_QUEUE_INITIAL_SIZE];
}
public void removeTailElement()
{
int t = tail;
eventsFromUser[t] = 0L;
eventsFromApp[t] = null;
eventsScreens[t] = null;
advanceTail();
}
public void run()
{
long fromUser;
Runnable fromApp;
Displayable screen;
Display display = Display.this;
do
{
waitEvent();
if(terminated) break;
if(isEmpty()) continue;
synchronized(this)
{
fromUser = peekEventFromUser();
fromApp = peekEventFromApp();
screen = peekEventScreen();
removeTailElement();
}
if(screen == null && ((short) fromUser) != EVENT_CUSTOM)
{
screen = display.getCurrent();
}
switch((short) fromUser)
{
case EVENT_EMPTY:
break;
case EVENT_PAINT:
display.handlePaint(screen);
break;
case EVENT_CUSTOM:
display.handleEvent(fromApp);
break;
default:
display.handleEvent(screen,
(short) fromUser, (short) (fromUser >> 16),
(short) (fromUser >> 32), (short) (fromUser >> 48));
break;
}
} while(true);
}
public void serviceRepaints(Displayable argument)
{
boolean init = false;
int left;
int top;
int right;
int bottom;
left = top = right = bottom = 0;
for(int i; ; )
{
long fromUser = 0L;
Runnable fromApp = null;
Displayable screen = null;
synchronized(this)
{
if((i = findImmediatelyRunnable(argument)) >= 0)
{
long[] u;
Runnable[] a;
Displayable[] s;
fromUser = (u = eventsFromUser)[i];
fromApp = (a = eventsFromApp)[i];
screen = (s = eventsScreens)[i];
u[i] = (long) EVENT_EMPTY;
a[i] = null;
s[i] = null;
}
}
if(i < 0) break;
if((char) fromUser == EVENT_PAINT)
{
left = init ? Math.min(left, 0) : 0;
top = init ? Math.min(top, 0) : 0;
right = init ? Math.max(right, screen.getClientWidth()) : screen.getClientWidth();
bottom = init ? Math.max(bottom, screen.getClientHeight()) : screen.getClientHeight();
init = true;
continue;
}
if(fromApp instanceof Rectangle)
{
Rectangle rect = (Rectangle) fromApp;
left = init ? Math.min(left, rect.getLeft()) : rect.getLeft();
top = init ? Math.min(top, rect.getTop()) : rect.getTop();
right = init ? Math.max(right, rect.getRight()) : rect.getRight();
bottom = init ? Math.max(bottom, rect.getBottom()) : rect.getBottom();
init = true;
continue;
}
}
if(init) (Display.this).handlePaint(argument, left, top, right - left, bottom - top);
}
public synchronized void setEvent(int event)
{
push((long) (event & 0xffff), null, null);
notify();
}
public synchronized void setEvent(int event, int param1, int param2, int param3)
{
push((long) (event & 0xffff) | (long) (param1 & 0xffff) << 16 |
(long) (param2 & 0xffff) << 32 | (long) (param3 & 0xffff) << 48, null, null);
notify();
}
public synchronized void setEvent(Runnable event)
{
push((long) EVENT_CUSTOM, event, null);
notify();
}
public synchronized void setPaint(Displayable screen)
{
push((long) EVENT_PAINT, null, screen);
notify();
}
public synchronized void terminate()
{
terminated = true;
notify();
}
private synchronized void waitEvent()
{
do
{
try
{
wait();
break;
}
catch(InterruptedException e)
{
e.printRealStackTrace();
}
} while(true);
}
private void push(long eventFromUser, Runnable eventFromApp, Displayable eventScreen)
{
int h;
int nc;
long[] lq = eventsFromUser;
Runnable[] rq = eventsFromApp;
Displayable[] dq = eventsScreens;
if(getFreeElements() == 0)
{
nc = (capacity << 1) - 1;
lq = eventsFromUser = expand(lq, nc);
rq = eventsFromApp = (Runnable[]) expand(rq, nc);
dq = eventsScreens = (Displayable[]) expand(dq, nc);
reset(nc);
}
h = head;
lq[h] = eventFromUser;
rq[h] = eventFromApp;
dq[h] = eventScreen;
advanceHead();
}
private int findImmediatelyRunnable(Object argument)
{
int t = tail;
int c = capacity;
long[] u = eventsFromUser;
Runnable[] e = eventsFromApp;
Displayable[] s = eventsScreens;
for(int i = head; i != t; )
{
Runnable r;
i = (c + i - 1) % c;
if((char) u[i] == EVENT_PAINT && s[i] == argument ||
(r = e[i]) instanceof ImmediatelyRunnable &&
((ImmediatelyRunnable) r).argument() == argument)
{
return i;
}
}
return -1;
}
private long peekEventFromUser()
{
return eventsFromUser[tail];
}
private Runnable peekEventFromApp()
{
return eventsFromApp[tail];
}
private Displayable peekEventScreen()
{
return eventsScreens[tail];
}
}
private final class MIDletExecution extends MIDletEventStream
{
public MIDletExecution()
{
}
public void run()
{
Display display = Display.this;
PrintStream printer = System.out;
MIDletProxy proxy = MIDletProxy.getInstance();
MIDletDispatcher dispatcher = display.dispatcher;
/* вывод сообщения о запуске мидлета */
printer.println("Запуск приложения…");
printer.println("Класс мидлета: ".concat(
dispatcher.getMIDletClass().getCanonicalName()));
/* запуск мидлета */
dispatcher.startMIDlet();
/* оповещение о начале работы мидлета */
proxy.notifyMIDletStarted();
if(dispatcher.mustDestroyed())
{
/* неудачная попытка запуска? */
printer.println("Неудачная попытка запуска – завершение работы приложения.");
display = null;
/* остановка мидлета */
dispatcher.stopMIDlet();
dispatcher = null;
/* оповещение о завершении работы мидлета */
proxy.notifyMIDletTerminated();
return;
}
/* мидлет удачно запущен */
printer.println("Приложение запущено и работает.");
dispatcher = null;
/* скрытие системного экрана */
display.hideSystemScreen();
display = null;
/* обработка событий мидлета */
super.run();
/* закрываем все хранилища записей */
RecordStore.closeAllRecordStores();
/* вывод сообщения о завершении мидлета */
printer.println("Приложение завершило работу.");
/* оповещение о завершении работы мидлета */
proxy.notifyMIDletTerminated();
}
}
private boolean applicationInBackground;
private int width;
private int height;
private long lastRepaintTimestamp;
private int[] guiElementsMetrics;
private Font[] guiElementsFonts;
private Graphics[] graphicsForModesOfSystemScreen; // можно использовать в качестве монитора для визуализации системных экранов
private Graphics[] graphicsForModesOfClientScreen; // можно использовать в качестве монитора для визуализации клиентских экранов
private Font defaultCommandFont;
private Graphics graphicsForSystemScreen;
private Graphics graphicsForClientScreen;
private RasterCanvas screenCanvas;
private Displayable shownSystemScreen;
private Displayable shownClientScreen;
private Displayable currentSystemScreen;
private Displayable currentClientScreen;
private Displayable currentClientAfterAlertScreen;
private MIDletDispatcher dispatcher;
private MIDletEventStream paintEvents;
private MIDletEventStream clientEvents;
protected Display(RasterCanvas screenCanvas)
{
int a;
int w;
int h;
int p0;
int p1;
int p2;
int p3;
int p4;
Font titleFont;
Font tickerFont;
Font sideCommandFont;
Font defaultCommandFont;
RasterCanvas systemCanvas;
RasterCanvas clientCanvas;
Graphics systemGraphics;
Graphics clientGraphics;
if(screenCanvas == null)
{
throw new NullPointerException("Display: аргумент screenCanvas равен нулевой ссылке.");
}
if(!screenCanvas.isOpaque())
{
throw new IllegalStateException("Display: аргумент screenCanvas должен ссылаться на непрозрачную канву.");
}
titleFont = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_SMALL);
tickerFont = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_SMALL);
sideCommandFont = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_SMALL);
defaultCommandFont = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_SMALL);
w = screenCanvas.getWidth();
h = screenCanvas.getHeight();
a = w * h;
systemCanvas = new RasterCanvas(new GraphicBuffer(new int[a], 0, w, w, h), true);
clientCanvas = new RasterCanvas(new GraphicBuffer(new int[a], 0, w, w, h), true);
p0 = 0;
p1 = p0 + (titleFont.getHeight() + 4);
p2 = p1 + (tickerFont.getHeight() + 4);
p3 = h - (Math.max(defaultCommandFont.getHeight(), sideCommandFont.getHeight()) + 4);
p4 = h;
this.width = w;
this.height = h;
this.guiElementsMetrics = new int[] {
/* | MODE_WITHOUT_TICKER| MODE_WITH_TICKER| MODE_FULL_SCREEN|*/
/* | TITLE|--| CLIENT|COMMANDS| TITLE| TICKER| CLIENT|COMMANDS|--|--| CLIENT|--|*/
/* LEFT*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* TOP*/ p0, 0, p1, p3, p0, p1, p2, p3, 0, 0, p0, 0,
/* WIDTH*/ w, 0, w, w, w, w, w, w, 0, 0, w, 0,
/*HEIGHT*/ p1 - p0, 0, p3 - p1, p4 - p3, p1 - p0, p2 - p1, p3 - p2, p4 - p3, 0, 0, p4 - p0, 0
};
this.guiElementsFonts = new Font[] {
titleFont, tickerFont, Font.getDefaultFont(), sideCommandFont
};
this.graphicsForModesOfSystemScreen = new Graphics[] {
new GraphicsClipRestricted(systemCanvas.subcanvas(0, p1, w, p3 - p1)),
new GraphicsClipRestricted(systemCanvas.subcanvas(0, p2, w, p3 - p2)),
systemGraphics = new Graphics(systemCanvas)
};
this.graphicsForModesOfClientScreen = new Graphics[] {
new GraphicsClipRestricted(clientCanvas.subcanvas(0, p1, w, p3 - p1)),
new GraphicsClipRestricted(clientCanvas.subcanvas(0, p2, w, p3 - p2)),
clientGraphics = new Graphics(clientCanvas)
};
this.defaultCommandFont = defaultCommandFont;
this.graphicsForSystemScreen = systemGraphics;
this.graphicsForClientScreen = clientGraphics;
this.screenCanvas = screenCanvas;
}
public void callSerially(Runnable event)
{
if(event != null)
{
clientEvents.setEvent(event);
}
}
public void setCurrent(Displayable next)
{
if(next == null)
{
return;
}
if(next.isSystem())
{
currentSystemScreen = next;
Run.getInstance().setEvent(this.new SystemScreenChangeEvent());
return;
}
currentClientAfterAlertScreen = next instanceof Alert ? currentClientScreen : null;
currentClientScreen = next;
clientEvents.setEvent(this.new ClientScreenChangeEvent());
}
public void setCurrent(Alert next, Displayable after)
{
if(next == null)
{
throw new NullPointerException("Display.setCurrent: " +
"параметр next равен нулевой ссылке.");
}
if(after == null)
{
throw new NullPointerException("Display.setCurrent: " +
"параметр after равен нулевой ссылке.");
}
if(next.isSystem() || after.isSystem())
{
throw new IllegalArgumentException("Display.setCurrent(Alert, Displayable): " +
"для системных экранов следует использовать метод setCurrent(Displayable).");
}
if(after instanceof Alert)
{
throw new IllegalArgumentException("Display.setCurrent(Alert, Displayable): " +
"параметр after не может быть типа Alert.");
}
currentClientAfterAlertScreen = after;
currentClientScreen = next;
clientEvents.setEvent(this.new ClientScreenChangeEvent());
}
public void setCurrentItem(Item item)
{
Container owner;
if(item == null)
{
throw new NullPointerException("Display.setCurrentItem: " +
"параметр item равен нулевой ссылке.");
}
if((owner = item.getOwner()) == null)
{
throw new IllegalStateException("Display.setCurrentItem: " +
"элемент item не содержится в каком-либо контейнере.");
}
owner.setCurrent(item, this);
}
public void hideSystemScreen()
{
currentSystemScreen = null;
Run.getInstance().setEvent(this.new SystemScreenChangeEvent());
}
public boolean flashBacklight(int duration)
{
if(duration < 0)
{
throw new IllegalArgumentException("Display.flashBacklight: " +
"параметр duration не может быть отрицательным.");
}
return isClientInForeground();
}
public boolean vibrate(int duration)
{
if(duration < 0)
{
throw new IllegalArgumentException("Display.vibrate: " +
"параметр duration не может быть отрицательным.");
}
return isClientInForeground();
}
public boolean isColor()
{
return true;
}
public int numColors()
{
return 0x01000000;
}
public int numAlphaLevels()
{
return 0x00000100;
}
public int getBestImageWidth(int type)
{
switch(type)
{
default:
throw new IllegalArgumentException("Display.getBestImageWidth: " +
"недопустимое значение параметра type.");
case CHOICE_GROUP_ELEMENT:
return 24;
case LIST_ELEMENT:
return 32;
case ALERT:
return 48;
}
}
public int getBestImageHeight(int type)
{
switch(type)
{
default:
throw new IllegalArgumentException("Display.getBestImageHeight: " +
"недопустимое значение параметра type.");
case CHOICE_GROUP_ELEMENT:
return 24;
case LIST_ELEMENT:
return 32;
case ALERT:
return 48;
}
}
public int getBorderStyle(boolean highlighted)
{
return Graphics.SOLID;
}
public int getColor(int colorSpecifier)
{
switch(colorSpecifier)
{
default:
throw new IllegalArgumentException("Display.getColor: " +
"недопустимое значение аргумента colorSpecifier.");
case COLOR_FOREGROUND:
return RasterCanvas.getSystemColor(0x20);
case COLOR_HIGHLIGHTED_BACKGROUND:
return RasterCanvas.getSystemColor(0x19);
case COLOR_HIGHLIGHTED_FOREGROUND:
return RasterCanvas.getSystemColor(0x21);
case COLOR_BACKGROUND:
case COLOR_BORDER:
case COLOR_HIGHLIGHTED_BORDER:
return RasterCanvas.getSystemColor(0x05);
}
}
public Displayable getCurrent()
{
return currentClientScreen;
}
public final void flushGraphics(Canvas screen, Image image, int left, int top, int width, int height)
{
Graphics context;
if(screen == null || !isShown(screen)) return;
context = getClientGraphicsFor(screen);
try
{
context.reset();
context.setClip(left, top, width, height);
context.drawImage(image, 0, 0, Graphics.LEFT | Graphics.TOP);
}
catch(RuntimeException e)
{
e.printRealStackTrace();
}
paintDisplay(screen, screen.isSystem() ? graphicsForSystemScreen : graphicsForClientScreen);
}
public final boolean isClientInForeground()
{
return !applicationInBackground && currentSystemScreen == null;
}
public final int getWidth()
{
return width;
}
public final int getHeight()
{
return height;
}
public final Displayable getActive()
{
Displayable systemScreen;
return (systemScreen = currentSystemScreen) != null ? systemScreen : currentClientScreen;
}
final void startMIDlet(Class midletClass)
{
MIDletEventStream p;
MIDletEventStream c;
synchronized(guiElementsMetrics)
{
if(dispatcher == null)
{
dispatcher = new MIDletDispatcher(midletClass);
paintEvents = p = this.new MIDletEventStream();
clientEvents = c = this.new MIDletExecution();
(new Thread(p, "Обработчик событий перерисовки экрана")).start();
(new Thread(c, "Обработчик событий мидлета")).start();
}
}
}
final void stopMIDlet()
{
MIDletEventStream m;
synchronized(guiElementsMetrics)
{
if((m = clientEvents) != null)
{
m.setEvent(this.new MIDletTerminationEvent());
}
}
}
final void terminateEventStreams()
{
paintEvents.terminate();
clientEvents.terminate();
}
final void setEvent(int event, int param1, int param2, int param3)
{
Displayable screen;
MIDletEventStream events;
switch(event)
{
case EVENT_WINDOW_SHOW:
applicationInBackground = false;
Displayable.tickerUpdate();
break;
case EVENT_WINDOW_HIDE:
applicationInBackground = true;
Displayable.tickerUpdate();
break;
}
if((screen = shownSystemScreen) == null) screen = shownClientScreen;
if(screen == null) return;
if(screen.isSystem())
{
handleEvent(screen, event, param1, param2, param3);
return;
}
if((events = clientEvents) != null)
{
events.setEvent(event, param1, param2, param3);
}
}
final void callSeriallyEvent(Displayable screen, Runnable event)
{
MIDletEventStream events;
if(event == null) return;
if(screen.isSystem())
{
Run.getInstance().setEvent(event);
return;
}
if((events = clientEvents) != null)
{
events.setEvent(event);
}
}
final void callSeriallyPaint(Displayable screen)
{
MIDletEventStream events;
if(screen.isSystem())
{
Run.getInstance().setEvent(this.new SystemScreenRepaintEvent(screen));
return;
}
if((events = SERIALIZE_REPAINT ? clientEvents : paintEvents) != null)
{
events.setPaint(screen);
}
}
final void callSeriallyPaint(Displayable screen, int left, int top, int width, int height)
{
MIDletEventStream events;
if(screen.isSystem())
{
Run.getInstance().setEvent(this.new SystemScreenRepaintRegionEvent(screen,
left, top, left + width, top + height));
return;
}
if((events = SERIALIZE_REPAINT ? clientEvents : paintEvents) != null)
{
events.setEvent(this.new ClientScreenRepaintRegionEvent(screen,
left, top, left + width, top + height));
}
}
final void serviceRepaints(Displayable screen)
{
MIDletEventStream eventstream;
if(screen.isSystem())
{
boolean init = false;
int left;
int top;
int right;
int bottom;
left = top = right = bottom = 0;
Runnable[] events = Run.getInstance().getImmediatelyServices(screen);
for(int len = events == null ? 0 : events.length, i = 0; i < len; i++)
{
Runnable event;
if((event = events[i]) != null && event instanceof Rectangle)
{
Rectangle rect = (Rectangle) event;
left = init ? Math.min(left, rect.getLeft()) : rect.getLeft();
top = init ? Math.min(top, rect.getTop()) : rect.getTop();
right = init ? Math.max(right, rect.getRight()) : rect.getRight();
bottom = init ? Math.max(bottom, rect.getBottom()) : rect.getBottom();
init = true;
}
}
if(init) handlePaint(screen, left, top, right - left, bottom - top);
return;
}
if((eventstream = SERIALIZE_REPAINT ? clientEvents : paintEvents) != null)
{
eventstream.serviceRepaints(screen);
}
}
final boolean isShown(Displayable screen)
{
return !applicationInBackground && (screen.isSystem() ? shownSystemScreen == screen :
shownSystemScreen == null && shownClientScreen == screen);
}
final int getGUIElementLeft(Displayable screen, int guiElement)
{
return getGUIElementMetric(screen.getMode(), guiElement, 0);
}
final int getGUIElementTop(Displayable screen, int guiElement)
{
return getGUIElementMetric(screen.getMode(), guiElement, 1);
}
final int getGUIElementWidth(Displayable screen, int guiElement)
{
return getGUIElementMetric(screen.getMode(), guiElement, 2);
}
final int getGUIElementHeight(Displayable screen, int guiElement)
{
return getGUIElementMetric(screen.getMode(), guiElement, 3);
}
final Font getGUIElementFont(int guiElement)
{
Font[] fontFor;
if(guiElement < 0 || guiElement >= (fontFor = guiElementsFonts).length)
{
throw new IllegalArgumentException("Display.getGUIElementFont: " +
"недопустимое значение параметра guiElement.");
}
return fontFor[guiElement];
}
final Font getDefaultCommandFont()
{
return defaultCommandFont;
}
final Graphics getClientGraphicsFor(Displayable screen)
{
return (screen.isSystem() ? graphicsForModesOfSystemScreen : graphicsForModesOfClientScreen)[screen.getMode()];
}
final Displayable getShownScreen()
{
Displayable screen;
return applicationInBackground ? null : (screen = shownSystemScreen) != null ? screen : shownClientScreen;
}
final Displayable getScreenAfterAlert()
{
return currentClientAfterAlertScreen;
}
final Exception getMIDletStartingCause()
{
MIDletDispatcher d;
return (d = dispatcher) != null ? d.getCause() : null;
}
private void handleEvent(Runnable event)
{
if(event == null) return;
try
{
event.run();
}
catch(RuntimeException e)
{
e.printRealStackTrace();
}
}
private void handleEvent(Displayable screen, int event, int param1, int param2, int param3)
{
if(screen == null) return;
try
{
switch(event)
{
case EVENT_WINDOW_SHOW:
screen.eventShow();
break;
case EVENT_WINDOW_HIDE:
screen.eventHide();
break;
case EVENT_KEY_PRESSED:
screen.eventKeyPressed(param1, param2 & 0xffff | param3 << 16);
break;
case EVENT_KEY_REPEATED:
screen.eventKeyRepeated(param1, param2 & 0xffff | param3 << 16);
break;
case EVENT_KEY_RELEASED:
screen.eventKeyReleased(param1);
break;
case EVENT_POINTER_PRESSED:
screen.eventPointerPressed(param1, param2, param3);
break;
case EVENT_POINTER_DRAGGED:
screen.eventPointerDragged(param1, param2);
break;
case EVENT_POINTER_RELEASED:
screen.eventPointerReleased(param1, param2, param3);
break;
}
}
catch(RuntimeException e)
{
e.printRealStackTrace();
}
if(event == EVENT_WINDOW_SHOW)
{
callSeriallyPaint(screen);
}
}
private void handlePaint(Displayable screen)
{
int elements;
Graphics context;
if(screen == null || !isShown(screen) || (elements = screen.getRepaintElements()) == 0) return;
context = screen.isSystem() ? graphicsForSystemScreen : graphicsForClientScreen;
try
{
context.reset();
screen.clearRepaintElements();
screen.eventPaint(context, elements);
}
catch(RuntimeException e)
{
e.printRealStackTrace();
}
paintDisplay(screen, context);
}
private void handlePaint(Displayable screen, int left, int top, int width, int height)
{
int elements;
Graphics context;
if(screen == null || !isShown(screen) || (elements = screen.getRepaintElements()) == 0) return;
context = screen.isSystem() ? graphicsForSystemScreen : graphicsForClientScreen;
try
{
context.reset();
screen.clearRepaintElements();
screen.eventPaint(context, elements, left, top, width, height);
}
catch(RuntimeException e)
{
e.printRealStackTrace();
}
paintDisplay(screen, context);
}
private void paintDisplay(Displayable screen, Graphics context)
{
long delay;
Object monitor;
if(!isShown(screen)) return;
synchronized(monitor = guiElementsMetrics)
{
long period = (long) MINIMUM_PERIOD;
long currTimestamp = System.currentTimeMillis();
long mustTimestamp = lastRepaintTimestamp + period;
lastRepaintTimestamp = currTimestamp + ((delay = mustTimestamp - currTimestamp) > period ?
delay = period : delay > 0L ? delay : 0L);
}
if(delay > 0L)
{
try
{
Thread.sleep(delay);
}
catch(InterruptedException e)
{
e.printRealStackTrace();
}
}
if(!isShown(screen)) return;
synchronized(monitor)
{
int srcScanlength;
int dstScanlength;
GraphicBuffer src;
GraphicBuffer dst;
srcScanlength = (src = context.getBuffer()).getScanlength();
dstScanlength = (dst = screenCanvas.getBuffer()).getScanlength();
if(srcScanlength == dstScanlength)
{
Array.copy(src.getPixels(), src.getOffset(), dst.getPixels(), dst.getOffset(),
dst.getWidth() + (dst.getHeight() - 1) * dstScanlength);
} else
{
int width = dst.getWidth();
int height = dst.getHeight();
int srcOffset = src.getOffset() + srcScanlength * src.getHeight();
int dstOffset = dst.getOffset() + dstScanlength * height;
int[] srcPixels = src.getPixels();
int[] dstPixels = dst.getPixels();
for(int i = height; i-- > 0; )
{
Array.copy(srcPixels, srcOffset -= srcScanlength, dstPixels, dstOffset -= dstScanlength, width);
}
}
RasterCanvas.getScreenCanvas().updateScreen();
}
}
private int getGUIElementMetric(int mode, int guiElement, int metric)
{
if(guiElement < 0 || guiElement >= 4)
{
throw new IllegalArgumentException("Display.getGUIElementMetrics: " +
"недопустимое значение параметра guiElement.");
}
return guiElementsMetrics[metric * 12 + mode * 4 + guiElement];
}
}