summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime.c490
1 files changed, 425 insertions, 65 deletions
diff --git a/runtime.c b/runtime.c
index 21499ce..af5d6bc 100644
--- a/runtime.c
+++ b/runtime.c
@@ -126,11 +126,12 @@
// - Graph
// - Sine Wave
// - Utility
-// - UTF-8, UTF-16
+// - UTF-8, UTF-16 encoding
// - Testing
// - Stackless coroutines
// - Allocator
// - Profiling
+// - BMP, PPM image formats
// - Graphics
// - Font
// - Adaptive resolution
@@ -614,6 +615,21 @@ typedef struct {
} Network_Address;
typedef struct {
+ b8 enable_text : 1;
+ b8 enable_image : 1;
+ b8 enable_sound : 1;
+
+ i64 text_len;
+ c8 *text;
+
+ i64 image_len;
+ u8 *image;
+
+ i64 sound_len;
+ u8 *sound;
+} Clipboard_Content;
+
+typedef struct {
c8 *title;
i32 frame_width;
i32 frame_height;
@@ -623,9 +639,6 @@ typedef struct {
b8 exact_resolution : 1;
b8 enable_gamma : 1;
b8 graceful_shutdown : 1;
- b8 enable_clipboard_text : 1;
- b8 enable_clipboard_image : 1;
- b8 enable_clipboard_sound : 1;
b8 done : 1;
b8 has_focus : 1;
@@ -652,23 +665,18 @@ typedef struct {
Input_Key *input;
Drop_File *drop_files;
- i64 clipboard_text_len;
- c8 *clipboard_text;
-
- i64 clipboard_image_len;
- u8 *clipboard_image;
-
- i64 clipboard_sound_len;
- u8 *clipboard_sound;
-
b8 key_down [MAX_NUM_KEYS];
b8 key_pressed [MAX_NUM_KEYS];
- void *window_handle_win32;
- u64 window_handle_x11;
+ Clipboard_Content clipboard;
i64 memory_buffer_size;
u8 *memory_buffer;
+
+ // Platform-specific data
+
+ void *window_handle_win32;
+ u64 window_handle_x11;
} Platform;
// UTF-8
@@ -2272,16 +2280,30 @@ static void drop_files_set_data_(i64 index, i64 data_size) {
//
// ================================================================
-static void store_clipboard_text_(i64 size, c8 *data) {
- if (size < 0 || data == NULL)
+static void store_clipboard_text_(i64 len, c8 *data) {
+ if (len < 0) {
+ LOG_error("Sanity");
return;
+ }
- resize_dynamic_array_exact(&g_platform.clipboard_text_len, (void **) &g_platform.clipboard_text, 1, size + 1);
+ if (len != 0 && data == NULL) {
+ LOG_error("Sanity");
+ return;
+ }
- i64 len = g_platform.clipboard_text_len - 1;
- mem_cpy_(g_platform.clipboard_text, data, len);
- if (len >= 0)
- g_platform.clipboard_text[len] = '\0';
+ if (!g_platform.clipboard.enable_text) {
+ LOG_error("Clipboard text not enabled.");
+ return;
+ }
+
+ resize_dynamic_array_exact(&g_platform.clipboard.text_len, (void **) &g_platform.clipboard.text, 1, len + 1);
+
+ if (g_platform.clipboard.text_len != len + 1)
+ // Out of memory
+ return;
+
+ mem_cpy_(g_platform.clipboard.text, data, len);
+ g_platform.clipboard.text[len] = '\0';
}
// ================================================================
@@ -2581,11 +2603,18 @@ b8 bmp_write_to_memory(i64 *dst_len, u8 *dst, i64 width, i64 height, i64 stride,
i64 headers_size = sizeof(BMP_File_Header_) + sizeof(BMP_Info_Header_);
i64 file_size = headers_size + image_size;
- *dst_len = file_size;
-
- if (dst == NULL)
+ if (dst == NULL) {
// Only calculate required file size.
+ *dst_len = file_size;
return 1;
+ }
+
+ if (*dst_len < file_size) {
+ *dst_len = file_size;
+
+ LOG_error("Not enough space.");
+ return 0;
+ }
BMP_File_Header_ file_header = {
.signature = BMP_Signature_,
@@ -2642,32 +2671,352 @@ b8 bmp_write_to_memory(i64 *dst_len, u8 *dst, i64 width, i64 height, i64 stride,
return 1;
}
+static b8 parse_u64_dec_(u64 *dst, c8 **src, c8 *eof) {
+ if (src == NULL || *src == NULL || eof == NULL) {
+ LOG_error("Sanity");
+ return 0;
+ }
+
+ if (*src >= eof || **src < '0' || **src > '9')
+ return 0;
+
+ if (dst != NULL) {
+ *dst = 0;
+ while (*src < eof && **src >= '0' && **src <= '9') {
+ *dst *= 10;
+ *dst += **src - '0';
+ ++*src;
+ }
+ } else
+ while (*src < eof && **src >= '0' && **src <= '9')
+ ++*src;
+
+ return 1;
+}
+
+static b8 parse_u1_(u8 *dst, c8 **src, c8 *eof) {
+ if (src == NULL || *src == NULL || eof == NULL) {
+ LOG_error("Sanity");
+ return 0;
+ }
+
+ if (*src >= eof || **src < '0' || **src > '1')
+ return 0;
+
+ if (dst != NULL)
+ *dst = **src == '0' ? 0 : 1;
+
+ ++*src;
+
+ return 1;
+}
+
+static void ppm_skip_spaces_(c8 **src, c8 *eof) {
+ if (src == NULL || *src == NULL || eof == NULL) {
+ LOG_error("Sanity");
+ return;
+ }
+
+ while (*src < eof && (**src == ' ' || **src == '\r' || **src == '\n'))
+ if (**src == '#')
+ do
+ ++*src;
+ while (*src < eof && **src != '\n');
+ else
+ ++*src;
+}
+
+static b8 ppm_parse_u64_(u64 *dst, c8 **src, c8 *eof) {
+ ppm_skip_spaces_(src, eof);
+ return parse_u64_dec_(dst, src, eof);
+}
+
+static b8 ppm_parse_u1_(u8 *dst, c8 **src, c8 *eof) {
+ ppm_skip_spaces_(src, eof);
+ return parse_u1_(dst, src, eof);
+}
+
b8 ppm_read_from_memory(i64 *width, i64 *height, i64 pixels_len, vec4_f32 *pixels, i64 src_len, u8 *src) {
- // TODO
+ // NOTE: Binary Netpbm formats don't make sense so we ignore them.
- (void) width;
- (void) height;
- (void) pixels_len;
- (void) pixels;
- (void) src_len;
- (void) src;
+ if ((width == NULL) != (height == NULL)) {
+ LOG_error("Sanity");
+ return 0;
+ }
- LOG_error("Not implemented.");
- return 0;
+ if (src_len <= 0 || src == NULL) {
+ LOG_error("Sanity");
+ return 0;
+ }
+
+ if (src_len < 3) {
+ LOG_error("File too small.");
+ return 0;
+ }
+
+ if (src[0] != 'P' && (src[1] < '1' || src[1] > '7')) {
+ LOG_error("Invalid magic number.");
+ return 0;
+ }
+
+ if (src[1] != '1' && src[1] != '2' && src[1] != '3') {
+ LOG_error("Unsupported format.");
+ return 0;
+ }
+
+ c8 *s = (c8 *) src + 2;
+ c8 *eof = (c8 *) src + src_len;
+
+ u64 image_width, image_height;
+
+ if (!ppm_parse_u64_(&image_width, &s, eof) || !ppm_parse_u64_(&image_height, &s, eof)) {
+ LOG_error("Failed to parse width and height.");
+ return 0;
+ }
+
+ if ((image_width & 0x8000000000000000ull) != 0 || (image_height & 0x8000000000000000) != 0) {
+ LOG_error("Too large width and height.");
+ return 0;
+ }
+
+ u64 pixel_depth = 1;
+
+ if (src[1] == '2' || src[1] == '3')
+ if (!ppm_parse_u64_(&pixel_depth, &s, eof)) {
+ LOG_error("Failed to parse pixel depth.");
+ return 0;
+ }
+
+ if (pixel_depth == 0 || pixel_depth > 0xffffu) {
+ LOG_error("Invalid pixel depth value.");
+ return 0;
+ }
+
+ *width = image_width;
+ *height = image_height;
+
+ if (pixels_len == 0 && pixels == NULL)
+ // Only validate format and return width and height.
+ return 1;
+
+ if ((i64) image_width * (i64) image_height > pixels_len) {
+ LOG_error("Not enough space.");
+ return 0;
+ }
+
+ switch (src[1]) {
+ case '1': {
+ // Plain bitmap
+
+ for (u64 j = 0; j < image_height; ++j)
+ for (u64 i = 0; i < image_width; ++i) {
+ u8 bit;
+ if (!ppm_parse_u1_(&bit, &s, eof)) {
+ LOG_error("Failed to parse pixels.");
+ return 0;
+ }
+ pixels[j * image_width + i] = (vec4_f32) {
+ bit ? 1.0f : 0.0f,
+ bit ? 1.0f : 0.0f,
+ bit ? 1.0f : 0.0f,
+ 1.0f,
+ };
+ }
+ } break;
+
+ case '2': {
+ // Plain graymap
+
+ for (u64 j = 0; j < image_height; ++j)
+ for (u64 i = 0; i < image_width; ++i) {
+ u64 value;
+ if (!ppm_parse_u64_(&value, &s, eof)) {
+ LOG_error("Failed to parse pixel value.");
+ return 0;
+ }
+ if (value > pixel_depth) {
+ LOG_error("Invalid pixel value.");
+ return 0;
+ }
+ f32 amount = (f32) value / (f32) pixel_depth;
+ pixels[j * image_width + i] = (vec4_f32) {
+ amount,
+ amount,
+ amount,
+ 1.0f,
+ };
+ }
+ } break;
+
+ case '3': {
+ // Plain pixmap
+
+ for (u64 j = 0; j < image_height; ++j)
+ for (u64 i = 0; i < image_width; ++i) {
+ u64 r, g, b;
+ if (!ppm_parse_u64_(&r, &s, eof) || !ppm_parse_u64_(&g, &s, eof) || !ppm_parse_u64_(&b, &s, eof)) {
+ LOG_error("Failed to parse pixel values.");
+ return 0;
+ }
+ if (r > pixel_depth || g > pixel_depth || b > pixel_depth) {
+ LOG_error("Invalid pixel value.");
+ return 0;
+ }
+ pixels[j * image_width + i] = (vec4_f32) {
+ (f32) r / (f32) pixel_depth,
+ (f32) g / (f32) pixel_depth,
+ (f32) b / (f32) pixel_depth,
+ 1.0f,
+ };
+ }
+ } break;
+
+ default:
+ LOG_error("Sanity");
+ return 0;
+ }
+
+ ppm_skip_spaces_(&s, eof);
+
+ if (s < eof) {
+ LOG_error("Unexpected characters.");
+ return 0;
+ }
+
+ return 1;
+}
+
+static i64 len_of_printed_u64_dec_(u64 x) {
+ i64 len = 1;
+
+ while (x >= 10) {
+ ++len;
+ x /= 10;
+ }
+
+ return len;
+}
+
+static b8 print_u64_dec_(c8 **dst, c8 *eof, u64 x) {
+ if (dst == NULL || *dst == NULL || eof == NULL) {
+ LOG_error("Sanity");
+ return 0;
+ }
+
+ u64 m = 1;
+ while (x / m >= 10)
+ m *= 10;
+
+ while (m >= 10) {
+ if (*dst >= eof) {
+ LOG_error("EOF");
+ return 0;
+ }
+
+ **dst = '0' + (x / m);
+ ++*dst;
+
+ x -= (x / m) * m;
+ m /= 10;
+ }
+
+ if (*dst >= eof) {
+ LOG_error("EOF");
+ return 0;
+ }
+
+ **dst = '0' + x;
+ ++*dst;
+
+ return 1;
+}
+
+static b8 print_str_(c8 **dst, c8 *eof, i64 src_len, c8 *src) {
+ if (dst == NULL || *dst == NULL || eof == NULL || src == NULL || src_len < 0) {
+ LOG_error("Sanity");
+ return 0;
+ }
+
+ if (*dst + src_len > eof)
+ return 0;
+
+ c8 *s_end = src + src_len;
+ for (; src < s_end; ++*dst, ++src)
+ **dst = *src;
+
+ return 1;
}
b8 ppm_write_to_memory(i64 *dst_len, u8 *dst, i64 width, i64 height, i64 stride, vec4_f32 *pixels) {
- // TODO
+ if (dst_len == NULL || pixels == NULL) {
+ LOG_error("Sanity");
+ return 0;
+ }
- (void) dst_len;
- (void) dst;
- (void) width;
- (void) height;
- (void) stride;
- (void) pixels;
+ if (width <= 0 || height <= 0 || stride == 0) {
+ LOG_error("Sanity");
+ return 0;
+ }
- LOG_error("Not implemented.");
- return 0;
+ i64 file_size = 0;
+
+ file_size += 3; // magic number
+ file_size += 1 + len_of_printed_u64_dec_((u64) width);
+ file_size += 1 + len_of_printed_u64_dec_((u64) height);
+ file_size += 4; // 255 \n
+
+ for (i64 j = 0; j < height; ++j)
+ for (i64 i = 0; i < width; ++i) {
+ u32 r = (u32) (pixels[j * stride + i].x * 255.0 + 0.5);
+ u32 g = (u32) (pixels[j * stride + i].y * 255.0 + 0.5);
+ u32 b = (u32) (pixels[j * stride + i].z * 255.0 + 0.5);
+
+ file_size += 1 + len_of_printed_u64_dec_(r);
+ file_size += 1 + len_of_printed_u64_dec_(g);
+ file_size += 1 + len_of_printed_u64_dec_(b);
+ }
+
+ if (dst == NULL) {
+ // Only calculate required file size.
+ *dst_len = file_size;
+ return 1;
+ }
+
+ if (*dst_len < file_size) {
+ *dst_len = file_size;
+
+ LOG_error("Not enough space.");
+ return 0;
+ }
+
+ c8 *d = (c8 *) dst;
+ c8 *eof = (c8 *) dst + file_size;
+
+ if (!print_str_ (&d, eof, 3, "P3\n")
+ || !print_u64_dec_(&d, eof, (u64) width) || !print_str_(&d, eof, 1, " ")
+ || !print_u64_dec_(&d, eof, (u64) height) || !print_str_(&d, eof, 1, " ")
+ || !print_u64_dec_(&d, eof, 255) || !print_str_(&d, eof, 1, "\n")
+ ) {
+ LOG_error("Sanity");
+ return 0;
+ }
+
+ for (i64 j = 0; j < height; ++j)
+ for (i64 i = 0; i < width; ++i) {
+ u32 r = (u32) (pixels[j * stride + i].x * 255.0 + 0.5);
+ u32 g = (u32) (pixels[j * stride + i].y * 255.0 + 0.5);
+ u32 b = (u32) (pixels[j * stride + i].z * 255.0 + 0.5);
+
+ if (!print_u64_dec_(&d, eof, r) || !print_str_(&d, eof, 1, " ")
+ || !print_u64_dec_(&d, eof, g) || !print_str_(&d, eof, 1, " ")
+ || !print_u64_dec_(&d, eof, b) || !print_str_(&d, eof, 1, "\n")
+ ) {
+ LOG_error("Sanity");
+ return 0;
+ }
+ }
+
+ return 1;
}
b8 wav_read_from_memory(i64 *sample_rate_hz, i64 *num_channels, i64 *num_samples, i64 frames_len, f32 *frames, i64 src_len, u8 *src) {
@@ -3493,11 +3842,13 @@ typedef struct {
} WL_Output_;
static i32 anonymous_shm_open(void) {
- c8 name[13] = "scr_XXXXXX";
+ c8 name[13] = "scr_000000";
i32 retries = 1000000;
for (i32 i = 0; i < retries; ++i) {
- snprintf(name + 4, 7, "%06d", i);
+ i64 n = len_of_printed_u64_dec_(i);
+ c8 *d = name + sizeof name - 1 - n;
+ print_u64_dec_(&d, name + sizeof name - 1, i);
i32 fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
if (fd >= 0) {
@@ -4203,14 +4554,17 @@ void shutdown_all_systems(void) {
close_all_dynamic_libraries_();
g_platform.input_len = 0;
- resize_dynamic_array_exact(&g_platform.input_capacity, (void **) &g_platform.input, sizeof *g_platform.input, 0);
- resize_dynamic_array_exact(&g_platform.clipboard_text_len, (void **) &g_platform.clipboard_text, 1, 0);
+ resize_dynamic_array_exact(&g_platform.input_capacity, (void **) &g_platform.input, sizeof *g_platform.input, 0);
+
+ resize_dynamic_array_exact(&g_platform.clipboard.text_len, (void **) &g_platform.clipboard.text, 1, 0);
+ resize_dynamic_array_exact(&g_platform.clipboard.image_len, (void **) &g_platform.clipboard.image, 1, 0);
+ resize_dynamic_array_exact(&g_platform.clipboard.sound_len, (void **) &g_platform.clipboard.sound, 1, 0);
}
void request_clipboard_(void) {
if (_requested_clipboard)
return;
- if (!g_platform.enable_clipboard_text && !g_platform.enable_clipboard_image && !g_platform.enable_clipboard_sound)
+ if (!g_platform.clipboard.enable_text && !g_platform.clipboard.enable_image && !g_platform.clipboard.enable_sound)
return;
XConvertSelection(_display, _clipboard, _targets, _clipboard, _window, CurrentTime);
@@ -4375,9 +4729,9 @@ i32 handle_main_window_events(void) {
XGetSelectionOwner(_display, _clipboard) == _window) {
if (ev.xselectionrequest.property != None) {
if (ev.xselectionrequest.target == _targets) {
- b8 has_text = g_platform.clipboard_text_len > 0;
- b8 has_image = g_platform.clipboard_image_len > 0;
- b8 has_sound = g_platform.clipboard_sound_len > 0;
+ b8 has_text = g_platform.clipboard.text_len > 0;
+ b8 has_image = g_platform.clipboard.image_len > 0;
+ b8 has_sound = g_platform.clipboard.sound_len > 0;
if (!has_text && !has_image && !has_sound)
XDeleteProperty(
@@ -4412,8 +4766,8 @@ i32 handle_main_window_events(void) {
ev.xselectionrequest.target,
8,
PropModeReplace,
- (u8 *) g_platform.clipboard_text,
- g_platform.clipboard_text_len
+ (u8 *) g_platform.clipboard.text,
+ g_platform.clipboard.text_len
);
} else if (ev.xselectionrequest.target == _image_bmp) {
// TODO
@@ -4547,40 +4901,40 @@ i32 handle_main_window_events(void) {
break;
}
- if (g_platform.enable_clipboard_text && target_text != None)
+ if (g_platform.clipboard.enable_text && target_text != None)
XConvertSelection(_display, _clipboard, target_text, _clipboard, _window, CurrentTime);
- if (g_platform.enable_clipboard_image && target_image != None)
+ if (g_platform.clipboard.enable_image && target_image != None)
XConvertSelection(_display, _clipboard, target_image, _clipboard, _window, CurrentTime);
- if (g_platform.enable_clipboard_sound && target_sound != None)
+ if (g_platform.clipboard.enable_sound && target_sound != None)
XConvertSelection(_display, _clipboard, target_sound, _clipboard, _window, CurrentTime);
if (target_text == None && target_image == None && target_sound == None)
_requested_clipboard = 0;
} else if (ev.xselection.target == XA_STRING || ev.xselection.target == _text_plain || ev.xselection.target == _utf8_string) {
- if (g_platform.enable_clipboard_text) {
- resize_dynamic_array_exact(&g_platform.clipboard_text_len, (void **) &g_platform.clipboard_text, 1, len + 1);
- if (g_platform.clipboard_text_len == len + 1) {
- mem_cpy_(g_platform.clipboard_text, data, len);
- g_platform.clipboard_text[len] = '\0';
+ if (g_platform.clipboard.enable_text) {
+ resize_dynamic_array_exact(&g_platform.clipboard.text_len, (void **) &g_platform.clipboard.text, 1, len + 1);
+ if (g_platform.clipboard.text_len == len + 1) {
+ mem_cpy_(g_platform.clipboard.text, data, len);
+ g_platform.clipboard.text[len] = '\0';
}
}
_requested_clipboard = 0;
} else if (ev.xselection.target == _image_bmp) {
- if (g_platform.enable_clipboard_image) {
+ if (g_platform.clipboard.enable_image) {
// TODO
LOG_error("Receive BMP image - not implemented.");
}
_requested_clipboard = 0;
} else if (ev.xselection.target == _image_ppm) {
- if (g_platform.enable_clipboard_image) {
+ if (g_platform.clipboard.enable_image) {
// TODO
LOG_error("Receive PPM image - not implemented.");
}
_requested_clipboard = 0;
} else if (ev.xselection.target == _audio_wav) {
- if (g_platform.enable_clipboard_sound) {
+ if (g_platform.clipboard.enable_sound) {
// TODO
LOG_error("Receive WAV audio - not implemented.");
}
@@ -4785,7 +5139,7 @@ void write_clipboard_text(i64 size, c8 *data) {
store_clipboard_text_(size, data);
- if (g_platform.clipboard_text_len > 0)
+ if (g_platform.clipboard.text_len > 0)
XSetSelectionOwner(_display, _clipboard, _window, CurrentTime);
}
@@ -5757,6 +6111,8 @@ TEST("BMP image") {
resize_dynamic_array_exact(&pixels_len, (void **) &pixels, sizeof *pixels, 0);
resize_dynamic_array_exact(&bytes_len, (void **) &bytes, sizeof *bytes, 0);
+
+ // TODO: Test with actual images.
}
TEST("PPM image") {
@@ -5822,6 +6178,8 @@ TEST("PPM image") {
resize_dynamic_array_exact(&pixels_len, (void **) &pixels, sizeof *pixels, 0);
resize_dynamic_array_exact(&bytes_len, (void **) &bytes, sizeof *bytes, 0);
+
+ // TODO: Test with actual images.
}
TEST("WAV audio") {
@@ -5889,6 +6247,8 @@ TEST("WAV audio") {
resize_dynamic_array_exact(&frames_len, (void **) &frames, sizeof *frames, 0);
resize_dynamic_array_exact(&bytes_len, (void **) &bytes, sizeof *bytes, 0);
+
+ // TODO: Test with actual wav files.
}
#undef TEST_FILE