Cross-platform C SDK logo

Cross-platform C SDK

OSApp

❮ Back
Next ❯
This page has been automatically translated using the Google Translate API services. We are working on improving texts. Thank you for your understanding and patience.

Functions

type*(*FPtr_app_create (void))
void(*FPtr_app_update (...))
uint32_t(*FPtr_task_main (...))
void(*FPtr_task_update (...))
void(*FPtr_task_end (...))
voidosmain (...)
voidosmain_sync (...)
voidosapp_finish (void)
uint32_tosapp_argc (void)
uint32_tosapp_argv (...)
voidosapp_task (...)
voidosapp_menubar (...)
voidosapp_open_url (...)

The OSApp library starts and manages the message cycle of a desktop application (Figure 1). Although the Gui library could be integrated into existing applications through a plugin, if we want to create an application from scratch, we will need to manage the events that the operating system sends to the program.


1. main() and osmain()

The classic main function is the starting point of any C/C++ command line program (Figure 2). Its operation does not involve any difficulty and can be summarized in:

Scheme showing the main phases in the execution of a program in C/C++.
Figure 2: Running a console C application.
  • The operating system loads the program into memory and calls the function main() to start its execution.
  • The sentences are executed sequentially and in the order in which they are written. This order can be altered by means of control sentences (for, if, switch, etc.) or function calls.
  • If input/output is necessary, the program will wait for the communication to end and continue with the execution.
  • When the end of the function is reached main () or an exit() sentence is executed, the program will end and the operating system will download it from memory.

However, in desktop applications (event driven), the execution cycle is a bit more complicated. In essence, the program is continuously executing a loop waiting for the user to perform some action (Figure 3) (Listing 1). In Hello World! you have a simple example:

Scheme showing the main phases in the execution of a desktop program in C/C++.
Figure 3: Running a desktop C application.
  • The operating system loads the program into memory and calls the main() function. Now it is encapsulated inside the osmain macro which initiates certain structures necessary for event capture and management.
  • At some point in this initial process, the application constructor will be called (the first parameter of osmain()) that the main object should create. Since the program is continuously returning control to the operating system, the state of the data and windows will be maintained in this object.
  • Once initialized, the application will enter a loop known as a message cycle (Figure 4), while waiting for the user to perform some action on the program interface.
  • Scheme showing how the message cycle is implemented in each operating system.
    Figure 4: Message cycle implementation.
  • When this occurs, the operating system will capture the event and send it to the application.
  • If the application has defined a handle for that event, it will be invoked and the response code will be executed. An application can receive hundreds of messages but will only respond to those it deems necessary, ignoring the rest.
  • There is a special exit event that is generated by calling osapp_finish. When this happens, osmain() start freeing up resources and preparing a clean exit. At some point the destructor of the application will be called (second parameter of osmain()) to do its part of the job, closing possible open files and destroying the main object.
  • The operating system unload the application from memory.
  • The pink blocks are platform dependent and are implemented within NAppGUI.
  • The orange blocks are multiplatform (fully portable) and are implemented within the application.
  • Listing 1: Elementary skeleton of a desktop application.
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
    typedef struct _app_t App;
    struct _app_t
    {
        // Program data
        Window *window;
    };
    
    static App* i_create(void)
    {
        App *app = heap_new(App);
        // Init program data, GUI and Event handlers
        app->window = ...
        return app;
    }
    
    static void i_destroy(App *app)
    {
        // Destroy program data
        window_destroy(&(*app)->window);
        heap_delete(app, App);
    }
    
    osmain(i_create, i_destroy, "", App);
    

2. Synchronous applications

Certain types of applications including video games, media players or simulators, need to be updated at regular intervals, whether or not the user intervenes (Figure 5) (Listing 2). For these cases we will need a variant of osmain, which accepts an update function and a time interval. In Bricks you have an example.

  • Use osmain_sync to start a synchronous application.
  • Event graphic in synchronous applications.
    Figure 5: Events in synchronous applications.
    Listing 2: Elemental skeleton of a synchronous application.
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    
    typedef struct _app_t App;
    struct _app_t
    {
        // Program data
        Window *window;
    };
    
    static App* i_create(void)
    {
        App *app = heap_new(App);
        // Init program data, GUI and Event handlers
        app->window = ...
        return app;
    }
    
    static void i_update(App *app, const real64_t prtime, const real64_t ctime)
    {
        // Update program state every 40ms
    }
    
    static void i_destroy(App *app)
    {
        // Destroy program data
        window_destroy(&(*app)->window);
        heap_delete(app, App);
    }
    
    osmain_sync(0.04, i_create, i_destroy, i_update, "", App);
    

3. Multi-threaded tasks

Both synchronous and asynchronous applications execute the message cycle on a single CPU thread. This means that if, in response to an event, a relatively slow task must be executed, the application will be "frozen" until it is finished (Figure 6)(a). This will produce an unwanted effect since the program will not respond for a few seconds, giving the impression that it has been blocked. The solution is to launch a task in parallel (Figure 6)(b) (Listing 3), quickly release the thread that manages the GUI. In Multi-threaded login you have an example of the use of tasks.

  • Use osapp_task to launch a new task in a parallel thread.
  • Graph comparing code in one thread or several threads.
    Figure 6: (a) Interface lock due to a slow function. (b) Slow function in a parallel thread.
    Listing 3: New task in a parallel thread.
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    // Runs in new thread
    static uint32_t i_task_main(TaskData *data)
    {
        // Do the task work here!
    }
    
    // Runs in GUI thread
    static void i_task_update(TaskData *data)
    {
        // Update the GUI here!
    }
    
    // Runs in GUI thread
    static void i_task_end(TaskData *data, const uint32_t rvalue)
    {
        // Finish task code here!
    }
    
    osapp_task(tdata, .04, i_task_main, i_task_update, i_task_end, TaskData);
    

The new thread will begin its execution in task_main. This function should not access the interface elements, just perform calculations or input/output tasks. If it is necessary to update the GUI for the duration of the task (increasing a progress bar or similar), it must be done in task_update, indicating in updtime the update interval. The new thread will end when it returns from task_main, moment to be called task_end in the main thread. Obviously, if both threads access shared variables, they must be protected by a Mutex.

❮ Back
Next ❯

FPtr_app_create

An application constructor prototype.

type*
(*FPtr_app_create)(void);

Return

Application object.


FPtr_app_update

Function prototype for update a synchronous application.

void
(*FPtr_app_update)(type *app,
                   const real64_t prtime,
                   const real64_t ctime);
app

Application object.

prtime

Previous update time.

ctime

Current time.


FPtr_task_main

Function prototype for start a task.

uint32_t
(*FPtr_task_main)(type *data);
data

Initial task data.

Return

Task return value.


FPtr_task_update

Function prototype of a task update.

void
(*FPtr_task_update)(type *data);
data

Task data.


FPtr_task_end

Function prototype of a task completion.

void
(*FPtr_task_end)(type *data,
                 const uint32_t rvalue);
data

Task Data.

rvalue

Task return value.


osmain ()

Start a desktop application.

void
osmain(FPtr_app_create func_create,
       FPtr_destroy func_destroy,
       const char_t *options,
       type);
func_create

Application object constructor.

func_destroy

Application object destructor.

options

Options string.

type

Type of application object.

Remarks

In Hello World! you have a simple example of desktop application.


osmain_sync ()

Start a synchronous desktop application.

void
osmain_sync(const real64_t lframe,
            FPtr_app_create func_create,
            FPtr_destroy func_destroy,
            FPtr_app_update func_update,
            const char_t *options,
            type);
lframe

Time in seconds of the update interval (0.04 = 25 fps).

func_create

Application object constructor.

func_destroy

Application object destructor.

func_update

Function to be called in each update interval.

options

Options string.

type

Type of application object.

Remarks

See Synchronous applications.


osapp_finish ()

End a desktop application, destroying the message cycle and the application object.

void
osapp_finish(void);

osapp_argc ()

Gets the number of parameters of the command that the application ran. It is the argc value of the main() function.

uint32_t
osapp_argc(void);

Return

The number of parameters.


osapp_argv ()

Gets one of the arguments of the command that the application ran. It is the argv value of the main() function.

uint32_t
osapp_argv(const uint32_t index,
           char_t *argv,
           const uint32_t size);
1
2
3
4
5
6
7
8
uint32_t i, n = osapp_argc();
bstd_printf("Number of args: %d\n", n);
for (i = 0; i < n; ++i)
{
    char_t argv[128];
    uint32_t nb = osapp_argv(i, argv, sizeof(argv));
    bstd_printf("argv[%d]: %s (%d bytes)\n", i, argv, nb);
}
index

The index of the parameter.

argv

Buffer where the parameter will be written.

size

Number of maximum bytes in argv.

Return

The number of bytes written to argv, including the null character '\0'.


osapp_task ()

Launch a task in parallel, avoiding the thread lock that controls the user interface.

void
osapp_task(type *data,
           const real32_t updtime,
           FPtr_task_main func_main,
           FPtr_task_update func_update,
           FPtr_task_end func_end,
           type);
data

Initial task data.

updtime

Update interval time, if required.

func_main

Task start function.

func_update

Task update function.

func_end

Function to be called when finishing the task.

type

Type of initial task data.

Remarks

See Multi-threaded tasks.


osapp_menubar ()

Set the general menu bar of the application.

void
osapp_menubar(Menu *menu,
              Window *window);
menu

The menu.

window

The window that will host the menu.

Remarks

In macOS the application menu is not linked to any window.


osapp_open_url ()

Open an Internet address using the default operating system browser.

void
osapp_open_url(const char_t *url);
url

URL address.

❮ Back
Next ❯