About BREW Applications
BREW Programming Environment
BREW applications are event driven. Each event (application boot up, key press, etc...) is handled by a specific event handling code.
BREW is single task and single thread. The screen is fixed for every model and user input events can only come from the keyboard.
Differences Between BREW Versions
BREW does not vary from one chip platform to another. However, distinct chip platforms may utilize different versions of BREW SDK. All versions of BREW are backwards compatible.
Porting Hazards with mobile Java to BREW
- There is no Garbage collection in BREW.
- BREW is single thread.
- There is no string class in BREW.
- BREW does not have border checking for array indexes.
- BREW does not come with a GUI framework.
Porting "Life Game" from mobile Java to BREW
The porting procedure is demonstrated using the Life Game for mobile Java as an example.
BREW C++ Edition Life Game source code
BREW defines "global values" in an applet structure, which is passed as the first argument of the event handler. ( See list 1 ).
* List 1 : Inside applet structure typedef struct _LifeGameApp { AEEApplet a ; //Do not modify AEEDeviceInfo DeviceInfo; //Can be modified IDisplay *pIDisplay; IShell *pIShell; WindowManager *WindowManager; } LifeGameApp;
This applet structure corresponds to the LifeGameApplication for mobile Java. Applet structures are not designed to hold member functions. The applet structure has an access pointer to the WindowsManager, because it adjusts and manages the application.
BREW does not have UI components such as Panel or Canvas in Java. Instead, a class Window is defined and then inherited by newly implemented XXXPanel and XXXCanvas classes. ( See list 2 )
XXXPanel implements paint() for handling drawing events, and XXXCanvas implements keyPress( uint 16 key ) for handling key events.
* List 2 : Window class: substituting Panel and Canvas class /*abstract*/ class Window { protected: WindowManager* wm; public: Window(WindowManager* wm_) { wm = wm_; } virtual ~Window() {} virtual void paint() = 0; virtual void keyPress(uint16 key) = 0; };
The event handler LifeGameApp_HandleEvent is invoked when a key is pressed. ( See list 3 )
* List 3 : Event handler static boolean LifeGameApp_HandleEvent(LifeGameApp* app, AEEEvent eCode, uint16 wParam, uint32 dwParam) { if (app->WindowManager != NULL) { if (app->WindowManager->textCtl1 != NULL) { if (ITEXTCTL_HandleEvent(app->WindowManager->textCtl1, eCode, wParam, dwParam)) { return(TRUE); } } if (app->WindowManager->textCtl2 != NULL) { if (ITEXTCTL_HandleEvent(app->WindowManager->textCtl2, eCode, wParam, dwParam)) { return(TRUE); } } } switch (eCode) { case EVT_APP_START: //Executed in the beginning app->WindowManager = new WindowManager(app); app->WindowManager->setCurrent(new LifeGameMainPanel (app->WindowManager)); return(TRUE); case EVT_APP_STOP: delete app->WindowManager; return(TRUE); // Suspend and resume process is omitted in this code. case EVT_APP_SUSPEND: return(TRUE); case EVT_APP_RESUME: return(TRUE); case EVT_APP_MESSAGE: return(TRUE); case EVT_KEY: app->WindowManager->keyPress(wParam); return(TRUE); default: break; } return FALSE; }
The WindowManager class assumes the functions of the mobile Java's LifeGameApplication class. ( See list 4 )
The setCurrent(new XXXWindow(this)) function places a new window ( instance of the Window class ) in the foreground, and deletes the old window. When a key is pressed, the keyPress(key) function of the window in the foreground is invoked.
The function changeState(state) is the same as in mobile Java.
* List 4 : Class for managing Window class WindowManager { public: IDisplay* display; IShell* shell; LifeGame* lifeGame; Window* frontWindow; ITextCtl* textCtl1; ITextCtl* textCtl2; public: SoftKeyWindow* softkeyWindow; public: WindowManager (LifeGameApp* app); ~WindowManager(); void changeState(STATE_ENUM state); void setCurrent(Window* window); void keyPress(uint16 key) { frontWindow->keyPress(key); } void update(IDisplay* disp); };
Porting the XXXCanvas Class
* List 5 : Corresponds to the Canvas class class LifeGameMainCanvas : public Window { public: LifeGameMainCanvas(WindowManager* wm_); virtual ~LifeGameMainCanvas(); virtual void paint(); virtual void keyPress(uint16 key); static void OnTimerEntry(void* data); void next(); }; #define TIMER_INTERVAL 300 LifeGameMainCanvas::LifeGameMainCanvas (WindowManager* wm_) : Window(wm_) { //Set timer ISHELL_SetTimer(wm->shell, TIMER_INTERVAL, LifeGameMainCanvas::OnTimerEntry, this); wm->softkeyWindow->setText("Back", "", "Finish"); paint(); } LifeGameMainCanvas::~LifeGameMainCanvas() { //Cancel timer ISHELL_CancelTimer(wm->shell, LifeGameMainCanvas::OnTimerEntry, this); } void LifeGameMainCanvas::paint() { IGraphics* graphic; // IGraphics get object ISHELL_CreateInstance(wm->shell, AEECLSID_GRAPHICS, (void**)&graphic); IGRAPHICS_SetBackground(graphic, 255, 255, 255); IGRAPHICS_ClearViewport(graphic); wm->lifeGame->paint(wm->display); wm->update(wm->display); } void LifeGameMainCanvas::keyPress(uint16 key) { switch(key) { case AVK_SOFT1: wm->changeState(STATE_TITLE); break; case AVK_SOFT2: ISHELL_CloseApplet(wm->shell, FALSE); break; } return; } void LifeGameMainCanvas::OnTimerEntry(void* data) { ((LifeGameMainCanvas*)data)->next(); } void LifeGameMainCanvas::next(){ wm->lifeGame->next(); //Proceed life game to next age paint(); //Set the timer again ISHELL_SetTimer(wm->shell, TIMER_INTERVAL, LifeGameMainCanvas::OnTimerEntry, this); }
When LifeGameMainCanvas is in the foreground and a key is pressed, the keyPress() function in this class is invoked. Soft key events should be handled within keyPress().
Life Game must proceed to subsequent generations at regular time intervals. Multiple threads managed this process in mobile Java, but since BREW is single thread, a timer API is used.
BREW can not process any event longer than 1 second in a single event handler. Events longer than a second, need to be broken up into several parts and processed seperatly. ISHELL_SetTimer can be used for this purpose.
Porting the XXXPanel Class
* List 6 : Corresponds to the Panel class class LifeGameMainPanel : public Window { private: int cursor; public: LifeGameMainPanel(WindowManager* wm_); virtual void paint(); virtual void keyPress(uint16 key); }; LifeGameMainPanel::LifeGameMainPanel (WindowManager* appli_) : Window(appli_) { cursor = 0; wm->softkeyWindow->setText("", "", "Quit"); paint(); } void LifeGameMainPanel::paint() { IGraphics* graphic; // IGraphics Get object ISHELL_CreateInstance(wm->shell, AEECLSID_GRAPHICS, (void**)&graphic); IGRAPHICS_SetBackground(graphic, 255, 255, 255); IGRAPHICS_ClearViewport(graphic); //Delete screen //Get the font height const int height = IDISPLAY_GetFontMetrics(wm->display, AEE_FONT_NORMAL, NULL, NULL); const int space = 6; DrawText(wm->display, "Life Game", 15, space); DrawText(wm->display, "Start", 15, height + space * 2); DrawText(wm->display, "Edit", 15, height * 2 + space * 3); DrawText(wm->display, "Save", 15, height * 3 + space * 4); DrawText(wm->display, "Load", 15, height * 4 + space * 5); DrawText(wm->display, "Change size", 15, height * 5 + space * 6); DrawRect(wm->display, 13, height + space * 2, 80, height, MAKE_RGB(0, 0, 0)); DrawRect(wm->display, 13, height * 2 + space * 3, 80, height, MAKE_RGB(0, 0, 0)); DrawRect(wm->display, 13, height * 3 + space * 4, 80, height, MAKE_RGB(0, 0, 0)); DrawRect(wm->display, 13, height * 4 + space * 5, 80, height, MAKE_RGB(0, 0, 0)); DrawRect(wm->display, 13, height * 5 + space * 6, 140, height, MAKE_RGB(0, 0, 0)); int dx = 80 + 4; if (cursor == 4) dx = 140+4; DrawRect(wm->display, 13-2, (height + space) * (cursor + 1) + space -2, dx, height+4, MAKE_RGB(255, 0, 0)); wm->update(wm->display); } void LifeGameMainPanel::keyPress(uint16 key) { switch(key) { case AVK_DOWN: if (cursor < 4) { cursor++; paint(); } break; case AVK_UP: if (cursor > 0) { cursor--; paint(); } break; case AVK_SELECT: switch (cursor) { case 0: wm->changeState(STATE_GAMESTART); break; case 1: wm->changeState(STATE_EDIT); break; case 2: wm->changeState(STATE_SAVE); break; case 3: wm->changeState(STATE_LOAD); break; case 4: wm->changeState(STATE_CHANGESIZE); break; } break; case AVK_SOFT2: ISHELL_CloseApplet(wm->shell, FALSE); break; } }
-
A difference between mobile Java and BREW is BREW's lack off button controls.
In the figure on the left, buttons are drawn using rectangles and texts.
When up / down keys are pressed, a red cursor ( controlled by a cursor variables ) moves across the buttons.
The text on the bottom right corner represents a soft key.
The SoftKeyWindow Class ( Draws Soft Keys )
* List 7 : Handling soft keys class SoftKeyWindow : public Window { private: //Strings displayed in the bottom //left corner, middle and bottom right corner BPPString textLeft, textCenter, textRight; int width; //screen width public: SoftKeyWindow(WindowManager* wm_, int width_); //Calculate soft key area (bottom of the display) //paint it white and draw strings (omitted here) void paint(); //Set text (omitted here) void setText(const BPPString& textLeft_, const BPPString& textCenter_, const BPPString& textRight_); void keyPress(uint16 key) {}; };
The WindowManager's update function is implemented in list 8. When this function is called in some other window's paint function, the soft keys will be drawn.
* List 8 : Implements update function in WindowManager void WindowManager::update(IDisplay* disp) { //Draw soft key window softkeyWindow->paint(); //API for redrawing IDISPLAY_Update(disp); }