Cross-platform C SDK logo

Cross-platform C SDK

Hello, World!

❮ 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.

Once upon a time, there was a company called Taligent. Taligent was created by IBM and Apple to develop a set of tools and libraries like Cocoa. About the time Taligent reached the peak of its mindshare, Aaron met one of its engineers at a trade show and asked him to create a simple application: A window appears with a button. When the button is clicked, the words "Hello, World!" appear in a text field. The engineer created a project and started subclassing madly, subclassing the window and the button and the event handler. Then he started generating code: dozens of lines to get the button and the text field onto the window. After 45 minutes, he was still trying to get the app to work. A couple of years later, Taligent quietly closed its doors forever. Hillegass, Preble & Chandler - Cocoa Programming for OSX.


We can say little about the meaning of the Hello World! every time we face a new technology or programming methodology. Therefore, let's get to the point.

Hello World! running on Windows 10. Hello World! running on WindowsXP.
Figure 1: Windows 10 and WindowsXP.
Hello World! running on MacOS Mojave. Hello World! running on MacOS Leopard.
Figure 2: macOS 10.14 Mojave and MacOSX 10.6 Snow Leopard.
Hello World! running on Ubuntu. Hello World! running on Raspbian.
Figure 3: GTK+3 Ambiance (Ubuntu) and Adwaita Dark (Raspbian).

1. The complete program

Listing 1: demo/hello/main.c
 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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
/* NAppGUI Hello World */

#include "nappgui.h"

typedef struct _app_t App;

struct _app_t
{
    Window *window;
    TextView *text;
    uint32_t clicks;
};

/*---------------------------------------------------------------------------*/

static void i_OnButton(App *app, Event *e)
{
    String *msg = str_printf("Button click (%d)\n", app->clicks);
    textview_writef(app->text, tc(msg));
    str_destroy(&msg);
    app->clicks += 1;
    unref(e);
}

/*---------------------------------------------------------------------------*/

static Panel *i_panel(App *app)
{
    Panel *panel = panel_create();
    Layout *layout = layout_create(1, 3);
    Label *label = label_create();
    Button *button = button_push();
    TextView *text = textview_create();
    app->text = text;
    label_text(label, "Hello!, I'm a label");
    button_text(button, "Click Me!");
    button_OnClick(button, listener(app, i_OnButton, App));
    layout_label(layout, label, 0, 0);
    layout_button(layout, button, 0, 1);
    layout_textview(layout, text, 0, 2);
    layout_hsize(layout, 0, 250);
    layout_vsize(layout, 2, 100);
    layout_margin(layout, 5);
    layout_vmargin(layout, 0, 5);
    layout_vmargin(layout, 1, 5);
    panel_layout(panel, layout);
    return panel;
}

/*---------------------------------------------------------------------------*/

static void i_OnClose(App *app, Event *e)
{
    osapp_finish();
    unref(app);
    unref(e);
}

/*---------------------------------------------------------------------------*/

static App *i_create(void)
{
    App *app = heap_new0(App);
    Panel *panel = i_panel(app);
    app->window = window_create(ekWNSTD, &panel);
    window_title(app->window, "Hello, World!");
    window_origin(app->window, v2df(500, 200));
    window_OnClose(app->window, listener(app, i_OnClose, App));
    window_show(app->window);
    return app;
}

/*---------------------------------------------------------------------------*/

static void i_destroy(App **app)
{
    window_destroy(&(*app)->window);
    heap_delete(app, App);
}

/*---------------------------------------------------------------------------*/

#include "osmain.h"
osmain(i_create, i_destroy, "", App)

2. Skeleton

A NAppGUI application starts in osmain, a multiplatform macro that unifies the initiation of a desktop program under different systems. It is defined in #include "osmain.h" and will receive four parameters: constructor, destructor, arguments (char_t) and the object type. In this way, any basic skeleton looks like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#include "nappgui.h"

typedef struct _app_t App;
struct _app_t
{
    Window *window;
};

static App *i_create(void)
{
    App *app = heap_new0(App);
    return app;
}

static void i_destroy(App **app)
{
    heap_delete(app, App);
}

#include "osmain.h"
osmain(i_create, i_destroy, "", App)

Directive #include "nappgui.h", includes a large part of NAppGUI with a single instruction. If you prefer, you can choose to include the headers separately as necessary. In this case, we should replace a single #include for eleven. The Library Reference, indicates which header to include according to the function module that we are going to use.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#include "gui.h"
#include "button.h"
#include "heap.h"
#include "label.h"
#include "layout.h"
#include "listener.h"
#include "panel.h"
#include "strings.h"
#include "v2d.h"
#include "vtext.h"
#include "window.h"

3. Constructor

The first osmain parameter is the application's constructor. As soon as the program starts, certain internal structures must be initialized, as well as start the message loop inherent in all desktop applications. When everything is ready, the constructor will be called to create the application object. This object can be of any type and does not need to be derived from any class Application or similar, we are in C ;-). Given the simplicity of this example, the application object only contains one window.

1
2
3
4
5
6
7
static App *i_create(void)
{
    App *app = heap_new0(App);
    Panel *panel = i_panel(app);
    app->window = window_create(ekWINDOW_STD, &panel);
    return app;
}

4. The Main Panel

To create the main window, we need the main panel, a container that integrates all the interface controls shown in the window. The space inside the panel is organized in an invisible grid called Layout. Each panel can have several layouts and alternate between them, but at least one is necessary. Within its cells we will locate the different interface controls.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
static Panel *i_panel(App *app)
{
    Panel *panel = panel_create();
    Layout *layout = layout_create(1, 3);
    Label *label = label_create();
    Button *button = button_push();
    Text *text = text_create();
    label_text(label, "Hello!, I'm a label");
    button_text(button, "Click Me!");
    layout_label(layout, label, 0, 0);
    layout_button(layout, button, 0, 1);
    layout_text(layout, text, 0, 2);
    panel_layout(panel, layout);
    return panel;
}

5. The destructor

When the application ends, osmain will call the destructor (second parameter of the macro) to release the application object and everything that depends on it, in order to make a clean exit of the program. We will put a lot of emphasis on this, since the failure to properly free all memory will be considered a serious coding error.

1
2
3
4
5
static void i_destroy(App **app)
{
    window_destroy(&(*app)->window);
    heap_delete(app, App);
}

6. Launch the window

By default, NAppGUI creates all windows in hidden mode, so it is necessary to show them explicitly. We establish a title, an initial position and launch it with window_show. We observe that in this first version our window is not very aesthetic (Figure 4). In a moment we will format it.

1
2
3
4
5
6
7
8
static App *i_create(void)
{
   ...
   window_title(app->main_window, "Hello World!");
   window_origin(app->main_window, v2df(500, 200));
   window_show(app->main_window);
   ...
}
Window with highly compressed controls since no format has yet been applied.
Figure 4: First version of Hello, World! (without format).

7. Layout format

To improve the appearance of our window, let's give the design a bit of format. Specifically, we are going to establish a column width and a height for the third row (text control). Then we will leave a margin on the edge and a separation between rows. (Figure 5).

1
2
3
4
5
layout_hsize(layout, 0, 200);
layout_vsize(layout, 2, 100);
layout_margin(layout, 5);
layout_vmargin(layout, 0, 5);
layout_vmargin(layout, 1, 5);
Properly formatted interface window.
Figure 5: Hello World! after Layout formatting.

8. Closing the program

When we press the button to close the main window, the program does not finish its execution. This is typical of the macOS applications, where they still run in the Dock although there is no window open. NAppGUI follows the same criteria of not closing the program, so we must make an explicit call to the function osapp_finish. To do this, we will capture the button event, through the macro listener.

1
2
3
4
5
6
7
8
9
static void i_OnClose(App *app, Event *e)
{
    osapp_finish();
}

static App *i_create(void)
{
   window_OnClose(app->main_window, listener(app, i_OnClose, App));
}

9. Button Events

Finally, we will capture the click event of the button and print a message in the text box every time you press. We are going to implement the handler i_OnButton, responsible for composing and displaying the message and we will connect it to the Button control that we have created previously.

1
2
3
4
5
6
7
8
9
static void i_OnButton(App *app, Event *e)
{
    String *msg = str_printf("Button click (%d)\n", app->clicks);
    text_insert(app->vtext, tc(msg));
    str_destroy(&msg);
    app->clicks += 1;
}
...
button_OnClick(button, listener(app, i_OnButton, App));
An event is an action that occurs during the program execution. The operating system captures it and sends it to us through its controller (defined in listener()). More in Events.
❮ Back
Next ❯