Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
ex_gui_hexview.c
Go to the documentation of this file.
1/*
2 * Nilorea Library
3 * Copyright (C) 2005-2026 Castagnier Mickael
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14 * implied. See the License for the specific language governing
15 * permissions and limitations under the License.
16 *
17 * SPDX-License-Identifier: Apache-2.0
18 */
19
26
27#define ALLEGRO_UNSTABLE 1
28
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <unistd.h>
33
34#include "nilorea/n_common.h"
35#include "nilorea/n_log.h"
36#include "nilorea/n_gui.h"
37
38static int log_level = LOG_ERR;
39static int failures = 0;
40
41static int process_args(int argc, char** argv) {
42 int opt;
43 while ((opt = getopt(argc, argv, "V:")) != -1) {
44 switch (opt) {
45 case 'V':
46 if (!strcmp("LOG_NULL", optarg))
48 else if (!strcmp("LOG_NOTICE", optarg))
50 else if (!strcmp("LOG_INFO", optarg))
52 else if (!strcmp("LOG_ERR", optarg))
54 else if (!strcmp("LOG_DEBUG", optarg))
56 else
57 return -1;
58 break;
59 default:
60 return -1;
61 }
62 }
63 return 0;
64}
65
66static void expect_true(const char* label, int cond) {
67 if (!cond) {
68 n_log(LOG_ERR, "%s: condition false", label);
69 failures++;
70 }
71}
72
73/* Feed one key to the GUI as if the user had pressed it. The editable hex view is
74 the only widget here that edits arbitrary bytes, so its whole model (nibble
75 typing, insert, delete, cursor movement) is driven through the real event path
76 rather than by poking the struct: that is what keeps the dispatch wiring covered
77 too. */
78static void send_key(N_GUI_CTX* gui, int keycode, int unichar, int modifiers) {
79 ALLEGRO_EVENT ev;
80 memset(&ev, 0, sizeof(ev));
81 ev.type = ALLEGRO_EVENT_KEY_CHAR;
82 ev.keyboard.keycode = keycode;
83 ev.keyboard.unichar = unichar;
84 ev.keyboard.modifiers = (unsigned int)modifiers;
86}
87
88/* Compare the widget's bytes against an expected buffer. */
89static int hex_equals(N_GUI_CTX* gui, int hv, const unsigned char* want, size_t want_len) {
90 size_t len = 0;
91 const unsigned char* got = n_gui_hexview_get_data(gui, hv, &len);
92 if (len != want_len) return 0;
93 if (want_len == 0) return 1;
94 return got && memcmp(got, want, want_len) == 0;
95}
96
97int main(int argc, char** argv) {
99 if (process_args(argc, argv) != 0) {
100 fprintf(stderr, "Usage: %s [-V LOG_LEVEL]\n", argv[0]);
101 return 2;
102 }
104
105 if (!al_init() || !al_init_font_addon()) {
106 n_log(LOG_ERR, "allegro init failed");
107 return 1;
108 }
109 ALLEGRO_FONT* font = al_create_builtin_font();
110 if (!font) {
111 n_log(LOG_ERR, "al_create_builtin_font failed");
112 return 1;
113 }
115 if (!gui) {
116 n_log(LOG_ERR, "n_gui_new_ctx failed");
117 al_destroy_font(font);
118 return 1;
119 }
120 n_gui_set_display_size(gui, 800.0f, 600.0f);
121
122 int win = n_gui_add_window(gui, "Panel", 0.0f, 0.0f, 800.0f, 600.0f);
123 expect_true("window created", win >= 0);
124
125 int hv = n_gui_add_hexview(gui, win, 0.0f, 0.0f, 600.0f, 400.0f);
126 expect_true("hexview created", hv >= 0);
127 expect_true("empty length is zero", n_gui_hexview_get_length(gui, hv) == 0);
128
129 /* binary-safe payload including embedded NUL and high bytes */
130 unsigned char payload[260];
131 size_t i;
132 for (i = 0; i < sizeof(payload); i++)
133 payload[i] = (unsigned char)(i & 0xFF);
134 payload[5] = 0x00;
135 n_gui_hexview_set_data(gui, hv, payload, sizeof(payload));
136 expect_true("length matches payload", n_gui_hexview_get_length(gui, hv) == sizeof(payload));
137
138 /* replacing with NULL clears the buffer */
139 n_gui_hexview_set_data(gui, hv, NULL, 0);
140 expect_true("cleared length is zero", n_gui_hexview_get_length(gui, hv) == 0);
141
142 /* ---- editing ---- */
143 {
144 static const unsigned char start[4] = {0xDE, 0xAD, 0xBE, 0xEF};
145 unsigned char want[8];
146 size_t glen = 0;
147
148 n_gui_hexview_set_data(gui, hv, start, sizeof(start));
149 expect_true("data borrows back", hex_equals(gui, hv, start, sizeof(start)));
150 expect_true("cursor starts at zero", n_gui_hexview_get_cursor(gui, hv) == 0);
151
152 /* a read-only view ignores keys: editing is opt-in, so an existing viewer
153 cannot start mutating its data because the user clicked in it */
154 n_gui_set_focus(gui, hv);
155 send_key(gui, ALLEGRO_KEY_1, '1', 0);
156 expect_true("read-only view is unchanged", hex_equals(gui, hv, start, sizeof(start)));
157
159 n_gui_set_focus(gui, hv);
160
161 /* typing two hex digits fills one byte and advances to the next */
162 send_key(gui, ALLEGRO_KEY_C, 'c', 0);
163 send_key(gui, ALLEGRO_KEY_A, 'a', 0);
164 memcpy(want, start, sizeof(start));
165 want[0] = 0xCA;
166 expect_true("two digits write one byte", hex_equals(gui, hv, want, 4));
167 expect_true("cursor advanced one byte", n_gui_hexview_get_cursor(gui, hv) == 1);
168
169 /* a single digit writes the high nibble and leaves the low one alone */
170 send_key(gui, ALLEGRO_KEY_F, 'f', 0);
171 want[1] = 0xFD; /* was 0xAD */
172 expect_true("one digit writes the high nibble", hex_equals(gui, hv, want, 4));
173
174 /* Insert adds a zero byte before the cursor, Delete removes one */
176 send_key(gui, ALLEGRO_KEY_INSERT, 0, 0);
177 expect_true("insert grew the buffer", n_gui_hexview_get_length(gui, hv) == 5);
178 {
179 const unsigned char* got = n_gui_hexview_get_data(gui, hv, &glen);
180 expect_true("inserted byte is zero", got && got[0] == 0x00);
181 }
182 send_key(gui, ALLEGRO_KEY_DELETE, 0, 0);
183 expect_true("delete restored the buffer", hex_equals(gui, hv, want, 4));
184
185 /* Backspace removes the byte before the cursor and moves back onto it */
187 send_key(gui, ALLEGRO_KEY_BACKSPACE, 0, 0);
188 expect_true("backspace removed the last byte", n_gui_hexview_get_length(gui, hv) == 3);
189 expect_true("backspace moved the cursor back", n_gui_hexview_get_cursor(gui, hv) == 3);
190
191 /* typing past the end appends, so a buffer can be built from nothing */
192 n_gui_hexview_set_data(gui, hv, NULL, 0);
194 n_gui_set_focus(gui, hv);
195 send_key(gui, ALLEGRO_KEY_0, '0', 0);
196 send_key(gui, ALLEGRO_KEY_D, 'd', 0);
197 send_key(gui, ALLEGRO_KEY_0, '0', 0);
198 send_key(gui, ALLEGRO_KEY_A, 'a', 0);
199 {
200 static const unsigned char crlf[2] = {0x0D, 0x0A};
201 expect_true("typing from empty appends bytes", hex_equals(gui, hv, crlf, 2));
202 }
203
204 /* every byte value survives, including NUL and the high half: that is the
205 whole reason this widget exists rather than a textarea */
206 {
207 unsigned char all[256];
208 size_t i2;
209 for (i2 = 0; i2 < sizeof(all); i2++)
210 all[i2] = (unsigned char)i2;
211 n_gui_hexview_set_data(gui, hv, all, sizeof(all));
212 expect_true("all 256 byte values round-trip", hex_equals(gui, hv, all, sizeof(all)));
213 }
214
215 /* guards */
216 expect_true("get_data on a bad id is NULL", n_gui_hexview_get_data(gui, -1, &glen) == NULL && glen == 0);
217 n_gui_hexview_set_cursor(gui, hv, 99999);
218 expect_true("cursor clamps to the length", n_gui_hexview_get_cursor(gui, hv) == n_gui_hexview_get_length(gui, hv));
219 }
220
222 al_destroy_font(font);
223
224 if (failures) {
225 n_log(LOG_ERR, "ex_gui_hexview: %d failure(s)", failures);
226 return 1;
227 }
228 n_log(LOG_NOTICE, "ex_gui_hexview: all checks passed");
229 return 0;
230}
void process_args(int argc, char **argv)
static int failures
Definition ex_atlas.c:51
int main(void)
Definition ex_atlas.c:117
static void expect_true(const char *label, int cond)
Definition ex_atlas.c:53
static int log_level
static N_GUI_CTX * gui
static int hex_equals(N_GUI_CTX *gui, int hv, const unsigned char *want, size_t want_len)
static void send_key(N_GUI_CTX *gui, int keycode, int unichar, int modifiers)
FONT * font
the default 8x8 font, valid after allegro_init()
void n_gui_set_display_size(N_GUI_CTX *ctx, float w, float h)
set the display (viewport) size so global scrollbars can be computed.
Definition n_gui.c:12348
int n_gui_add_hexview(N_GUI_CTX *ctx, int window_id, float x, float y, float w, float h)
Add a read-only hexadecimal byte viewer.
Definition n_gui.c:3372
void n_gui_hexview_set_data(N_GUI_CTX *ctx, int widget_id, const unsigned char *data, size_t len)
set (copy) the bytes shown by a hex viewer
Definition n_gui.c:4805
void n_gui_hexview_set_cursor(N_GUI_CTX *ctx, int widget_id, size_t offset)
move the edit cursor to a byte offset
Definition n_gui.c:4872
const unsigned char * n_gui_hexview_get_data(N_GUI_CTX *ctx, int widget_id, size_t *len)
borrow the bytes a hex view holds
Definition n_gui.c:4858
int n_gui_process_event(N_GUI_CTX *ctx, ALLEGRO_EVENT event)
Process an allegro event through the GUI system.
Definition n_gui.c:13611
void n_gui_set_focus(N_GUI_CTX *ctx, int widget_id)
Set keyboard focus to a specific widget.
Definition n_gui.c:3908
size_t n_gui_hexview_get_length(N_GUI_CTX *ctx, int widget_id)
get the number of bytes currently held by a hex viewer
Definition n_gui.c:4899
void n_gui_hexview_set_editable(N_GUI_CTX *ctx, int widget_id, int editable)
make a hex view editable, or read-only again
Definition n_gui.c:4831
int n_gui_add_window(N_GUI_CTX *ctx, const char *title, float x, float y, float w, float h)
Add a new pseudo-window to the context.
Definition n_gui.c:1277
N_GUI_CTX * n_gui_new_ctx(ALLEGRO_FONT *default_font)
Create a new GUI context.
Definition n_gui.c:1103
void n_gui_destroy_ctx(N_GUI_CTX **ctx)
Destroy a GUI context and all its windows/widgets.
Definition n_gui.c:1209
size_t n_gui_hexview_get_cursor(N_GUI_CTX *ctx, int widget_id)
the byte offset the edit cursor sits on
Definition n_gui.c:4887
The top-level GUI context that holds all windows.
Definition n_gui.h:1669
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition n_log.h:113
#define LOG_DEBUG
debug-level messages
Definition n_log.h:108
#define LOG_ERR
error conditions
Definition n_log.h:100
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL ).
Definition n_log.c:123
#define LOG_NOTICE
normal but significant condition
Definition n_log.h:104
#define LOG_NULL
no log output
Definition n_log.h:70
#define LOG_INFO
informational
Definition n_log.h:106
Common headers and low-level functions & define.
GUI system: buttons, sliders, text areas, checkboxes, scrollbars, dropdown menus, windows.
Generic log system.