OSApp
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 (...)) |
void | osmain (...) |
void | osmain_sync (...) |
void | osapp_finish (void) |
uint32_t | osapp_argc (void) |
uint32_t | osapp_argv (...) |
void | osapp_task (...) |
void | osapp_menubar (...) |
void | osapp_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.
- Use osmain to start a desktop application.
- Use osapp_finish to end a desktop application.
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:
- 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 anexit()
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:
- 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.
- 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 ofosmain()
) 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.
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.
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.
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.
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
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 |
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. |