6 ex__gui__colorwheel_8c
gullradriel edited this page 2026-10-09 00:55:18 +02:00

examples/ex_gui_colorwheel.c

Functions

Name
int process_args(int argc, char ** argv)
read -V
void on_change(int widget_id, int dragging, void * user_data)
change callback
int near(float a, float b)
is a near b
int main(int argc, char ** argv)
run the checks
ALLEGRO_COLOR grey(float u, float v, void * user_data)
a caller's colour: grey everywhere
void feed(N_GUI_CTX * gui, int type, int x, int y, int button)
feed one mouse event at display coordinates
void expect_true(const char * what, int ok)
count a failed check
void drag(N_GUI_CTX * gui, float fu, float fv, float tu, float tv)
press at one point, drag to another, release

Attributes

Name
int log_level
log level from -V
int failures
failed checks
int drag_changes
int colour_asked
set when the caller's colour was asked for
int changes
changes seen, and those while dragging

Defines

Name
AT_Y(v)
as AT_X, for v, which goes up
AT_X(u)
the wheel sits at 50,50 and is 200 by 200, its disc of radius 98 centred at 150,150: display position of a wheel coordinate
ALLEGRO_UNSTABLE

Functions Documentation

function process_args

static int process_args(
    int argc,
    char ** argv
)

read -V

Parameters:

  • argc argument count
  • argv arguments

Return: 0, or -1 on a bad argument

function on_change

static void on_change(
    int widget_id,
    int dragging,
    void * user_data
)

change callback

Parameters:

  • widget_id the wheel
  • dragging set while the button is held
  • user_data unused

function near

static int near(
    float a,
    float b
)

is a near b

Parameters:

  • a one
  • b the other

Return: non-zero when within about a pixel

function main

int main(
    int argc,
    char ** argv
)

run the checks

Parameters:

  • argc argument count
  • argv arguments

Return: 0 on success

function grey

static ALLEGRO_COLOR grey(
    float u,
    float v,
    void * user_data
)

a caller's colour: grey everywhere

Parameters:

  • u across
  • v up
  • user_data unused

Return: grey

function feed

static void feed(
    N_GUI_CTX * gui,
    int type,
    int x,
    int y,
    int button
)

feed one mouse event at display coordinates

Parameters:

  • gui the GUI
  • type the Allegro event type
  • x display x
  • y display y
  • button the button

function expect_true

static void expect_true(
    const char * what,
    int ok
)

count a failed check

Parameters:

  • what the check
  • ok its result

function drag

static void drag(
    N_GUI_CTX * gui,
    float fu,
    float fv,
    float tu,
    float tv
)

press at one point, drag to another, release

Parameters:

  • gui the GUI
  • fu from, across
  • fv from, up
  • tu to, across
  • tv to, up

Attributes Documentation

variable log_level

static int log_level = 3;

log level from -V

variable failures

static int failures = 0;

failed checks

variable drag_changes

static int drag_changes = 0;

variable colour_asked

static int colour_asked = 0;

set when the caller's colour was asked for

variable changes

static int changes = 0;

changes seen, and those while dragging

Macros Documentation

define AT_Y

#define AT_Y(
    v
)
((int)(150.0f - (v) * 98.0f + 0.5f))

as AT_X, for v, which goes up

define AT_X

#define AT_X(
    u
)
((int)(150.0f + (u) * 98.0f + 0.5f))

the wheel sits at 50,50 and is 200 by 200, its disc of radius 98 centred at 150,150: display position of a wheel coordinate

define ALLEGRO_UNSTABLE

#define ALLEGRO_UNSTABLE 1

Source code

/*
 * Nilorea Library
 * Copyright (C) 2005-2026 Castagnier Mickael
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * permissions and limitations under the License.
 *
 * SPDX-License-Identifier: Apache-2.0
 */


#define ALLEGRO_UNSTABLE 1

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <allegro5/allegro_primitives.h>

#include "nilorea/n_common.h"
#include "nilorea/n_log.h"
#include "nilorea/n_gui.h"
#include "nilorea/n_gui_colorwheel.h"

static int log_level = LOG_ERR;
static int failures = 0;
static int changes = 0, drag_changes = 0;
static int colour_asked = 0;

static void expect_true(const char* what, int ok) {
    if (!ok) {
        n_log(LOG_ERR, "FAIL: %s", what);
        failures++;
    } else {
        n_log(LOG_INFO, "ok: %s", what);
    }
}

static int process_args(int argc, char** argv) {
    int opt = 0;

    while ((opt = getopt(argc, argv, "V:")) != -1) {
        if (opt == 'V') {
            log_level = atoi(optarg);
        } else {
            return -1;
        }
    }
    return 0;
}

static void on_change(int widget_id, int dragging, void* user_data) {
    (void)widget_id;
    (void)user_data;
    changes++;
    if (dragging) drag_changes++;
}

static ALLEGRO_COLOR grey(float u, float v, void* user_data) {
    (void)u;
    (void)v;
    (void)user_data;
    colour_asked = 1;
    return al_map_rgb_f(0.5f, 0.5f, 0.5f);
}

static void feed(N_GUI_CTX* gui, int type, int x, int y, int button) {
    ALLEGRO_EVENT ev;

    memset(&ev, 0, sizeof(ev));
    ev.type = (ALLEGRO_EVENT_TYPE)type;
    ev.mouse.x = x;
    ev.mouse.y = y;
    ev.mouse.dx = type == ALLEGRO_EVENT_MOUSE_AXES ? 1 : 0;
    ev.mouse.button = (unsigned int)button;
    n_gui_process_event(gui, ev);
}

#define AT_X(u) ((int)(150.0f + (u) * 98.0f + 0.5f))
#define AT_Y(v) ((int)(150.0f - (v) * 98.0f + 0.5f))

static void drag(N_GUI_CTX* gui, float fu, float fv, float tu, float tv) {
    feed(gui, ALLEGRO_EVENT_MOUSE_AXES, AT_X(fu), AT_Y(fv), 0);
    feed(gui, ALLEGRO_EVENT_MOUSE_BUTTON_DOWN, AT_X(fu), AT_Y(fv), 1);
    feed(gui, ALLEGRO_EVENT_MOUSE_AXES, AT_X(tu), AT_Y(tv), 0);
    feed(gui, ALLEGRO_EVENT_MOUSE_BUTTON_UP, AT_X(tu), AT_Y(tv), 1);
}

static int near(float a, float b) {
    return a - b < 0.02f && b - a < 0.02f;
}

int main(int argc, char** argv) {
    float u = 0.0f, v = 0.0f;

    set_log_level(LOG_ERR);
    if (process_args(argc, argv) != 0) {
        fprintf(stderr, "Usage: %s [-V LOG_LEVEL]\n", argv[0]);
        return 2;
    }
    set_log_level(log_level);
    if (!al_init() || !al_init_font_addon() || !al_init_primitives_addon()) {
        n_log(LOG_ERR, "allegro init failed");
        return 1;
    }
    ALLEGRO_FONT* font = al_create_builtin_font();
    if (!font) return 1;
    N_GUI_CTX* gui = n_gui_new_ctx(font);
    if (!gui) {
        al_destroy_font(font);
        return 1;
    }
    n_gui_set_display_size(gui, 400.0f, 300.0f);
    int win = n_gui_add_window(gui, "W", 0.0f, 0.0f, 400.0f, 300.0f);
    n_gui_window_set_flags(gui, win, N_GUI_WIN_FRAMELESS);
    n_gui_open_window(gui, win);

    int wheel = n_gui_colorwheel_new(gui, win, 50.0f, 50.0f, 200.0f, 200.0f, on_change, NULL);
    expect_true("a wheel is made", wheel >= 0);
    expect_true("it starts in the centre", n_gui_colorwheel_get(gui, wheel, &u, &v) && u == 0.0f && v == 0.0f);

    drag(gui, 0.0f, 0.0f, 0.5f, 0.5f);
    n_gui_colorwheel_get(gui, wheel, &u, &v);
    expect_true("a drag moves the point", near(u, 0.5f) && near(v, 0.5f));
    expect_true("with changes while dragging and one at the release", drag_changes >= 2 && changes == drag_changes + 1);

    drag(gui, 0.5f, 0.5f, 1.2f, 0.0f);
    n_gui_colorwheel_get(gui, wheel, &u, &v);
    expect_true("past the rim it stays on it", near(u, 1.0f) && near(v, 0.0f));

    feed(gui, ALLEGRO_EVENT_MOUSE_AXES, AT_X(0.3f), AT_Y(-0.3f), 0);
    feed(gui, ALLEGRO_EVENT_MOUSE_BUTTON_DOWN, AT_X(0.3f), AT_Y(-0.3f), 2);
    feed(gui, ALLEGRO_EVENT_MOUSE_BUTTON_UP, AT_X(0.3f), AT_Y(-0.3f), 2);
    n_gui_colorwheel_get(gui, wheel, &u, &v);
    expect_true("a right press puts it back in the centre", u == 0.0f && v == 0.0f);

    /* Two quick presses at the same place: the first moves the point there,
     * the second puts it back in the centre and starts no drag, so the
     * release and a move after it leave it there. */
    n_gui_set_double_click_time(gui, 10.0);
    feed(gui, ALLEGRO_EVENT_MOUSE_AXES, AT_X(0.4f), AT_Y(0.2f), 0);
    feed(gui, ALLEGRO_EVENT_MOUSE_BUTTON_DOWN, AT_X(0.4f), AT_Y(0.2f), 1);
    feed(gui, ALLEGRO_EVENT_MOUSE_BUTTON_UP, AT_X(0.4f), AT_Y(0.2f), 1);
    n_gui_colorwheel_get(gui, wheel, &u, &v);
    expect_true("a press moves the point", near(u, 0.4f) && near(v, 0.2f));
    feed(gui, ALLEGRO_EVENT_MOUSE_BUTTON_DOWN, AT_X(0.4f), AT_Y(0.2f), 1);
    feed(gui, ALLEGRO_EVENT_MOUSE_AXES, AT_X(0.6f), AT_Y(0.1f), 0);
    feed(gui, ALLEGRO_EVENT_MOUSE_BUTTON_UP, AT_X(0.6f), AT_Y(0.1f), 1);
    n_gui_colorwheel_get(gui, wheel, &u, &v);
    expect_true("a double click puts it back in the centre, and the drag after does not move it", u == 0.0f && v == 0.0f);
    n_gui_set_double_click_time(gui, 0.4);

    expect_true("set from code", n_gui_colorwheel_set(gui, wheel, -0.25f, 0.5f));
    n_gui_colorwheel_get(gui, wheel, &u, &v);
    expect_true("reads back", u == -0.25f && v == 0.5f);
    expect_true("set outside is kept on the rim", n_gui_colorwheel_set(gui, wheel, 3.0f, 4.0f));
    n_gui_colorwheel_get(gui, wheel, &u, &v);
    expect_true("at the same angle", near(u, 0.6f) && near(v, 0.8f));

    feed(gui, ALLEGRO_EVENT_MOUSE_AXES, AT_X(0.0f), AT_Y(0.0f), 0);
    feed(gui, ALLEGRO_EVENT_MOUSE_BUTTON_DOWN, AT_X(0.0f), AT_Y(0.0f), 1);
    expect_true("set is refused during a drag", n_gui_colorwheel_set(gui, wheel, 0.1f, 0.1f) == FALSE);
    feed(gui, ALLEGRO_EVENT_MOUSE_BUTTON_UP, AT_X(0.0f), AT_Y(0.0f), 1);

    {
        ALLEGRO_COLOR c = n_gui_colorwheel_hue(1.0f, 0.0f, NULL);
        float r = 0.0f, g = 0.0f, b = 0.0f;

        al_unmap_rgb_f(c, &r, &g, &b);
        expect_true("the default disc is red to the right", r > 0.99f && g < 0.01f && b < 0.01f);
        c = n_gui_colorwheel_hue(0.0f, 0.0f, NULL);
        al_unmap_rgb_f(c, &r, &g, &b);
        expect_true("and white in the centre", r > 0.99f && g > 0.99f && b > 0.99f);
    }
    {
        int label = n_gui_add_label(gui, win, "not a wheel", 10.0f, 270.0f, 100.0f, 20.0f, N_GUI_ALIGN_LEFT);

        expect_true("a widget that is not a wheel is refused", n_gui_colorwheel_get(gui, label, &u, &v) == FALSE);
    }
    {
        ALLEGRO_BITMAP* target = NULL;
        int old = al_get_new_bitmap_flags();

        n_gui_colorwheel_set_colour(gui, wheel, grey, NULL);
        al_set_new_bitmap_flags(ALLEGRO_MEMORY_BITMAP);
        target = al_create_bitmap(400, 300);
        al_set_new_bitmap_flags(old);
        al_set_target_bitmap(target);
        al_clear_to_color(al_map_rgb(0, 0, 0));
        n_gui_draw(gui);
        al_set_target_bitmap(NULL);
        al_destroy_bitmap(target);
        expect_true("the caller's colours paint the disc", colour_asked);
    }

    n_gui_destroy_ctx(&gui);
    al_destroy_font(font);
    if (failures) {
        fprintf(stderr, "ex_gui_colorwheel: %d failure(s)\n", failures);
        return 1;
    }
    printf("ex_gui_colorwheel: ok\n");
    return 0;
}