First, you will learn how to create your own mobile project. Then, you will add the prewritten game logic of a small Arkanoid-like game called "Mopoid". This provides an interesting place to get to know important parts of development for Symbian OS, including the following:
* Preparation: Setup of the tools and SDK
* Creating a new project
* Testing the project
* Defining the menu
* Displaying the about box
* Getting the application to the mobile phone!
* Adding the game engine to your project
* Loading images (.svg vector graphics)
* Scaling and displaying the graphics
* Handling keys
* Displaying text
* Reading and writing files
* Setting the application icon
* Handling being in the background
* Periodic events
* Exercises
You will see the results of your actions right away. Whenever a speciality of Symbian OS comes into sight (like memory handling), a short explanation will give you a brief overview of why it is this way and how to work with it.
Of course, during this tutorial, we will only scratch the surface of all those topics, as each of them would easily fill an own tutorial of this size. But this tutorial will give you a basic understanding of how development for mobile phones using C++ works, and is a good starting point for your own projects.
The original version of this tutorial has been released in December 2004 and was based on S60 1.x. In January 2009, the updated version for Symbian OS 9 / S60 3.x+ was released. The tutorial has been rewritten, the slides created from scratch, the game updated and greatly extended. Choose the version that fits to your target platform - most likely, this is going to be the new version. It's recommended to use both the tutorial document and the slides when working through the tutorial on your own.
3. 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
4. Schedule
● Instead of theory, we’ll do a practical project
4 Andreas Jakl, 2009
5. Mopoid
● Tasks Defining and displaying text,
fonts, descriptors
Create your own application
using the UI designer Using vector graphics
Define a menu and an about Scalable UI
box Asynchronous events,
Leaves, Panics and Cleanup timers
Stack Key handling
Project structure, libraries Files, data caging (platform
Class types, naming security)
conventions Foreground / background
Dynamic menu
5 Andreas Jakl, 2009
6. 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
// TODO: Refresh back light timer
Slides with explanations of specific Symbian OS
concepts are marked in the upper right corner
6 Andreas Jakl, 2009
7. 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)
7 Andreas Jakl, 2009
9. S60 SDKs
SDK OS version Devices
1st Ed. v6.0 7650
● Choose SDK depending on 1st Ed, FP 1 v6.1 N-Gage, SX1,
3650, Sendo-
required compatibility and X, …
features 2nd Ed v7.0s 6600, …
2nd Ed, FP 1 v7.0s 7610, 6670,
● Binary compatibility break 6260, …
with Symbian OS 9 2nd Ed, FP 2 v8.0 6630, 6680,
6681, …
● www.forum.nokia.com 2nd Ed, FP 3 v8.1 N70, N90, …
3rd Ed v9.1 E61, N73,
N75, …
Commonly used for
3rd Ed, FP 1 v9.2 N95, E90, …
maximum compatibility
3rd Ed, FP 2 v9.3 N96, N78, …
5th Ed v9.4 5800, N97, …
Introduces touch
5th Ed, FP1 v9.5? ?
screens to S60
9 Andreas Jakl, 2009
10. Installation
1. Carbide.c++ 2.0 Developer Edition (or later)
http://www.forum.nokia.com/main/resources/tools_and_sdks/car
bide/index.html
2. Perl 5.6.x (Set the path variable!) – not 5.8 / 5.10!
http://downloads.activestate.com/ActivePerl/Windows/5.6/Active
Perl-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/
Install all tools on the same drive (recommended: C:, no network drive!)
10 Andreas Jakl, 2009
11. 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!
11 Andreas Jakl, 2009
12. Workspace
● Workspace location:
Has to be on the same drive as the SDK ( C:)
Must not contain space characters
Example: C:Symbiandev
12 Andreas Jakl, 2009
13. Create new project
● File New C++ Application for S60 Project
13 Andreas Jakl, 2009
14. Create new project
● Call it: Mopoid
● Check again if the path is really on the SDK drive (C:)
and does not contain space characters!
14 Andreas Jakl, 2009
15. 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
15 Andreas Jakl, 2009
16. Choose UI-Design
● Select the empty design
We’ll do low level graphics
on an empty canvas
16 Andreas Jakl, 2009
17. Application UID (UID3)
● Development for v9:
0xE0000000 - 0xEFFFFFFF
Development UID automatically
assigned by Carbide
● Get UIDs for public
applications: Range Purpose
Different range for signed / 0x00000000 – 0x0FFFFFFF Development use (< v9)
unsigned 0x10000000 – 0x1FFFFFFF Legacy UID (pre-v9)
0x20000000 – 0x2FFFFFFF v9 protected UIDs
Get one for free at:
0xA0000000 – 0xAFFFFFFF v9 unprotected UIDs
http://www.symbiansigned.com/
0xE0000000 – 0xEFFFFFFF Development use (v9)
0xF0000000 – 0xFFFFFFFF Legacy UID (pre-v9)
17 Andreas Jakl, 2009
19. 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
19 Andreas Jakl, 2009
20. S60 UI
● Status Pane
Status Pane Information about running
application & device (e.g.
Main Pane battery strength)
● Main Pane
Control Pane
Application content
● Control Pane
Softkey labels
20 Andreas Jakl, 2009
23. You can only use the
mouse to navigate on the
Compile Project screen on touch-enabled
emulators! Otherwise, use
the buttons below.
● (optional) Project Build Project
● Run Run
● Or:
23 Andreas Jakl, 2009
24. 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
24 Andreas Jakl, 2009
25. Possible Problems
***Generating makefiles.
bldmake.bat bldfiles WINSCW UDEB
WARNING: EPOCROOT does not specify an existing directory
● Compilation error: BLDMAKE ERROR: Directory "Symbian9.1S60_3rd_MREPOC32" 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
your workspace / project is not on the same drive as
the Symbian OS SDK (usually C:)
(told you to put it there several times )
25 Andreas Jakl, 2009
26. 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
26 Andreas Jakl, 2009
27. 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
27 Andreas Jakl, 2009
28. .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”
28 Andreas Jakl, 2009
29. 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
29 Andreas Jakl, 2009
30. 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
Window Preferences... General Workspace Build automatically
or: Project Build automatically
30 Andreas Jakl, 2009
31. Application structure
Application class
● Entry point for OS
● Defines application UID
● Creates Document class
● Normally no changes
required here.
31 Andreas Jakl, 2009
32. Application structure (2)
Document class
● Takes care of data model
● Creates Application UI
(AppUI-class)
● Usually no changes required
in this class either.
32 Andreas Jakl, 2009
33. Application structure (3)
AppUI class
● “Controller” of the app.
● Not visible itself
● Manages views (container)
● General, application-wide
event handling
(exit, pause, ...)
33 Andreas Jakl, 2009
34. Application structure (4)
View class
● Manages title- and status
pane
● Command handling
(for this view)
34 Andreas Jakl, 2009
35. Application structure (5)
Container class
● Handling of UI components
● Manages “what is visible” in
the main pane (= main part
of the screen)
35 Andreas Jakl, 2009
38. Hint: the UI designer creates source code when you
save the design. This can be problematic when you
Menu Definition change or rename something.
Therefore, only save when you’re finished!
● Open the UI design (MopoidContainer.uidesign)
● Create 3 menu items:
Start new game
About
Exit
38 Andreas Jakl, 2009
39. Exit Command
● Change the command id of the “Exit” command to
EAknCmdExit
Optional: Change the title
39 Andreas Jakl, 2009
40. 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”
40 Andreas Jakl, 2009
41. Connection: Menu Dialog
● Open the options menu
● Right click “Handle ‘Selected’ Event” for the “About”
menu item
41 Andreas Jakl, 2009
42. 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
TBool CMopoidContainerView::HandleAboutMenuItemSelectedL( TInt aCommand )
{
RunNoteAboutL();
return ETrue;
}
42 Andreas Jakl, 2009
44. Exceptions – Java
● Try & Catch for handling exceptions
● Functions can throw “Exceptions”
Calling function Integer Class
Try { …
int x = Integer.parseInt(“1234”); static int parseInt throws
NumberFormatException {
} catch (NumberFormatException e) {
…
System.out.println(“Unable to convert
this String to a number.”); }
} …
44 Andreas Jakl, 2009
45. The TRAP(D) macros are
Leave – Symbian defined in e32cmn.h
● TRAP(D) catches exceptions (“Leave”)
● Functions send out leave
● Function name marked by an L-suffix
Main-Function DoExampleL()-Function
TRAPD(err, DoExampleL()); void DoExampleL()
if (err) {
{ RFs fsSession; // Connect to the file
console->Printf(KTxtFailed, err); server
} User::LeaveIfError(fsSession.Connect());
// …
TRAPD-Makro declares
fsSession.Close();
err as TInt and = Leaves if the Connect()
}
KErrNone function does not
return KErrNone
45 Andreas Jakl, 2009
46. Central Exception Handling
New (ELeave) …
… NewL() …
… User::Leave() …
… ConstructL() …
F5L() …
… F6L() ….
… F8L() ….
… F3L() …
F4L() …
TRAPD(err, F2L());
if (err) …
46 Andreas Jakl, 2009
47. 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
47 Andreas Jakl, 2009
48. S60 Framework Leave is passed on until
(traps the leave a TRAP statement is reached
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;
}
No memory left to
void CMopoidContainerView::RunNoteAboutL( const TDesC* aOverrideText )
{ allocate the object
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 );
}
48 } Andreas Jakl, 2009
50. Extraction and Sources
1. Close Carbide.c++
2. Unzip Mopoid.Update.zip to c:Symbiandev
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
50 Andreas Jakl, 2009
51. 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.
51 Andreas Jakl, 2009
52. 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
52 Andreas Jakl, 2009
53. Localization – Overview
If using .rls-files instead of .loc,
use rls_string instead of #define
<AppName>.l01 <AppName>.l02 <AppName>.l03
UK English French German
#define STR_hi “Hello” #define STR_hi “Salut” #define STR_hi “Hallo”
<AppName>.loc
Include currently active <AppName>.r01
language UK English compiled
#ifdef LANGUAGE_01... resource file
<AppName>.rss
<AppName>.r02
Strings replaced with
Resource Compiler French compiled
#defines from currently
resource file
active .lxx
<AppName>.r03
German compiled
resource file
53 Andreas Jakl, 2009
54. String Resources
● Define text in one place – and not in the source code
● Easy to modify, easy to localize
<AppName>.l01 / .rls ... (Text only!) <Program>.cpp (C++ Source Code)
#define STR_Loading “Loading...” #include <stringloader.h>
#define STR_Caption “Hello World” #include <<AppName>.rsg>
<AppName>.rss (UI Definition) void C<AppName>AppUi::DisplayInfo()
#include “<AppName>.l01” {
RESOURCE LOCALISABLE_APP_INFO r_localisable_app_info HBufC* buf = StringLoader::LoadLC ( R_LOADING );
{ [...]
short_caption = STR_Caption; } CleanupStack::PopAndDestroy(buf);
RESOURCE TBUF r_loading { buf = STR_Loading; } }
[...]
<AppName>.rsg (Generated by resource compiler)
#define R_LOCALISABLE_APP_INFO 0x66a61005
#define R_LOADING 0x66a61006
54 Andreas Jakl, 2009
55. Additional Game Text
● Define strings like explained on the previous slide Add an additional
space char after
MopoidContainer.l01 / MopoidContainer.rssi the “:”
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
55 Andreas Jakl, 2009
56. New Game
● In the UI designer, create a new event handler for the
“Start new game” menu item
CMopoidContainerView::HandleStart_new_gameMenuItemSelectedL()
if (iMopoidContainer->iGameEngine)
iMopoidContainer->iGameEngine->StartNewGameL();
56 Andreas Jakl, 2009
57. Graphics
● Today’s phones: screen orientation / size changes
● Mopoid uses vector graphics
● S60: support for SVG-T
57 Andreas Jakl, 2009
58. Adding Game Graphics
● Add a multi image file (.mif) to the
icons-makefile:
groupIcons_aif_scalable_dc.mk
● Target directory:
resourceappsMopoidGraphics.mif
● Header generation: Header
● Header file:
epoc32includeMopoidGraphics.mbg
58 Andreas Jakl, 2009
59. Adding Game Graphics
● Choose images: bricks, panel and ball
● Set the mask depth for the bricks to n/a
59 Andreas Jakl, 2009
60. Copying Data to the Emulator
● C:-drive of the phone is emulated in
<EPOCROOT>winscwc
e.g.: C:Symbian9.1S60_3rd_MREpoc32winscwc
● Additional game resources required during runtime:
sounds and level definition file
1. Open Mopoid.mmp and
write down the UID3
60 Andreas Jakl, 2009
61. Copying Data to the Emulator
● Open groupbld.inf
Lists which project definition & make files belong to the
project and which files to export
● Export the files to the private directory
The makefile will
automatically create
the .mif-file in the
61 Andreas Jakl, 2009
right dir
62. 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: “/” “”
62 Andreas Jakl, 2009
63. Phone Release
● Copy additional data files to the phone
● Contents of the SIS-file defined in sisMopoid.pkg
Mopoid.pkg
; The icon-file
"$(EPOCROOT)epoc32datazresourceappsMopoidGraphics.mif"
-"!:resourceappsMopoidGraphics.mif"
; Sound files
"..gfxhit.wav"
-"!:privateE2C4F75Bhit.wav" Adapt the UID to
"..gfxbounce.wav" your own!
-"!:privateE2C4F75Bbounce.wav“
; Levels definition file
"..gfxlevels.dat"
-"!:privateE2C4F75Blevels.dat"
; Required for the application to be covered by backup/restore facility
"..sisbackup_registration.xml" -"!:privateE2C4F75Bbackup_registration.xml"
63 Andreas Jakl, 2009
64. 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
64 Andreas Jakl, 2009
65. 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
65 Andreas Jakl, 2009
66. 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
66 Andreas Jakl, 2009
67. Background Info: Build Toolchain
● Involved files and commands in a command-line build:
Input files in <projectdir> Command Outputs
Intermediate build files in
epoc32build<projectdir>
bld.inf > bldmake bldfiles
abld.bat in <projectdir>
Intermediate files in
epoc32build<projectdir>
project.mmp > abld build wins udeb
Binary files in
eopc32releasewinscwudeb
67 Andreas Jakl, 2009
68. 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
68 Andreas Jakl, 2009
69. 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)
69 Andreas Jakl, 2009
70. Getting the images out of the .mif-file
Loading Images
70 Andreas Jakl, 2009
71. Class Types
● CSpriteHandler loads, scales and provides images
Why the “C”? Draw the
image
Set vector
image size in
pixels
Load image
(CAknIconUtils)
.mif-File
SVG-Images
71 Andreas Jakl, 2009
72. Fundamental Types
● Defined in <SDK-path>epoc32includee32def.h
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
72 Andreas Jakl, 2009
73. Examples
Example: TInt, TReal
TInt x = 5;
TReal y = (TReal)x + 0.5;
Example: TBool
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: void / TAny*
// Symbian OS uses ‘void’ for ‘nothing’ and ‘TAny*’ for ‘pointer to
anyting’
void Foo(); // Returns no result
TAny* p; // Pointer to anything
73 Andreas Jakl, 2009
74. Variables | T Classes | C Classes | R Classes | M Classes | Static Classes
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
74 Andreas Jakl, 2009
75. Variables | T Classes | C Classes | R Classes | M Classes | Static Classes
T Classes – Example
TPoint definition from e32cmn.h
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: Usage example
/** The x coordinate. */
void CMyControl::Draw(const TRect &aRect)
TInt iX;
const
/** The y coordinate. */
{
TInt iY;
CWindowGc& gc = SystemGc ();
};
gc.DrawLine (TPoint (0, 0), iLastPoint);
}
75 Andreas Jakl, 2009
76. 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
76 Andreas Jakl, 2009
77. 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
77 Andreas Jakl, 2009
78. Variables | T Classes | C Classes | R Classes | M Classes | Static Classes
C Classes – Example
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: // Constructors
CSprite( TInt aXVelocity, /* ... */, CFbsBitmap* aImage, CFbsBitmap* aMask);
void ConstructL();
private: // Data
TPoint iPosition;
const CFbsBitmap * const iImage;
const CFbsBitmap * const iMask;
};
78 Andreas Jakl, 2009
79. 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!
79 Andreas Jakl, 2009
80. Variables | T Classes | C Classes | R Classes | M Classes | Static Classes
R Classes – Example
void CMyClass::SendCachedDataL()
{
RSocketServ socketServer;
// Connect to the SocketServer
User::LeaveIfError( socketServer.Connect() );
// Make sure Close() is called at the end
CleanupClosePushL( socketServer );
// …
CleanupStack::PopAndDestroy(); // Calls Close() on the socket server object
}
80 Andreas Jakl, 2009
81. 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
81 Andreas Jakl, 2009
82. Variables | T Classes | C Classes | R Classes | M Classes | Static Classes
M Classes – Example
// Interface definition
class MMdaAudioPlayerCallback
{
public:
virtual void MapcInitComplete(TInt aError, const TTimeIntervalMicroSeconds& aDuration) =
0;
virtual void MapcPlayComplete(TInt aError) = 0;
}; The CBase-derivation always
has to be first!
// Implementing class
class CSoundPlayer : public CBase, public MMdaAudioPlayerCallback
{
// [...]
protected: // Functions from base classes
void MapcInitComplete( TInt aError, const TTimeIntervalMicroSeconds& aDuration );
void MapcPlayComplete( TInt aError );
// [...]
}
82 Andreas Jakl, 2009
83. 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
Examples
TInt computerMoveX = Math::Rand(iSeed) % iGridSize.iWidth;
User::After(1000); // Suspends the current thread for 1000 microseconds
Mem::FillZ(&targetData, 12); // Zero-fills 12-byte block starting from &targetData
83 Andreas Jakl, 2009
84. Variables | T Classes | C Classes | R Classes | M Classes | Static Classes
Summary – Classes
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, Owns resources other than on the default heap.
RWriteStream, RWindow Usually allocated as members or automatic variables.
Usually require Close() to free resources.
M Mixin, MGraphicsDeviceMap, Interface consisting of virtual functions.
interface MEikMenuObserver Implementation derives from it. Only approved use of
multiple inheritance.
Static User, Math, Mem Consists purely of static functions, cannot be
class instantiated into an object.
84 Andreas Jakl, 2009
85. When to use which class type?
Usually an interface M class
Class contains no member data
Sometimes C class
Class contains only static functions
Static class no prefix
(or factory classes)
Member data has no destructor / Only contains native types (T classes) or
does not need special cleanup “uses-a” data T class
Size of the data contained by the class Stack is limited avoid T class
will be large (> 512 bytes) typically a C class
Usually a C class. Also R-classes, which
Class owns data that needs cleanup
are seldom implemented yourself
85 Andreas Jakl, 2009
86. 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;
Why the “i”?
86 Andreas Jakl, 2009
87. Variables | T Classes | C Classes | R Classes | M Classes | Static Classes
Variable Naming Conventions
Prefix Category Examples Description
E Enumerated EMonday, ESolidBrush Values in an enumeration – which itself should have T
constant prefix: EMonday is a member of TDayOfWeek
K Constant KMaxFileName, Constants from #define or const TInt.
KRgbWhite
i Member iDevice, iX Any non-static member variable. i refers to ‘instance’
variable
a Argument aDevice, aX Function argument. Stands for ‘argument’ aOrigin, not
anOrigin!
Automatic device, x Automatic: variable that is created automatically when
variable required and destroyed when out of scope
87 Andreas Jakl, 2009
88. Variables | T Classes | C Classes | R Classes | M Classes | Static Classes
Example – Variables
enum TStuffState // Declares an enumeration, prefix T
{
EInitialized = 0, // Individual elements with prefix E
EError
};
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 i
TStuffState iState;
};
void TStuff::DoStuff(TInt aLength)
{
if (aLength > KMaxLength)
iState = EError;
else
iLength = aLength; // Note: no ambiguities!
}
88 Andreas Jakl, 2009
89. Fixed Arrays
● Standard C++ Array:
TInt myArray[25];
● The same using Symbian OS wrapper class:
TFixedArray<TInt, 25> myArray;
<ptr> [0] [1] … [24]
myArray
25x TInt
89 Andreas Jakl, 2009
90. 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
90 Andreas Jakl, 2009
91. Loading Images
● Create bitmap from specified ID in .mif-file
CSpriteHandler::ConstructL()
iSprites[ESpritePanel] = AknIconUtils::CreateIconL(
fileName, EMbmMopoidgraphicsPanel);
● 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
91 Andreas Jakl, 2009
92. 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)
CSpriteHandler::ConstructL()
AknIconUtils::CreateIconL(iSprites[ESpritePanel], iSprites[ESpritePanelM],
fileName, EMbmMopoidgraphicsPanel,
EMbmMopoidgraphicsPanel_mask);
92 Andreas Jakl, 2009
93. 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
Defined in
MopoidSharedData.h
93 Andreas Jakl, 2009
94. Setting the Image Size
● Dynamically set by the game engine – according to
screen resolution
● Handled in:
void CSpriteHandler::DoSetSpriteSize(
const MopoidShared::TSpriteIds aSpriteId,
const TSize& aNewSize)
● Set image size:
CSpriteHandler::DoSetSpriteSize()
AknIconUtils::SetSize(iSprites[aSpriteId], aNewSize);
94 Andreas Jakl, 2009
95. Memory Leaks
● Test it:
Launch Mopoid in the emulator
Close the app in the emulator (not the emulator itself!)
95 Andreas Jakl, 2009
96. Deleting Images
● Images owned by sprite handler
Delete pointers in the destructor
CSpriteHandler::~CSpriteHandler()
iSprites.DeleteAll();
(= same as for-loop with delete on every element)
96 Andreas Jakl, 2009
97. 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!
// Stray signal detected!
_LIT(KMsgStraySignal, "Stray signaln");
User::Panic(KMsgStraySignal, 1); // Panic with code 1
97 Andreas Jakl, 2009
98. Panics
● Try it – add a serious error
CSpriteHandler::ConstructL()
iSprites[999] = AknIconUtils::CreateIconL(
fileName, EMbmMopoidgraphicsPanel);
● Range checked by TFixedArray (in debug builds)
98 Andreas Jakl, 2009
99. Information about Panics
● SDK doc: Symbian OS v9.x Symbian OS reference
System panic reference USER
Tip: install the automated panic lookup plug-in for Carbide.c++:
99 Andreas Jakl, 2009
http://www.symbianresources.com/projects/paniclookup.php
101. 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
101 Andreas Jakl, 2009
102. Scalable UI in Mopoid – Overview
Framework
Container Base Class (CCoeControl)
HandleResourceChange() SizeChanged()
Container (CMopoidContainer)
HandleResourceChange() SetExtentToWholeScreen() SizeChanged()
Game Engine (CMopoidGameEngine)
SetScreenSize()
102 Andreas Jakl, 2009
103. Scalable UI – Source Code
● Upon start-up and subsequent orientation / resolution
changes, the framework calls HandleResourceChange()
of all visible container classes
CMopoidContainer::HandleResourceChange(TInt aType)
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()
}
● Applies the new size (whole screen) to the container
103 Andreas Jakl, 2009
104. Scalable UI – Source Code
● In CMopoidContainer::SizeChanged()
Query the current resolution
Inform the game engine
CMopoidContainer::SizeChanged()
iGameEngine->SetScreenSize(this->Size());
104 Andreas Jakl, 2009
105. Finally get some content on the screen
Displaying Graphics
105 Andreas Jakl, 2009
106. Back Buffer
Transferred
as a whole
when drawing
is finished
Back Buffer
Screen
106 Andreas Jakl, 2009
107. Game Loop
CMopoidGameEngine
Calculate time
difference
CPeriodic (static)
TimerCallBack()
Animate the
panel
DoFrame()
Draw the frame
to the back
buffer
Request a Asynch. call
redraw event
CMopoidContainer
Copy the back
buffer to the
107 Andreas Jakl, 2009
screen
108. DrawFrame()
● First: clear the back buffer
CMopoidGameEngine::DrawFrame()
// 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 ENullPen
Brush ESolidBrush,
KRgbBlack
108 Andreas Jakl, 2009
109. Drawing Bricks (w/o transparency)
● Bricks are drawn in the for-loop
CMopoidGameEngine::DrawFrame()
iBackBufferBmpGc->BitBlt(ConvToScreenCoord(
iGrid->ConvertGridToXY(x, y)),
iSpriteHandler->GetSprite(spriteId));
● 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
109 Andreas Jakl, 2009
110. Drawing Panel and Ball (with transp.)
● Ball sprite
CMopoidGameEngine::DrawFrame()
iBackBufferBmpGc->BitBltMasked(ConvToScreenCoord(ballSpritePos),
iSpriteHandler->GetSprite(MopoidShared::ESpriteBall),
iSpriteHandler->GetSprite(MopoidShared::ESpriteBall)->SizeInPixels(),
iSpriteHandler->GetSprite(MopoidShared::ESpriteBallM), EFalse);
● Do the same for the panel near the end of the method
Position: iPanel.iPos
Bitmap IDs: ESpritePanel / ESpritePanelM
110 Andreas Jakl, 2009
111. Screen Flickering
● Note: UI Designer clears the screen with white before
the bitmap is copied results in flickering on some
devices
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]
● Changes will get overwritten whenever you change the
design
111 Andreas Jakl, 2009
112. White Border around Sprites
● On S60 3rd Ed.-devices & the MR-emulator, there is a
white border around the sprites (fixed in FP1+)
112 Andreas Jakl, 2009
113. Keeping the game loop alive
Periodic Events
113 Andreas Jakl, 2009
114. 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!
114 Andreas Jakl, 2009
115. Event Handling
Asynchronous Asynchronous
Service Providers Service Providers
Event 1 Event 2 Event 3 Event 1 Event 2 Event 3
Event Event Event Active Scheduler (in app. thread)
Handler Handler Handler
(Thread) (Thread) (Thread) Event Event Event
Handler Handler Handler
(AO) (AO) (AO)
Application (Possibly an extra thread) Application (thread)
Traditional Event-Handling Active Objects
(Symbian OS)
115 Andreas Jakl, 2009
116. 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
116 Andreas Jakl, 2009
117. 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!
MopoidGameEngine.h
CPeriodic* iPeriodicTimer;
117 Andreas Jakl, 2009
118. 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
CMopoidGameEngine::StartTimerL()
const TTimeIntervalMicroSeconds32 KTickInterval = 5000;
118 Andreas Jakl, 2009
119. 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!
CMopoidGameEngine::StartTimerL()
if (!iPeriodicTimer)
{
iPeriodicTimer = CPeriodic::NewL(CActive::EPriorityStandard);
}
119 Andreas Jakl, 2009
120. Starting the Timer
CMopoidGameEngine::StartTimerL()
iPeriodicTimer->Start(KTickInterval, KTickInterval,
TCallBack(TimerCallBack, this));
● 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
MopoidGameEngine.h (already declared, just for reference)
static TInt TimerCallBack(TAny* aObject);
120 Andreas Jakl, 2009
121. 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
TInt CMopoidGameEngine::TimerCallBack(TAny* aObject)
{
// Call non-static method
return ((CMopoidGameEngine*)aObject)->DoFrame();
}
● Call-backs might not be regular
Call-back of a different active object is running or
higher priority call-backs waiting
DoFrame() checks time difference from last frame
121 Andreas Jakl, 2009
122. Stopping the Timer
● Cancel the timer
● Delete the instance (recommended by the SDK doc)
void CMopoidGameEngine::StopTimer()
{
// If the timer object exists: stop and delete it.
if (iPeriodicTimer)
{
iPeriodicTimer->Cancel();
delete iPeriodicTimer;
iPeriodicTimer = NULL;
}
}
122 Andreas Jakl, 2009
123. 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:
CMopoidGameEngine::DoFrame()
User::ResetInactivityTime();
123 Andreas Jakl, 2009
124. Let’s start to play!
Handling Keys
124 Andreas Jakl, 2009
125. Control Stack
Controls put on the stack in the order: A, B, C, D
● Symbian OS maintains Control X
an internal control stack
ECoeStackPriorityDefault (=0) Control Y
● All controls that should Keyboard Focus
receive key events are
put on the stack ECoeStackPriorityMenu (=10) Control Z
● Starting at stack pos. 0,
events are offered to ECoeStackPriorityDialog (=50)
each control until they
Events
Stack position 0
are consumed by a ECoeStackPriorityDialog (=50)
control
CMopoidContainerView::DoActivateL()
AppUi()->AddToStackL( *this, 2009
125 Andreas Jakl, iMopoidContainer );
126. Was the key event handled by us?
● Controls are offered key events in:
TKeyResponse CMopoidContainer::OfferKeyEventL(
const TKeyEvent& aKeyEvent, TEventCode aType)
● Return value depending on if the event was handled:
EKeyWasConsumed: We used the key event, do not send it to
Return other controls
(TKeyResponse)
EKeyWasNotConsumed: This key event was not interesting
for us, send it to other controls
126 Andreas Jakl, 2009
127. Keys
● Type of event sent in aType parameter
EEventKeyDown EEventKey EEventKeyUp
● 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!
127 Andreas Jakl, 2009
128. Keep the user informed
Displaying Text
128 Andreas Jakl, 2009
129. Fonts
● Games use low level methods to draw text
Highly sophisticated games usually their own bitmap font
● Retrieving a standard system font
CMopoidSettings::ConstructL()
iFontUsed = (CFont*) AknLayoutUtils::LayoutFontFromId(
EAknLogicalFontPrimarySmallFont);
iFontHeight = iFontUsed->AscentInPixels();
● Apply the font to the context
CMopoidGameEngine::DrawTextL()
iBackBufferBmpGc->UseFont(iSettings->iFontUsed);
129 Andreas Jakl, 2009
130. Font Colour
● Setup for drawing text (in red colour)
Brush was already set to a null brush before
CMopoidGameEngine::DrawTextL()
iBackBufferBmpGc->SetPenStyle(CGraphicsContext::ESolidPen);
iBackBufferBmpGc->SetPenColor(KRgbRed);
TBuf<30> tempStr;
ESolidPen,
Pen KRgbRed
text text
Brush ENullBrush
130 Andreas Jakl, 2009
131. Buffer Descriptors
● Comparable to (const) char[] of C
● Directly contain the string
● Use C++ templates to specify length (parameter)
Constant: TBufC<5>
5 ‘H’ ‘e’ ‘l’ ‘l’ ‘o’
iLength
(TDesC)
Modifiable: TBuf<9>
5 9 ‘H’ ‘e’ ‘l’ ‘l’ ‘o’
iLength iMaxLength
(TDesC) (TDes)
131 Andreas Jakl, 2009
133. Initializing the TBuf
● Assigning Strings
// Define constant string literal
_LIT(KString1, "Hello ");
_LIT(KString2, "World");
// Copy KString1 into new TBuf with max. length of 15
TBuf<15> buf1(KString1);
// Same as above, this time using Copy() and KString2
TBuf<15> buf2; // Initialize empty TBuf with max. length of 15
buf2.Copy(KString2);
// Append contents of buf2 to buf1
buf1.Append(buf2);
// Create constant descriptor based on KString1
TBufC<15> cBuf1(KString1);
// Replace contents of cBuf1 with contents of buf1
cBuf1 = buf1;
133 Andreas Jakl, 2009
134. Using the TBuf
_LIT(KHelloWorld, "Hello World"); // Defines constant string literal
const TInt maxLength = 15;
// Create a modifiable buffer
TBuf<maxLength> buf;
TInt curLength = buf.Length(); // ...... ?
TInt maxLength2 = buf.MaxLength(); // ...... ?
// Set the contents of the buffer
buf = KHelloWorld;
curLength = buf.Length(); // ...... ?
TInt curSize = buf.Size(); // ...... ?
TText ch = buf[1]; // ...... ?
134 Andreas Jakl, 2009
135. Using the TBuf
_LIT(KHelloWorld, "Hello World"); // Defines constant string literal
const TInt maxLength = 15;
// Create a modifiable buffer
TBuf<maxLength> buf;
TInt curLength = buf.Length(); // == 0
TInt maxLength2 = buf.MaxLength(); // == 20
// Set the contents of the buffer
buf = KHelloWorld;
curLength = buf.Length(); // == 11
TInt curSize = buf.Size(); // == 22
TText ch = buf[1]; // == ‘e’
135 Andreas Jakl, 2009
136. Maximum Length
_LIT(KHello, "Hello "); // Defines constant string literal
TBuf<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. […]”
136 Andreas Jakl, 2009
137. Inheritance Hierarchy
● Abstract base class
because of:
Generalisation
(use base type for
parameters!)
Provide basic
functions shared by
all types (e.g.
Compare(), constant modifiable
Find(), Mid(), ...)
137 Andreas Jakl, 2009
138. Reading Text from Resource Files
● Multiple options to read text from resource files
● Here: through environment from Symbian OS
framework
CMopoidGameEngine::DrawTextL()
CEikonEnv* env = CEikonEnv::Static();
● Read text from resource files into the descriptor
CMopoidGameEngine::DrawTextL()
env->ReadResourceL(tempStr, R_HIGHSCORE);
● (Remember: you declared the resource yourself!)
MopoidContainer.rssi
RESOURCE TBUF r_highscore { buf = STR_highscore; }
138 Andreas Jakl, 2009
139. Formatting and Displaying Text
● tempStr now contains “High Score: ”
● Append the current score:
CMopoidGameEngine::DrawTextL()
tempStr.AppendNum(iSettings->iHighScore);
● Draw the text
x-coord: 5
y was calculated directly above
CMopoidGameEngine::DrawTextL()
iBackBufferBmpGc->DrawText(tempStr,
TPoint(5, yTextStartBottom));
139 Andreas Jakl, 2009
140. Current Score
● Like for the previous text:
Resource: R_SCORE
Number: iSettings->iScore
x-coordinate: 5
y-coordinate:
one line above previous text
yTextStartBottom - iSettings->iTextYOffset
140 Andreas Jakl, 2009
141. Aligning Text
● Print text on the right side of the screen
CMopoidGameEngine::DrawTextL()
iBackBufferBmpGc->DrawText(tempStr, iSettings->iScreenRect,
yTextStartBottom,
CGraphicsContext::ERight, 3);
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
141 Andreas Jakl, 2009
142. Final State
● Remove the comment from the code block
( activate the rest of the DrawTextL()-method)
● The final state:
142 Andreas Jakl, 2009
144. Preparations
● Filename and version have already been declared
MopoidGameEngine.h
#define MOPOID_FILE_VERSION_NUMBER 1
_LIT(KMopoidDataFile, "settings.dat");
● We will use the CFileStore-class
add the required include file (see SDK doc)
MopoidGameEngine.h
#include <s32file.h>
144 Andreas Jakl, 2009
145. Data Caging – Overview
• All binaries of all apps in one
dir
• Can only be executed from
Executable there
• Usually: no read / write access
• sysbin
• Bitmaps, fonts, help files
Resources • Usually: read-only
• resource
• Each application its own dir
• Used for settings, ...
Private
• Usually: read / write only for
Data own private dir
• private<UID3>
145 Andreas Jakl, 2009
146. Data Caging
● App. only has access to:
Separating code
Own directories and data!
“Open” directories
● Access based on capabilities and identity
SystemAppsJourneyJourney.app SysBinJourney.exe
SystemAppsJourneyJourney.mbm ResourceAppsJourney.mbm
SystemAppsJourneyJourney.rsc Private10003a3fJourney.rsc
Symbian OS, pre-V9 Symbian OS 9.x
146 Andreas Jakl, 2009
147. Data Caging – Directories
Directory Capability Capability Used for
(Read) (Write)
sys AllFiles TCB Applications can only be executed from here.
resource - TCB Fonts, bitmaps, help-files, … Only read-access
for 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, …
147 Andreas Jakl, 2009
148. More about directories
● Data caging provides secure area for application’s data
● All executables installed to sysbin
Risk of filename clashes – use your unique UID as part of
the executable filename
Removable drives:
Hash stored to c:syshash
Prevents execution of modified executables
148 Andreas Jakl, 2009
149. Data Files
● Data file should be stored in our private directory:
private<UID3>settings.dat
● Dynamically add the path:
CMopoidGameEngine::SaveGameProgessL()
NCommonFunctions::AddPrivateDirL(iFs,
KMopoidDataFile, fileName);
● Central part of this custom utility function:
NCommonFunctions::AddPrivateDirL()
aFs.PrivatePath( aCompleteName );
149 Andreas Jakl, 2009
150. File Store
● Many ways to access files are possible (RFile, ...)
● Here: CFileStore – one of the most flexible Store (File)
Encapsulates file-based, persistent stores Stream (ID)
Stores multiple streams, each identified by an ID Stream (ID)
...
Allows easy serialization of objects
Direct file store: doesn’t allow modifying data – not important
for us, we will always replace the stream
CMopoidGameEngine::SaveGameProgessL()
CFileStore* store = CDirectFileStore::ReplaceLC(
iFs, fileName, EFileWrite);
store->SetTypeL(KDirectFileStoreLayoutUid);
150 Andreas Jakl, 2009
151. 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
CMopoidGameEngine::SaveGameProgessL()
RStoreWriteStream stream;
TStreamId id = stream.CreateLC(*store);
151 Andreas Jakl, 2009
152. Writing Data
● For now: file version and high score
CMopoidGameEngine::SaveGameProgessL()
// Write file version number
stream.WriteInt32L(MOPOID_FILE_VERSION_NUMBER);
// Write highscore
stream.WriteInt32L(iSettings->iHighScore);
● 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!
152 Andreas Jakl, 2009
153. Closing the File
● Make sure that all data is written and close everything
CMopoidGameEngine::SaveGameProgessL()
// 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);
153 Andreas Jakl, 2009
154. 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!
CImage
void CMyObj::DoSomethingL()
{ void CImage::DoSomethingDangerousL()
CImage* img = new (ELeave) CImage(); {
User::Leave(KErrNotFound);
img->DoSomethingDangerousL(); }
delete img;
} img = pointer on the stack
154 to an instance 2009 heap
Andreas Jakl, on the
155. Cleanup Stack
● Memory situation if a leave occurs:
img
Stack Heap
Object stays on the heap;
Pointer to delete the instance is lost
memory leak
155 Andreas Jakl, 2009
156. Cleanup Stack
● Solution: Cleanup Stack
void CMyObj::DoSomethingL() img
{
CImage* img = new (ELeave) CImage(); CleanupStack
CleanupStack::PushL(img);
img->DoSomethingDangerousL();
CleanupStack::PopAndDestroy();
}
img
Stack Heap
When no leave occurs: object still
has to be deleted by you + Cleanup stack saves a second pointer
removed from the cleanup stack! to the object on the heap.
156 Andreas Jakl, 2009
157. 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
CMopoidGameEngine::SaveGameProgessL()
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);
157 Andreas Jakl, 2009
158. Loading
● Similar to saving files
1. Open the file store
CMopoidGameEngine::LoadGameProgessL()
CFileStore* store = CDirectFileStore::OpenLC(
iFs, fileName, EFileRead);
2. Open the root stream (no ID is required for the root)
CMopoidGameEngine::LoadGameProgessL()
RStoreReadStream stream;
stream.OpenLC(*store, store->Root());
158 Andreas Jakl, 2009
159. Reading Data
3. Read the data
CMopoidGameEngine::LoadGameProgessL()
TInt versionNumber = stream.ReadInt32L();
if (versionNumber != MOPOID_FILE_VERSION_NUMBER)
User::Leave(KErrNotFound);
iSettings->iHighScore = stream.ReadInt32L();
If the file version doesn’t match – same behaviour as if
the file wasn’t found. You could import an old version
instead.
159 Andreas Jakl, 2009
160. If the File Was Not Found
● File not found-error (or wrong version)
Ignored
Settings-class initialized with default values
CMopoidGameEngine::ConstructL()
// Load high score and settings
TRAPD(err, LoadGameProgressL());
if (err != KErrNone && err != KErrNotFound)
User::Leave(err);
● All other errors (e.g., not enough disc space left)
TRAP’d leave is sent out again
160 Andreas Jakl, 2009
161. Cleanup
4. Close all handles
CMopoidGameEngine::LoadGameProgessL()
CleanupStack::PopAndDestroy(&stream);
CleanupStack::PopAndDestroy(store);
● 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
161 Andreas Jakl, 2009
162. Cleanup – Uninstallation
● Files that are to be removed during
uninstallation: specified in the package file
Mopoid.pkg
; --- Settings-file
""-"!:privateE2C4F75Bsettings.dat",FN
● “FN” means “FileNull” – SDK help:
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.
162 Andreas Jakl, 2009
163. Make a good appearance in the phone menu:
Application Icon
163 Andreas Jakl, 2009
164. 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
164 Andreas Jakl, 2009
165. Is anyone out there?
Foreground / Background
165 Andreas Jakl, 2009
166. 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
166 Andreas Jakl, 2009
167. 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
MopoidContainerView.h
void HandleForegroundEventL(TBool aForeground);
167 Andreas Jakl, 2009
168. 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!
MopoidContainerView.cpp
// 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);
168 Andreas Jakl, 2009
}
169. Adapting the Game to the Environment
Sound Volume / Dynamic Menus
169 Andreas Jakl, 2009
170. 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
170 Andreas Jakl, 2009
171. (De) activating Sound
● Send the command to the game engine
TBool CMopoidContainerView::HandleSound_OnMenuItemSelectedL( TInt aCommand )
{
if (iMopoidContainer && iMopoidContainer->iGameEngine)
{
iMopoidContainer->iGameEngine->SetSoundLevelL(ETrue);
}
return ETrue;
}
● ... 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()
171 Andreas Jakl, 2009
172. 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:
MopoidContainerView.h
void DynInitMenuPaneL(TInt aResourceId, CEikMenuPane* aMenuPane);
172 Andreas Jakl, 2009
173. 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
MopoidContainerView::DynInitMenuPaneL()
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());
}
173 Andreas Jakl, 2009
175. Mopoid
● Tasks Defining and displaying text,
fonts, descriptors
Create your own application
using the UI designer Using vector graphics
Define a menu and an about Scalable UI
box Asynchronous events,
Leaves, Panics and Cleanup timers
Stack Key handling
Project structure, libraries Files, data caging (platform
Class types, naming security)
conventions Foreground / background
Dynamic menu
175 Andreas Jakl, 2009