#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define require_equals(a, b) do { if((a) != (b)) { fprintf(stderr, "Assert failed on line %d: %s == %s\n", __LINE__, #a, #b); abort(); } } while(0) #define require_not_equals(a, b) do { if((a) == (b)) { fprintf(stderr, "Assert failed on line %d: %s != %s\n", __LINE__, #a, #b); abort(); } } while(0) typedef struct { mgl_texture *texture; mgl_font *font; mgl_font *cjk_font; mgl_vertex_buffer *vertex_buffer1; mgl_shader_program *shader_program; mgl_clock clock; int fps; int fps_counter; } Userdata; static void draw(mgl_window *window, void *userdata) { mgl_context *context = mgl_get_context(); Userdata *u = userdata; mgl_shader_program_set_uniform_vec2f(u->shader_program, "resolution", (mgl_vec2f){ u->texture->width, u->texture->height }); static float rot = 0.0f; rot += 5.0f; mgl_sprite sprite; mgl_sprite_init(&sprite, u->texture); mgl_sprite_set_position(&sprite, (mgl_vec2f){ 100.0f - 10.0f + u->texture->width * 0.5f * 2.0f, u->texture->height * 0.5f * 2.0f }); mgl_sprite_set_color(&sprite, (mgl_color){255, 255, 255, 128}); mgl_sprite_set_rotation(&sprite, rot); mgl_sprite_set_origin(&sprite, (mgl_vec2f){ u->texture->width * 0.5f, u->texture->height * 0.5f }); sprite.scale = (mgl_vec2f){ 2.0f, 2.0f }; mgl_shader_program_use(u->shader_program); mgl_sprite_draw(context, &sprite); mgl_shader_program_use(NULL); ++u->fps_counter; if(mgl_clock_get_elapsed_time_seconds(&u->clock) >= 1.0) { mgl_clock_restart(&u->clock); u->fps = u->fps_counter; u->fps_counter = 0; fprintf(stderr, "fps: %d\n", u->fps); } char str[512]; snprintf(str, sizeof(str), "|,.-_/1234567890½!\"#¤%%&/()=?¡@£$€¥{[]}\\'abcdefghijklmnopqrstuvwxyzáàäåãâíìîïúùũüûéèẽëêóòõôöABCDEFGHIJKLMNOPQRSTUVWXYZÁÀÄÅÃÂÍÌÎÏÚÙŨÜÛÉÈẼËÊÓÒÕÔÖ └> pacman -Q quickmedia-git"); mgl_text text; mgl_text_init(&text, u->font, str, strlen(str)); mgl_text_draw(context, &text); mgl_text_deinit(&text); const char *cjk_str = "饕餮 로 초성, 중성, 종성을 東京都と周辺7県で首都圏を構成\n" "【システマ流】誰でも出来る「不眠.ストレス」に打ち勝つ呼吸法(東京都・埼玉県・千葉県・神奈川県)の総人口は約 千葉県・神奈川日本の民間研究所が年に発表した「世界の都市総合力ランキング」では、ロンド\n" "ンとニューヨークに次ぐ世界3位と評価された年に首都圏整備法の施行に伴い廃止された。このように首都建設法の廃止により区部の東部には、隅田川、荒川、江戸川、中川などの河口部に沖積平野が広がっ日本国内におけ\n" "る気候区分では23区〜多摩東部および伊豆諸島は太平洋側気候、多摩西部などは中央高地式気候に属する。小笠原諸島は南日本気候である。特徴としては、四季の変化が明瞭であり、天気が日によって変化しやすい。夏季は\n" "高温・多雨となり、冬季は晴れて乾燥する東京都区部 - 気象庁露場のあった大手町付近の観測による最低気温が最も高くなることも珍しくなかった。しかし、夏場の最高気温自体はそれほど高くもない。一方、内陸寄りにあ\n" "る練馬区のアメダス観測[注 7][22]地域では冬日は珍しくなく、新宿区や渋谷区などの都心部でも冬日の観測はよく見られる。また、気象観測所のある千代田区内におい足立区、荒川区、板橋区、江戸川区、大田区、葛飾区、\n" "北区、江東区、品川区、渋谷区、新宿区、杉並区、墨田区、世田谷区、台東区、中央区、千代田区、豊島区、中野区、練馬区、文京区、港区、目黒区昭島市、あきる野市、稲城市、青梅市、清瀬市、国立市、小金井市、国分寺市、\n" "小平市、狛江市、立川市、多摩市、調布市、西東京市、八王子市、羽村市、東久留米市、東村山市、東大和市、日野市、府中市、福生市、町田市、三鷹市、武蔵野市、武蔵村山市"; mgl_text cjk_text; mgl_text_init(&cjk_text, u->cjk_font, cjk_str, strlen(cjk_str)); mgl_text_set_position(&cjk_text, (mgl_vec2f){ 0.0f, 500.0f }); mgl_text_draw(context, &cjk_text); mgl_text_deinit(&cjk_text); mgl_view prev_view; mgl_window_get_view(window, &prev_view); mgl_vec2i view_size = { .x = 200, .y = 200 }; mgl_view new_view = { .position = { window->size.x/2 - view_size.x/2, window->size.y/2 - view_size.y/2 }, .size = view_size }; mgl_window_set_view(window, &new_view); mgl_vertex vertices[4] = { (mgl_vertex){ .position = {-new_view.position.x + window->cursor_position.x, -new_view.position.y + window->cursor_position.y}, .texcoords = {0.0f, 0.0f}, .color = {255, 0, 0, 255} }, (mgl_vertex){ .position = {-new_view.position.x + window->cursor_position.x + 400.0f, -new_view.position.y + window->cursor_position.y}, .texcoords = {0.0f, 0.0f}, .color = {0, 255, 0, 255}, }, (mgl_vertex){ .position = {-new_view.position.x + window->cursor_position.x + 400.0f, -new_view.position.y + window->cursor_position.y + 400.0f}, .texcoords = {0.0f, 0.0f}, .color = {0, 0, 255, 255}, }, (mgl_vertex){ .position = {-new_view.position.x + window->cursor_position.x, -new_view.position.y + window->cursor_position.y + 400.0f}, .texcoords = {0.0f, 0.0f}, .color = {255, 255, 0, 255} } }; mgl_vertices_draw(context, vertices, 4, MGL_PRIMITIVE_QUADS, (mgl_vec2f){ 0.0f, 0.0f }); mgl_window_set_view(window, &prev_view); mgl_rectangle rect = { .position = { window->cursor_position.x, window->cursor_position.y }, .size = { u->font->texture.width, u->font->texture.height }, .color = { 255, 255, 255, 255 }, }; mgl_rectangle_draw(context, &rect); mgl_vertex vertices1[4] = { (mgl_vertex){ .position = {0.0f, 0.0f}, .texcoords = {0.0f, 0.0f}, .color = {255, 0, 0, 100} }, (mgl_vertex){ .position = {u->font->texture.width, 0.0f}, .texcoords = {u->font->texture.width, 0.0f}, .color = {0, 255, 0, 100}, }, (mgl_vertex){ .position = {u->font->texture.width, u->font->texture.height}, .texcoords = {u->font->texture.width, u->font->texture.height}, .color = {0, 0, 255, 100}, }, (mgl_vertex){ .position = {0.0f, u->font->texture.height}, .texcoords = {0.0f, u->font->texture.height}, .color = {255, 0, 255, 100} } }; mgl_vertex_buffer_update(u->vertex_buffer1, vertices1, 4, MGL_PRIMITIVE_QUADS, MGL_USAGE_STREAM); mgl_vertex_buffer_set_position(u->vertex_buffer1, (mgl_vec2f){ window->cursor_position.x, window->cursor_position.y }); mgl_vertex_buffer_draw(context, u->vertex_buffer1, &u->font->texture); } static void test_hash_map_get_insert(mgl_font_char_map *char_map, uint32_t unicode, bool exists) { mgl_font_glyph font_glyph; font_glyph.advance = 5.0f; if(exists) { require_not_equals(mgl_font_char_map_get(char_map, unicode), NULL); } else { require_equals(mgl_font_char_map_get(char_map, unicode), NULL); require_equals(mgl_font_char_map_insert(char_map, unicode, &font_glyph, NULL), 0); } } static void test_hash_map() { mgl_font_char_map char_map; mgl_font_char_map_init(&char_map); test_hash_map_get_insert(&char_map, 'h', false); test_hash_map_get_insert(&char_map, 'e', false); test_hash_map_get_insert(&char_map, 'l', false); test_hash_map_get_insert(&char_map, 'l', true); test_hash_map_get_insert(&char_map, 'o', false); test_hash_map_get_insert(&char_map, ' ', false); test_hash_map_get_insert(&char_map, 'f', false); test_hash_map_get_insert(&char_map, 'p', false); test_hash_map_get_insert(&char_map, 's', false); require_not_equals(mgl_font_char_map_get(&char_map, 'h'), NULL); require_not_equals(mgl_font_char_map_get(&char_map, 'e'), NULL); require_not_equals(mgl_font_char_map_get(&char_map, 'l'), NULL); require_not_equals(mgl_font_char_map_get(&char_map, 'o'), NULL); require_not_equals(mgl_font_char_map_get(&char_map, ' '), NULL); require_not_equals(mgl_font_char_map_get(&char_map, 'f'), NULL); require_not_equals(mgl_font_char_map_get(&char_map, 'p'), NULL); require_not_equals(mgl_font_char_map_get(&char_map, 's'), NULL); mgl_font_char_map_deinit(&char_map); } static void test_utf8() { uint32_t codepoint = 0; size_t codepoint_length = 0; require_equals(mgl_utf8_decode("a", 1, &codepoint, &codepoint_length), true); require_equals(codepoint, 0x61); require_equals(codepoint_length, 1); require_equals(mgl_utf8_decode("á", 2, &codepoint, &codepoint_length), true); require_equals(codepoint, 0xE1); require_equals(codepoint_length, 2); require_equals(mgl_utf8_decode("‡", 3, &codepoint, &codepoint_length), true); require_equals(codepoint, 0x2021); require_equals(codepoint_length, 3); require_equals(mgl_utf8_decode("𒀀", 4, &codepoint, &codepoint_length), true); require_equals(codepoint, 0x12000); require_equals(codepoint_length, 4); require_equals(mgl_utf8_get_start_of_codepoint("abc", 3, 0), 0); require_equals(mgl_utf8_get_start_of_codepoint("abc", 3, 2), 2); require_equals(mgl_utf8_get_start_of_codepoint("abö", 3, 4), 2); } static bool clipboard_callback(const unsigned char *data, size_t size, mgl_clipboard_type clipboard_type, void *userdata) { fprintf(stderr, "clipboard type: %d, size: %zu, data: %s\n", clipboard_type, size, data); return true; } int main(int argc, char **argv) { test_hash_map(); test_utf8(); if(mgl_init() != 0) return 1; mgl_texture texture; mgl_memory_mapped_file font_file; mgl_memory_mapped_file cjk_font_file; mgl_font font; mgl_font cjk_font; mgl_vertex_buffer vertex_buffer1; mgl_shader_program shader_program; Userdata userdata; userdata.texture = &texture; userdata.font = &font; userdata.cjk_font = &cjk_font; userdata.vertex_buffer1 = &vertex_buffer1; userdata.shader_program = &shader_program; userdata.fps = 0; userdata.fps_counter = 0; mgl_clock_init(&userdata.clock); mgl_window window; if(mgl_window_create(&window, "mgl", &(mgl_window_create_params){ .size = {1280, 720}, .min_size = { 1000, 1000 } }) != 0) return 1; if(mgl_texture_init(&texture) != 0) return 1; if(mgl_texture_load_from_file(&texture, "tests/X11.jpg", &(mgl_texture_load_options){ false, false }) != 0) return 1; if(mgl_mapped_file_load("/usr/share/fonts/TTF/Hack-Regular.ttf", &font_file, &(mgl_memory_mapped_file_load_options){ .readable = true, .writable = false }) != 0) return 1; if(mgl_mapped_file_load("/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc", &cjk_font_file, &(mgl_memory_mapped_file_load_options){ .readable = true, .writable = false }) != 0) return 1; if(mgl_font_load_from_file(&font, &font_file, 26) != 0) return 1; if(mgl_font_load_from_file(&cjk_font, &cjk_font_file, 26) != 0) return 1; if(mgl_shader_program_init(&shader_program) != 0) return 1; if(mgl_shader_program_add_shader_from_file(&shader_program, "tests/circle_mask.glsl", MGL_SHADER_FRAGMENT) != 0) return 1; if(mgl_shader_program_finalize(&shader_program) != 0) return 1; mgl_vertex_buffer_init(&vertex_buffer1); mgl_event event; while(mgl_window_is_open(&window)) { while(mgl_window_poll_event(&window, &event)) { switch(event.type) { case MGL_EVENT_TEXT_ENTERED: { fprintf(stderr, "text event, codepoint: %u, str: %s\n", event.text.codepoint, event.text.str); break; } case MGL_EVENT_KEY_PRESSED: { if(event.key.code == MGL_KEY_C) { const char *str = "東京都, Tōkyō-to"; mgl_window_set_clipboard(&window, str, strlen(str)); fprintf(stderr, "Copied '%s' to the clipboard\n", str); } else if(event.key.code == MGL_KEY_V) { if(mgl_window_get_clipboard(&window, clipboard_callback, NULL, MGL_CLIPBOARD_TYPE_ALL)) { fprintf(stderr, "Successfully copied clipboard!\n"); } else { fprintf(stderr, "Failed to copy clipboard!\n"); } } else if(event.key.code == MGL_KEY_F) { mgl_window_set_fullscreen(&window, !mgl_window_is_fullscreen(&window)); } else if(event.key.code == MGL_KEY_X) { bool enable_vsync = !mgl_window_is_vsync_enabled(&window); mgl_window_set_vsync_enabled(&window, enable_vsync); fprintf(stderr, "vsync %s\n", enable_vsync ? "enabled" : "disabled"); } fprintf(stderr, "key press event, code: %u\n", event.key.code); break; } case MGL_EVENT_KEY_RELEASED: { fprintf(stderr, "key release event, code: %u\n", event.key.code); break; } } } mgl_window_clear(&window, (mgl_color){0, 0, 0, 255}); draw(&window, &userdata); mgl_window_display(&window); } mgl_vertex_buffer_deinit(&vertex_buffer1); mgl_shader_program_deinit(&shader_program); mgl_font_unload(&cjk_font); mgl_font_unload(&font); mgl_mapped_file_unload(&cjk_font_file); mgl_mapped_file_unload(&font_file); mgl_texture_unload(&texture); mgl_window_deinit(&window); mgl_deinit(); return 0; }