Date post: | 11-May-2015 |
Category: |
Technology |
Upload: | andreas-jakl |
View: | 2,634 times |
Download: | 2 times |
mopoid
Symbian OS Workshop
1 Andreas Jakl, 2009 v2.4.1 – 19 April 2009
Disclaimer
● These slides are provided free of charge at http://www.symbianresources.com and are used during Symbian OS courses at the University of Applied Sciences in Hagenberg, Austria ( http://www.fh-hagenberg.at/ )
● Respecting the copyright laws, you are allowed to use them:
for your own, personal, non-commercial use
in the academic environment
● In all other cases (e.g. for commercial training), please contact [email protected]
● The correctness of the contents of these materials cannot be guaranteed. Andreas Jakl is not liable for incorrect information or damage that may arise from using the materials.
● Parts of these materials are based on information from Symbian Press-books published by John Wiley & Sons, Ltd. This document contains copyright materials which are proprietary to Symbian, UIQ, Nokia and SonyEricsson. “S60™” is a trademark of Nokia. “UIQ™” is a trademark of UIQ Technology. Pictures of mobile phones or applications are copyright their respective manufacturers / developers. “Symbian ™”, “Symbian OS ™” and all other Symbian-based marks and logos are trademarks of Symbian Software Limited and are used under license. © Symbian Software Limited 2006.
Andreas Jakl, 20092
Motivation
● Why C++ for Symbian OS / S60?
High performance
(Nearly) unlimited possibilities
Ideal platform for innovative projects!
● Bonus: increase your value on the market with Symbian OS knowledge
3 Andreas Jakl, 2009
Schedule
● Instead of theory, we’ll do a practical project
Andreas Jakl, 20094
Mopoid
● Tasks
Create your own application using the UI designer
Define a menu and an about box
Leaves, Panics and Cleanup Stack
Project structure, libraries
Class types, naming conventions
Defining and displaying text, fonts, descriptors
Using vector graphics
Scalable UI
Asynchronous events, timers
Key handling
Files, data caging (platform security)
Foreground / background
Dynamic menuAndreas Jakl, 20095
Conventions
● Slides are a mixture of explanations and tasks you have to do
TODOs are marked on the left side of the slides
After the game framework is imported, source code edits are also marked with a // TODO: comment
Slides with explanations of specific Symbian OS concepts are marked in the upper right corner
Andreas Jakl, 20096
// TODO: Refresh back light timer
IDEs – Carbide.c++
● Carbide.c++ (based on Eclipse)
New main development platform
Integrates Eclipse with required tools
● Editions:
Express: basic
Developer: UI-designer, on-device debugging
Professional: Performance tools
OEM: ROM and JTAG support
(all free starting with Carbide.c++ 2.0)
Andreas Jakl, 20097
Carbide.c++
Andreas Jakl, 20098
S60 SDKs
● Choose SDK depending on required compatibility and features
● Binary compatibility break with Symbian OS 9
● www.forum.nokia.com
Andreas Jakl, 2009
SDK OS version Devices
1st Ed. v6.0 7650
1st Ed, FP 1 v6.1 N-Gage, SX1, 3650, Sendo-X, …
2nd Ed v7.0s 6600, …
2nd Ed, FP 1 v7.0s 7610, 6670, 6260, …
2nd Ed, FP 2 v8.0 6630, 6680, 6681, …
2nd Ed, FP 3 v8.1 N70, N90, …
3rd Ed v9.1 E61, N73, N75, …
3rd Ed, FP 1 v9.2 N95, E90, …
3rd Ed, FP 2 v9.3 N96, N78, …
5th Ed v9.4 5800, N97, …
5th Ed, FP1 v9.5? ?
Commonly used for maximum compatibility
9
Introduces touch screens to S60
Installation
1. Carbide.c++ 2.0 Developer Edition (or later)http://www.forum.nokia.com/main/resources/tools_and_sdks/carbide/index.html
2. Perl 5.6.x (Set the path variable!) – not 5.8 / 5.10!http://downloads.activestate.com/ActivePerl/Windows/5.6/ActivePerl-5.6.1.638-MSWin32-x86.msi
3. SDK(s) (S60 3rd Ed. MR + newer)http://www.forum.nokia.com/Resources_and_Information/Tools/Platforms/S60_Platform_SDKs/
Andreas Jakl, 200910
Install all tools on the same drive (recommended: C:\, no network drive!)
Updating Carbide.c++
● Go to:
Help Software Updates Find and Install ...
Search for new features to install
● Only install updates from the Carbide.c++ update site!
Updating other components might overwrite Carbide.c++ settings!
Andreas Jakl, 200911
Workspace
● Workspace location:
Has to be on the same drive as the SDK ( C:\)
Must not contain space characters
Example: C:\Symbian\dev
Andreas Jakl, 200912
Create new project
● File New C++ Application for S60 Project
Andreas Jakl, 200913
Create new project
● Call it: Mopoid
● Check again if the path is really on the SDK drive (C:\) and does not contain space characters!
Andreas Jakl, 200914
Phone Build
● Build configurations:
WINSCW: Build for the windows-based emulator
ARMV5: Optimized builds for the device using the ARM RealView-compiler (commercial)
GCCE: Standard builds for the device using the free GCC(E) compiler. Comes with the SDK
Andreas Jakl, 200915
Choose UI-Design
● Select the empty design
We’ll do low level graphics on an empty canvas
Andreas Jakl, 200916
Application UID (UID3)
● Development for v9: 0xE0000000 - 0xEFFFFFFF
Development UID automatically assigned by Carbide
● Get UIDs for public applications:
Different range for signed / unsigned
Get one for free at: http://www.symbiansigned.com/
Andreas Jakl, 200917
Range Purpose
0x00000000 – 0x0FFFFFFF Development use (< v9)
0x10000000 – 0x1FFFFFFF Legacy UID (pre-v9)
0x20000000 – 0x2FFFFFFF v9 protected UIDs
0xA0000000 – 0xAFFFFFFF v9 unprotected UIDs
0xE0000000 – 0xEFFFFFFF Development use (v9)
0xF0000000 – 0xFFFFFFFF Legacy UID (pre-v9)
Our Project
Andreas Jakl, 200918
UI-Components
● Window
Sub-component of the device screen
Principal window filling up entire screen
Not used for display
Can contain many panes
● Pane
Sub-component of a window
Can contain many sub-panes
Andreas Jakl, 200919
S60 UI
● Status Pane
Information about running application & device (e.g. battery strength)
● Main Pane
Application content
● Control Pane
Softkey labels
Andreas Jakl, 200920
Status Pane
Main Pane
Control Pane
CompileFirst tests
Andreas Jakl, 200921
Choose Build Target
● Active Build Configuration Emulator
Andreas Jakl, 200922
Compile Project
● (optional) Project Build Project
● Run Run
● Or:
Andreas Jakl, 200923
You can only use the mouse to navigate on the screen on touch-enabled
emulators! Otherwise, use the buttons below.
Launch Configuration
● Launch: Mopoid.exe
Game is directly started in the emulator
Emulator shuts down when app exits
● Launch: Emulator
Start game manually from the menu
Adv.: recompile without closing the emulator, error messages on app shutdown more visible
● Available from: Carbide.c++ 1.3.1+ with SDKs > S60 3rd MR
Andreas Jakl, 200924
Possible Problems
● Compilation error:
your workspace / project is not on the same drive as the Symbian OS SDK (usually C:\)(told you to put it there several times )
Andreas Jakl, 200925
***Generating makefiles.bldmake.bat bldfiles WINSCW UDEB WARNING: EPOCROOT does not specify an existing directoryBLDMAKE ERROR: Directory "\Symbian\9.1\S60_3rd_MR\EPOC32\" does not exist
Total Time: 0 sec===Build Command = build WINSCW UDEB -v===Exec error:Launching failed***build returned with exit value = -1***Stopping. Check the Problems view or Console output for errors.-1
Emulator
● Application added to the end of the list in the “Installat.” folder
● Can be moved with “Options Move *to folder+”
● Useful when working on the same project for some time
Andreas Jakl, 200926
Building for the Device
● Change build configuration to Phone Release (GCCE)
● .sisx-file created in /sis/-folder of your project
● Send to device using PC Suite or Bluetooth
Andreas Jakl, 200927
.sis vs .sisx
● Both have been created, installing .sis:
● .sisx = signed version of the .sis
● Without a valid certificate: self-signed with automatically generated certificate
● Installation of self-signed applications not allowed by default on some devices
Change: Application manager Software installation change from “Signed only” to “All”
Andreas Jakl, 200928
Own Build Configurations for Devices
● When importing a project or adding a build configuration (= SDK) later on:
Right-click the project Properties Carbide.c++ Carbide Build Configurations
Check that the current configuration is “Phone Release (GCCE)” for the S60 3rd Ed. MR-SDK
Go to the “SIS Builder”-tab
Click on “Add” to bring up “SIS Properties” dialog
Choose the .pkg file
Andreas Jakl, 200929
Automatic Building
● You might be used to Eclipse displaying errors as you type...
● ... but this is for Java, not for C++!
● You can still enable automatic compilation on resource change – might be useful for smaller projects
Andreas Jakl, 200930
Window Preferences... General Workspace Build automaticallyor: Project Build automatically
Application structure
Application class
● Entry point for OS
● Defines application UID
● Creates Document class
● Normally no changesrequired here.
Andreas Jakl, 200931
Application structure (2)
Document class
● Takes care of data model
● Creates Application UI (AppUI-class)
● Usually no changes requiredin this class either.
Andreas Jakl, 200932
Application structure (3)
AppUI class
● “Controller” of the app.
● Not visible itself
● Manages views (container)
● General, application-wide event handling (exit, pause, ...)
Andreas Jakl, 200933
Application structure (4)
View class
● Manages title- and statuspane
● Command handling(for this view)
Andreas Jakl, 200934
Application structure (5)
Container class
● Handling of UI components
● Manages “what is visible” in the main pane (= main part of the screen)
Andreas Jakl, 200935
Cone
Uikon
Avkon
AppArc
App Startup (S60 Views)
Andreas Jakl, 200936
CMyApplication CMyDocument CMyAppUi CMyView1 CMyContainer1
CAknApplication CAknDocument
CAknViewAppUi
CAknViewCAknAppUi
CAknAppUiBase
CEikAppUiCEikDocumentCEikApplication
CCoeAppUiCApaDocumentCApaApplication CCoeControl
CreateDocumentL() CreateAppUiL() ConstructL() DoActivateL()RunApplication
Avkon View Switching Application
Menu, About Boxes and LeavesUI-Design
Andreas Jakl, 200937
Menu Definition
● Open the UI design (MopoidContainer.uidesign)
● Create 3 menu items:
Start new game
About
Exit
Andreas Jakl, 200938
Hint: the UI designer creates source code when you save the design. This can be problematic when you change or rename something.Therefore, only save when you’re finished!
Exit Command
● Change the command id of the “Exit” command to EAknCmdExit
Andreas Jakl, 200939
Optional: Change the title
About Box
● Add a “Standard Note” dialog
● Modify its properties:
Type: “information” (actually not 100% correct, should give information about an unexpected situation according to UI guidelines. But it’s fine for this demo)
Text: your about message...
Name: “noteAbout”
Andreas Jakl, 200940
Connection: Menu Dialog
● Open the options menu
● Right click “Handle ‘Selected’ Event” for the “About” menu item
Andreas Jakl, 200941
Connection: Menu Dialog
● UI-Designer created:
RunNoteAboutL() – to display the note dialog
HandleAboutMenuItemSelectedL() – called when the menu item is selected
● Our task:
Run the note when the menu item is selected
Andreas Jakl, 200942
TBool CMopoidContainerView::HandleAboutMenuItemSelectedL( TInt aCommand )
{
RunNoteAboutL();
return ETrue;
}
Result
Andreas Jakl, 200943
Exceptions – Java
● Try & Catch for handling exceptions
● Functions can throw “Exceptions”
Andreas Jakl, 200944
Calling function
Try {
int x = Integer.parseInt(“1234”);
} catch (NumberFormatException e) {
System.out.println(“Unable to convert this String to a number.”);
}
Integer Class
…
static int parseInt throwsNumberFormatException {
…
}
…
Leave – Symbian
● TRAP(D) catches exceptions (“Leave”)
● Functions send out leave
● Function name marked by an L-suffix
Andreas Jakl, 200945
Main-Function
TRAPD(err, DoExampleL());if (err)
{console->Printf(KTxtFailed, err);}
DoExampleL()-Function
void DoExampleL(){
RFs fsSession; // Connect to the file server
User::LeaveIfError(fsSession.Connect()); // …fsSession.Close();}
TRAPD-Makro declares err as TInt and =
KErrNoneLeaves if the Connect()
function does not return KErrNone
The TRAP(D) macros are defined in e32cmn.h
Central Exception Handling
Andreas Jakl, 200946
TRAPD(err, F2L());if (err) …
New (ELeave) …… NewL() …
… User::Leave() …… ConstructL() …
F5L() …… F6L() ….… F8L() ….
… F3L() …F4L() …
Handling Leaves
● Try to implement central leave-handling
● If leave not handled by your code error-message shown by the UI-framework!
● Therefore: Only handle leaves yourself if they influence your application
Andreas Jakl, 200947
Andreas Jakl, 200948
S60 Framework(traps the leave
somewhere)
void CMopoidContainerView::HandleCommandL( TInt aCommand )
{
TBool commandHandled = EFalse;
switch ( aCommand )
{
case EMopoidContainerViewAboutMenuItemCommand:
commandHandled = HandleAboutMenuItemSelectedL( aCommand );
break;
// ... TBool CMopoidContainerView::HandleAboutMenuItemSelectedL( TInt aCommand )
{
RunNoteAboutL();
return ETrue;
}
void CMopoidContainerView::RunNoteAboutL( const TDesC* aOverrideText )
{
CAknInformationNote* note = new ( ELeave ) CAknInformationNote();
if ( aOverrideText == NULL )
{
HBufC* noteText = StringLoader::LoadLC(R_MOPOID_CONTAINER_NOTE_ABOUT);
note->ExecuteLD( *noteText );
CleanupStack::PopAndDestroy( noteText );
}
else
{
note->ExecuteLD( *aOverrideText );
}
}
No memory left toallocate the object
Leave is passed on until a TRAP statement is reached
Game EngineAdd the pre-implemented framework to your project
Andreas Jakl, 200949
Extraction and Sources
1. Close Carbide.c++
2. Unzip Mopoid.Update.zip to c:\Symbian\dev\
Keep the directory structure
Overwrite all files (if there is no warning, the directory is wrong)
3. Start Carbide.c++
4. Click on the project, press F5 (Refresh)
5. Open mopoid.mmp (project definition file)
Switch to the “Sources”-tab
Make sure all files in the “src”-dir are ticked. The header files do not have to be included here
Andreas Jakl, 200950
Libraries
5. Switch to the “Libraries”-tab and add:
bitgdi.lib
mediaclientaudio.lib
mediaclientimage.lib
● How to find out which libraries you need?
SDK documentation for the APIs that you use.Otherwise, you’ll get C++ linker errors.
Andreas Jakl, 200951
Text in Symbian OS
● Mobile phones are global localization is vital!
● Text is defined in C++ resource files
One resource file per language
Separate UI elements from source code!
Easier to maintain
Only appropriate language is loaded
Andreas Jakl, 200952
Localization – Overview
Andreas Jakl, 200953
<AppName>.r03German compiled
resource file
<AppName>.r02French compiled
resource file
<AppName>.r01UK English compiled
resource file
Resource Compiler
<AppName>.rssStrings replaced with
#defines from currently active .lxx
<AppName>.locInclude currently active
language#ifdef LANGUAGE_01...
<AppName>.l01UK English
#define STR_hi “Hello”
<AppName>.l02French
#define STR_hi “Salut”
<AppName>.l03German
#define STR_hi “Hallo”
If using .rls-files instead of .loc, use rls_string instead of #define
String Resources
● Define text in one place – and not in the source code
● Easy to modify, easy to localize
Andreas Jakl, 200954
<AppName>.l01 / .rls ... (Text only!)#define STR_Loading “Loading...”#define STR_Caption “Hello World”
<AppName>.rss (UI Definition)#include “<AppName>.l01”RESOURCE LOCALISABLE_APP_INFO r_localisable_app_info{
short_caption = STR_Caption; }RESOURCE TBUF r_loading { buf = STR_Loading; }[...]
<Program>.cpp (C++ Source Code)#include <stringloader.h>#include <<AppName>.rsg>
void C<AppName>AppUi::DisplayInfo(){
HBufC* buf = StringLoader::LoadLC ( R_LOADING );[...]CleanupStack::PopAndDestroy(buf);
}
<AppName>.rsg (Generated by resource compiler)#define R_LOCALISABLE_APP_INFO 0x66a61005#define R_LOADING 0x66a61006
Additional Game Text
● Define strings like explained on the previous slide
MopoidContainer.l01 / MopoidContainer.rssi
Andreas Jakl, 200955
Resource name String name Text
r_score STR_score Score:
r_level STR_level Level:
r_pause STR_pause Game Paused
r_gameover STR_gameover Game Over
r_finished STR_finished You made it!
r_enterlevel STR_enterlevel Entering Level:
r_lifelost STR_lifelost Live Lost!
r_lives STR_lives Lives:
r_pressjoystick STR_pressjoystick Press Joystick
r_highscore STR_highscore High Score:
r_title STR_title mopoid
Add an additional space char after
the “:”
New Game
● In the UI designer, create a new event handler for the “Start new game” menu item
Andreas Jakl, 200956
if (iMopoidContainer->iGameEngine)
iMopoidContainer->iGameEngine->StartNewGameL();
CMopoidContainerView::HandleStart_new_gameMenuItemSelectedL()
Graphics
● Today’s phones: screen orientation / size changes
● Mopoid uses vector graphics
● S60: support for SVG-T
Andreas Jakl, 200957
Adding Game Graphics
● Add a multi image file (.mif) to the icons-makefile: \group\Icons_aif_scalable_dc.mk
● Target directory: resource\apps\MopoidGraphics.mif
● Header generation: Header
● Header file:epoc32\include\MopoidGraphics.mbg
Andreas Jakl, 200958
Adding Game Graphics
● Choose images: bricks, panel and ball
● Set the mask depth for the bricks to n/a
Andreas Jakl, 200959
Copying Data to the Emulator
● C:\-drive of the phone is emulated in
<EPOCROOT>\winscw\c
e.g.: C:\Symbian\9.1\S60_3rd_MR\Epoc32\winscw\c
● Additional game resources required during runtime: sounds and level definition file
1. Open Mopoid.mmp and write down the UID3
Andreas Jakl, 200960
Copying Data to the Emulator
● Open \group\bld.inf
Lists which project definition & make files belong to the project and which files to export
● Export the files to the private directory
Andreas Jakl, 200961
The makefile will automatically create
the .mif-file in the right dir
Copying Data – Carbide.c++ 1.2
● Bug in Carbide.c++ 1.2: directories saved with wrong slashes, resulting in this error:
● Solution: Correct slashes in bld.inf-file (not needed for 1.3+)
Find & replace: “/” “\”
Andreas Jakl, 200962
Phone Release
● Copy additional data files to the phone
● Contents of the SIS-file defined in \sis\Mopoid.pkg
Andreas Jakl, 200963
; The icon-file
"$(EPOCROOT)epoc32\data\z\resource\apps\MopoidGraphics.mif"
-"!:\resource\apps\MopoidGraphics.mif"
; Sound files
"..\gfx\hit.wav"
-"!:\private\E2C4F75B\hit.wav"
"..\gfx\bounce.wav"
-"!:\private\E2C4F75B\bounce.wav“
; Levels definition file
"..\gfx\levels.dat"
-"!:\private\E2C4F75B\levels.dat"
; Required for the application to be covered by backup/restore facility
"..\sis\backup_registration.xml" -"!:\private\E2C4F75B\backup_registration.xml"
Adapt the UID to your own!
Mopoid.pkg
Sound Bug-Fix
● Error in the S60 3rd Ed., MR-SDK – includes are not set correctly for the sound API, resulting in this error:
● Solution: add a system include to Mopoid.mmp:
<EPOCROOT>/include/mmf/plugin
Andreas Jakl, 200964
Testing
● Before building, choose “Project” “Clean”
● This is what Mopoid should look like now (white, empty):
● ... if there are serious problems, use Mopoid.Merged.zip
Andreas Jakl, 200965
Troubleshooting
● When encountering problems, clean your project
Toolchain might not detect some changes in files, resulting in compile errors
Cleaning is always a good idea, with any language on any platform!
● Frequent problem in this step:
MopoidGraphics.mbg is not found when compiling
Next slides: how to make sure everything is cleaned to solve this issue
Andreas Jakl, 200966
Background Info: Build Toolchain
● Involved files and commands in a command-line build:
Andreas Jakl, 200967
bld.inf
project.mmp
> bldmake bldfiles
> abld build wins udeb
Intermediate build files in\epoc32\build\<projectdir>
abld.bat in <projectdir>
Intermediate files in \epoc32\build\<projectdir>
Binary files in\eopc32\release\winscw\udeb\
Input files in <projectdir> Command Outputs
Clean Everything
● Clean Levels
Level 0 (abld clean): Removes everything built by abld <target>.This includes: all intermediate files created during compilation and all the executables and import libraries created by the linker.
Level 1 (abld reallyclean): As clean, but additionally removes exported files and makefiles.
Level 2 (abld reallyclean, bldmake clean): Removes all files created by bldmake.
● Change to Level 2:
Window Preferences Carbide.c++ Build SBSv1-tab
Andreas Jakl, 200968
Manual Cleaning
● In case cleaning alone doesn’t help - manually delete all files created by your project:
Make sure the emulator isn’t running
Search for “mopoid” in <SDK-dir>\epoc32\
Delete all files (not folders, problematic with Windows)
Andreas Jakl, 200969
Loading ImagesGetting the images out of the .mif-file
Andreas Jakl, 200970
Class Types
● CSpriteHandler loads, scales and provides images
Andreas Jakl, 200971
SVG-Images
.mif-File
Load image (CAknIconUtils)
Set vector image size in
pixels
Draw the image
Why the “C”?
Fundamental Types
● Defined in <SDK-path>\epoc32\include\e32def.h
Andreas Jakl, 200972
Standard ANSI Symbian OS Description
int TInt Signed (32-bit) Integer
unsigend int TUint Unsigned (32-bit) Integer
float TReal32 Single-precision IEEE 754 floating-point
double TReal, TReal64 Double-precision IEEE 754 floating-point
long long TInt64 Uses native 64-bit support
bool TBool (ETrue, False) Equates to int due to early compilers
void* TAny* “Pointer to anything”
also available: TText[8|16], TInt[8|16|32], TUint[8|16|32], TUint64
Examples
Andreas Jakl, 200973
TBool b = ETrue;// Bad style: ETrue = 1, but any non-zero number should be interpreted as true!if (b == ETrue) { ... }// Good style:if (b) { ... }
Example: TBool
// Symbian OS uses ‘void’ for ‘nothing’ and ‘TAny*’ for ‘pointer to anyting’void Foo(); // Returns no resultTAny* p; // Pointer to anything
Example: void / TAny*
TInt x = 5;TReal y = (TReal)x + 0.5;
Example: TInt, TReal
T Classes
● Remember the fundamental types (TInt, ...)?
● T classes similar behaviour
Do not have a destructor
– Therefore, no member data that has a destructor
Contain all data internally
– No pointers, references or handles (“has-a” relation)
● Can be created on the stack and the heap
● Also usually used instead of a traditional C struct
Andreas Jakl, 200974
Variables | T Classes | C Classes | R Classes | M Classes | Static Classes
T Classes – Example
Andreas Jakl, 200975
class TPoint{
public:enum TUninitialized { EUninitialized };/** Constructs default point, initialising its iX and iY members to zero. */TPoint(TUninitialized) {}inline TPoint();inline TPoint(TInt aX, TInt aY);IMPORT_C TBool operator==(const TPoint& aPoint) const;// [...]IMPORT_C TPoint operator-() const;IMPORT_C void SetXY(TInt aX, TInt aY);IMPORT_C TSize AsSize() const;
public:/** The x coordinate. */TInt iX;/** The y coordinate. */TInt iY;};
TPoint definition from e32cmn.h
void CMyControl::Draw(const TRect &aRect) const
{CWindowGc& gc = SystemGc ();gc.DrawLine (TPoint (0, 0), iLastPoint);}
Usage example
Variables | T Classes | C Classes | R Classes | M Classes | Static Classes
C Classes
● Properties of C classes (‘C’ for ‘cleanup’)
Usually created on the heap(they’re often too large for stack themselves)
Usually own pointers to large objects, resources, ...
● Derive from CBase (directly or indirectly)
Safe construction / Destruction
Zero initialization
Andreas Jakl, 200976
Variables | T Classes | C Classes | R Classes | M Classes | Static Classes
C Classes – Characteristics
● Safe construction / destruction
CBase defines virtual destructor
– C++ therefore calls destructor in correct order
– Also required for the cleanup stack (later module)
Declares a private copy constructor and assignment operator
– Prevents errors
– If required: derived class has to declare it
● Zero initialization
CBase overloads new-operator
Zero-initializes all member data
Andreas Jakl, 200977
Variables | T Classes | C Classes | R Classes | M Classes | Static Classes
C Classes – Example
Andreas Jakl, 200978
class CSprite : public CBase{public: // Constructors and destructors
static CSprite* NewL( TInt aXVelocity, /* ... */, CFbsBitmap* aImage, CFbsBitmap* aMask);
static CSprite* NewLC( TInt aXVelocity, /* ... */, CFbsBitmap* aImage, CFbsBitmap* aMask);
virtual ~CSprite();
public: // New methods (omitted for clarity)
private: // ConstructorsCSprite( TInt aXVelocity, /* ... */, CFbsBitmap* aImage, CFbsBitmap* aMask);void ConstructL();
private: // DataTPoint iPosition;const CFbsBitmap * const iImage;const CFbsBitmap * const iMask;
};
Variables | T Classes | C Classes | R Classes | M Classes | Static Classes
R Classes
● Own an external resource handle, e.g.:
Server session (RFs – file server session)
Memory (RArray, RBuf)
● Initialization:
Open(), Create() or Initialize()
Close() or Reset() instead of destructor
No automated Close() through the destructor!
Andreas Jakl, 200979
Variables | T Classes | C Classes | R Classes | M Classes | Static Classes
R Classes – Example
Andreas Jakl, 200980
void CMyClass::SendCachedDataL(){RSocketServ socketServer;
// Connect to the SocketServerUser::LeaveIfError( socketServer.Connect() );// Make sure Close() is called at the endCleanupClosePushL( socketServer );
// …
CleanupStack::PopAndDestroy(); // Calls Close() on the socket server object}
Variables | T Classes | C Classes | R Classes | M Classes | Static Classes
M Classes
● M class
M is for “mixin”
Used for defining interface classes
Declares pure virtual functions
Should not contain members or constructors
● Implementing class
Usually derives from CBase and the interface
Only form of multiple inheritance encouraged on Symbian OS
Andreas Jakl, 200981
Variables | T Classes | C Classes | R Classes | M Classes | Static Classes
M Classes – Example
Andreas Jakl, 200982
// Interface definitionclass MMdaAudioPlayerCallback
{public:
virtual void MapcInitComplete(TInt aError, const TTimeIntervalMicroSeconds& aDuration) = 0;
virtual void MapcPlayComplete(TInt aError) = 0;};
// Implementing classclass CSoundPlayer : public CBase, public MMdaAudioPlayerCallback
{// [...]
protected: // Functions from base classesvoid MapcInitComplete( TInt aError, const TTimeIntervalMicroSeconds& aDuration );void MapcPlayComplete( TInt aError );// [...]}
The CBase-derivation always has to be first!
Variables | T Classes | C Classes | R Classes | M Classes | Static Classes
Static Classes
● Static classes provide utility code
● Can not be instantiated
● No prefix letter
Andreas Jakl, 200983
TInt computerMoveX = Math::Rand(iSeed) % iGridSize.iWidth;User::After(1000); // Suspends the current thread for 1000 microsecondsMem::FillZ(&targetData, 12); // Zero-fills 12-byte block starting from &targetData
Examples
Variables | T Classes | C Classes | R Classes | M Classes | Static Classes
Summary – Classes
Andreas Jakl, 200984
Prefix Category Examples Description
T Type TDesC, TPoint, TFileName No destructor, “has-a” data owned internally
C Class CBase, CActive, CFbsBitmap Any class has to be derived from CBase. Always allocated on the heap. Member data automatically initialized with zero. Important for cleanup stack.
R Resource RFile, RTimer, RWriteStream, RWindow
Owns resources other than on the default heap. Usually allocated as members or automatic variables. Usually require Close() to free resources.
M Mixin, interface
MGraphicsDeviceMap, MEikMenuObserver
Interface consisting of virtual functions. Implementation derives from it. Only approved use of multiple inheritance.
Static class
User, Math, Mem Consists purely of static functions, cannot be instantiated into an object.
Variables | T Classes | C Classes | R Classes | M Classes | Static Classes
When to use which class type?
Andreas Jakl, 200985
Class contains no member data
Class contains only static functions (or factory classes)
Member data has no destructor / does not need special cleanup
Size of the data contained by the class will be large (> 512 bytes)
Class owns data that needs cleanup
Usually an interface M classSometimes C class
Static class no prefix
Only contains native types (T classes) or
“uses-a” data T class
Stack is limited avoid T class typically a C class
Usually a C class. Also R-classes, which are seldom implemented yourself
Loading the .mif-File
● Define the .mif-filename in CSpriteHandler::ConstructL()
_LIT(KMifFile, "MopoidGraphics.mif");
● Uncomment the line to dynamically add the folder
NCommonFunctions::AddResourceDirL(KMifFile, fileName);
● Images will be stored in iSprites-array
TFixedArray<CFbsBitmap*, MopoidShared::ENumSprites> iSprites;
Andreas Jakl, 200986
Why the “i”?
Variable Naming Conventions
Andreas Jakl, 200987
Prefix Category Examples Description
E Enumerated constant
EMonday, ESolidBrush Values in an enumeration – which itself should have T prefix: EMonday is a member of TDayOfWeek
K Constant KMaxFileName, KRgbWhite
Constants from #define or const TInt.
i Member variable
iDevice, iX Any non-static member variable. i refers to ‘instance’
a Argument aDevice, aX Function argument. Stands for ‘argument’ aOrigin, not anOrigin!
Automaticvariable
device, x Automatic: variable that is created automatically when required and destroyed when out of scope
Variables | T Classes | C Classes | R Classes | M Classes | Static Classes
Example – Variables
Andreas Jakl, 200988
enum TStuffState // Declares an enumeration, prefix T{EInitialized = 0, // Individual elements with prefix EEError};
const TInt KMaxLength = 50; // Constant with prefix K
class TStuff // T type class{
public:void DoStuff(TInt aLength); // Function argument with prefix a
private:TInt iLength; // Member (instance) variable with prefix iTStuffState iState;};
void TStuff::DoStuff(TInt aLength){if (aLength > KMaxLength)
iState = EError;else
iLength = aLength; // Note: no ambiguities!}
Variables | T Classes | C Classes | R Classes | M Classes | Static Classes
Fixed Arrays
● Standard C++ Array:
TInt myArray[25];
● The same using Symbian OS wrapper class:
TFixedArray<TInt, 25> myArray;
Andreas Jakl, 200989
[0]
myArray
[1] … [24]
25x TInt
<ptr>
TFixedArray - Advantages
● Range checking (Panic if out of range)
At(): Checks range in debug- and release-builds
[]: Checks range in debug-builds only
● Comfort functions, e.g.:
DeleteAll(): invokes delete on each element
Reset(): fills each element with zeros
Count(): number of elements in the array
Begin(), End(): to navigate the array
Andreas Jakl, 200990
Loading Images
● Create bitmap from specified ID in .mif-file
● Store it in the iSprites array
● IDs?
Right-click Icons_aif_scalable_dc.mk Edit MBM / MIF MopoidGraphics.mif
or: open <EPOCROOT>/include/MopoidGraphics.mbg
Andreas Jakl, 200991
iSprites[ESpritePanel] = AknIconUtils::CreateIconL(
fileName, EMbmMopoidgraphicsPanel);
CSpriteHandler::ConstructL()
Loading Image + Mask
● Load image and mask at the same time
Otherwise, alpha blending wouldn’t work as expected
Panel and ball images need a transparency mask (rounded corners)
Bricks are rectangular, no mask required ( use method from previous slide)
Andreas Jakl, 200992
AknIconUtils::CreateIconL(iSprites[ESpritePanel], iSprites[ESpritePanelM],
fileName, EMbmMopoidgraphicsPanel,
EMbmMopoidgraphicsPanel_mask);
CSpriteHandler::ConstructL()
Load all Images
iSprites array-position .mif-ID
ESpritePanel EMbmMopoidgraphicsPanel
ESpritePanelM EMbmMopoidgraphicsPanel_mask
ESpriteBall EMbmMopoidgraphicsBall
ESpriteBallM EMbmMopoidgraphicsBall_mask
ESpriteBrickNormal EMbmMopoidgraphicsBrick_green
ESpriteBrickIndestructible EMbmMopoidgraphicsBrick_brown
ESpriteBrickDouble EMbmMopoidgraphicsBrick_yellow
ESpriteBrickMoving EMbmMopoidgraphicsBrick_red
ESpriteBrickHarderBrick EMbmMopoidgraphicsBrick_violet
ESpriteBrickSofterBrick EMbmMopoidgraphicsBrick_blue
Andreas Jakl, 200993
Defined in MopoidSharedData.h
Setting the Image Size
● Dynamically set by the game engine – according to screen resolution
● Handled in:
● Set image size:
Andreas Jakl, 200994
void CSpriteHandler::DoSetSpriteSize(
const MopoidShared::TSpriteIds aSpriteId,
const TSize& aNewSize)
AknIconUtils::SetSize(iSprites[aSpriteId], aNewSize);
CSpriteHandler::DoSetSpriteSize()
Memory Leaks
● Test it:
Launch Mopoid in the emulator
Close the app in the emulator (not the emulator itself!)
Andreas Jakl, 200995
Deleting Images
● Images owned by sprite handler Delete pointers in the destructor
(= same as for-loop with delete on every element)
Andreas Jakl, 200996
iSprites.DeleteAll();
CSpriteHandler::~CSpriteHandler()
Panics
● ... cannot be caught and handled!
● Terminates thread (= usually the whole application)
● Use them for checking code logic only
● Can also be sent out by the system for critical errors
● If a panic happens:
Make sure you fix it, as you can’t handle it!
Andreas Jakl, 200997
// Stray signal detected!_LIT(KMsgStraySignal, "Stray signal\n");User::Panic(KMsgStraySignal, 1); // Panic with code 1
Panics
● Try it – add a serious error
● Range checked by TFixedArray (in debug builds)
Andreas Jakl, 200998
iSprites[999] = AknIconUtils::CreateIconL(
fileName, EMbmMopoidgraphicsPanel);
CSpriteHandler::ConstructL()
Information about Panics
● SDK doc: Symbian OS v9.x Symbian OS reference System panic reference USER
Andreas Jakl, 200999Tip: install the automated panic lookup plug-in for Carbide.c++:http://www.symbianresources.com/projects/paniclookup.php
Scalable UIAdapting to different screen layouts
Andreas Jakl, 2009100
Screen Layout Changes
● Most current S60 phones:
portrait & landscape QVGA (240 x 320)Changed by app or auto screen rotation (sensor)
● Others:
E90: outer screen 240 x 320, inner: 800 x 352
5500 Sport: 208 x 208
E70: 352 x 416
● Future phones:
VGA +
● App should adapt to all resolutions and be notified about changes
Andreas Jakl, 2009101
Scalable UI in Mopoid – Overview
Andreas Jakl, 2009102
Container (CMopoidContainer)
Container Base Class (CCoeControl)
Game Engine (CMopoidGameEngine)
HandleResourceChange()
HandleResourceChange() SetExtentToWholeScreen()
SizeChanged()
SizeChanged()
SetScreenSize()
Framework
Scalable UI – Source Code
● Upon start-up and subsequent orientation / resolution changes, the framework calls HandleResourceChange() of all visible container classes
● Applies the new size (whole screen) to the container
Andreas Jakl, 2009103
if(aType == KEikDynamicLayoutVariantSwitch)
{
// User switched the layout configuration
// or the screen resolution
// -> we have to recreate the layout
SetExtentToWholeScreen();
// Results in a call of SizeChanged()
}
CMopoidContainer::HandleResourceChange(TInt aType)
Scalable UI – Source Code
● In CMopoidContainer::SizeChanged()
Query the current resolution
Inform the game engine
Andreas Jakl, 2009104
iGameEngine->SetScreenSize(this->Size());
CMopoidContainer::SizeChanged()
Displaying GraphicsFinally get some content on the screen
Andreas Jakl, 2009105
Back Buffer
Back Buffer
Screen
Transferredas a whole
when drawingis finished
106 Andreas Jakl, 2009
CMopoidGameEngine
CMopoidContainer
Game Loop
(static) TimerCallBack()
CPeriodic
DoFrame()
Calculate time difference
Animate thepanel
Draw the frameto the back
buffer
Request a redraw event
Copy the back buffer to the
screen
Asynch. call
107 Andreas Jakl, 2009
DrawFrame()
● First: clear the back buffer
Andreas Jakl, 2009108
// Clear back buffer
iBackBufferBmpGc->SetPenStyle(CGraphicsContext::ENullPen);
iBackBufferBmpGc->SetBrushColor(KRgbBlack);
iBackBufferBmpGc->SetBrushStyle(CGraphicsContext::ESolidBrush);
iBackBufferBmpGc->DrawRect( TRect(TPoint(0,0),iSettings->iScreenSize) );
iBackBufferBmpGc->SetBrushStyle(CGraphicsContext::ENullBrush);
Pen
Brush
ENullPen
ESolidBrush, KRgbBlack
CMopoidGameEngine::DrawFrame()
Drawing Bricks (w/o transparency)
● Bricks are drawn in the for-loop
● ConvToScreenCoord()
Game internally calculates in VGA resolution
Converts coordinates to current screen res
Returns a TPoint (contains two TInt)
● GetSprite()
Returns the correct CFbsBitmap*
spriteID: set in switch-statement just before = brick type
Andreas Jakl, 2009109
iBackBufferBmpGc->BitBlt(ConvToScreenCoord(
iGrid->ConvertGridToXY(x, y)),
iSpriteHandler->GetSprite(spriteId));
CMopoidGameEngine::DrawFrame()
Drawing Panel and Ball (with transp.)
● Ball sprite
● Do the same for the panel near the end of the method
Position: iPanel.iPos
Bitmap IDs: ESpritePanel / ESpritePanelM
Andreas Jakl, 2009110
iBackBufferBmpGc->BitBltMasked(ConvToScreenCoord(ballSpritePos),
iSpriteHandler->GetSprite(MopoidShared::ESpriteBall),
iSpriteHandler->GetSprite(MopoidShared::ESpriteBall)->SizeInPixels(),
iSpriteHandler->GetSprite(MopoidShared::ESpriteBallM), EFalse);
CMopoidGameEngine::DrawFrame()
Screen Flickering
● Note: UI Designer clears the screen with white before the bitmap is copied results in flickering on some devices
● Changes will get overwritten whenever you change the design
Andreas Jakl, 2009111
void CMopoidContainer::Draw(const TRect& aRect) const
{
// [[[ begin generated region: do not modify [Generated Contents]
CWindowGc& gc = SystemGc();
gc.Clear( aRect ); // Remove this line to prevent flickering
// ]]] end generated region [Generated Contents]
White Border around Sprites
● On S60 3rd Ed.-devices & the MR-emulator, there is a white border around the sprites (fixed in FP1+)
Andreas Jakl, 2009112
Periodic EventsKeeping the game loop alive
Andreas Jakl, 2009113
What is an Active Object?
● Requests asynchronous service
provided by Asynchronous Service Provider
● Receives call-back when finished / error
message handling by Active Scheduler
Active Object ≈ Listener!
Andreas Jakl, 2009114
Event Handling
Andreas Jakl, 2009115
Event 1 Event 2 Event 3
Application (Possibly an extra thread)
Event 1 Event 2 Event 3
Application (thread)
Active Scheduler (in app. thread)
Traditional Event-Handling Active Objects(Symbian OS)
Event Handler(Thread)
Event Handler(Thread)
Event Handler(Thread) Event
Handler(AO)
Event Handler
(AO)
Event Handler
(AO)
Asynchronous Service Providers
Asynchronous Service Providers
Active Objects
● Asynchronous Service Provider (ASP)
Executes asynchronous task
e.g. timer, socket-related request, take a camera picture, load and prepare a sound file, ...
● Application-side:
AOs encapsulate ASP and event handling (after the request to the ASP has finished)
● Modular separation:
Active Scheduler (AS): event completion processing for all ASPs
Active Object (AO): individual event handling
Andreas Jakl, 2009116
Timers
● A timer is an asynchronous service
Pre-implemented: we don’t have to write the active object ourselves
But be aware of the characteristics (non-pre-emptive multitasking between AOs in one thread)
Don’t stick in call-back routine for too long makes app unresponsive, blocks user event handling!
Andreas Jakl, 2009117
CPeriodic* iPeriodicTimer;
MopoidGameEngine.h
Call-Back Interval
● Starting is necessary several times
First start
New game
Resume paused game
● encapsulated in CMopoidGameEngine::StartTimerL()
● Define call-back interval:
5000 micro seconds = 0,005 seconds
Andreas Jakl, 2009118
const TTimeIntervalMicroSeconds32 KTickInterval = 5000;
CMopoidGameEngine::StartTimerL()
Allocating the Timer Instance
● Allocate timer object
Standard priority is sufficient
Remember: only affects other AOs of this app. It’s not the system-wide thread/process priority!
Andreas Jakl, 2009119
if (!iPeriodicTimer)
{
iPeriodicTimer = CPeriodic::NewL(CActive::EPriorityStandard);
}
CMopoidGameEngine::StartTimerL()
Starting the Timer
● TCallBack-object
First parameter: TAny* (= void*) function pointer to call-back function.Implementing method has to take one parameter of type TAny* and return a TInt
Second parameter: pointer that will passed to the static call-back function
Andreas Jakl, 2009120
iPeriodicTimer->Start(KTickInterval, KTickInterval,
TCallBack(TimerCallBack, this));
CMopoidGameEngine::StartTimerL()
static TInt TimerCallBack(TAny* aObject);
MopoidGameEngine.h (already declared, just for reference)
Call-Back
● Usual task of call-back function
cast the parameter to the correct class
call a non-static call-back method for complete access to the class
● Call-backs might not be regular
Call-back of a different active object is running orhigher priority call-backs waiting
DoFrame() checks time difference from last frame
Andreas Jakl, 2009121
TInt CMopoidGameEngine::TimerCallBack(TAny* aObject)
{
// Call non-static method
return ((CMopoidGameEngine*)aObject)->DoFrame();
}
Stopping the Timer
● Cancel the timer
● Delete the instance (recommended by the SDK doc)
Andreas Jakl, 2009122
void CMopoidGameEngine::StopTimer()
{
// If the timer object exists: stop and delete it.
if (iPeriodicTimer)
{
iPeriodicTimer->Cancel();
delete iPeriodicTimer;
iPeriodicTimer = NULL;
}
}
Back Light
● Ball might stay in the upper part of the screen for some time, no key presses required
● Make sure the back light doesn’t always turn off
● Simulate user activity by resetting the inactivity timer of the framework:
Andreas Jakl, 2009123
User::ResetInactivityTime();
CMopoidGameEngine::DoFrame()
Handling KeysLet’s start to play!
Andreas Jakl, 2009124
Control Stack
● Symbian OS maintains an internal control stack
● All controls that should receive key events are put on the stack
● Starting at stack pos. 0, events are offered to each control until they are consumed by a control
Andreas Jakl, 2009125
ECoeStackPriorityDefault (=0)
ECoeStackPriorityMenu (=10)
ECoeStackPriorityDialog (=50)
ECoeStackPriorityDialog (=50)
Even
ts
Control X
Control YKeyboard Focus
Control Z
Controls put on the stack in the order: A, B, C, D
Stack position 0
AppUi()->AddToStackL( *this, iMopoidContainer );
CMopoidContainerView::DoActivateL()
Was the key event handled by us?
● Controls are offered key events in:
● Return value depending on if the event was handled:
Andreas Jakl, 2009126
TKeyResponse CMopoidContainer::OfferKeyEventL(
const TKeyEvent& aKeyEvent, TEventCode aType)
Return(TKeyResponse)
EKeyWasConsumed: We used the key event, do not send it toother controls
EKeyWasNotConsumed: This key event was not interestingfor us, send it to other controls
Keys
Andreas Jakl, 2009127
● Type of event sent in aType parameter
● Handling keys for left & right
Add cases to the switch statement:EStdKeyLeftArrow, EStdKeyRightArrow
Send the event to the game engine:iGameEngine->iKeyHandler.LeftPressed()
Set the prepared return variable “handled” to EKeyWasConsumed
Don’t forget the break for the case-statement!
EEventKeyDown EEventKey EEventKeyUp
Displaying TextKeep the user informed
Andreas Jakl, 2009128
Fonts
● Games use low level methods to draw text
Highly sophisticated games usually their own bitmap font
● Retrieving a standard system font
● Apply the font to the context
Andreas Jakl, 2009129
iFontUsed = (CFont*) AknLayoutUtils::LayoutFontFromId(
EAknLogicalFontPrimarySmallFont);
iFontHeight = iFontUsed->AscentInPixels();
CMopoidSettings::ConstructL()
iBackBufferBmpGc->UseFont(iSettings->iFontUsed);
CMopoidGameEngine::DrawTextL()
Font Colour
● Setup for drawing text (in red colour)
Brush was already set to a null brush before
Andreas Jakl, 2009130
ENullBrush
ESolidPen, KRgbRed
text
iBackBufferBmpGc->SetPenStyle(CGraphicsContext::ESolidPen);
iBackBufferBmpGc->SetPenColor(KRgbRed);
TBuf<30> tempStr;
CMopoidGameEngine::DrawTextL()
Pen
Brushtext
Buffer Descriptors
● Comparable to (const) char[] of C
● Directly contain the string
● Use C++ templates to specify length (parameter)
Andreas Jakl, 2009131
TBufC<5>
TBuf<9>
9 ‘H’5 ‘e’ ‘l’ ‘l’ ‘o’
‘H’5 ‘e’ ‘l’ ‘l’ ‘o’
iLength(TDesC)
iMaxLength(TDes)
iLength(TDesC)
Constant:
Modifiable:
Size, Length, MaxLength
● TBuf<9> with text “Hello”
Andreas Jakl, 2009132
9 ‘H’5 ‘e’ ‘l’ ‘l’ ‘o’
Length() 5 (characters)MaxLength() 9 (characters)Size() 10 (bytes)MaxSize() 18 (bytes)
4 bytes 4 bytes 2 2 ...
Unicode characters!
Initializing the TBuf
● Assigning Strings
Andreas Jakl, 2009133
// Define constant string literal_LIT(KString1, "Hello ");_LIT(KString2, "World");// Copy KString1 into new TBuf with max. length of 15TBuf<15> buf1(KString1);// Same as above, this time using Copy() and KString2TBuf<15> buf2; // Initialize empty TBuf with max. length of 15buf2.Copy(KString2);// Append contents of buf2 to buf1buf1.Append(buf2);// Create constant descriptor based on KString1TBufC<15> cBuf1(KString1);// Replace contents of cBuf1 with contents of buf1cBuf1 = buf1;
Using the TBuf
Andreas Jakl, 2009134
_LIT(KHelloWorld, "Hello World"); // Defines constant string literalconst TInt maxLength = 15;// Create a modifiable bufferTBuf<maxLength> buf;TInt curLength = buf.Length(); // ...... ?TInt maxLength2 = buf.MaxLength(); // ...... ?// Set the contents of the bufferbuf = KHelloWorld;curLength = buf.Length(); // ...... ?TInt curSize = buf.Size(); // ...... ?TText ch = buf[1]; // ...... ?
Using the TBuf
Andreas Jakl, 2009135
_LIT(KHelloWorld, "Hello World"); // Defines constant string literalconst TInt maxLength = 15;// Create a modifiable bufferTBuf<maxLength> buf;TInt curLength = buf.Length(); // == 0TInt maxLength2 = buf.MaxLength(); // == 20// Set the contents of the bufferbuf = KHelloWorld;curLength = buf.Length(); // == 11TInt curSize = buf.Size(); // == 22TText ch = buf[1]; // == ‘e’
Maximum Length
Andreas Jakl, 2009136
_LIT(KHello, "Hello "); // Defines constant string literalTBuf<15> buf(KHello); // buf == “Hello ”buf.Append(KHello); // buf == “Hello Hello “buf.Append(KHello); // Exceeds maximum length Panic!
SDK-Doc for USER 11-panic:“This panic is raised when any operation that moves or copies data to a 16-bit variant descriptor, causes the length of that descriptor to exceed its maximum length. […]”
Inheritance Hierarchy
● Abstract base class because of:
Generalisation(use base type for parameters!)
Provide basic functions shared by all types (e.g. Compare(), Find(), Mid(), ...)
Andreas Jakl, 2009137
constant modifiable
Reading Text from Resource Files
● Multiple options to read text from resource files
● Here: through environment from Symbian OS framework
● Read text from resource files into the descriptor
● (Remember: you declared the resource yourself!)
Andreas Jakl, 2009138
CEikonEnv* env = CEikonEnv::Static();
CMopoidGameEngine::DrawTextL()
env->ReadResourceL(tempStr, R_HIGHSCORE);
CMopoidGameEngine::DrawTextL()
RESOURCE TBUF r_highscore { buf = STR_highscore; }
MopoidContainer.rssi
Formatting and Displaying Text
● tempStr now contains “High Score: ”
● Append the current score:
● Draw the text
x-coord: 5
y was calculated directly above
Andreas Jakl, 2009139
tempStr.AppendNum(iSettings->iHighScore);
CMopoidGameEngine::DrawTextL()
iBackBufferBmpGc->DrawText(tempStr,
TPoint(5, yTextStartBottom));
CMopoidGameEngine::DrawTextL()
Current Score
● Like for the previous text:
Resource: R_SCORE
Number: iSettings->iScore
x-coordinate: 5
y-coordinate: one line above previous textyTextStartBottom - iSettings->iTextYOffset
Andreas Jakl, 2009140
Aligning Text
● Print text on the right side of the screen
Andreas Jakl, 2009141
Level Text: R_LEVEL
Value: iSettings->iLevel
Position:
y: yTextStartBottom
x-offset: 3 (from the right side of the screen)
Lives Text: R_LIVES
Value: iSettings->iLives
Position:
y: yTextStartBottom - iSettings->iTextYOffset
x-offset: 3
iBackBufferBmpGc->DrawText(tempStr, iSettings->iScreenRect,
yTextStartBottom,
CGraphicsContext::ERight, 3);
CMopoidGameEngine::DrawTextL()
Final State
● Remove the comment from the code block ( activate the rest of the DrawTextL()-method)
● The final state:
Andreas Jakl, 2009142
Reading / Writing FilesBe persistent
Andreas Jakl, 2009143
Preparations
● Filename and version have already been declared
● We will use the CFileStore-class add the required include file (see SDK doc)
Andreas Jakl, 2009144
#define MOPOID_FILE_VERSION_NUMBER 1
_LIT(KMopoidDataFile, "settings.dat");
MopoidGameEngine.h
#include <s32file.h>
MopoidGameEngine.h
Data Caging – Overview
Andreas Jakl, 2009145
Executable
• All binaries of all apps in onedir
• Can only be executed fromthere
• Usually: no read / write access
• \sys\bin\
Resources• Bitmaps, fonts, help files
• Usually: read-only
• \resource\
Private Data
• Each application its own dir
• Used for settings, ...
• Usually: read / write only forown private dir
• \private\<UID3>\
Symbian OS 9.xSymbian OS, pre-V9
Data Caging
● App. only has access to:
Own directories
“Open” directories
● Access based on capabilities and identity
Andreas Jakl, 2009146
\System\Apps\Journey\Journey.app
\System\Apps\Journey\Journey.mbm
\System\Apps\Journey\Journey.rsc
\Sys\Bin\Journey.exe
\Resource\Apps\Journey.mbm
\Private\10003a3f\Journey.rsc
Separating code and data!
Data Caging – Directories
Andreas Jakl, 2009147
Directory Capability(Read)
Capability(Write)
Used for
\sys AllFiles TCB Applications can only be executed from here.
\resource - TCB Fonts, bitmaps, help-files, … Only read-accessfor apps, to prevent corruption.
\private\<own SID> - - For application data (settings, …). DLLs do not have own private dir, but instead use that of their loading process.
\private\<other SID> AllFiles AllFiles AllFiles-capability required to access private data of other apps.
All others - - Unrestricted data – images, …
More about directories
● Data caging provides secure area for application’s data
● All executables installed to \sys\bin
Risk of filename clashes – use your unique UID as part of the executable filename
Removable drives:Hash stored to c:\sys\hashPrevents execution of modified executables
Andreas Jakl, 2009148
Data Files
● Data file should be stored in our private directory:\private\<UID3>\settings.dat
● Dynamically add the path:
● Central part of this custom utility function:
Andreas Jakl, 2009149
NCommonFunctions::AddPrivateDirL(iFs,
KMopoidDataFile, fileName);
CMopoidGameEngine::SaveGameProgessL()
aFs.PrivatePath( aCompleteName );
NCommonFunctions::AddPrivateDirL()
File Store
● Many ways to access files are possible (RFile, ...)
● Here: CFileStore – one of the most flexible
Encapsulates file-based, persistent stores
Stores multiple streams, each identified by an ID
Allows easy serialization of objects
Direct file store: doesn’t allow modifying data – not important for us, we will always replace the stream
Andreas Jakl, 2009150
CFileStore* store = CDirectFileStore::ReplaceLC(
iFs, fileName, EFileWrite);
store->SetTypeL(KDirectFileStoreLayoutUid);
CMopoidGameEngine::SaveGameProgessL()
Store (File)
Stream (ID)
Stream (ID)...
Creating a Stream
● One stream has to be the root stream
We only use one stream – set it to the root stream (later)
Multiple streams: e.g., for a database
Andreas Jakl, 2009151
RStoreWriteStream stream;
TStreamId id = stream.CreateLC(*store);
CMopoidGameEngine::SaveGameProgessL()
Writing Data
● For now: file version and high score
● Saving the file version?
Updating the game != uninstallation
Keeps additional files
New game version might add additional settings
Should know file version, to import or discard old file!
Andreas Jakl, 2009152
// Write file version number
stream.WriteInt32L(MOPOID_FILE_VERSION_NUMBER);
// Write highscore
stream.WriteInt32L(iSettings->iHighScore);
CMopoidGameEngine::SaveGameProgessL()
Closing the File
● Make sure that all data is written and close everything
Andreas Jakl, 2009153
// Commit the changes to the stream
stream.CommitL();
CleanupStack::PopAndDestroy(&stream);
// Set the stream in the store and commit the store
store->SetRootL(id);
store->CommitL();
CleanupStack::PopAndDestroy(store);
CMopoidGameEngine::SaveGameProgessL()
Cleanup Stack
● Situation: function creates local heap-object
● Before code gets to the delete-statement: error
Function is left (Leave)
Pointer-address on the stack is freed
Object itself is orphaned memory leak!
Andreas Jakl, 2009154
void CMyObj::DoSomethingL(){
}
CImage* img = new (ELeave) CImage();
img->DoSomethingDangerousL();
delete img;
void CImage::DoSomethingDangerousL(){User::Leave(KErrNotFound);}
CImage
img = pointer on the stack to an instance on the heap
Cleanup Stack
● Memory situation if a leave occurs:
Andreas Jakl, 2009155
img
Stack Heap
Object stays on the heap;Pointer to delete the instance is lost
memory leak
Cleanup Stack
● Solution: Cleanup Stack
Andreas Jakl, 2009156
img
Stack Heap
img
CleanupStack
Cleanup stack saves a second pointerto the object on the heap.
void CMyObj::DoSomethingL(){
CImage* img = new (ELeave) CImage();CleanupStack::PushL(img);img->DoSomethingDangerousL();CleanupStack::PopAndDestroy();
}
When no leave occurs: object still has to be deleted by you + removed from the cleanup stack!
R-Classes Cleanup
● R-classes usually have extra methods to free the resources
Just calling the destructor wouldn’t be sufficient!
In the case of the RBuf descriptor (heap based string): Close()
Special method to put the instance on the cleanup stack – has to tell it how to destroy the object
Andreas Jakl, 2009157
RBuf fileName;
// Push the variable onto the cleanup stack
fileName.CleanupClosePushL();
// ... any leave here would also destroy the obj.
// Pop and destroy (calls Close() on the object)
CleanupStack::PopAndDestroy(&fileName);
CMopoidGameEngine::SaveGameProgessL()
Loading
● Similar to saving files
1. Open the file store
2. Open the root stream (no ID is required for the root)
Andreas Jakl, 2009158
CFileStore* store = CDirectFileStore::OpenLC(
iFs, fileName, EFileRead);
CMopoidGameEngine::LoadGameProgessL()
RStoreReadStream stream;
stream.OpenLC(*store, store->Root());
CMopoidGameEngine::LoadGameProgessL()
Reading Data
3. Read the data
If the file version doesn’t match – same behaviour as if the file wasn’t found. You could import an old version instead.
Andreas Jakl, 2009159
TInt versionNumber = stream.ReadInt32L();
if (versionNumber != MOPOID_FILE_VERSION_NUMBER)
User::Leave(KErrNotFound);
iSettings->iHighScore = stream.ReadInt32L();
CMopoidGameEngine::LoadGameProgessL()
If the File Was Not Found
● File not found-error (or wrong version)
Ignored
Settings-class initialized with default values
● All other errors (e.g., not enough disc space left)
TRAP’d leave is sent out again
Andreas Jakl, 2009160
// Load high score and settings
TRAPD(err, LoadGameProgressL());
if (err != KErrNone && err != KErrNotFound)
User::Leave(err);
CMopoidGameEngine::ConstructL()
Cleanup
4. Close all handles
● What about cleanup upon deinstallation?
Required by Symbian Signed criteria!
The whole private directory would be deleted anyway, but it’s still better to explicitly specify it
Andreas Jakl, 2009161
CleanupStack::PopAndDestroy(&stream);
CleanupStack::PopAndDestroy(store);
CMopoidGameEngine::LoadGameProgessL()
Cleanup – Uninstallation
● Files that are to be removed during uninstallation: specified in the package file
● “FN” means “FileNull” – SDK help:
Andreas Jakl, 2009162
; --- Settings-file
""-"!:\private\E2C4F75B\settings.dat",FN
Mopoid.pkg
A file which does not yet exist, so is not included in the sis file. It is created by the running application and will be deleted when the application is removed. The name assigned to the source file is unimportant and should be empty, i.e. “”. Note that such files will not be deleted when upgrading to a later version. This ensures that such files as .ini files, which store application preferences, are not lost in an upgrade.
Application IconMake a good appearance in the phone menu:
Andreas Jakl, 2009163
Application Icon
● Icons have to be .svg-files as well, encapsulated in a .mif
● Set for the whole application
Open application.uidesign
Switch to the “AppUi”-tab
Right-click on the title pane
Choose: “Override Context Icon”
Select the “Mopoid_aif.mif”-file
Image: qgn_menu_Mopoid.svg
Andreas Jakl, 2009164
Foreground / BackgroundIs anyone out there?
Andreas Jakl, 2009165
Background?
● Symbian OS = multitasking system
● Sent to the background due to:
Incoming call
Warnings (e.g., low battery)
User sending you to the background
● Applications should react to this
Save current state
Free shared common resources (e.g., camera)
Pause games
Enter low power state
Andreas Jakl, 2009166
Foreground Notification
● Abstact method of the View base class (CAknView)
● Out task: override this method, called automatically!
Declare it in a protected section of the view header
Andreas Jakl, 2009167
void HandleForegroundEventL(TBool aForeground);
MopoidContainerView.h
Foreground Handling
● Desired behaviour
Pause the game when it looses the foreground
Do not resume the game when it regains foreground status –player can’t continue playing 1 ms after the call!
Andreas Jakl, 2009168
// Handle any change of focus
void CMopoidContainerView::HandleForegroundEventL(TBool aForeground) {
if (aForeground) { // gained focus
// Don't resume the game - wait for user to resume it
if (iMopoidContainer && iMopoidContainer->iGameEngine)
iMopoidContainer->iGameEngine->iHaveFocus = ETrue;
} else { // lost focus – Pause game
if (iMopoidContainer && iMopoidContainer->iGameEngine) {
iMopoidContainer->iGameEngine->PauseGame();
iMopoidContainer->iGameEngine->iHaveFocus = EFalse;
}
}
// call base class event handler
CAknView::HandleForegroundEventL(aForeground);
}
MopoidContainerView.cpp
Sound Volume / Dynamic MenusAdapting the Game to the Environment
Andreas Jakl, 2009169
Sounds and Mobile Games
● Mobile Phones are used everywhere
train, toilet, bus stop, ...
● Sound is not always appropriate!
● Gameloft: games ask desired volume on every start-up!
● Add two additional menu items to MopoidContainer.uidesign
Sound On
Sound Off
● Create event handler methods
Andreas Jakl, 2009170
(De) activating Sound
● Send the command to the game engine
● ... do the inverse for the sound off-event!
● Add the sound state to the settings file
Accessible from the game engine via iSettings->iSoundLevel
Write it using WriteInt32L()
Andreas Jakl, 2009171
TBool CMopoidContainerView::HandleSound_OnMenuItemSelectedL( TInt aCommand )
{
if (iMopoidContainer && iMopoidContainer->iGameEngine)
{
iMopoidContainer->iGameEngine->SetSoundLevelL(ETrue);
}
return ETrue;
}
Dynamic Menus
● The visibility state of a menu item is not directly defined (in S60, UIQ 3 is different)
● Instead, framework calls a method each time right before the menu is displayed
● App gets the chance to dim individual items there
● Override the method from the CAknView base class:
Andreas Jakl, 2009172
void DynInitMenuPaneL(TInt aResourceId, CEikMenuPane* aMenuPane);
MopoidContainerView.h
Dimming Menu Items
● Check which resource is currently initialized – the menu pane is relevant
● Menu IDs are defined by the UI designer in MopoidContainer.hrh
● Dim “Sound Off / On” menu items according to sound state
Andreas Jakl, 2009173
if (R_MOPOID_CONTAINER_MENU_PANE1_MENU_PANE == aResourceId &&
iMopoidContainer && iMopoidContainer->iGameEngine)
{
aMenuPane->SetItemDimmed(EMopoidContainerViewSound_OnMenuItemCommand,
iMopoidContainer->iGameEngine->IsSoundOn());
aMenuPane->SetItemDimmed(EMopoidContainerViewSound_OffMenuItemCommand,
!iMopoidContainer->iGameEngine->IsSoundOn());
}
MopoidContainerView::DynInitMenuPaneL()
SummaryWe’re finished!
Andreas Jakl, 2009174
Mopoid
● Tasks
Create your own application using the UI designer
Define a menu and an about box
Leaves, Panics and Cleanup Stack
Project structure, libraries
Class types, naming conventions
Defining and displaying text, fonts, descriptors
Using vector graphics
Scalable UI
Asynchronous events, timers
Key handling
Files, data caging (platform security)
Foreground / background
Dynamic menuAndreas Jakl, 2009175
Thanks for your attention!That’s it
Andreas Jakl, 2009176