Register
handmade.network » Wiki » GUI Program Skeleton

Windows

 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
#include <windows.h>

LRESULT CALLBACK WindowProcedure(HWND window, UINT message, WPARAM wParam, LPARAM lParam) {
    if (message == WM_DESTROY) {
        // If we receive the destroy message, then quit the program.
        PostQuitMessage(0);
        return 0;
    } else {
        // We don't handle this message. Use the default handler.
        return DefWindowProc(window, message, wParam, lParam);
    }
}

int CALLBACK WinMain(HINSTANCE instance, HINSTANCE previousInstance, 
        LPSTR commandLine, int commandShow) {
    // Create the window class.
    WNDCLASS windowClass = {};
    windowClass.style = CS_HREDRAW | CS_VREDRAW;
    windowClass.lpfnWndProc = WindowProcedure;
    windowClass.hInstance = instance;
    windowClass.lpszClassName = "MyWindowClass";
    windowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
    windowClass.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
    RegisterClass(&windowClass);

    // Create and show the actual window.
    HWND window = CreateWindow("MyWindowClass", "Hello, GUI!", WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 400, 200, NULL, NULL, instance, NULL);
    ShowWindow(window, commandShow);
    UpdateWindow(window);

    // Process window messages.
    MSG message;
    while (GetMessage(&message, NULL, 0, 0) > 0) {
        TranslateMessage(&message);
        DispatchMessage(&message);
    }

    return message.wParam;
}

Link with user32.lib.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#include <windows.h>
static bool Running;

...
while(Running) {
    while ( PeekMessage( &message, 0, 0, 0, PM_REMOVE ) ) {
        if ( message.message == WM_QUIT) {
            Running = false;
        }
        TranslateMessage(&message);
        DispatchMessage(&message); 
    }
    // Do your stuff here
}
...

With DPI Scaling

  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
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#include <windows.h>
#include <commctrl.h> 
#include <shellscalingapi.h>

#pragma comment(linker,"\"/manifestdependency:type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")

HINSTANCE instance;
HMODULE shcore;

typedef HRESULT (*GetDpiForMonitorType)(HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, UINT *dpiX, UINT *dpiY);
typedef BOOL (*SetProcessDpiAwarenessContextType)(DPI_AWARENESS_CONTEXT value);

HFONT GetFont(HWND window) {
    return (HFONT) GetWindowLongPtr(GetAncestor(window, GA_ROOT), 0);
}

int GetDPIScale(HWND window) {
    return (int) GetWindowLongPtr(GetAncestor(window, GA_ROOT), sizeof(LONG_PTR));
}

bool CALLBACK SetFont(HWND child, LPARAM font) {
    SendMessage(child, WM_SETFONT, font, true);
    return true;
}

void RecreateFont(HWND window) {
    if (shcore) {
        HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTOPRIMARY);
        UINT x, y;
        GetDpiForMonitorType getDpiForMonitor = (GetDpiForMonitorType) GetProcAddress(shcore, "GetDpiForMonitor");
        getDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &x, &y);
        SetWindowLongPtr(window, sizeof(LONG_PTR), y);
    } else {
        HDC screen = GetDC(NULL);
        SetWindowLongPtr(window, sizeof(LONG_PTR), GetDeviceCaps(screen, LOGPIXELSY));
        ReleaseDC(NULL, screen);
    }

    NONCLIENTMETRICSW metrics = {};
    metrics.cbSize = sizeof(NONCLIENTMETRICSW);
    SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(metrics), &metrics, 0);
    metrics.lfMessageFont.lfHeight = -((9 * GetWindowLongPtr(window, sizeof(LONG_PTR))) / 72);
    if (GetWindowLongPtr(window, 0)) DeleteObject((HFONT) GetWindowLongPtr(window, 0));
    SetWindowLongPtr(window, 0, (LONG_PTR) CreateFontIndirectW(&metrics.lfMessageFont));
    EnumChildWindows(window, (WNDENUMPROC) SetFont, (LPARAM) GetWindowLongPtr(window, 0));
}

LRESULT CALLBACK WindowProcedure(HWND window, UINT message, WPARAM wParam, LPARAM lParam) {
    if (message == WM_DESTROY) {
        PostQuitMessage(0);
    } else if (message == WM_DPICHANGED) {
        if (shcore) {
            RECT *newBounds = (RECT *) lParam;
            RecreateFont(window);
            MoveWindow(window, newBounds->left, newBounds->top, newBounds->right - newBounds->left, newBounds->bottom - newBounds->top, TRUE);
        }
    } else if (message == WM_SIZE) {
        int width = LOWORD(lParam), height = HIWORD(lParam);
        // TODO Layout child windows.
    } else if (message == WM_PAINT) {
        PAINTSTRUCT paint;
        HDC hdc = BeginPaint(window, &paint);
        SelectObject(hdc, GetFont(window));
        TextOut(hdc, 0, 0, "Hello, world!", 13);
        // TODO Paint window contents.
        EndPaint(window, &paint);
    } else {
        return DefWindowProc(window, message, wParam, lParam);
    }

    return 0;
}

int __stdcall WinMain(HINSTANCE _instance, HINSTANCE previousInstance, char *commandLine, int commandShow) {
    instance = (HINSTANCE) _instance;

    OSVERSIONINFOEXW version = { sizeof(version) };
    ((LONG (*)(PRTL_OSVERSIONINFOEXW)) GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "RtlGetVersion"))(&version);

    if (version.dwMajorVersion >= 10) {
        shcore = LoadLibrary("shcore.dll");
        SetProcessDpiAwarenessContextType setProcessDpiAwarenessContext = (SetProcessDpiAwarenessContextType) GetProcAddress(LoadLibrary("user32.dll"), "SetProcessDpiAwarenessContext");
        setProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
    } else {
        SetProcessDPIAware();
    }

    CoInitialize(NULL);

    INITCOMMONCONTROLSEX icc = {};
    icc.dwSize = sizeof(INITCOMMONCONTROLSEX);
    icc.dwICC = ICC_WIN95_CLASSES;
    InitCommonControlsEx(&icc);

    WNDCLASSEX windowClass = {};
    windowClass.cbSize = sizeof(WNDCLASSEX);
    windowClass.cbWndExtra = sizeof(LONG_PTR) * 2 /* font, dpiScale */;
    windowClass.style = CS_HREDRAW | CS_VREDRAW;
    windowClass.lpfnWndProc = WindowProcedure;
    windowClass.hInstance = instance;
    windowClass.lpszClassName = "MyWindowClass";
    windowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
    windowClass.hbrBackground = (HBRUSH) COLOR_WINDOW;
    RegisterClassEx(&windowClass);

    HWND root = CreateWindowEx(0, "MyWindowClass", "My Window" /* TODO Window title. */, 
        WS_CLIPSIBLINGS | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU,
        CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, NULL, NULL, instance, NULL);
    RecreateFont(root);
    ShowWindow(root, commandShow);

    MSG message;

    while (GetMessage(&message, NULL, 0, 0) > 0) {
        TranslateMessage(&message);
        DispatchMessage(&message);
    }

    return message.wParam;
}

Link with Shell32.lib Advapi32.lib User32.lib comctl32.lib gdi32.lib ole32.lib.

Linux / X11

 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
// Compile with gcc this_file.c -lX11
#include <X11/Xlib.h>

int main(void){

    // Create connection to the X Server.
    Display* dpy = XOpenDisplay(NULL);

    // Create window.
    Window   win = XCreateSimpleWindow(
        dpy,
        DefaultRootWindow(dpy), // Parent
        0, 0,                   // X/Y, often overridden by window manager
        400, 200,               // Width/Height
        0, 0,                   // Border
        WhitePixel(dpy, 0)      // Background
    );

    char msg[] = "Hello, GUI!";

    // Set window title.
    XStoreName(dpy, win, msg);

    // Choose which events we want.
    XSelectInput(dpy, win,
                 SubstructureNotifyMask // Window size changes (ConfigureNotify)
                 | ExposureMask         // Window needs to be repainted (Expose)
                 | PointerMotionMask    // Mouse moves (MotionNotify)
                 | ButtonPressMask
                 | ButtonReleaseMask
                 | KeyPressMask);

    // Make window visible and give it focus.
    XMapRaised(dpy, win);

    // For window manager close button event.
    Atom win_closed_id = XInternAtom(dpy, "WM_DELETE_WINDOW", 0);
    XSetWMProtocols(dpy, win, &win_closed_id, 1);

    for (;;) {
        XEvent ev;
        XNextEvent(dpy, &ev);

        // WM close button pushed, exit.
        if (ev.type == ClientMessage && ev.xclient.data.l[0] == win_closed_id) {
            return 0;
        }

        // Expose event tells us to repaint (some of) the window.
        if (ev.type == Expose) {
            XDrawString(dpy, win, DefaultGC(dpy, 0), 30, 30, msg, sizeof(msg)-1);
        }
    }
}

Gtk

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#include <gtk/gtk.h>

static void ActivateApplication(GtkApplication* application, void *data) {
    GtkWidget *window = gtk_application_window_new(application);
    gtk_window_set_title(GTK_WINDOW(window), "Hello, GUI!");
    gtk_window_set_default_size(GTK_WINDOW(window), 400, 200);
    gtk_widget_show_all(window);
}

int main(int argc, char **argv) {
    GtkApplication *application = gtk_application_new("my.app.id", G_APPLICATION_FLAGS_NONE);
    g_signal_connect(application, "activate", G_CALLBACK(ActivateApplication), NULL);
    return g_application_run(G_APPLICATION(application), argc, argv);
}

Compile with:

gcc `pkg-config --cflags gtk+-3.0` -o gtk-test gtk-test.c `pkg-config --libs gtk+-3.0`