Page MenuHomePhabricator

Startup Theme Tutorial - smoke init
Updated 1,925 Days AgoPublic

Abstract: This is a tutorial on startup themes for the E17 window manager. Design concepts are covered as well as the required edje elements needed to make the theme work properly. The smoke_init startup theme is used as a subject and a short Blender animation process flow is included.

PageOutline

Startup Design Goals

E17 startup themes are most likely the easiest of themes to create. They have a simple protocol and very short lifetime. Their purpose is to entertain the user with some eye candy while Enlightenment is starting and enlightenment really doesn't take very long to start. This constrains the animation elements, if any, to short runs and loops so startup themes are a useful place for those interesting animations ideas that don't belong anywhere else.

For smoke_init the goal of the theme was to provide a graphical entry into the smoke window manager theme. The startup design parameters were,

  • the smoke_init background graphics will match the smoke background
  • like the default startup theme there will be 3 phases
    • the intro animation
    • the loop animation
    • the ending fade
  • smoke_init will focus on the E17 logo and work nicely with other WM themes

When the startup application finishes and E17 starts, the transition between the two is almost seamless so the hope was that the one would blend nicely into the other if the backgrounds matched.

Implementation Approach

This is the finished preview image submitted to e-org for the smoke_init startup theme. The text elements are from the default theme because the focus of the implementation was to be on the background and the animation.

{F2371, size=full}

The background is derived from the smoke theme, leaving the substance of the work in the animation. This boils down into specific tasks,

  • basic assemblage; collecting the background images, fonts, and edje code to form the foundation of the theme
  • creating the animation
  • inserting the animation into the edje code so that it works efficiently
  • finishing touches and tweaks

Assemblage

The edje file for a startup theme contains a collection of two groups.

  • e/init/splash is the primary splash graphics that will be presented on the user's main screen
  • e/init/extra_screen is a graphic that will be presented on any attached screens

The extra screen graphic should be simple and graphically similar to the splash screen.

The Background

The background is done in two parts: a radial gradient underlay part that provides color and a black and white cloud image overlay part that specifies some transparency. The first step is to define a spectrum for the gradient background that will define a color transition between a blue-green and a medium gray.

#define MEDGRAY 153 153 153
spectra {
   spectrum {
      name: "bg.gradient";
      color: 147 216 185 255 1;
      color: MEDGRAY 255 1;
   }
}

This spectrum is then used to define underlay gradient part, which is defined as a macro because the same code will be used for both groups. The radial gradient definition specifies a full transition between the two colors in the spectrum and the fill origin defines the center of the first color (the blue-green) at 20% from the top-left corner.

#define UNDERLAY_PART()                         \
part {                                          \
   name: "underlay";                            \
   mouse_events: 0;                             \
   type: GRADIENT;                              \
   description {                                \
      state: "default" 0.0;                     \
      gradient {                                \
         spectrum: "bg.gradient";               \
         type: "radial";                        \
         rel1 {                                 \
            relative: 0.0 0.0;                  \
            offset: 0 0;                        \
         }                                      \
         rel2 {                                 \
            relative: 0.0 1.0;                  \
            offset: 0 0;                        \
         }                                      \
      }                                         \
      fill {                                    \
         origin {                               \
            relative: 0.20 0.20;                \
            offset: 0 0;                        \
         }                                      \
      }                                         \
      rel1 {                                    \
         relative: 0.0 0.0;                     \
         offset: 0 0;                           \
      }                                         \
      rel2 {                                    \
         relative: 1.0 1.0;                     \
         offset: 0 0;                           \
      }                                         \
   }                                            \
}

The overlay is a cloud image whose alpha is set to about 50% to allow the underlay to come through as a tint. This is an image that doesn't require critical sharpness so there is an opportunity for some resource savings by defining the image as lossy.

images {
   image: "images/smoke_bg.png" LOSSY 70;
}
#define OVERLAY_PART                            \
part {                                          \
   name: "overlay";                             \
   mouse_events: 0;                             \
   type: IMAGE;                                 \
   description {                                \
      state: "default" 0.0;                     \
      rel1 {                                    \
         relative: 0.0 0.0;                     \
         offset: 0 0;                           \
      }                                         \
      rel2 {                                    \
         relative: 1.0 1.0;                     \
         offset: -1 -1;                         \
      }                                         \
      image {                                   \
         normal: "images/smoke_bg.png";         \
      }                                         \
      color: 255 255 255 120;                   \
   }                                            \
}

{F2368, size=full}

To make the splash group interesting most of the startup elements are specified between these two parts; to make the extra screen group simple, only these two parts are specified. The convenience of the macros can be seen in the simple definition of the extra screen group:

group {
   name: "e/init/extra_screen";
   parts {
      UNDERLAY_PART();
      OVERLAY_PART();
   }
}

Any attached screens will only contain the base background.

The Standard Init Elements

Before discussing the interesting part of the splash screen there are a few fundamental parts in a startup theme that are included in smoke_init but only require a brief description:

  • The disable query is a user convenience and is optional. Checking the box for this query will disable the startup theme so it will not be displayed on future startups. If the query is not in the theme the user will need to use the configure dialog to disable the startup theme. In the smoke_init theme this disable query was ripped from the default startup theme.
  • The enlightenment version in a part named e.text.version and is placed at the lower right part of the screen.
  • The title (e.text.title) receives and shows the name of the window manager ("enlightenment") and is positioned above the cloud overlay for emphasis.
  • The status text (e.text.status) and the startup application icons (e.swallow.icons) are placed below the title and below the cloud overlay.

Animating with Blender

The smoke theme has some rolling elements in the window borders and clock module so it was decided very early that the animation would roll or scroll in some fashion. The final animation is a cube that contains the ubiquitous tilted e logo in a different color on each face and is rotated on its z-axis, pausing at each 90-degree increment.

The tasks have been identified for the animation:

  • Create the Enlightenment images that will go onto the faces of the cube
  • Create the animated cube
  • Generate the frame images and incorporate them into the edje code

The GIMP is used for the cube faces but the method is very fundamental and could just as easily be done in Inkscape or !ImageMagick. Blender is used for the animation. There are good tutorials available for both GIMP and Blender and I hope you can find answers in those for any deficiencies in this tutorial.

Creating the faces of the cube

I wanted the cube to look like a child's wooden block with a different color enlightenment logo on each face.

The first step is to copy the basic black PNG logo to a local folder as logo-e.png. The dimensions of this image are 240x325 pixels and to keep the image square when we place it on the blender cube object it is necessary to compose a square image in the GIMP. Select File->New... in GIMP and specify a 325-pixel square workspace.

{F2361, size=full}

Place the logo image onto the workspace as a layer by using the File->Open as layers... dialog. By default the image is centered in the workspace.

Duplicate this layer using the Layers dialog. You should now have 3 layers: the default background, the original image layer, and the duplicate layer on top.

{F2363, size=full}

The duplicated layer is topmost and is highlighted, meaning it is the active layer, so it is convenient at this time to add the color to the logo. This is easy to do by locking the alpha channel for the layer and then drag the color you want from a color palette or chooser. Lighter colors work best because we are going to create a dark outline but please experiment. The colors I have chosen are all from the pastel palette that comes with the GIMP.

The dark outline will be created by blurring the black logo beneath the colored logo. Select the middle layer containing the original black logo. Select Filters->Blur->Gaussian Blur... in the main image view. The default blur radius of 5 works fine but 6 seems to look slightly better.

{F2360, size=full}

Save the image as cube_face_0.png after taking the defaults on the dialogs that GIMP presents regarding merging the visible layers and image quality. Repeat this process to create a total of 4 cube faces.

Mapping the images onto the cube

Blender users will recognize the default cube that you get when you start blender so I have a confession to make: this animation started as a placeholder. It was intended as filler while I worked out the details of the edje code. After using the animation for a few days I began to enjoy the simplicity of it. Because the cube model is provided by Blender this tutorial can concentrate on lighting, camera positioning, and generating the animation images.

First, the images are placed onto the cube, and the easiest way to begin is to set up the Blender windows to facilitate placing the images onto an object. When you bring up Blender you get a single large modeling window that defaults to Object mode with the default object (our cube) selected. Deselect the cube by pressing the AKEY (the letter A). Change the mode to UV Face Select by pressing the FKEY. The default view is from the top (note the axis graphic in the lower left of the main view that indicates we are viewing the XY plane) and for this tutorial we want the view to be from the side. Press the NUM1 (number 1 on the numeric keypad) to change to a side view. Note the change in the axis graphic. This is my preference --- you could just as easily have left the default and rotated around the Y-axis. It all depends on where you are comfortable standing in 3D space.

{F2357, size=full}

Note that the tab key can be used to toggle between Edit mode and UV Face Select; this will be important later. There is one more thing to do before adding images.

The default window type in Blender is 3D View and to add the images the window needs to be UV/Image Editor. The most convenient would be to view the cube object with both window types because that would allow us to rotate the object and select faces in the 3D View and without much manipulation the images can be added in the UV/Image Editor window. To split the 3D viewport, press the middle mouse button (MMB in blender-speak) on the seam between the viewport and the button window below it.

{F2364, size=full}

Select Split Area to create a control line which you can center over the 3D viewport to split the view in two. Now find the Window Type popup in the lower left of the 3D viewport, just below the axis graphic, and change the window type to UV/Image Editor.

{F2370, size=full}

The Blender work area is now ready for adding images.

{F2369, size=full}

After going through all that trouble to prep Blender for image mapping, the actual process is rather simple. The 3D View is on the left in face mode so that faces can be selected. The view of the UV/Image editor is on the right and will be used to add the images you created with the GIMP.

New Blender users: what you are looking at is straight on at a single face of the cube. To get a better sense of the orientation, press and hold MMB in the 3D view to pan around the object in 3D space. Return to the original view by pressing NUM1.

  • In the left window select a face by pressing RMB on the face of the cube. Note the color of the selected edges in both windows which provide clues to the orientation of the face.
  • In the right side image editor, open a file browser by selecting Image->Open...
  • In the browser, use MMB to browse to and select one of the face images.
  • The edge coloring mentioned in the first step might be more obvious now. To correct the rotation, move the cursor to the left view and press RKEY to bring up the Rotation popup menu. Select UV Co-ordinates to make a single rotation. You may have to make more than one rotation to get the image properly oriented.

Add the next face by rotating the cube until the next face is in view and can be selected and repeat the process for all lateral faces. The cube is rotated in 15-degree increments by pressing NUM6 (NUM4 if you prefer the other direction). If you were to press and hold MMB in the 3D view and move the cursor diagonally, your view should look something like this.

{F2358, size=full}

Enough work has been done at this point to warrant saving your work. Use File->Save (or CTRL+WKEY) to write your Blender file.

Lighting the cube

Along with the default cube model, blender gives you a single camera and a lamp to light your scene. The single lamp is not enough light for what we want to do and if you were to render the scene right now it would be pretty dark and boring. Here are the steps to take to get our "image mapping setup" back to a "basic modeling setup" so we can play with the lighting.

  • Over the seam that separates the left and right views, press MMB to get the popup menu and select Join Areas. You want to join the left view to the right. Click when you get that big arrow going in the correct direction.
  • Change to "Object mode" by pressing LMB on the popup indicator to the right of mode display that currently shows "UV Face Select".

{F2365, size=full}

  • If the face images have disappeared it is because the "Draw Type" is not set to "Textured". The illustration shows the popup that allows you to select this using LMB, but it is useful to know the hotkey, ALT+ZKEY, because you may want to toggle between these two draw types as you do lighting.

{F2372, size=full}

At this point you should have one large Object Mode view with a Textured Draw Type; if you press NUM1 you will be looking at a face of the cube and the image that you mapped to that face is visible. Use the scroll wheel on your mouse to zoom in and out of the view; zooming out will also reveal the lamp and camera objects.

Before starting the lighting the camera needs to be put into position. To get oriented in the 3D view, use the mouse wheel to change your viewing position in the scene. To see what the camera sees, use NUM0. The goal is to set the camera directly on the y-axis pointing squarely at the cube. This is basic object moving in blender and is reasonably efficient when you get used to the interface.

  • Select the camera with RMB
  • Press NUM0 to make sure we are looking at the ZX plane (note the axis indicator in the lower left corner of the view).
  • To "grab" the object and constrain moves along the Z-axis, press GKEY-ZKEY. Moving the mouse will move the camera up and down the Z-axis. Move the camera so that its pivot is on the X-axis, press LMB to finish the move.
  • Press NUM7 for the top view, the XY plane.
  • Move the camera along the X-axis (GKEY-XKEY) until the camera pivot is on the Y-axis.

The camera pivot is now on the Y-axis, it just needs to be rotated to face the cube. This is done with simple rotations.

  • Still in the top view (NUM7) with the camera selected, press RKEY to start a rotation. A dotted line will appear between the pivot and the mouse cursor to help with alignment. Finer grain control of the angle is possible by pressing RKEY with the cursor further from the object.
  • Press NUM3 and make a similar rotation in the ZY plane.
  • For an (almost) final adjustment, view the object from the camera NUM0 and, use RKEY again if another rotational adjustment is needed to square the camera.

After saving your work (CTRL+WKEY), reward yourself with your first render. First some setup.

  • Select the object by pressing RMB on the cube.
  • Press F5 for the material button panel.
  • Find and enable the "!TexFace" button to tell Blender that you want renders to include the images you mapped to the object.

{F2373, size=full}

  • Press F10 for the render button panel.
  • Turn off ray tracing ("Ray") and set the render to 50% for now.

The last step is optional but will help speed up the render processing. If you have a slow-ish CPU you might want to set OSA to a lower number which reduces the amount of over sampling but for the final animation you will want this bumped up a little bit. For this simple image there is no ray tracing or shadows to render anyway. Press F12 to render and viola! a black cube. Looking at the top view (NUM7) you can see that the only light in the scene is on the other side of the cube from the camera and you just rendered the dark side of the cube. Select the lamp (RMB) and move it (GKEY) to a location between the camera and the cube. Check the ZY plane (NUM3) and make a similar adjustment to the lamp. Press F12 to render a cube face with a single lamp.

It will also be convenient to output the images to a local folder instead of the default (/tmp) folder. This is done in the Output tab on the left side of this button panel.

This scene needs more light but it can wait until after we animate the cube.

Animating the cube (finally!)

Before we get to the fun there is setting up to do. While in Object mode, press F10 for the render settings. The plan is to create 100 PNG images for a full 360-degree rotation of the cube. Later, in edje code, most of the images will be used as tween images. In the Anim tab, set the End: value to 100. In the Format tab, set the image type to PNG with RGBA support.

{F2366, size=full}

At the very top of the Blender application window you will see that you have been working in pre-defined screen called "SR:2-Model". Use the pulldown menu to change to the screen called "SR:1-Animation". This dangerous looking screen allows way more complicated animations than we are going to do so don't let it intimidate you.

Some sanity checks before we get started:

  • Press NUM7 to view the scene from the top looking at the XY plane
  • Make sure the cube is selected
  • Save your work (CTRL+WKEY) just in case you mangle something

Just below the 3D view is the animation timeline showing the start and end frame indexes that we have already set. To the right of those numbers is the current frame.

{F2362, size=full}

It is good to have a plan before you start. In our eventual edje code we are going to rotate the cube and at certain intervals check to see if enlightenment has started. The animation will be 4-seconds long (25 frames/second with 100 frames), its a cube (!) and a lateral rotation, and each face is a 90-degree rotation from the previous face (!). Now that all that complicated math is out of the way, let's get that done in blender.

Start by inserting the first keyframe at the current cube rotation. Press IKEY to bring up the Insert Key popup menu and select Rot.

{F2359, size=full}

The next keyframe is at animation frame 25. Set the current frame to 25 in the timeline and rotate the cube 90-degrees around the Z-axis by pressing RKEY ZKEY 9KEY 0KEY and accept it with the ENTER key. Press IKEY as you did for the first keyframe, selecting Rot as the Insert Key. Repeat this process for frame 50, 75, and 100. If you have done this correctly the result is an animation loop that you can view in wireframe using the play control button to the right side of the timeline.

At this point I wasted some CPU cycles rendering the animation but I can save you a little bit of time by telling you that the lighting needs adjustment. As the cube rotates the single lamp leaves a side in shadow and it would be nice to light that as well. The image below is from the 16th frame of the animation with our current lighting.

{F2374, size=full}

It would be desirable for the lighting to emphasize the rotation and this can be accomplished with a single soft spot to fill the left side of the scene. Add the lamp using the menubar, Add->Lamp->Spot. You can change the type of lamp if you wish but this is a good light for this purpose. Place the lamp to the left of the camera and position it just as you did with the camera, using GKEY to move it and RKEY to rotate it. The spot is directional so you want the centerline passing through the cube. There are other adjustments to help with the lighting in the button panel for lamps. While the lamp is selected, press F5 to get the lamp controls. Before adjusting energy, adjust the Distance so that the spot range is just past the cube. Note that the spot has affected the intensity of the other lamp and you may need to select the lamp and tweak it also. Render the scen at that frame (F12) and the first frame until you are satisfied with the lighting. The end result had some amount of overexposure that contributed to an interesting affect.

Press CTRL+F12 to start the render process and wait until all 100 frames have been rendered. Press CTRL+F11 to play the animation.

Now that we have images ...

Before we stuff the images into the edje code it would be a good idea to tighten things up a little bit. You can only spend so much time with the camera position and framing and it is probable that you will have some post-processing. There are a number of considerations for this post-processing.

  • Crop the images to a size that has the smallest amount of waste
  • Apply utilities to reduce the image loading overhead without reducing quality
  • Use fewer images
  • The goal is always a good quality animation

Cropping

Here is a simple process for cropping the images. As the cube rotates it is the most narrow when showing a full face (at the 90-degree keyframes) but at the 45-degree midpoints the cube image is both wider and taller. Use GIMP to find the bounding rectangle of these extremes.

  • Open the frame 1 image (0001.png) in the GIMP
  • Use File->Open as Layers... to open frame 12 and 18.
  • Set the view to 200% and drag guides a few pixels outside the boundaries.

{F2367, size=full}

  • Use the rectangle tool to select that bound region, making sure you have the default View->Snap to Guides set. In the dialog box for the rectangle tool, you will see the offsets and dimensions of the rectangle within the image that we can use to script a crop process using !ImageMagick's convert utility.

{F2356, size=full}

That script looks like this,

#!/usr/bin/perl
use strict;
my $outfolder = "./cube";
mkdir $outfolder if (!-e $outfolder);
my $i = 0;
while ($i < 100) {
    $i++;
    my $src = sprintf("%04d.png", $i);
    my $out = sprintf("%s/cube_%04d.png", $outfolder, $i);
    my $cmd = "convert $src -crop 306x270+63+13 +repage $out";
    print $cmd, "\n";
    system($cmd);
#    printf("tween: \"%s\";\n", $out);
#    printf("image: \"%s\" FRAME;\n", $out);
}

Those last two commented lines look interesting. I bet if you uncommented them, piped the output to sort, the result could become useful bits of edje code. The result of this crop process trims approximately 40% of unused transparent fat.

To squeeze another 4.5% reduction you can use pngcrush but you can get more substantial savings by not using all of the images. When you run the animation in Blender, you are seeing Blender's animation engine flipping through the images at the frame rate you specified (25FPS.) When you insert tween images into edje code, the enlightenment engine will manage transitions in a time interval you specify and it is very likely that an edje animation can be just as smooth without all the images. The best way to understand this is to write some edje code.

The smoke_init logo part

Our rotating logo-on-a-cube animation is all described within a single part called "logo_cube". Since we want to fade the logo away after enlightenment starts, it is clipped to another part that you can look at by decompiling the edje file. The default description is mostly a place holder since this part is not intended to be viewed statically.

part {
   name: "logo_cube";
   clip_to: "logo_cube_clip";
   mouse_events: 0;
   description {
      state: "default" 0.0;
      visible: 0;
      image {
         normal: "cube/cube_001.png";
      }
   }

Since the animation is a loop, it is convenient to have a starting part. Basically, when the roll completes its 360-degree rotation it momentarily transitions to this part before transitioning to the 90-degree point.

description {
   state: "roll_start" 0.0;
   visible: 1;
   rel1 {
      relative: 0.0 0.0;
      offset: 0 0;
      to: "logo_cube_clip";
   }
   rel2 {
      relative: 1.0 1.0;
      offset: -1 -1;
      to: "logo_cube_clip";
   }
   image {
      normal: "cube/cube_001.png";
   }
}

The full rotation is described in 4 parts, roll_to_90, roll_to_180, 270, and 360. They are all similar so I will describe only the first part.

description {
   state: "roll_to_90" 0.0;
   inherit: "roll_start" 0.0;
   image {
      normal: "cube/cube_025.png";
      tween: "cube/cube_003.png";
      tween: "cube/cube_005.png";
      tween: "cube/cube_007.png";
      tween: "cube/cube_009.png";
      tween: "cube/cube_011.png";
      tween: "cube/cube_013.png";
      tween: "cube/cube_015.png";
      tween: "cube/cube_017.png";
      tween: "cube/cube_019.png";
      tween: "cube/cube_021.png";
      tween: "cube/cube_023.png";
   }
}

Most of the positional work of this part description is done for us because it inherits from "roll_start". It displays the cube face at the first 90-degree turn at frame 25 after animating through the odd frames subsequent to that face. The first test build had all frames, 2 through 24, as tween images but that added nothing to the quality of the animation and the edje file was unnecessarily larger. After motivating the cube to move from one face to the next, there is a check to see if enlightenment is ready to go --- it is time to look at the programming section.

The programs section

An init theme lives for the window manager; enlightenment tells it when to start and when it is ready to stop. This tutorial doesn't cover every program in smoke_init but it covers the parts that make the animation work within the framework of the init protocol.

The first program that gets run is called "init" and it receives the "load" signal from the WM. It is responsible for the simple task of initializing the variables the program uses. The variable e_started is used as a flag and rot_count counts the number of 90-degree turns. The turns are tracked because enlightenment may tell us it is done before we complete a 360-degree turn. It was found during testing that this is indeed the case but a 2 or 3 second animation is a little boring so we use up a little more time.

program {
   name: "init";
   signal: "load";
   source: "";
   script {
      set_int(e_started,0);
      set_int(rot_count,0);
   }
}

The next program receives the "show" signal from the WM and it gives an opportunity to get things going. I called it "init_pause" because it forces a 1-second delay to the beginning of the animation. This was done because the first view of the animation was after it had already turned the first 90-degrees. Taking a linear 1-second transition to set the "logo_cube" part to its default state is just a stall tactic. The important part of this program is that after it uses up that second it shows the cube and starts the animation.

program {
   name: "init_pause";
   signal: "show";
   source: "";
   action: STATE_SET "default" 0.0;
   transition: LINEAR 1.0;
   target: "logo_cube";
   after: "cube_show";
   after: "cube_anim_start";
}

Because the "logo_cube" part is clipped to the "logo_cube_clip" part, to make the cube visible all that is needed is to make the clip part visible.

program {
   name: "cube_show";
   action: STATE_SET "visible" 0.0;
   transition: SINUSOIDAL 1.5;
   target: "logo_cube_clip";
}

The animation starts by sending the cube to the first state of its animation after waiting a half second. The pause is choreography. As the cube becomes visible it also starts rolling. Note the after directive to check for the WM start.

program {
   name: "cube_anim_start";
   action: STATE_SET "roll_start" 0.0;
   in: 0.50 0.0;
   target: "logo_cube";
   after: "cube_check_90";
}

The cube checks are done by macro because each 90-degree rotation gets the same treatment.

  • Get and increment the rotation count.
  • Get the e_started flag to see if we need to start cleaning up.
  • If we haven't done a full 360-degree rotation (4 turns), just roll to the next face.
  • If we have made more than 4 turns but the e_started flag is not set, roll to the next face.
  • If we are ready to clean up:
    • The first time in, run the program called "cleanup"
    • Use the e_start variable as a flag to make sure we make 2 more face turns.
#define CUBE_CHECK(NEXT)                                \
program {                                               \
   name: "cube_check_"NEXT;                             \
   script {                                             \
      new rots;                                         \
      new cleanup;                                      \
      rots = get_int(rot_count);                        \
      set_int(rot_count,rots+1);                        \
      cleanup = get_int(e_started);                     \
      if (rots > 4 && cleanup != 0) {                   \
         if (cleanup == 1) {                            \
            run_program(PROGRAM:"cleanup");             \
         }                                              \
         if (cleanup < 3) {                             \
            set_int(e_started,cleanup+1);               \
            run_program(PROGRAM:"roll_to_"NEXT);        \
         }                                              \
      }                                                 \
      else {                                            \
         run_program(PROGRAM:"roll_to_"NEXT);           \
      }                                                 \
   }                                                    \
}

Here is how the macro is used. I defined the transition so I could play with the transition type. The subtlety of a SINUSOIDAL transition is that it makes the pause in between turns more pronounced. A LINEAR transition will almost look like the cube is spinning. The pause seemed more interesting.

#define ROTATION_TRANSITION SINUSOIDAL 1.0

CUBE_CHECK("90");
program {
   name: "roll_to_90";
   action: STATE_SET "roll_to_90" 0.0;
   transition: ROTATION_TRANSITION;
   target: "logo_cube";
   after: "cube_check_180";
}

As mentioned earlier in this tutorial, the init theme protocol is very simple. The WM will send us a signal when it is done and will expect a response from us. To handle the "e,state,done" signal from enligtenment, the "done" program sets the "e_started" flag. Review the CUBE_CHECK macro to see how this is used.

program {
   name: "done";
   signal: "e,state,done";
   source: "e";
   script {
      set_int(e_started, 1); // flag completion
   }
}

The "cleanup" program is called from CUBE_CHECK and will cause a 2-second transition back to transparency. Once that is done, the "done_ok" program fulfills our protocol obligation by emitting the "e,state,done_ok" signal.

program {
   name: "cleanup";
   action: STATE_SET "default" 0.0;
   target: "logo_cube_clip";
   transition: LINEAR 2.0;
   after: "done_ok";
}
program {
   name: "done_ok";
   action: SIGNAL_EMIT "e,state,done_ok" "e";
}

Imported from https://trac.enlightenment.org/e/wiki/StartupThemeTutorial_SmokeInit
History:
1 jt_a 2011-01-12 11:27:30 import from old wiki
2 jt_a 2011-01-12 11:36:45 blockquote + notalink
3 jt_a 2011-01-12 11:39:23
4 jt_a 2011-01-12 12:12:31
5 jt_a 2011-01-12 12:43:29 image borders
6 jt_a 2011-01-12 13:10:02 pageoutline
7 jt_a 2011-01-12 13:43:46 fix image links
8 jt_a 2011-01-12 13:48:40 damn !

Last Author
beber
Last Edited
Sep 1 2013, 3:18 PM
Projects
None
Subscribers
None