Symbian OS - Mopoid Next Gen - Slides


Published on

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.

Published in: Technology
  • Be the first to comment

  • Be the first to like this

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

Symbian OS - Mopoid Next Gen - Slides

  1. 1. mopoid Symbian OS Workshop 1 Andreas Jakl, 2009 v2.4.1 – 19 April 2009
  2. 2. Disclaimer ● These slides are provided free of charge at and are used during Symbian OS courses at the University of Applied Sciences in Hagenberg, Austria ( ) ● 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 ● 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. 2 Andreas Jakl, 2009
  3. 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. 4. Schedule ● Instead of theory, we’ll do a practical project 4 Andreas Jakl, 2009
  5. 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. 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. 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
  8. 8. Carbide.c++ 8 Andreas Jakl, 2009
  9. 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, … ● 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. 10. Installation 1. Carbide.c++ 2.0 Developer Edition (or later) bide/index.html 2. Perl 5.6.x (Set the path variable!) – not 5.8 / 5.10! Perl- 3. SDK(s) (S60 3rd Ed. MR + newer) Platforms/S60_Platform_SDKs/  Install all tools on the same drive (recommended: C:, no network drive!) 10 Andreas Jakl, 2009
  11. 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. 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. 13. Create new project ● File  New  C++ Application for S60 Project 13 Andreas Jakl, 2009
  14. 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. 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. 16. Choose UI-Design ● Select the empty design We’ll do low level graphics on an empty canvas 16 Andreas Jakl, 2009
  17. 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 0xE0000000 – 0xEFFFFFFF Development use (v9) 0xF0000000 – 0xFFFFFFFF Legacy UID (pre-v9) 17 Andreas Jakl, 2009
  18. 18. Our Project 18 Andreas Jakl, 2009
  19. 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. 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
  21. 21. First tests Compile 21 Andreas Jakl, 2009
  22. 22. Choose Build Target ● Active Build Configuration  Emulator 22 Andreas Jakl, 2009
  23. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 34. Application structure (4) View class ● Manages title- and status pane ● Command handling (for this view) 34 Andreas Jakl, 2009
  35. 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
  36. 36. App Startup (S60 Views) RunApplication CreateDocumentL() CreateAppUiL() ConstructL() DoActivateL() Avkon View Switching Application CMyApplication CMyDocument CMyAppUi CMyView1 CMyContainer1 Avkon CAknViewAppUi CAknApplication CAknDocument CAknAppUi CAknView CAknAppUiBase Uikon CEikApplication CEikDocument CEikAppUi AppArc Cone CApaApplication CApaDocument CCoeAppUi CCoeControl 36 Andreas Jakl, 2009
  37. 37. UI-Design Menu, About Boxes and Leaves 37 Andreas Jakl, 2009
  38. 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. 39. Exit Command ● Change the command id of the “Exit” command to EAknCmdExit Optional: Change the title 39 Andreas Jakl, 2009
  40. 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. 41. Connection: Menu  Dialog ● Open the options menu ● Right click  “Handle ‘Selected’ Event” for the “About” menu item 41 Andreas Jakl, 2009
  42. 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
  43. 43. Result 43 Andreas Jakl, 2009
  44. 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. 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. 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. 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. 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
  49. 49. Add the pre-implemented framework to your project Game Engine 49 Andreas Jakl, 2009
  50. 50. Extraction and Sources 1. Close Carbide.c++ 2. Unzip 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. 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. 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. 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. 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. 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. 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. 57. Graphics ● Today’s phones: screen orientation / size changes ● Mopoid uses vector graphics ● S60: support for SVG-T 57 Andreas Jakl, 2009
  58. 58. Adding Game Graphics ● Add a multi image file (.mif) to the icons-makefile: ● Target directory: resourceappsMopoidGraphics.mif ● Header generation: Header ● Header file: epoc32includeMopoidGraphics.mbg 58 Andreas Jakl, 2009
  59. 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. 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. 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. 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. 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. 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. 65. Testing ● Before building, choose “Project”  “Clean” ● This is what Mopoid should look like now (white, empty): ● ... if there are serious problems, use 65 Andreas Jakl, 2009
  66. 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. 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. 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. 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. 70. Getting the images out of the .mif-file Loading Images 70 Andreas Jakl, 2009
  71. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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  Edit MBM / MIF  MopoidGraphics.mif or: open <EPOCROOT>/include/MopoidGraphics.mbg 91 Andreas Jakl, 2009
  92. 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. 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. 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. 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. 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. 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. 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. 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
  100. 100. Adapting to different screen layouts Scalable UI 100 Andreas Jakl, 2009
  101. 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. 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. 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. 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. 105. Finally get some content on the screen Displaying Graphics 105 Andreas Jakl, 2009
  106. 106. Back Buffer Transferred as a whole when drawing is finished Back Buffer Screen 106 Andreas Jakl, 2009
  107. 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. 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. 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. 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. 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. 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. 113. Keeping the game loop alive Periodic Events 113 Andreas Jakl, 2009
  114. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 124. Let’s start to play! Handling Keys 124 Andreas Jakl, 2009
  125. 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. 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. 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. 128. Keep the user informed Displaying Text 128 Andreas Jakl, 2009
  129. 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. 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. 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
  132. 132. Size, Length, MaxLength ● TBuf<9> with text “Hello” 5 9 ‘H’ ‘e’ ‘l’ ‘l’ ‘o’ 4 bytes 4 bytes 2 2 ... Unicode characters! Length() 5 (characters) MaxLength() 9 (characters) Size() 10 (bytes) MaxSize() 18 (bytes) 132 Andreas Jakl, 2009
  133. 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. 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. 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. 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. 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. 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. 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. 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. 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. 142. Final State ● Remove the comment from the code block ( activate the rest of the DrawTextL()-method) ● The final state: 142 Andreas Jakl, 2009
  143. 143. Be persistent Reading / Writing Files 143 Andreas Jakl, 2009
  144. 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. 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. 146. Data Caging ● App. only has access to: Separating code Own directories and data! “Open” directories ● Access based on capabilities and identity SysBinJourney.exe SystemAppsJourneyJourney.mbm ResourceAppsJourney.mbm SystemAppsJourneyJourney.rsc Private10003a3fJourney.rsc Symbian OS, pre-V9 Symbian OS 9.x 146 Andreas Jakl, 2009
  147. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 163. Make a good appearance in the phone menu: Application Icon 163 Andreas Jakl, 2009
  164. 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. 165. Is anyone out there? Foreground / Background 165 Andreas Jakl, 2009
  166. 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. 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. 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. 169. Adapting the Game to the Environment Sound Volume / Dynamic Menus 169 Andreas Jakl, 2009
  170. 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. 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. 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. 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
  174. 174. We’re finished! Summary 174 Andreas Jakl, 2009
  175. 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
  176. 176. That’s it Thanks for your attention! 176 Andreas Jakl, 2009