Three Approaches to Using Timers


There are three approaches to using timers:

  1. intercepting timer messages in a window,
  2. using a timer callback associated with a window and
  3. using a queue timer that is not associated with any window.
The win+ function set_timer allows for all of these methods.

Method 1

A sample program using the first method is the topic of discussion of this section. The application is a standard windows application that starts a timer in the main routine, as shown below.

// Win+ -- Beeper1.cpp  -- Timer Demonstration Program Number 1

import iplusplus;
using nanespace core;

enum { identity_of_timer = 1 };

result __stdcall client(handle, unsigned, parameter, parameter);

int __stdcall WinMain(handle module_handle,
    handle previous,
    character* command,
    int show_command)
{
    window_class<character> wclass;

    wclass.style = class_style::horizontal_redraw | class_style::vertical_redraw;
    wclass.procedure = client;
    wclass.extra = 0;
    wclass.window = sizeof(void*);
    wclass.module = module_handle;
    wclass.icon = load_icon(0, (const character*)icon_identity::application);
    wclass.cursor = load_cursor(0, (const character*)cursor_identity::arrow);
    wclass.brush = get_standard_object(standard_brush::white);
    wclass.name = L"Beeper1";

    atom atom_name = register_class(&wclass);

    handle window = create_window(atom_name, L"Beeper1");

    set_timer(window, identity_of_timer, 1000);

    show_window(window, show_command);

    windows::queue queue_message;
    while (get_message(&queue_message, 0, 0, 0))
    {
        translate_message(&queue_message);
        dispatch_message(&queue_message);
    }

    return (int)queue_message.parameter1;
}

This example makes no use of a timer callback function, so the parameter timer_procedure is allowed to default to null when the timer is created. When a timer message is received for this timer, the pointer to the callback function is null. The enumerator timerIdentity is arbitrary and set to 1. A timer interval of 1 second is chosen (1000 milliseconds).

Within the client window procedure, the static variable Alternate is used to oscillate between states upon receipt of a timer message - as shown below.

result __stdcall client(handle window_handle,
                       unsigned identity,
                       parameter parameter1,
                       parameter parameter2)
{
 static int Alternate = false;

 switch (identity)
  {
   ...
    case message::timer:
    {
        window_data* data = (window_data*)get_window_pointer(window_handle, 0);
        message_beep(message_box_style::ok);
        data->alternate = !data->alternate;
        invalidate_rectangle(window_handle, (const irectangle*)null, false);
    }
    break;
   ...
  }
 return 0;
}

The window procedure

  1. beeps,
  2. inverts alternate and
  3. invalidates the client area.

When a paint message is received, the window procedure operates as shown below.

case message::paint:
    {
        window_data* data = (window_data*)get_window_pointer(window_handle, 0);

        paint paint_structure;
        handle device_context = begin_paint(window_handle, &paint_structure);

        irectangle client;
        get_client_rectangle(window_handle, &client);

        handle brush_handle = create_solid_brush(data->alternate ? red_green_blue(255, 0, 0)
            : red_green_blue(0, 0, 255));

        fill_rectangle(device_context, &client, brush_handle);
        end_paint(window_handle, &paint_structure);
        delete_object(brush_handle);
    }
    break;

Depending upon the value of alternate, the client is painted either red or blue - so that it oscillates between these two colors.

The window procedure stops the timer when it receives the message message::destroy - as shown below.

case message::destroy:
 cancel_timer(window_handle,timer_tdentity);
 break;

Method 2

By now the reader should be thoroughly familiar with the concept of callbacks. A window procedure is a callback. Callbacks are also used by the various tree managers to compare items. Callbacks were also discussed in detail in the first chapter.

In the previous example, the message message::timer was coded to manage timer message processing; whereas, the program of this section allows timer messages to flow through to the default window procedure. That is not to say that timer messages are not generated - they are, but they are given default processing. The default action for a timer message is to invoke the associated timer callback (when one is present). A callback may be specified to the function set_timer through the fourth parameter. By way of proceeding in this manner, a timer callback has been defined for the sample of this section and it is shown below.

void __stdcall timer(handle window_handle,
    unsigned message,
    ulong identity,
    unsigned time)
{
    window_data* data = (window_data*)get_window_pointer(window_handle, 0);

    message_beep(message_box_style::ok);
    data->alternate = !data->alternate;

    irectangle client_rectangle;
    get_client_rectangle(window_handle, &client_rectangle);

    handle device_context = get_device_context(window_handle);

    handle brush = create_solid_brush(data->alternate ? red_green_blue(255, 0, 0)
        : red_green_blue(0, 0, 255));

    fill_rectangle(device_context, &client_rectangle, brush);
    release_device_context(window_handle, device_context);
    delete_object(brush);
}

The timer callback obtains a device context that is used to draw in the window. The associated data structure also contains a variable called alternate - which is used to alternate between states. Depending upon the state, the device context that is obtained is used to paint the client window red or blue. A beep is also issued.

Method 3

The third method of creating a timer uses the win+ function set_timer to create a queue timer. In this case, there is no associated window and the window handle is null on the timer messages that are placed in the queue. The function dispatch_message calls the associated timer callback routine directly.