Chapter contents:


libosso-flashlight/flashlight.c

/**
 * A simple LibOSSO non-GUI program.
 *
 * This maemo code example is licensed under a MIT-style license,
 * that can be found in the file called "License" in the same
 * directory as this file.
 * Copyright (c) 2007 Nokia Corporation. All rights reserved.
 *
 * Demonstrates how to utilize LibOSSO for trivial requests as well as
 * how to receive state change notifications from the system.
 *
 * The program will use LibOSSO to enable the backlight on the screen
 * of a device, and then periodically extend the backlight timeout
 * mechanism in the device, in order to keep the backlight on. As
 * such, it can be used a "flashlight"-replacement, but really is just
 * a demonstration program about using LibOSSO.
 *
 * The program will either refuse to run, or stop, when the device is
 * placed into "Flight"-mode. This is achieved by utilizing the
 * LibOSSO device state change callbacks.
 *
 * Other demonstrations include displaying Note dialogs to user from
 * a non-GUI program, utilizing the infoprint component and LibOSSO
 * error and state decoding.
 *
 * If you really want a "flashlight" application, you should consider
 * writing a small Hildon application that will display a fully white
 * GtkCanvas and switch it to run in full screen mode. You might also
 * want to maximize the backlight for the duration of the program. The
 * brightness is controlled via a GConf key (an integer from 1 to 9)
 * /system/osso/dsm/display/display_brightness and changing that key
 * will affect brightness automagically (assuming the backlight
 * dimming timer has not expired, but that is what this program stops
 * from happening).
 *
 * NOTE: Keeping the screen and backlight on without a good reason is
 *       not a good idea as this drains the battery on the device.
 *
 * NOTE: When running this on the SDK, you will need to use the
 *       run-standalone.sh script, otherwise LibOSSO initialization
 *       will fail. The RPC method calls will all succeed in the
 *       SDK (assuming you have AF running), but since there is no
 *       real "screen blank/backlight dimming" functionality in the
 *       SDK, you won't see any changes. Also, it is not possible to
 *       simulate state change events in the SDK (so you will have to
 *       terminate the program using Ctrl+c).
 */

#include <glib.h>
#include <libosso.h>
#include <stdlib.h> /* EXIT_SUCCESS */
#include <string.h> /* memset */

/* Application state.

   Contains the necessary state to control the application lifetime
   and use LibOSSO. */
typedef struct {
  /* The GMainLoop that will run our code. */
  GMainLoop* mainloop;
  /* LibOSSO context that is necessary to use LibOSSO functions. */
  osso_context_t* ossoContext;
  /* Flag to tell the timer that it should stop running. Also utilized
     to tell the main program that the device is already in Flight-
     mode and the program shouldn't continue startup. */
  gboolean running;
} ApplicationState;

/**
 * Utility to return a pointer to a statically allocated string giving
 * the textural representation of LibOSSO errors. Has no internal
 * state (safe to use from threads).
 *
 * LibOSSO does not come with a function for this, so we define one
 * ourselves.
 */
static const gchar* ossoErrorStr(osso_return_t errCode) {

  switch (errCode) {
    case OSSO_OK:
      return "No error (OSSO_OK)";
    case OSSO_ERROR:
      return "Some kind of error occurred (OSSO_ERROR)";
    case OSSO_INVALID:
      return "At least one parameter is invalid (OSSO_INVALID)";
    case OSSO_RPC_ERROR:
      return "Osso RPC method returned an error (OSSO_RPC_ERROR)";
    case OSSO_ERROR_NAME:
      return "(undocumented error) (OSSO_ERROR_NAME)";
    case OSSO_ERROR_NO_STATE:
      return "No state file found to read (OSSO_ERROR_NO_STATE)";
    case OSSO_ERROR_STATE_SIZE:
      return "Size of state file unexpected (OSSO_ERROR_STATE_SIZE)";
    default:
      return "Unknown/Undefined";
  }
}

/**
 * Utility to ask the device to pause the screen blanking timeout.
 * Does not return success/status.
 */
static void delayDisplayBlanking(ApplicationState* state) {

  osso_return_t result;

  g_assert(state != NULL);

  result = osso_display_blanking_pause(state->ossoContext);
  if (result != OSSO_OK) {
    g_printerr(PROGNAME ":delayDisplayBlanking. Failed (%s)\n",
               ossoErrorStr(result));
    /* But continue anyway. */
  } else {
    g_print(PROGNAME ":delayDisplayBlanking RPC succeeded\n");
  }
}

/**
 * Timer callback that will be called within 45 seconds after
 * installing the callback and will ask the platform to defer any
 * display blanking for another 60 seconds.
 *
 * This will also prevent the device from going into suspend
 * (according to LibOSSO documentation).
 *
 * It will continue doing this until the program is terminated.
 *
 * NOTE: Normally timers shouldn't be abused like this since they
 *       bring the CPU out from power saving state. Because the screen
 *       backlight dimming might be activated again after 60 seconds
 *       of receiving the "pause" message, we need to keep sending the
 *       "pause" messages more often than every 60 seconds. 45 seconds
 *       seems like a safe choice.
 */
static gboolean delayBlankingCallback(gpointer data) {
  ApplicationState* state = (ApplicationState*)data;

  g_print(PROGNAME ":delayBlankingCallback Starting\n");

  g_assert(state != NULL);
  /* If we're not supposed to be running anymore, return immediately
     and ask caller to remove the timeout. */
  if (state->running == FALSE) {
    g_print(PROGNAME ":delayBlankingCallback Removing\n");
    return FALSE;
  }

  /* Delay the blanking for a while still (60 seconds). */
  delayDisplayBlanking(state);

  g_print(PROGNAME ":delayBlankingCallback Done\n");

  /* We want the same callback to be invoked from the timer
     launch again, so we return TRUE. */
  return TRUE;
}

/* Small macro to return "YES" or "no" based on given parameter.
   Used in printDeviceState below. YES is in capital letters in order
   for it to "stand out" in the program output (since it's much
   rarer). */
#define BOOLSTR(p) ((p)?"YES":"no")

/**
 * Utility to decode the hwstate structure and print it out in human
 * readable format. Mainly useful for debugging on the device.
 *
 * The mode constants unfortunately are not documented in LibOSSO.
 */
static void printDeviceState(osso_hw_state_t* hwState) {

  gchar* modeStr = "Unknown";

  g_assert(hwState != NULL);

  switch(hwState->sig_device_mode_ind) {
    case OSSO_DEVMODE_NORMAL:
      /* Non-flight-mode. */
      modeStr = "Normal";
      break;
    case OSSO_DEVMODE_FLIGHT:
      /* Power button -> "Offline mode". */
      modeStr = "Flight";
      break;
    case OSSO_DEVMODE_OFFLINE:
      /* Unknown. Even if all connections are severed, this mode will
         not be triggered. */
      modeStr = "Offline";
      break;
    case OSSO_DEVMODE_INVALID:
      /* Unknown. */
      modeStr = "Invalid(?)";
      break;
    default:
      /* Leave at "Unknown". */
      break;
  }
  g_print(
    "Mode: %s, Shutdown: %s, Save: %s, MemLow: %s, RedAct: %s\n",
    modeStr,
    /* Set to TRUE if the device is shutting down. */
    BOOLSTR(hwState->shutdown_ind),
    /* Set to TRUE if our program has registered for autosave and
       now is the moment to save user data. */
    BOOLSTR(hwState->save_unsaved_data_ind),
    /* Set to TRUE when device is running low on memory. If possible,
       memory should be freed by our program. */
    BOOLSTR(hwState->memory_low_ind),
    /* Set to TRUE when system wants us to be less active. */
    BOOLSTR(hwState->system_inactivity_ind));
}

/**
 * This callback will be called by LibOSSO whenever the device state
 * changes. It will also be called right after registration to inform
 * the current device state.
 *
 * The device state structure contains flags telling about conditions
 * that might affect applications, as well as the mode of the device
 * (for example telling whether the device is in "in-flight"-mode).
 */
static void deviceStateChanged(osso_hw_state_t* hwState,
                               gpointer data) {
  ApplicationState* state = (ApplicationState*)data;

  g_print(PROGNAME ":deviceStateChanged Starting\n");

  printDeviceState(hwState);

  /* If device is in/going into "flight-mode" (called "Offline" on
     some devices), we stop our operation automatically. Obviously
     this makes flashlight useless (as an application) if someone gets
     stuck in a dark cargo bay of a plane with snakes.. But we still
     need a way to shut down the application and react to device
     changes, and this is the easiest state to test with.

     Note that since offline mode will terminate network connections,
     you will need to test this on the device itself, not over ssh. */
  if (hwState->sig_device_mode_ind == OSSO_DEVMODE_FLIGHT) {
    g_print(PROGNAME ":deviceStateChanged In/going into offline.\n");

    /* Terminate the mainloop.
       NOTE: Since this callback is executed immediately on
             registration, the mainloop object is not yet "running",
             hence calling quit on it will be ineffective! _quit only
             works when the mainloop is running. */
    g_main_loop_quit(state->mainloop);
    /* We also set the running to correct state to fix the above
       problem. */
    state->running = FALSE;
  }
}

/**
 * Utility to display an "exiting" message to user using infoprint.
 */
static void displayExitMessage(ApplicationState* state,
                               const gchar* msg) {

  osso_return_t result;

  g_assert(state != NULL);

  g_print(PROGNAME ":displayExitMessage Displaying exit message\n");
  result = osso_system_note_infoprint(state->ossoContext, msg, NULL);
  if (result != OSSO_OK) {
    /* This is rather harsh, since we terminate the whole program if
       the infoprint RPC fails. It is used to display messages at
       program exit anyway, so this isn't a critical issue. */
    g_error(PROGNAME ": Error doing infoprint (%s)\n",
            ossoErrorStr(result));
  }
}

/**
 * Utility to setup the application state.
 *
 * 1) Initialize LibOSSO (this will connect to D-Bus)
 * 2) Create a mainloop object
 * 3) Register the device state change callback
 *    The callback will be called once immediately on registration.
 *    The callback will reset the state->running to FALSE when the
 *    program needs to terminate so we'll know whether the program
 *    should run at all. If not, display an error dialog.
 *    (This is the case if the device will be in "Flight"-mode when
 *    the program starts.)
 * 4) Register the timer callback (which will keep the screen from
 *    blanking).
 * 5) Un-blank the screen.
 * 6) Display a dialog to the user (on the background) warning about
 *    battery drain.
 * 7) Send the first "delay backlight dimming" command.
 *
 * Returns TRUE when everything went ok, FALSE when caller should call
 * releaseAppState and terminate. The code below will print out the
 * errors if necessary.
 */
static gboolean setupAppState(ApplicationState* state) {

  osso_return_t result;

  g_assert(state != NULL);

  g_print(PROGNAME ":setupAppState starting\n");

  /* Zero out the state. Has the benefit of setting all pointers to
     NULLs and all gbooleans to FALSE. This is useful when we'll need
     to determine what to release later. */
  memset(state, 0, sizeof(ApplicationState));

  g_print(PROGNAME ":setupAppState Initializing LibOSSO\n");
  /* Parameters for osso_initialize:
     gchar* : Application "name". This is not the name that will be
              visible to user, but rather the name that other programs
              can use to communicate with this program (eventually
              over D-Bus). Note that if your name does _not_ include
              dot, 'com.nokia.' will be prepended to the name
              automatically.
     gchar* : Application version.
     gboolean : Unused / no effect.
     GMainContext* : Context under which LibOSSO will integrate into.
              Leave to NULL in order to use the default context (which
              will be true for programs that use one GMainLoop, from
              the default context). */
  state->ossoContext = osso_initialize(ProgName, "1.0", FALSE, NULL);
  if (state->ossoContext == NULL) {
    g_printerr(PROGNAME ": Failed to initialize LibOSSO\n");
    return FALSE;
  }

  g_print(PROGNAME ":setupAppState Creating a GMainLoop object\n");
  /* Create a new GMainLoop object, with default context (NULL) and
     initial "running"-state set to FALSE. */
  state->mainloop = g_main_loop_new(NULL, FALSE);
  if (state->mainloop == NULL) {
    g_printerr(PROGNAME ": Failed to create a GMainLoop\n");
    return FALSE;
  }

  g_print(PROGNAME
          ":setupAddState Adding hw-state change callback.\n");
  /* The callback will be called immediately with the state, so we
     need to know whether we're in offline mode to start with. If so,
     the callback will set the running-member to FALSE (and we'll
     check it below). */
  state->running = TRUE;
  /* In order to receive information about device state and changes
     in it, we register our callback here.

     Parameters for the osso_hw_set_event_cb():
       osso_context_t* :  LibOSSO context object to use.
       osso_hw_state_t* : Pointer to a device state type that we're
                          interested in. NULL for "all states".
       osso_hw_cb_f* :    Function to call on state changes.
       gpointer :         User-data passed to callback. */
  result = osso_hw_set_event_cb(state->ossoContext,
                                NULL, /* We're interested in all. */
                                deviceStateChanged,
                                state);
  if (result != OSSO_OK) {
    g_printerr(PROGNAME
               ":setupAppState Failed to get state change CB\n");
    /* Since we cannot reliably know when to terminate later on
       without state information, we will refuse to run because of the
       error. */
    return FALSE;
  }
  /* We're in "Flight" mode? */
  if (state->running == FALSE) {
    g_print(PROGNAME ":setupAppState In offline, not continuing.\n");
    displayExitMessage(state, ProgName " not available in Offline mode");
    return FALSE;
  }

  g_print(PROGNAME ":setupAppState Adding blanking delay timer.\n");
  if (g_timeout_add(45000,
                    (GSourceFunc)delayBlankingCallback,
                    state) == 0) {
    /* If g_timeout_add returns 0, it signifies an invalid event
       source id. This means that adding the timer failed. */
    g_printerr(PROGNAME ": Failed to create a new timer callback\n");
    return FALSE;
  }

  /* Un-blank the display (will always succeed in the SDK). */
  g_print(PROGNAME ":setupAppState Unblanking the display\n");
  result = osso_display_state_on(state->ossoContext);
  if (result != OSSO_OK) {
    g_printerr(PROGNAME ": Failed in osso_display_state_on (%s)\n",
               ossoErrorStr(result));
    /* If the RPC call fails, odds are that nothing else will work
       either, so we decide to quit instead. */
    return FALSE;
  }

  /* Display a "Note"-dialog with a WARNING icon.
     The Dialog is MODAL, so user cannot do anything with the stylus
     until the Ok is selected, or the Back-key is pressed. */

  /* In most cases, you should avoid displaying modal dialogs,
     especially when running without a normal GUI (like this program
     is). Instead, you might want to use osso_system_note_infoprint()
     instead.

     In our case, we really really want the user to realize that
     the program will cause extra battery usage. Hence the warning,
     and a modal note dialog. If you run this on a device, you might
     also hear a special alerting sound. */

  /* Other icons available:
      OSSO_GN_NOTICE: For general notices.
     OSSO_GN_WARNING: For warning messages.
       OSSO_GN_ERROR: For error messages.
        OSSO_GN_WAIT: For messages about "delaying" for something (an
                      hourglass icon is displayed).
                   5: Animated progress indicator. */

  /* The text must be UTF-8 formatted, and may contain newlines, but
     other markup is not supported (and you should limit the amount of
     text that you will put into the dialog).

     If we'd be interested in the return data from the RPC method,
     we could pass a pointer to a osso_rpc_t where the result would
     be then stored.

     Unfortunately for us, the Note dialog returns "void" and returns
     it immediately. This means that the dialog will be waiting for
     the user to press "Ok", but we won't get notified about that.
     In fact, we'll continue running immediately after asking for the
     dialog. This is common to most of the convenience wrappers in
     LibOSSO.

     So, we pass NULL to tell LibOSSO not to bother with the return
     value. */

  g_print(PROGNAME ":setupAppState Displaying Note dialog\n");
  result = osso_system_note_dialog(state->ossoContext,
                                   /* UTF-8 text into the dialog */
                                   "Started " ProgName ".\n"
                       "Please remember to stop it when you're done, "
                                "in order to conserve battery power.",
                                   /* Icon to use */
                                   OSSO_GN_WARNING,
                                   /* We're not interested in the RPC
                                      return value. */
                                   NULL);
  if (result != OSSO_OK) {
    g_error(PROGNAME ": Error displaying Note dialog (%s)\n",
            ossoErrorStr(result));
  }
  g_print(PROGNAME ":setupAppState Requested for the dialog\n");

  /* Then delay the blanking timeout so that our timer callback has a
     chance to run before that. */
  delayDisplayBlanking(state);

  g_print(PROGNAME ":setupAppState Completed\n");

  /* State set up. */
  return TRUE;
}

/**
 * Release all resources allocated by setupAppState in reverse order.
 */
static void releaseAppState(ApplicationState* state) {

  g_print(PROGNAME ":releaseAppState starting\n");

  g_assert(state != NULL);

  /* First set the running state to FALSE so that if the timer will
     (for some reason) be launched, it will remove itself from the
     timer call list. This shouldn't be possible since we are running
     only with one thread. */
  state->running = FALSE;

  /* Normally we would also release the timer, but since the only way
     to do that is from the timer callback itself, there's not much we
     can do about it here. */

  /* Remove the device state change callback. It is possible that we
     run this even if the callback was never installed, but it is not
     harmful. */
  if (state->ossoContext != NULL) {
    osso_hw_unset_event_cb(state->ossoContext, NULL);
  }

  /* Release the mainloop object. */
  if (state->mainloop != NULL) {
    g_print(PROGNAME ":releaseAppState Releasing mainloop object.\n");
    g_main_loop_unref(state->mainloop);
    state->mainloop = NULL;
  }

  /* Lastly, free up the LibOSSO context. */
  if (state->ossoContext != NULL) {
    g_print(PROGNAME ":releaseAppState De-init LibOSSO.\n");
    osso_deinitialize(state->ossoContext);
    state->ossoContext = NULL;
  }
  /* All resources released. */
}

/**
 * Main program:
 *
 * 1) Setup application state
 * 2) Start mainloop
 * 3) Release application state & terminate
 */
int main(int argc, char** argv) {

  /* We'll keep one application state in our program and allocate
     space for it from the stack. */
  ApplicationState state = {};

  g_print(PROGNAME ":main Starting\n");
  /* Attempt to setup the application state and if something goes
     wrong, do cleanup here and exit. */
  if (setupAppState(&state) == FALSE) {
    g_print(PROGNAME ":main Setup failed, doing cleanup\n");
    releaseAppState(&state);
    g_print(PROGNAME ":main Terminating with failure\n");
    return EXIT_FAILURE;
  }

  g_print(PROGNAME ":main Starting mainloop processing\n");
  g_main_loop_run(state.mainloop);
  g_print(PROGNAME ":main Out of main loop (shutting down)\n");
  /* We come here when the application state has the running flag set
     to FALSE and the device state changed callback has decided to
     terminate the program. Display a message to the user about
     termination next. */
  displayExitMessage(&state, ProgName " exiting");

  /* Release the state and exit with success. */
  releaseAppState(&state);

  g_print(PROGNAME ":main Quitting\n");
  return EXIT_SUCCESS;
}

[ libosso-flashlight/flashlight.c ]


libosso-flashlight/Makefile

#
# Makefile for the simple LibOSSO flashlight program
#

# define a list of pkg-config packages we want to use
pkg_packages := glib-2.0 libosso

PKG_CFLAGS  := $(shell pkg-config --cflags $(pkg_packages))
PKG_LDFLAGS := $(shell pkg-config --libs $(pkg_packages))
# Add warnings and debugging info
ADD_CFLAGS := -g -Wall

# Combine
CFLAGS  := $(PKG_CFLAGS) $(ADD_CFLAGS) $(CFLAGS)
LDFLAGS := $(PKG_LDFLAGS) $(LDFLAGS)

targets = flashlight

all: $(targets)

# For simple one-file programs, combining both compiling and linking
# makes sense.
#
# PROGNAME = name of program to prefix to all printout in program
# ProgName = name of program to use for LibOSSO registration and user
#            visible dialogs/infoprints.
flashlight: flashlight.c
	$(CC) $(CFLAGS) -DPROGNAME=\"$@\" -DProgName=\"FlashLight\" \
	  $< -o $@ $(LDFLAGS)

.PHONY: clean all
clean:
	$(RM) $(targets)

[ libosso-flashlight/Makefile ]


Copyright © 2007-2008 Nokia Corporation. All rights reserved.