diff options
168 files changed, 58495 insertions, 0 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..0bd9523 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,72 @@ +stages: + - build + - deploy + +.variables_template: &variables_definition + variables: + BASE_NAME: "sherlock.lv2" + PKG_CONFIG_PATH: "/opt/lv2/lib/pkgconfig:/opt/${CI_BUILD_NAME}/lib/pkgconfig:/usr/lib/${CI_BUILD_NAME}/pkgconfig" + TOOLCHAIN_FILE: "${CI_PROJECT_DIR}/cmake/${CI_BUILD_NAME}.cmake" + +.common_template: &common_definition + <<: *variables_definition + stage: build + artifacts: + name: "${BASE_NAME}-$(cat VERSION)-${CI_BUILD_NAME}" + paths: + - "${BASE_NAME}-$(cat VERSION)/" + +.build_template: &build_definition + <<: *common_definition + script: + - mkdir build + - pushd build + - cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=${CI_PROJECT_DIR} -DPLUGIN_DEST="${BASE_NAME}-$(cat ../VERSION)/${CI_BUILD_NAME}/${BASE_NAME}" -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_FILE} .. + - cmake .. # needed for darwin + - make + - make install + +.universal_linux_template: &universal_linux_definition + image: ventosus/universal-linux-gnu + <<: *build_definition + +.arm_linux_template: &arm_linux_definition + image: ventosus/arm-linux-gnueabihf + <<: *build_definition + +.universal_w64_template: &universal_w64_definition + image: ventosus/universal-w64-mingw32 + <<: *build_definition + +.universal_apple_template: &universal_apple_definition + image: ventosus/universal-apple-darwin + <<: *build_definition + +# building in docker +x86_64-linux-gnu: + <<: *universal_linux_definition + +i686-linux-gnu: + <<: *universal_linux_definition + +arm-linux-gnueabihf: + <<: *arm_linux_definition + +x86_64-w64-mingw32: + <<: *universal_w64_definition + +i686-w64-mingw32: + <<: *universal_w64_definition + +universal-apple-darwin: + <<: *universal_apple_definition + +pack: + <<: *variables_definition + stage: deploy + script: + - echo 'packing up...' + artifacts: + name: "${BASE_NAME}-$(cat VERSION)" + paths: + - "${BASE_NAME}-$(cat VERSION)/" diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..22803e6 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,153 @@ +cmake_minimum_required(VERSION 2.8) + +project(sherlock.lv2) + +include_directories(${PROJECT_SOURCE_DIR}) +include_directories(${PROJECT_SOURCE_DIR}/osc.lv2) +include_directories(${PROJECT_SOURCE_DIR}/props.lv2) +include_directories(${PROJECT_SOURCE_DIR}/pugl) + +set(CMAKE_FIND_FRAMEWORK FIRST) + +set(CMAKE_C_FLAGS "-fdata-sections -ffunction-sections ${CMAKE_C_FLAGS}") +set(CMAKE_C_FLAGS "-std=gnu11 -Wextra -Wno-unused-parameter -ffast-math -fvisibility=hidden ${CMAKE_C_FLAGS}") +set(CMAKE_C_FLAGS "-Wshadow -Wimplicit-function-declaration -Wredundant-decls -Wmissing-prototypes -Wstrict-prototypes ${CMAKE_C_FLAGS}") + +if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + set(CMAKE_MODULE_LINKER_FLAGS "-Wl,-z,defs ${CMAKE_MODULE_LINKER_FLAGS}") + set(CMAKE_MODULE_LINKER_FLAGS "-Wl,-z,nodelete ${CMAKE_MODULE_LINKER_FLAGS}") +elseif(WIN32) + set(CMAKE_C_FLAGS "-mstackrealign ${CMAKE_C_FLAGS}") +endif() + +if(APPLE) + set(CMAKE_MODULE_LINKER_FLAGS "-Wl,-dead_strip ${CMAKE_MODULE_LINKER_FLAGS}") +else() + set(CMAKE_MODULE_LINKER_FLAGS "-Wl,--gc-sections -Wl,-s ${CMAKE_MODULE_LINKER_FLAGS}") +endif() + +add_definitions("-D_GNU_SOURCE=1") # asprintf + +if(WIN32) + set(SHERLOCK_UI_TYPE "WindowsUI") +elseif(APPLE) + set(SHERLOCK_UI_TYPE "CocoaUI") +else() + set(SHERLOCK_UI_TYPE "X11UI") +endif() + +file(STRINGS "VERSION" SHERLOCK_VERSION) +string(REPLACE "." ";" VERSION_LIST ${SHERLOCK_VERSION}) +list(GET VERSION_LIST 0 SHERLOCK_MAJOR_VERSION) +list(GET VERSION_LIST 1 SHERLOCK_MINOR_VERSION) +list(GET VERSION_LIST 2 SHERLOCK_MICRO_VERSION) +add_definitions("-DSHERLOCK_VERSION=\"${SHERLOCK_VERSION}\"") + +if(NOT DEFINED PLUGIN_DEST) + set(PLUGIN_DEST lib/lv2/sherlock.lv2) +endif() + +find_package(PkgConfig) # ${PKG_CONFIG_FOUND} + + +set(LIBS_UI m) + +pkg_search_module(LV2 REQUIRED lv2>=1.10) +include_directories(${LV2_INCLUDE_DIRS}) + +find_package(OpenGL) +if(${OPENGL_FOUND}) + set(LIBS_UI ${LIBS_UI} ${OPENGL_LIBRARIES}) +else() # try pkg-config + pkg_search_module(GL REQUIRED gl) + if(${GL_FOUND}) + set(LIBS_UI ${LIBS_UI} ${GL_LDFLAGS}) + else() + message(FATAL_ERROR "OpenGL not found") + endif() +endif() +add_definitions("-DPUGL_HAVE_GL") + +if(WIN32) + find_library(GDI32_LIBRARY NAMES gdi32) + if(GDI32_LIBRARY) + set(LIBS_UI ${LIBS_UI} ${GDI32_LIBRARY}) + else() + message(FATAL_ERROR "gdi32 library not found") + endif() + + find_library(USER32_LIBRARY NAMES user32) + if(USER32_LIBRARY) + set(LIBS_UI ${LIBS_UI} ${USER32_LIBRARY}) + else() + message(FATAL_ERROR "user32 library not found") + endif() + + set(TAR_UI ${TAR_UI} pugl/pugl/pugl_win.cpp) + +elseif(APPLE) + find_library(COCOA_LIBRARY NAMES Cocoa) + if(COCOA_LIBRARY) + set(LIBS_UI ${LIBS_UI} ${COCOA_LIBRARY}) + else() + message(FATAL_ERROR "Cocoa framework not found") + endif() + + set(TAR_UI ${TAR_UI} pugl/pugl/pugl_osx.m) + +else() # GNU/Linux + pkg_search_module(X11 REQUIRED x11>=1.6) + include_directories(${X11_INCLUDE_DIRS}) + set(LIBS_UI ${LIBS_UI} ${X11_LDFLAGS}) + + pkg_search_module(XEXT REQUIRED xext>=1.3) + include_directories(${XEXT_INCLUDE_DIRS}) + set(LIBS_UI ${LIBS_UI} ${XEXT_LDFLAGS}) + + set(TAR_UI ${TAR_UI} pugl/pugl/pugl_x11.c) +endif() + +pkg_search_module(SRATOM REQUIRED sratom-0>=0.4.0) +include_directories(${SRATOM_INCLUDE_DIRS}) +if(DEFINED STATIC_SRATOM) + set(LIBS_UI ${STATIC_SRATOM} ${STATIC_SORD} ${STATIC_SERD} ${LIBS_UI}) +else() + set(LIBS_UI ${LIBS_UI} ${SRATOM_LDFLAGS}) +endif() + + +add_library(sherlock MODULE + sherlock.c + atom_inspector.c + midi_inspector.c + osc_inspector.c) +set_target_properties(sherlock PROPERTIES PREFIX "") +if(NOT WIN32) + set_target_properties(sherlock PROPERTIES LINK_FLAGS "-Wl,-e,lv2_descriptor") +endif() +install(TARGETS sherlock DESTINATION ${PLUGIN_DEST}) + +#find_package(FLEX) +#flex_target(encoder encoder.l ${PROJECT_BINARY_DIR}/encoder.c +# COMPILE_FLAGS "--header-file=${PROJECT_BINARY_DIR}/encoder.h --prefix=enc") + +add_library(sherlock_nk MODULE + sherlock_nk.c + midi_inspector_nk.c + atom_inspector_nk.c + osc_inspector_nk.c + ${TAR_UI}) +target_link_libraries(sherlock_nk + ${LIBS_UI}) +set_target_properties(sherlock_nk PROPERTIES PREFIX "") +if(NOT WIN32) + set_target_properties(sherlock_nk PROPERTIES LINK_FLAGS "-Wl,-e,lv2ui_descriptor") +endif() +install(TARGETS sherlock_nk DESTINATION ${PLUGIN_DEST}) + +configure_file(${PROJECT_SOURCE_DIR}/manifest.ttl.in ${PROJECT_BINARY_DIR}/manifest.ttl) +install(FILES ${PROJECT_BINARY_DIR}/manifest.ttl DESTINATION ${PLUGIN_DEST}) +install(FILES ${PROJECT_SOURCE_DIR}/sherlock.ttl DESTINATION ${PLUGIN_DEST}) +install(FILES ${PROJECT_SOURCE_DIR}/sherlock_ui.ttl DESTINATION ${PLUGIN_DEST}) + +install(FILES ${PROJECT_SOURCE_DIR}/nuklear/extra_font/Cousine-Regular.ttf DESTINATION ${PLUGIN_DEST}) diff --git a/README.md b/README.md new file mode 100644 index 0000000..2c57307 --- /dev/null +++ b/README.md @@ -0,0 +1,43 @@ +# Sherlock + +## An investigative LV2 plugin bundle + +### Webpage + +Get more information at: [http://open-music-kontrollers.ch/lv2/sherlock](http://open-music-kontrollers.ch/lv2/sherlock) + +### Build status + +[](https://gitlab.com/OpenMusicKontrollers/sherlock.lv2/commits/master) + +### Dependencies + +* [LV2](http://lv2plug.in) (LV2 Plugin Standard) +* [pugl](http://drobilla.net/software/pugl) (Portable API for OpenGL GUIs) +* [nuklear](https://github.com/vurtun/nuklear) (Immediate-mode GUI) + +### Build / install + + git clone https://github.com/OpenMusicKontrollers/sherlock.lv2.git + cd sherlock.lv2 + mkdir build + cd build + make + sudo make install + +### License + +Copyright (c) 2015-2016 Hanspeter Portner (dev@open-music-kontrollers.ch) + +This is free software: you can redistribute it and/or modify +it under the terms of the Artistic License 2.0 as published by +The Perl Foundation. + +This source is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +Artistic License 2.0 for more details. + +You should have received a copy of the Artistic License 2.0 +along the source as a COPYING file. If not, obtain it from +<http://www.perlfoundation.org/artistic_license_2_0>. @@ -0,0 +1 @@ +0.11.2731 diff --git a/atom_inspector.c b/atom_inspector.c new file mode 100644 index 0000000..9e71b43 --- /dev/null +++ b/atom_inspector.c @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2015-2016 Hanspeter Portner (dev@open-music-kontrollers.ch) + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the Artistic License 2.0 as published by + * The Perl Foundation. + * + * This source is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Artistic License 2.0 for more details. + * + * You should have received a copy of the Artistic License 2.0 + * along the source as a COPYING file. If not, obtain it from + * http://www.perlfoundation.org/artistic_license_2_0. + */ + +#include <stdio.h> +#include <stdlib.h> + +#include <sherlock.h> + +typedef struct _handle_t handle_t; + +struct _handle_t { + LV2_URID_Map *map; + const LV2_Atom_Sequence *control_in; + LV2_Atom_Sequence *control_out; + LV2_Atom_Sequence *notify; + LV2_Atom_Forge forge; + + LV2_URID time_position; + LV2_URID time_frame; + + int64_t frame; + + PROPS_T(props, MAX_NPROPS); + state_t state; + state_t stash; +}; + +static LV2_Handle +instantiate(const LV2_Descriptor* descriptor, double rate, + const char *bundle_path, const LV2_Feature *const *features) +{ + int i; + handle_t *handle = calloc(1, sizeof(handle_t)); + if(!handle) + return NULL; + + for(i=0; features[i]; i++) + if(!strcmp(features[i]->URI, LV2_URID__map)) + handle->map = (LV2_URID_Map *)features[i]->data; + + if(!handle->map) + { + fprintf(stderr, "%s: Host does not support urid:map\n", descriptor->URI); + free(handle); + return NULL; + } + + handle->time_position = handle->map->map(handle->map->handle, LV2_TIME__Position); + handle->time_frame = handle->map->map(handle->map->handle, LV2_TIME__frame); + + lv2_atom_forge_init(&handle->forge, handle->map); + + if(!props_init(&handle->props, MAX_NPROPS, descriptor->URI, handle->map, handle)) + { + fprintf(stderr, "failed to allocate property structure\n"); + free(handle); + return NULL; + } + + if( !props_register(&handle->props, &stat_overwrite, &handle->state.overwrite, &handle->stash.overwrite) + || !props_register(&handle->props, &stat_block, &handle->state.block, &handle->stash.block) + || !props_register(&handle->props, &stat_follow, &handle->state.follow, &handle->stash.follow) ) + { + free(handle); + return NULL; + } + + return handle; +} + +static void +connect_port(LV2_Handle instance, uint32_t port, void *data) +{ + handle_t *handle = (handle_t *)instance; + + switch(port) + { + case 0: + handle->control_in = (const LV2_Atom_Sequence *)data; + break; + case 1: + handle->control_out = (LV2_Atom_Sequence *)data; + break; + case 2: + handle->notify = (LV2_Atom_Sequence *)data; + break; + default: + break; + } +} + +static void +run(LV2_Handle instance, uint32_t nsamples) +{ + handle_t *handle = (handle_t *)instance; + uint32_t capacity; + LV2_Atom_Forge *forge = &handle->forge; + LV2_Atom_Forge_Frame frame; + LV2_Atom_Forge_Ref ref; + + // size of input sequence + const size_t size = lv2_atom_total_size(&handle->control_in->atom); + + capacity = handle->control_out->atom.size; + lv2_atom_forge_set_buffer(forge, (uint8_t *)handle->control_out, capacity); + ref = lv2_atom_forge_sequence_head(forge, &frame, 0); + + LV2_ATOM_SEQUENCE_FOREACH(handle->control_in, ev) + { + const LV2_Atom_Object *obj = (const LV2_Atom_Object *)&ev->body; + const int64_t frames = ev->time.frames; + + // copy all events to through port + if(ref) + ref = lv2_atom_forge_frame_time(forge, frames); + if(ref) + ref = lv2_atom_forge_raw(forge, &obj->atom, lv2_atom_total_size(&obj->atom)); + if(ref) + lv2_atom_forge_pad(forge, obj->atom.size); + + if( !props_advance(&handle->props, forge, frames, obj, &ref) + && lv2_atom_forge_is_object_type(forge, obj->atom.type) + && (obj->body.otype == handle->time_position) ) + { + const LV2_Atom_Long *time_frame = NULL; + lv2_atom_object_get(obj, handle->time_frame, &time_frame, NULL); + if(time_frame) + handle->frame = time_frame->body - frames; + } + } + + if(ref) + lv2_atom_forge_pop(forge, &frame); + else + lv2_atom_sequence_clear(handle->control_out); + + // forge whole sequence as single event + capacity = handle->notify->atom.size; + lv2_atom_forge_set_buffer(forge, (uint8_t *)handle->notify, capacity); + ref = lv2_atom_forge_sequence_head(forge, &frame, 0); + + // only serialize sequence to UI if there were actually any events + if(handle->control_in->atom.size > sizeof(LV2_Atom_Sequence_Body)) + { + LV2_Atom_Forge_Frame tup_frame; + + if(ref) + ref = lv2_atom_forge_frame_time(forge, 0); + if(ref) + ref = lv2_atom_forge_tuple(forge, &tup_frame); + if(ref) + ref = lv2_atom_forge_long(forge, handle->frame); + if(ref) + ref = lv2_atom_forge_int(forge, nsamples); + if(ref) + ref = lv2_atom_forge_write(forge, handle->control_in, size); + if(ref) + lv2_atom_forge_pop(forge, &tup_frame); + + } + + if(ref) + lv2_atom_forge_pop(forge, &frame); + else + lv2_atom_sequence_clear(handle->notify); + + handle->frame += nsamples; +} + +static void +cleanup(LV2_Handle instance) +{ + handle_t *handle = (handle_t *)instance; + + free(handle); +} + +static LV2_State_Status +_state_save(LV2_Handle instance, LV2_State_Store_Function store, + LV2_State_Handle state, uint32_t flags, + const LV2_Feature *const *features) +{ + handle_t *handle = instance; + + return props_save(&handle->props, &handle->forge, store, state, flags, features); +} + +static LV2_State_Status +_state_restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve, + LV2_State_Handle state, uint32_t flags, + const LV2_Feature *const *features) +{ + handle_t *handle = instance; + + return props_restore(&handle->props, &handle->forge, retrieve, state, flags, features); +} + +static const LV2_State_Interface state_iface = { + .save = _state_save, + .restore = _state_restore +}; + +static const void * +extension_data(const char *uri) +{ + if(!strcmp(uri, LV2_STATE__interface)) + return &state_iface; + return NULL; +} + +const LV2_Descriptor atom_inspector = { + .URI = SHERLOCK_ATOM_INSPECTOR_URI, + .instantiate = instantiate, + .connect_port = connect_port, + .activate = NULL, + .run = run, + .deactivate = NULL, + .cleanup = cleanup, + .extension_data = extension_data +}; diff --git a/atom_inspector_nk.c b/atom_inspector_nk.c new file mode 100644 index 0000000..07b1a9e --- /dev/null +++ b/atom_inspector_nk.c @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2015-2016 Hanspeter Portner (dev@open-music-kontrollers.ch) + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the Artistic License 2.0 as published by + * The Perl Foundation. + * + * This source is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Artistic License 2.0 for more details. + * + * You should have received a copy of the Artistic License 2.0 + * along the source as a COPYING file. If not, obtain it from + * http://www.perlfoundation.org/artistic_license_2_0. + */ + +#include <ctype.h> +#include <inttypes.h> + +#include <sherlock.h> +#include <sherlock_nk.h> + +#define NS_RDF (const uint8_t*)"http://www.w3.org/1999/02/22-rdf-syntax-ns#" +#define NS_RDFS (const uint8_t*)"http://www.w3.org/2000/01/rdf-schema#" +#define NS_XSD (const uint8_t*)"http://www.w3.org/2001/XMLSchema#" +#define NS_OSC (const uint8_t*)"http://open-music-kontrollers.ch/lv2/osc#" +#define NS_XPRESS (const uint8_t*)"http://open-music-kontrollers.ch/lv2/xpress#" +#define NS_SPOD (const uint8_t*)"http://open-music-kontrollers.ch/lv2/synthpod#" +#define NS_CANVAS (const uint8_t*)"http://open-music-kontrollers.ch/lv2/canvas#" + +// copyied and adapted from libsratom +static inline char * +_sratom_to_turtle(Sratom* sratom, + LV2_URID_Unmap* unmap, + const char* base_uri, + const SerdNode* subject, + const SerdNode* predicate, + uint32_t type, + uint32_t size, + const void* body) +{ + SerdURI buri = SERD_URI_NULL; + SerdNode base = serd_node_new_uri_from_string((uint8_t *)(base_uri), NULL, &buri); + SerdEnv* env = serd_env_new(&base); + SerdChunk str = { NULL, 0 }; + + serd_env_set_prefix_from_strings(env, (const uint8_t *)"rdf", NS_RDF); + serd_env_set_prefix_from_strings(env, (const uint8_t *)"rdfs", NS_RDFS); + serd_env_set_prefix_from_strings(env, (const uint8_t *)"xsd", NS_XSD); + serd_env_set_prefix_from_strings(env, (const uint8_t *)"lv2", (const uint8_t *)LV2_CORE_PREFIX); + serd_env_set_prefix_from_strings(env, (const uint8_t *)"midi", (const uint8_t *)LV2_MIDI_PREFIX); + serd_env_set_prefix_from_strings(env, (const uint8_t *)"atom", (const uint8_t *)LV2_ATOM_PREFIX); + serd_env_set_prefix_from_strings(env, (const uint8_t *)"units", (const uint8_t *)LV2_UNITS_PREFIX); + serd_env_set_prefix_from_strings(env, (const uint8_t *)"ui", (const uint8_t *)LV2_UI_PREFIX); + serd_env_set_prefix_from_strings(env, (const uint8_t *)"time", (const uint8_t *)LV2_TIME_URI"#"); + serd_env_set_prefix_from_strings(env, (const uint8_t *)"patch", (const uint8_t *)LV2_PATCH_PREFIX); + + serd_env_set_prefix_from_strings(env, (const uint8_t *)"osc", NS_OSC); + serd_env_set_prefix_from_strings(env, (const uint8_t *)"xpress", NS_XPRESS); + serd_env_set_prefix_from_strings(env, (const uint8_t *)"spod", NS_SPOD); + serd_env_set_prefix_from_strings(env, (const uint8_t *)"canvas", NS_CANVAS); + + SerdWriter* writer = serd_writer_new( + SERD_TURTLE, + (SerdStyle)(SERD_STYLE_ABBREVIATED | + SERD_STYLE_RESOLVED | + SERD_STYLE_CURIED | + SERD_STYLE_ASCII), + env, &buri, serd_chunk_sink, &str); + + // Write @prefix directives + serd_env_foreach(env, + (SerdPrefixSink)serd_writer_set_prefix, + writer); + + sratom_set_sink(sratom, base_uri, + (SerdStatementSink)serd_writer_write_statement, + (SerdEndSink)serd_writer_end_anon, + writer); + sratom_write(sratom, unmap, SERD_EMPTY_S, + subject, predicate, type, size, body); + serd_writer_finish(writer); + + serd_writer_free(writer); + serd_env_free(env); + serd_node_free(&base); + return (char*)serd_chunk_sink_finish(&str); +} + +void +_atom_inspector_expose(struct nk_context *ctx, struct nk_rect wbounds, void *data) +{ + plughandle_t *handle = data; + + const float widget_h = handle->dy; + struct nk_style *style = &ctx->style; + const struct nk_vec2 window_padding = style->window.padding; + const struct nk_vec2 group_padding = style->window.group_padding; + + style->selectable.normal.data.color.a = 0x0; + style->selectable.hover.data.color.a = 0x0; + + if(nk_begin(ctx, "Window", wbounds, NK_WINDOW_NO_SCROLLBAR)) + { + nk_window_set_bounds(ctx, wbounds); + struct nk_panel *panel= nk_window_get_panel(ctx); + struct nk_command_buffer *canvas = nk_window_get_canvas(ctx); + + const float body_h = panel->bounds.h - 2*window_padding.y; + nk_layout_row_dynamic(ctx, body_h, 2); + if(nk_group_begin(ctx, "Left", NK_WINDOW_NO_SCROLLBAR)) + { + const float content_h = nk_window_get_height(ctx) - 2*window_padding.y - 4*group_padding.y - 2*widget_h; + nk_layout_row_dynamic(ctx, content_h, 1); + nk_flags flags = NK_WINDOW_BORDER; + if(handle->state.follow) + flags |= NK_WINDOW_NO_SCROLLBAR; + struct nk_list_view lview; + if(nk_list_view_begin(ctx, &lview, "Events", flags, widget_h, NK_MIN(handle->n_item, MAX_LINES))) + { + if(handle->state.follow) + { + lview.end = NK_MAX(handle->n_item, 0); + lview.begin = NK_MAX(lview.end - lview.count, 0); + } + for(int l = lview.begin; (l < lview.end) && (l < handle->n_item); l++) + { + item_t *itm = handle->items[l]; + + switch(itm->type) + { + case ITEM_TYPE_NONE: + { + // skip, never reached + } break; + case ITEM_TYPE_FRAME: + { + nk_layout_row_dynamic(ctx, widget_h, 3); + { + struct nk_rect b = nk_widget_bounds(ctx); + b.x -= group_padding.x; + b.w *= 3; + b.w += 4*group_padding.x; + nk_fill_rect(canvas, b, 0.f, nk_rgb(0x18, 0x18, 0x18)); + } + + nk_labelf_colored(ctx, NK_TEXT_LEFT, orange, "@%"PRIi64, itm->frame.offset); + nk_labelf_colored(ctx, NK_TEXT_CENTERED, green, "-%"PRIu32"-", itm->frame.counter); + nk_labelf_colored(ctx, NK_TEXT_RIGHT, violet, "%"PRIi32, itm->frame.nsamples); + } break; + + case ITEM_TYPE_EVENT: + { + LV2_Atom_Event *ev = &itm->event.ev; + const LV2_Atom *body = &ev->body; + const int64_t frames = ev->time.frames; + const char *uri = NULL; + if(lv2_atom_forge_is_object_type(&handle->forge, body->type)) + { + const LV2_Atom_Object *obj = (const LV2_Atom_Object *)body; + + if(obj->body.otype) + uri = handle->unmap->unmap(handle->unmap->handle, obj->body.otype); + else if(obj->body.id) + uri = handle->unmap->unmap(handle->unmap->handle, obj->body.id); + else + uri = "Unknown"; + } + else // not an object + { + uri = handle->unmap->unmap(handle->unmap->handle, body->type); + } + + nk_layout_row_begin(ctx, NK_DYNAMIC, widget_h, 3); + { + nk_layout_row_push(ctx, 0.1); + if(l % 2 == 0) + { + struct nk_rect b = nk_widget_bounds(ctx); + b.x -= group_padding.x; + b.w *= 10; + b.w += 8*group_padding.x; + nk_fill_rect(canvas, b, 0.f, nk_rgb(0x28, 0x28, 0x28)); + } + nk_labelf_colored(ctx, NK_TEXT_LEFT, yellow, "+%04"PRIi64, frames); + + nk_layout_row_push(ctx, 0.8); + if(nk_select_label(ctx, uri, NK_TEXT_LEFT, handle->selected == body)) + { + handle->ttl_dirty = handle->ttl_dirty + || (handle->selected != body); // has selection actually changed? + handle->selected = body; + } + + nk_layout_row_push(ctx, 0.1); + nk_labelf_colored(ctx, NK_TEXT_RIGHT, blue, "%"PRIu32, body->size); + } + nk_layout_row_end(ctx); + } break; + } + } + + nk_list_view_end(&lview); + } + + nk_layout_row_dynamic(ctx, widget_h, 3); + { + if(nk_checkbox_label(ctx, "overwrite", &handle->state.overwrite)) + _toggle(handle, handle->urid.overwrite, handle->state.overwrite, true); + if(nk_checkbox_label(ctx, "block", &handle->state.block)) + _toggle(handle, handle->urid.block, handle->state.block, true); + if(nk_checkbox_label(ctx, "follow", &handle->state.follow)) + _toggle(handle, handle->urid.follow, handle->state.follow, true); + } + + const bool max_reached = handle->n_item >= MAX_LINES; + nk_layout_row_dynamic(ctx, widget_h, 2); + if(nk_button_symbol_label(ctx, + max_reached ? NK_SYMBOL_TRIANGLE_RIGHT: NK_SYMBOL_NONE, + "clear", NK_TEXT_LEFT)) + { + _clear(handle); + } + nk_label(ctx, "Sherlock.lv2: "SHERLOCK_VERSION, NK_TEXT_RIGHT); + + nk_group_end(ctx); + } + + if(nk_group_begin(ctx, "Right", NK_WINDOW_NO_SCROLLBAR)) + { + const LV2_Atom *atom = handle->selected; + if(handle->ttl_dirty && atom) + { + char *ttl = _sratom_to_turtle(handle->sratom, handle->unmap, + handle->base_uri, NULL, NULL, + atom->type, atom->size, LV2_ATOM_BODY_CONST(atom)); + if(ttl) + { + struct nk_str *str = &handle->str; + const size_t len = strlen(ttl); + + nk_str_clear(str); + + char *from, *to; + for(from=ttl, to=strchr(from, '\t'); + to; + from=to+1, to=strchr(from, '\t')) + { + nk_str_append_text_utf8(str, from, to-from); + nk_str_append_text_utf8(str, " ", 2); + } + nk_str_append_text_utf8(str, from, strlen(from)); + + free(ttl); + } + + handle->ttl_dirty = false; + } + + const nk_flags flags = NK_EDIT_EDITOR; + char *str = nk_str_get(&handle->str); + int len = nk_str_len(&handle->str); + + if(len > 0) //FIXME + { + const float content_h = nk_window_get_height(ctx) - 2*window_padding.y - 2*group_padding.y; + nk_layout_row_dynamic(ctx, content_h, 1); + nk_edit_focus(ctx, flags); + const nk_flags mode = nk_edit_string(ctx, flags, str, &len, len, nk_filter_default); + (void)mode; + } + + nk_group_end(ctx); + } + } + nk_end(ctx); +} diff --git a/cmake/arm-linux-gnueabihf.cmake b/cmake/arm-linux-gnueabihf.cmake new file mode 100644 index 0000000..f6d0b2a --- /dev/null +++ b/cmake/arm-linux-gnueabihf.cmake @@ -0,0 +1,22 @@ +# the name of the target operating system +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR "armv7h") +set(TOOLCHAIN "arm-linux-gnueabihf") + +# which compilers to use for C and C++ +set(CMAKE_C_COMPILER "${TOOLCHAIN}-gcc") +set(CMAKE_CXX_COMPILER "${TOOLCHAIN}-g++") + +# here is the target environment located +set(CMAKE_FIND_ROOT_PATH "usr/${TOOLCHAIN}") + +# adjust the default behaviour of the FIND_XXX() commands: +# search headers and libraries in the target environment, search +# programs in the host environment +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +set(STATIC_SRATOM "/opt/${TOOLCHAIN}/lib/libsratom-0.a") +set(STATIC_SERD "/opt/${TOOLCHAIN}/lib/libserd-0.a") +set(STATIC_SORD "/opt/${TOOLCHAIN}/lib/libsord-0.a") diff --git a/cmake/i686-linux-gnu.cmake b/cmake/i686-linux-gnu.cmake new file mode 100644 index 0000000..f00ae3e --- /dev/null +++ b/cmake/i686-linux-gnu.cmake @@ -0,0 +1,11 @@ +# the name of the target operating system +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR "i686") +set(TOOLCHAIN "i686-linux-gnu") + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32" CACHE STRING "c++ flags") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32" CACHE STRING "c flags") + +set(STATIC_SRATOM "/opt/${TOOLCHAIN}/lib/libsratom-0.a") +set(STATIC_SERD "/opt/${TOOLCHAIN}/lib/libserd-0.a") +set(STATIC_SORD "/opt/${TOOLCHAIN}/lib/libsord-0.a") diff --git a/cmake/i686-w64-mingw32.cmake b/cmake/i686-w64-mingw32.cmake new file mode 100644 index 0000000..3564cad --- /dev/null +++ b/cmake/i686-w64-mingw32.cmake @@ -0,0 +1,25 @@ +# the name of the target operating system +set(CMAKE_SYSTEM_NAME Windows) +set(CMAKE_SYSTEM_PROCESSOR "i686") +set(TOOLCHAIN "i686-w64-mingw32") + +# which compilers to use for C and C++ +set(CMAKE_C_COMPILER "${TOOLCHAIN}-gcc") +set(CMAKE_CXX_COMPILER "${TOOLCHAIN}-g++") +set(CMAKE_RC_COMPILER "${TOOLCHAIN}-windres") + +# here is the target environment located +set(CMAKE_FIND_ROOT_PATH "/usr/${TOOLCHAIN}") + +# adjust the default behaviour of the FIND_XXX() commands: +# search headers and libraries in the target environment, search +# programs in the host environment +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +set(STATIC_SRATOM "/opt/${TOOLCHAIN}/lib/libsratom-0.a") +set(STATIC_SERD "/opt/${TOOLCHAIN}/lib/libserd-0.a") +set(STATIC_SORD "/opt/${TOOLCHAIN}/lib/libsord-0.a") + +set(WINE wine32) diff --git a/cmake/universal-apple-darwin.cmake b/cmake/universal-apple-darwin.cmake new file mode 100644 index 0000000..127e80f --- /dev/null +++ b/cmake/universal-apple-darwin.cmake @@ -0,0 +1,24 @@ +# the name of the target operating system +set(CMAKE_SYSTEM_NAME Darwin) +set(CMAKE_SYSTEM_PROCESSOR "x86_64") +set(TOOLCHAIN "universal-apple-darwin") + +set(CMAKE_OSX_ARCHITECTURES "x86_64;i386") + +# which compilers to use for C and C++ +set(CMAKE_C_COMPILER "/usr/${TOOLCHAIN}/bin/x86_64-apple-darwin15-clang") +set(CMAKE_CXX_COMPILER "/usr/${TOOLCHAIN}/bin/x86_64-apple-darwin15-clang++") + +# here is the target environment located +set(CMAKE_FIND_ROOT_PATH "/usr/${TOOLCHAIN}/SDK/MacOSX10.11.sdk") + +# adjust the default behaviour of the FIND_XXX() commands: +# search headers and libraries in the target environment, search +# programs in the host environment +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +set(STATIC_SRATOM "/opt/${TOOLCHAIN}/lib/libsratom-0.a") +set(STATIC_SERD "/opt/${TOOLCHAIN}/lib/libserd-0.a") +set(STATIC_SORD "/opt/${TOOLCHAIN}/lib/libsord-0.a") diff --git a/cmake/x86_64-linux-gnu.cmake b/cmake/x86_64-linux-gnu.cmake new file mode 100644 index 0000000..730a594 --- /dev/null +++ b/cmake/x86_64-linux-gnu.cmake @@ -0,0 +1,8 @@ +# the name of the target operating system +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR "x86_64") +set(TOOLCHAIN "x86_64-linux-gnu") + +set(STATIC_SRATOM "/opt/${TOOLCHAIN}/lib/libsratom-0.a") +set(STATIC_SERD "/opt/${TOOLCHAIN}/lib/libserd-0.a") +set(STATIC_SORD "/opt/${TOOLCHAIN}/lib/libsord-0.a") diff --git a/cmake/x86_64-w64-mingw32.cmake b/cmake/x86_64-w64-mingw32.cmake new file mode 100644 index 0000000..9e90894 --- /dev/null +++ b/cmake/x86_64-w64-mingw32.cmake @@ -0,0 +1,25 @@ +# the name of the target operating system +set(CMAKE_SYSTEM_NAME Windows) +set(CMAKE_SYSTEM_PROCESSOR "x86_64") +set(TOOLCHAIN "x86_64-w64-mingw32") + +# which compilers to use for C and C++ +set(CMAKE_C_COMPILER "${TOOLCHAIN}-gcc") +set(CMAKE_CXX_COMPILER "${TOOLCHAIN}-g++") +set(CMAKE_RC_COMPILER "${TOOLCHAIN}-windres") + +# here is the target environment located +set(CMAKE_FIND_ROOT_PATH "/usr/${TOOLCHAIN}") + +# adjust the default behaviour of the FIND_XXX() commands: +# search headers and libraries in the target environment, search +# programs in the host environment +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +set(STATIC_SRATOM "/opt/${TOOLCHAIN}/lib/libsratom-0.a") +set(STATIC_SERD "/opt/${TOOLCHAIN}/lib/libserd-0.a") +set(STATIC_SORD "/opt/${TOOLCHAIN}/lib/libsord-0.a") + +set(WINE wine64) diff --git a/encoder.l b/encoder.l new file mode 100644 index 0000000..d076f67 --- /dev/null +++ b/encoder.l @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2015-2016 Hanspeter Portner (dev@open-music-kontrollers.ch) + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the Artistic License 2.0 as published by + * The Perl Foundation. + * + * This source is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Artistic License 2.0 for more details. + * + * You should have received a copy of the Artistic License 2.0 + * along the source as a COPYING file. If not, obtain it from + * http://www.perlfoundation.org/artistic_license_2_0. + */ + +%{ +#include <stdio.h> +#include <string.h> + +#include <common.h> + +typedef enum _markup_type_t markup_type_t; +typedef struct _markup_item_t markup_item_t; + +enum _markup_type_t { + MARKUP_CODE, + MARKUP_PREFIX, + MARKUP_SUBJECT, + MARKUP_PREDICATE, + MARKUP_NUMBER, + MARKUP_STRING, + MARKUP_URI +}; + +struct _markup_item_t { + const char *begin; + const char *end; +}; + +static const markup_item_t markup_items [] = { + [MARKUP_CODE] = {"font=Mono style=Plain color=#ffffff", "font"}, + [MARKUP_PREFIX] = {"color=#cc00cc", "color"}, + [MARKUP_SUBJECT] = {"color=#00cccc", "color"}, + [MARKUP_PREDICATE] = {"color=#00cc00", "color"}, + [MARKUP_NUMBER] = {"color=#0000cc", "color"}, + [MARKUP_STRING] = {"color=#cc0000", "color"}, + [MARKUP_URI] = {"color=#cccc00", "color"} +}; + +static void +_add_plain(const char *content) +{ + encoder->append(content, encoder->data); +} + +static void +_add_singleton(const char *key) +{ + char buf [64]; + sprintf(buf, "<%s/>", key); + encoder->append(buf, encoder->data); +} + +static void +_add_markup_begin(markup_type_t type) +{ + char buf [64]; + sprintf(buf, "<%s>", markup_items[type].begin); + encoder->append(buf, encoder->data); +} + +static void +_add_markup_end(markup_type_t type) +{ + char buf [64]; + sprintf(buf, "</%s>", markup_items[type].end); + encoder->append(buf, encoder->data); +} + +static void +_add_markup(markup_type_t type, const char *content) +{ + char buf [64]; + sprintf(buf, "<%s>", markup_items[type].begin); + encoder->append(buf, encoder->data); + encoder->append(content, encoder->data); + sprintf(buf, "</%s>", markup_items[type].end); + encoder->append(buf, encoder->data); +} + +enum { + TK_NONE, + TK_PREFIX, + TK_SUBJECT, + TK_PREDICATE, + TK_NUMBER, + TK_URI_IN, + TK_URI_OUT, + TK_URI_ERR, + TK_STRING_IN, + TK_STRING_OUT, + TK_STRING_ERR, + TK_LONG_STRING_IN, + TK_LONG_STRING_OUT, + TK_WHITESPACE, + TK_RAW, + TK_TAB, + TK_NEWLINE, + TK_LT, + TK_GT, + TK_AMP, + TK_NAME, + TK_BADCHAR +}; + +%} + +%option reentrant noyywrap + +w [ \v\a]+ +name [_a-zA-Z@][_a-zA-Z0-9\.]* +n [0-9]+ +exp [Ee][+-]?{n} +number ({n}|{n}[.]{n}){exp}? +eol [\n\r] + +%x XSTRING +%x XLONG_STRING +%x XURI + +%% + +{w} return TK_WHITESPACE; +"\t" return TK_TAB; +{eol} return TK_NEWLINE; +"<" BEGIN(XURI); return TK_URI_IN; +\"\"\" BEGIN(XLONG_STRING); return TK_LONG_STRING_IN; +\" BEGIN(XSTRING); return TK_STRING_IN; +{name}: return TK_SUBJECT; +"@prefix" return TK_PREFIX; +"a" return TK_PREFIX; +{name} return TK_PREDICATE; +{number} return TK_NUMBER; +. return TK_RAW; + +<XURI> +{ + ">" BEGIN(0); return TK_URI_OUT; + {eol} BEGIN(0); return TK_URI_ERR; + . return TK_RAW; +} + +<XLONG_STRING> +{ + \\\" return TK_RAW; + \"\"\" BEGIN(0); return TK_LONG_STRING_OUT; + {w} return TK_WHITESPACE; + "\t" return TK_TAB; + {eol} return TK_NEWLINE; + "<" return TK_LT; + ">" return TK_GT; + "&" return TK_AMP; + . return TK_RAW; +} + +<XSTRING> +{ + \\\" return TK_RAW; + \" BEGIN(0); return TK_STRING_OUT; + {eol} BEGIN(0); return TK_STRING_ERR; + {w} return TK_WHITESPACE; + "\t" return TK_TAB; + {eol} return TK_NEWLINE; + "<" return TK_LT; + ">" return TK_GT; + "&" return TK_AMP; + . return TK_RAW; +} + +%% + +void +ttl_to_markup(const char *utf8, FILE *f) +{ + yyscan_t scanner; + YY_BUFFER_STATE buf; + + enclex_init(&scanner); + if(utf8) + { + buf = enc_scan_string(utf8, scanner); + } + else if(f) + { + encset_in(f, scanner); + buf = enc_create_buffer(NULL, YY_BUF_SIZE, scanner); + } + else + { + enclex_destroy(scanner); + return; + } + + encoder->begin(encoder->data); + _add_markup_begin(MARKUP_CODE); + + for(int tok=enclex(scanner); tok; tok=enclex(scanner)) + { + const char *txt = encget_text(scanner); + switch(tok) + { + case TK_PREFIX: + _add_markup(MARKUP_PREFIX, txt); + break; + + case TK_NUMBER: + _add_markup(MARKUP_NUMBER, txt); + break; + + case TK_URI_IN: + _add_markup_begin(MARKUP_URI); + _add_plain("<"); + break; + case TK_URI_OUT: + _add_plain(">"); + _add_markup_end(MARKUP_URI); + break; + case TK_URI_ERR: + _add_markup_end(MARKUP_URI); + _add_singleton("br"); + break; + + case TK_STRING_IN: + _add_markup_begin(MARKUP_STRING); + _add_plain("\""); + break; + case TK_STRING_OUT: + _add_plain("\""); + _add_markup_end(MARKUP_STRING); + break; + case TK_STRING_ERR: + _add_markup_end(MARKUP_STRING); + _add_singleton("br"); + break; + + case TK_LONG_STRING_IN: + _add_markup_begin(MARKUP_STRING); + _add_plain("\"\"\""); + break; + case TK_LONG_STRING_OUT: + _add_plain("\"\"\""); + _add_markup_end(MARKUP_STRING); + break; + + case TK_NEWLINE: + _add_singleton("br"); + break; + case TK_LT: + _add_plain("<"); + break; + case TK_GT: + _add_plain(">"); + break; + case TK_AMP: + _add_plain("&"); + break; + + case TK_BADCHAR: + break; + + case TK_TAB: + _add_plain(" "); + break; + + case TK_SUBJECT: + _add_markup(MARKUP_SUBJECT, txt); + break; + case TK_PREDICATE: + _add_markup(MARKUP_PREDICATE, txt); + break; + + case TK_NAME: + case TK_WHITESPACE: + case TK_RAW: + default: + _add_plain(txt); + break; + } + } + + _add_markup_end(MARKUP_CODE); + encoder->end(encoder->data); + + enc_delete_buffer(buf, scanner); + enclex_destroy(scanner); +} diff --git a/manifest.ttl.in b/manifest.ttl.in new file mode 100644 index 0000000..d71006b --- /dev/null +++ b/manifest.ttl.in @@ -0,0 +1,62 @@ +# Copyright (c) 2015-2016 Hanspeter Portner (dev@open-music-kontrollers.ch) +# +# This is free software: you can redistribute it and/or modify +# it under the terms of the Artistic License 2.0 as published by +# The Perl Foundation. +# +# This source is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# Artistic License 2.0 for more details. +# +# You should have received a copy of the Artistic License 2.0 +# along the source as a COPYING file. If not, obtain it from +# http://www.perlfoundation.org/artistic_license_2_0. + +@prefix lv2: <http://lv2plug.in/ns/lv2core#> . +@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . +@prefix ui: <http://lv2plug.in/ns/extensions/ui#> . + +@prefix sherlock: <http://open-music-kontrollers.ch/lv2/sherlock#> . + +# Atom Inspector Plugin +sherlock:atom_inspector + a lv2:Plugin ; + lv2:minorVersion @SHERLOCK_MINOR_VERSION@ ; + lv2:microVersion @SHERLOCK_MICRO_VERSION@ ; + lv2:binary <sherlock@CMAKE_SHARED_MODULE_SUFFIX@> ; + ui:ui sherlock:atom_inspector_4_nk ; + rdfs:seeAlso <sherlock.ttl> . + +sherlock:atom_inspector_4_nk + a ui:@SHERLOCK_UI_TYPE@ ; + ui:binary <sherlock_nk@CMAKE_SHARED_MODULE_SUFFIX@> ; + rdfs:seeAlso <sherlock_ui.ttl> . + +# MIDI Inspector Plugin +sherlock:midi_inspector + a lv2:Plugin ; + lv2:minorVersion @SHERLOCK_MINOR_VERSION@ ; + lv2:microVersion @SHERLOCK_MICRO_VERSION@ ; + lv2:binary <sherlock@CMAKE_SHARED_MODULE_SUFFIX@> ; + ui:ui sherlock:midi_inspector_4_nk ; + rdfs:seeAlso <sherlock.ttl> . + +sherlock:midi_inspector_4_nk + a ui:@SHERLOCK_UI_TYPE@ ; + ui:binary <sherlock_nk@CMAKE_SHARED_MODULE_SUFFIX@> ; + rdfs:seeAlso <sherlock_ui.ttl> . + +# OSC Inspector Plugin +sherlock:osc_inspector + a lv2:Plugin ; + lv2:minorVersion @SHERLOCK_MINOR_VERSION@ ; + lv2:microVersion @SHERLOCK_MICRO_VERSION@ ; + lv2:binary <sherlock@CMAKE_SHARED_MODULE_SUFFIX@> ; + ui:ui sherlock:osc_inspector_4_nk ; + rdfs:seeAlso <sherlock.ttl> . + +sherlock:osc_inspector_4_nk + a ui:@SHERLOCK_UI_TYPE@ ; + ui:binary <sherlock_nk@CMAKE_SHARED_MODULE_SUFFIX@> ; + rdfs:seeAlso <sherlock_ui.ttl> . diff --git a/midi_inspector.c b/midi_inspector.c new file mode 100644 index 0000000..29c50a0 --- /dev/null +++ b/midi_inspector.c @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2015-2016 Hanspeter Portner (dev@open-music-kontrollers.ch) + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the Artistic License 2.0 as published by + * The Perl Foundation. + * + * This source is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Artistic License 2.0 for more details. + * + * You should have received a copy of the Artistic License 2.0 + * along the source as a COPYING file. If not, obtain it from + * http://www.perlfoundation.org/artistic_license_2_0. + */ + +#include <stdio.h> +#include <stdlib.h> + +#include <sherlock.h> + +#include "lv2/lv2plug.in/ns/ext/midi/midi.h" + +typedef struct _handle_t handle_t; + +struct _handle_t { + LV2_URID_Map *map; + const LV2_Atom_Sequence *control_in; + LV2_Atom_Sequence *control_out; + LV2_Atom_Sequence *notify; + LV2_Atom_Forge forge; + + LV2_URID time_position; + LV2_URID time_frame; + LV2_URID midi_event; + + int64_t frame; + + PROPS_T(props, MAX_NPROPS); + state_t state; + state_t stash; +}; + +static LV2_Handle +instantiate(const LV2_Descriptor* descriptor, double rate, + const char *bundle_path, const LV2_Feature *const *features) +{ + int i; + handle_t *handle = calloc(1, sizeof(handle_t)); + if(!handle) + return NULL; + + for(i=0; features[i]; i++) + if(!strcmp(features[i]->URI, LV2_URID__map)) + handle->map = (LV2_URID_Map *)features[i]->data; + + if(!handle->map) + { + fprintf(stderr, "%s: Host does not support urid:map\n", descriptor->URI); + free(handle); + return NULL; + } + + handle->time_position = handle->map->map(handle->map->handle, LV2_TIME__Position); + handle->time_frame = handle->map->map(handle->map->handle, LV2_TIME__frame); + + handle->midi_event = handle->map->map(handle->map->handle, LV2_MIDI__MidiEvent); + + lv2_atom_forge_init(&handle->forge, handle->map); + + if(!props_init(&handle->props, MAX_NPROPS, descriptor->URI, handle->map, handle)) + { + fprintf(stderr, "failed to allocate property structure\n"); + free(handle); + return NULL; + } + + if( !props_register(&handle->props, &stat_overwrite, &handle->state.overwrite, &handle->stash.overwrite) + || !props_register(&handle->props, &stat_block, &handle->state.block, &handle->stash.block) + || !props_register(&handle->props, &stat_follow, &handle->state.follow, &handle->stash.follow) ) + { + free(handle); + return NULL; + } + + return handle; +} + +static void +connect_port(LV2_Handle instance, uint32_t port, void *data) +{ + handle_t *handle = (handle_t *)instance; + + switch(port) + { + case 0: + handle->control_in = (const LV2_Atom_Sequence *)data; + break; + case 1: + handle->control_out = (LV2_Atom_Sequence *)data; + break; + case 2: + handle->notify = (LV2_Atom_Sequence *)data; + break; + default: + break; + } +} + +static void +run(LV2_Handle instance, uint32_t nsamples) +{ + handle_t *handle = (handle_t *)instance; + uint32_t capacity; + LV2_Atom_Forge *forge = &handle->forge; + LV2_Atom_Forge_Frame frame [3]; + LV2_Atom_Forge_Ref ref; + + // size of input sequence + const size_t size = lv2_atom_total_size(&handle->control_in->atom); + + // copy whole input sequence to through port + capacity = handle->control_out->atom.size; + lv2_atom_forge_set_buffer(forge, (uint8_t *)handle->control_out, capacity); + ref = lv2_atom_forge_sequence_head(forge, frame, 0); + + LV2_ATOM_SEQUENCE_FOREACH(handle->control_in, ev) + { + const LV2_Atom_Object *obj = (const LV2_Atom_Object *)&ev->body; + const int64_t frames = ev->time.frames; + + // copy all events to through port + if(ref) + ref = lv2_atom_forge_frame_time(forge, frames); + if(ref) + ref = lv2_atom_forge_raw(forge, &obj->atom, lv2_atom_total_size(&obj->atom)); + if(ref) + lv2_atom_forge_pad(forge, obj->atom.size); + + if( !props_advance(&handle->props, forge, frames, obj, &ref) + && lv2_atom_forge_is_object_type(forge, obj->atom.type) + && (obj->body.otype == handle->time_position) ) + { + const LV2_Atom_Long *time_frame = NULL; + lv2_atom_object_get(obj, handle->time_frame, &time_frame, NULL); + if(time_frame) + handle->frame = time_frame->body - frames; + } + } + + if(ref) + lv2_atom_forge_pop(forge, frame); + else + lv2_atom_sequence_clear(handle->control_out); + + // forge whole sequence as single event + capacity = handle->notify->atom.size; + lv2_atom_forge_set_buffer(forge, (uint8_t *)handle->notify, capacity); + + bool has_midi = false; + + ref = lv2_atom_forge_sequence_head(forge, &frame[0], 0); + if(ref) + ref = lv2_atom_forge_frame_time(forge, 0); + if(ref) + ref = lv2_atom_forge_tuple(forge, &frame[1]); + if(ref) + ref = lv2_atom_forge_long(forge, handle->frame); + if(ref) + ref = lv2_atom_forge_int(forge, nsamples); + if(ref) + ref = lv2_atom_forge_sequence_head(forge, &frame[2], 0); + + // only serialize MIDI events to UI + LV2_ATOM_SEQUENCE_FOREACH(handle->control_in, ev) + { + if(ev->body.type == handle->midi_event) + { + has_midi = true; + if(ref) + ref = lv2_atom_forge_frame_time(forge, ev->time.frames); + if(ref) + ref = lv2_atom_forge_write(forge, &ev->body, sizeof(LV2_Atom) + ev->body.size); + } + } + + if(ref) + lv2_atom_forge_pop(forge, &frame[2]); + if(ref) + lv2_atom_forge_pop(forge, &frame[1]); + if(ref) + lv2_atom_forge_pop(forge, &frame[0]); + else + lv2_atom_sequence_clear(handle->notify); + + if(!has_midi) // don't send anything + lv2_atom_sequence_clear(handle->notify); + + handle->frame += nsamples; +} + +static void +cleanup(LV2_Handle instance) +{ + handle_t *handle = (handle_t *)instance; + + free(handle); +} + +static LV2_State_Status +_state_save(LV2_Handle instance, LV2_State_Store_Function store, + LV2_State_Handle state, uint32_t flags, + const LV2_Feature *const *features) +{ + handle_t *handle = instance; + + return props_save(&handle->props, &handle->forge, store, state, flags, features); +} + +static LV2_State_Status +_state_restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve, + LV2_State_Handle state, uint32_t flags, + const LV2_Feature *const *features) +{ + handle_t *handle = instance; + + return props_restore(&handle->props, &handle->forge, retrieve, state, flags, features); +} + +static const LV2_State_Interface state_iface = { + .save = _state_save, + .restore = _state_restore +}; + +static const void * +extension_data(const char *uri) +{ + if(!strcmp(uri, LV2_STATE__interface)) + return &state_iface; + return NULL; +} + +const LV2_Descriptor midi_inspector = { + .URI = SHERLOCK_MIDI_INSPECTOR_URI, + .instantiate = instantiate, + .connect_port = connect_port, + .activate = NULL, + .run = run, + .deactivate = NULL, + .cleanup = cleanup, + .extension_data = extension_data +}; diff --git a/midi_inspector_nk.c b/midi_inspector_nk.c new file mode 100644 index 0000000..2ac4d4f --- /dev/null +++ b/midi_inspector_nk.c @@ -0,0 +1,505 @@ +/* + * Copyright (c) 2015-2016 Hanspeter Portner (dev@open-music-kontrollers.ch) + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the Artistic License 2.0 as published by + * The Perl Foundation. + * + * This source is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Artistic License 2.0 for more details. + * + * You should have received a copy of the Artistic License 2.0 + * along the source as a COPYING file. If not, obtain it from + * http://www.perlfoundation.org/artistic_license_2_0. + */ + +#include <inttypes.h> + +#include <sherlock.h> +#include <sherlock_nk.h> + +typedef struct _midi_msg_t midi_msg_t; + +struct _midi_msg_t { + uint8_t type; + const char *key; +}; + +#define COMMANDS_NUM 18 +static const midi_msg_t commands [COMMANDS_NUM] = { + { LV2_MIDI_MSG_NOTE_OFF , "NoteOff" }, + { LV2_MIDI_MSG_NOTE_ON , "NoteOn" }, + { LV2_MIDI_MSG_NOTE_PRESSURE , "NotePressure" }, + { LV2_MIDI_MSG_CONTROLLER , "Controller" }, + { LV2_MIDI_MSG_PGM_CHANGE , "ProgramChange" }, + { LV2_MIDI_MSG_CHANNEL_PRESSURE , "ChannelPressure" }, + { LV2_MIDI_MSG_BENDER , "Bender" }, + { LV2_MIDI_MSG_SYSTEM_EXCLUSIVE , "SystemExclusive" }, + { LV2_MIDI_MSG_MTC_QUARTER , "QuarterFrame" }, + { LV2_MIDI_MSG_SONG_POS , "SongPosition" }, + { LV2_MIDI_MSG_SONG_SELECT , "SongSelect" }, + { LV2_MIDI_MSG_TUNE_REQUEST , "TuneRequest" }, + { LV2_MIDI_MSG_CLOCK , "Clock" }, + { LV2_MIDI_MSG_START , "Start" }, + { LV2_MIDI_MSG_CONTINUE , "Continue" }, + { LV2_MIDI_MSG_STOP , "Stop" }, + { LV2_MIDI_MSG_ACTIVE_SENSE , "ActiveSense" }, + { LV2_MIDI_MSG_RESET , "Reset" }, +}; + +#define CONTROLLERS_NUM 72 +static const midi_msg_t controllers [CONTROLLERS_NUM] = { + { LV2_MIDI_CTL_MSB_BANK , "BankSelection_MSB" }, + { LV2_MIDI_CTL_MSB_MODWHEEL , "Modulation_MSB" }, + { LV2_MIDI_CTL_MSB_BREATH , "Breath_MSB" }, + { LV2_MIDI_CTL_MSB_FOOT , "Foot_MSB" }, + { LV2_MIDI_CTL_MSB_PORTAMENTO_TIME , "PortamentoTime_MSB" }, + { LV2_MIDI_CTL_MSB_DATA_ENTRY , "DataEntry_MSB" }, + { LV2_MIDI_CTL_MSB_MAIN_VOLUME , "MainVolume_MSB" }, + { LV2_MIDI_CTL_MSB_BALANCE , "Balance_MSB" }, + { LV2_MIDI_CTL_MSB_PAN , "Panpot_MSB" }, + { LV2_MIDI_CTL_MSB_EXPRESSION , "Expression_MSB" }, + { LV2_MIDI_CTL_MSB_EFFECT1 , "Effect1_MSB" }, + { LV2_MIDI_CTL_MSB_EFFECT2 , "Effect2_MSB" }, + { LV2_MIDI_CTL_MSB_GENERAL_PURPOSE1 , "GeneralPurpose1_MSB" }, + { LV2_MIDI_CTL_MSB_GENERAL_PURPOSE2 , "GeneralPurpose2_MSB" }, + { LV2_MIDI_CTL_MSB_GENERAL_PURPOSE3 , "GeneralPurpose3_MSB" }, + { LV2_MIDI_CTL_MSB_GENERAL_PURPOSE4 , "GeneralPurpose4_MSB" }, + + { LV2_MIDI_CTL_LSB_BANK , "BankSelection_LSB" }, + { LV2_MIDI_CTL_LSB_MODWHEEL , "Modulation_LSB" }, + { LV2_MIDI_CTL_LSB_BREATH , "Breath_LSB" }, + { LV2_MIDI_CTL_LSB_FOOT , "Foot_LSB" }, + { LV2_MIDI_CTL_LSB_PORTAMENTO_TIME , "PortamentoTime_LSB" }, + { LV2_MIDI_CTL_LSB_DATA_ENTRY , "DataEntry_LSB" }, + { LV2_MIDI_CTL_LSB_MAIN_VOLUME , "MainVolume_LSB" }, + { LV2_MIDI_CTL_LSB_BALANCE , "Balance_LSB" }, + { LV2_MIDI_CTL_LSB_PAN , "Panpot_LSB" }, + { LV2_MIDI_CTL_LSB_EXPRESSION , "Expression_LSB" }, + { LV2_MIDI_CTL_LSB_EFFECT1 , "Effect1_LSB" }, + { LV2_MIDI_CTL_LSB_EFFECT2 , "Effect2_LSB" }, + { LV2_MIDI_CTL_LSB_GENERAL_PURPOSE1 , "GeneralPurpose1_LSB" }, + { LV2_MIDI_CTL_LSB_GENERAL_PURPOSE2 , "GeneralPurpose2_LSB" }, + { LV2_MIDI_CTL_LSB_GENERAL_PURPOSE3 , "GeneralPurpose3_LSB" }, + { LV2_MIDI_CTL_LSB_GENERAL_PURPOSE4 , "GeneralPurpose4_LSB" }, + + { LV2_MIDI_CTL_SUSTAIN , "SustainPedal" }, + { LV2_MIDI_CTL_PORTAMENTO , "Portamento" }, + { LV2_MIDI_CTL_SOSTENUTO , "Sostenuto" }, + { LV2_MIDI_CTL_SOFT_PEDAL , "SoftPedal" }, + { LV2_MIDI_CTL_LEGATO_FOOTSWITCH , "LegatoFootSwitch" }, + { LV2_MIDI_CTL_HOLD2 , "Hold2" }, + + { LV2_MIDI_CTL_SC1_SOUND_VARIATION , "SC1_SoundVariation" }, + { LV2_MIDI_CTL_SC2_TIMBRE , "SC2_Timbre" }, + { LV2_MIDI_CTL_SC3_RELEASE_TIME , "SC3_ReleaseTime" }, + { LV2_MIDI_CTL_SC4_ATTACK_TIME , "SC4_AttackTime" }, + { LV2_MIDI_CTL_SC5_BRIGHTNESS , "SC5_Brightness" }, + { LV2_MIDI_CTL_SC6 , "SC6" }, + { LV2_MIDI_CTL_SC7 , "SC7" }, + { LV2_MIDI_CTL_SC8 , "SC8" }, + { LV2_MIDI_CTL_SC9 , "SC9" }, + { LV2_MIDI_CTL_SC10 , "SC10" }, + + { LV2_MIDI_CTL_GENERAL_PURPOSE5 , "GeneralPurpose5" }, + { LV2_MIDI_CTL_GENERAL_PURPOSE6 , "GeneralPurpose6" }, + { LV2_MIDI_CTL_GENERAL_PURPOSE7 , "GeneralPurpose7" }, + { LV2_MIDI_CTL_GENERAL_PURPOSE8 , "GeneralPurpose8" }, + { LV2_MIDI_CTL_PORTAMENTO_CONTROL , "PortamentoControl" }, + + { LV2_MIDI_CTL_E1_REVERB_DEPTH , "E1_ReverbDepth" }, + { LV2_MIDI_CTL_E2_TREMOLO_DEPTH , "E2_TremoloDepth" }, + { LV2_MIDI_CTL_E3_CHORUS_DEPTH , "E3_ChorusDepth" }, + { LV2_MIDI_CTL_E4_DETUNE_DEPTH , "E4_DetuneDepth" }, + { LV2_MIDI_CTL_E5_PHASER_DEPTH , "E5_PhaserDepth" }, + + { LV2_MIDI_CTL_DATA_INCREMENT , "DataIncrement" }, + { LV2_MIDI_CTL_DATA_DECREMENT , "DataDecrement" }, + + { LV2_MIDI_CTL_NRPN_LSB , "NRPN_LSB" }, + { LV2_MIDI_CTL_NRPN_MSB , "NRPN_MSB" }, + + { LV2_MIDI_CTL_RPN_LSB , "RPN_LSB" }, + { LV2_MIDI_CTL_RPN_MSB , "RPN_MSB" }, + + { LV2_MIDI_CTL_ALL_SOUNDS_OFF , "AllSoundsOff" }, + { LV2_MIDI_CTL_RESET_CONTROLLERS , "ResetControllers" }, + { LV2_MIDI_CTL_LOCAL_CONTROL_SWITCH , "LocalControlSwitch" }, + { LV2_MIDI_CTL_ALL_NOTES_OFF , "AllNotesOff" }, + { LV2_MIDI_CTL_OMNI_OFF , "OmniOff" }, + { LV2_MIDI_CTL_OMNI_ON , "OmniOn" }, + { LV2_MIDI_CTL_MONO1 , "Mono1" }, + { LV2_MIDI_CTL_MONO2 , "Mono2" }, +}; + +#define TIMECODES_NUM 8 +static const midi_msg_t timecodes [TIMECODES_NUM] = { + { 0 , "FrameNumber_LSB" }, + { 1 , "FrameNumber_MSB" }, + { 2 , "Second_LSB" }, + { 3 , "Second_MSB" }, + { 4 , "Minute_LSB" }, + { 5 , "Minute_MSB" }, + { 6 , "Hour_LSB" }, + { 7 , "RateAndHour_MSB" }, +}; + +static int +_cmp_search(const void *itm1, const void *itm2) +{ + const midi_msg_t *msg1 = itm1; + const midi_msg_t *msg2 = itm2; + + if(msg1->type < msg2->type) + return -1; + else if(msg1->type > msg2->type) + return 1; + + return 0; +} + +static inline const midi_msg_t * +_search_command(uint8_t type) +{ + return bsearch(&type, commands, COMMANDS_NUM, sizeof(midi_msg_t), _cmp_search); +} + +static inline const midi_msg_t * +_search_controller(uint8_t type) +{ + return bsearch(&type, controllers, CONTROLLERS_NUM, sizeof(midi_msg_t), _cmp_search); +} + +static inline const midi_msg_t * +_search_timecode(uint8_t type) +{ + return bsearch(&type, timecodes, TIMECODES_NUM, sizeof(midi_msg_t), _cmp_search); +} + +static const char *keys [12] = { + "C", "C#", + "D", "D#", + "E", + "F", "F#", + "G", "G#", + "A", "A#", + "B" +}; + +static inline const char * +_note(uint8_t val, int8_t *octave) +{ + *octave = val / 12 - 1; + + return keys[val % 12]; +} + +static inline void +_shadow(struct nk_context *ctx, bool *shadow) +{ + if(*shadow) + { + struct nk_style *style = &ctx->style; + const struct nk_vec2 group_padding = style->window.group_padding; + struct nk_command_buffer *canvas = nk_window_get_canvas(ctx); + + struct nk_rect b = nk_widget_bounds(ctx); + b.x -= group_padding.x; + b.w *= 10; + b.w += 8*group_padding.x; + nk_fill_rect(canvas, b, 0.f, nk_rgb(0x28, 0x28, 0x28)); + } + + *shadow = !*shadow; +} + +void +_midi_inspector_expose(struct nk_context *ctx, struct nk_rect wbounds, void *data) +{ + plughandle_t *handle = data; + + const float widget_h = handle->dy; + struct nk_style *style = &ctx->style; + const struct nk_vec2 window_padding = style->window.padding; + const struct nk_vec2 group_padding = style->window.group_padding; + + if(nk_begin(ctx, "Window", wbounds, NK_WINDOW_NO_SCROLLBAR)) + { + nk_window_set_bounds(ctx, wbounds); + struct nk_panel *panel = nk_window_get_panel(ctx); + struct nk_command_buffer *canvas = nk_window_get_canvas(ctx); + + const float body_h = panel->bounds.h - 4*window_padding.y - 2*widget_h; + nk_layout_row_dynamic(ctx, body_h, 1); + nk_flags flags = NK_WINDOW_BORDER; + if(handle->state.follow) + flags |= NK_WINDOW_NO_SCROLLBAR; + struct nk_list_view lview; + if(nk_list_view_begin(ctx, &lview, "Events", flags, widget_h, NK_MIN(handle->n_item, MAX_LINES))) + { + if(handle->state.follow) + { + lview.end = NK_MAX(handle->n_item, 0); + lview.begin = NK_MAX(lview.end - lview.count, 0); + } + handle->shadow = lview.begin % 2 == 0; + for(int l = lview.begin; (l < lview.end) && (l < handle->n_item); l++) + { + item_t *itm = handle->items[l]; + + switch(itm->type) + { + case ITEM_TYPE_NONE: + { + // skip, was sysex payload + } break; + case ITEM_TYPE_FRAME: + { + nk_layout_row_dynamic(ctx, widget_h, 3); + { + struct nk_rect b = nk_widget_bounds(ctx); + b.x -= group_padding.x; + b.w *= 3; + b.w += 4*group_padding.x; + nk_fill_rect(canvas, b, 0.f, nk_rgb(0x18, 0x18, 0x18)); + } + + nk_labelf_colored(ctx, NK_TEXT_LEFT, orange, "@%"PRIi64, itm->frame.offset); + nk_labelf_colored(ctx, NK_TEXT_CENTERED, green, "-%"PRIu32"-", itm->frame.counter); + nk_labelf_colored(ctx, NK_TEXT_RIGHT, violet, "%"PRIi32, itm->frame.nsamples); + } break; + + case ITEM_TYPE_EVENT: + { + LV2_Atom_Event *ev = &itm->event.ev; + const LV2_Atom *body = &ev->body; + const int64_t frames = ev->time.frames; + const uint8_t *msg = LV2_ATOM_BODY_CONST(body); + const uint8_t cmd = (msg[0] & 0xf0) == 0xf0 + ? msg[0] + : msg[0] & 0xf0; + + const midi_msg_t *command_msg = _search_command(cmd); + const char *command_str = command_msg + ? command_msg->key + : "Unknown"; + + char tmp [16]; + nk_layout_row_begin(ctx, NK_DYNAMIC, widget_h, 7); + { + nk_layout_row_push(ctx, 0.1); + _shadow(ctx, &handle->shadow); + nk_labelf_colored(ctx, NK_TEXT_LEFT, yellow, "+%04"PRIi64, frames); + + nk_layout_row_push(ctx, 0.2); + const unsigned rem = body->size; + const unsigned to = rem >= 4 ? 4 : rem % 4; + for(unsigned i=0, ptr=0; i<to; i++, ptr+=3) + sprintf(&tmp[ptr], "%02"PRIX8" ", msg[i]); + tmp[to*3 - 1] = '\0'; + nk_label_colored(ctx, tmp, NK_TEXT_LEFT, white); + + nk_layout_row_push(ctx, 0.2); + nk_label_colored(ctx, command_str, NK_TEXT_LEFT, magenta); + + switch(cmd) + { + case LV2_MIDI_MSG_NOTE_OFF: + // fall-through + case LV2_MIDI_MSG_NOTE_ON: + // fall-through + case LV2_MIDI_MSG_NOTE_PRESSURE: + { + nk_layout_row_push(ctx, 0.1); + nk_labelf_colored(ctx, NK_TEXT_RIGHT, white, "Ch:%02"PRIu8, + (msg[0] & 0x0f) + 1); + + nk_layout_row_push(ctx, 0.2); + int8_t octave; + const char *key = _note(msg[1], &octave); + nk_labelf_colored(ctx, NK_TEXT_RIGHT, white, "%s%+"PRIi8, key, octave); + + nk_layout_row_push(ctx, 0.1); + nk_labelf_colored(ctx, NK_TEXT_RIGHT, white, "%"PRIu8, msg[2]); + } break; + case LV2_MIDI_MSG_CONTROLLER: + { + nk_layout_row_push(ctx, 0.1); + nk_labelf_colored(ctx, NK_TEXT_RIGHT, white, "Ch:%02"PRIu8, + (msg[0] & 0x0f) + 1); + + const midi_msg_t *controller_msg = _search_controller(msg[1]); + const char *controller_str = controller_msg + ? controller_msg->key + : "Unknown"; + nk_layout_row_push(ctx, 0.2); + nk_label_colored(ctx, controller_str, NK_TEXT_RIGHT, white); + + nk_layout_row_push(ctx, 0.1); + nk_labelf_colored(ctx, NK_TEXT_RIGHT, white, "%"PRIu8, msg[2]); + } break; + case LV2_MIDI_MSG_PGM_CHANGE: + // fall-through + case LV2_MIDI_MSG_CHANNEL_PRESSURE: + { + nk_layout_row_push(ctx, 0.1); + nk_labelf_colored(ctx, NK_TEXT_RIGHT, white, "Ch:%02"PRIu8, + (msg[0] & 0x0f) + 1); + + nk_layout_row_push(ctx, 0.2); + nk_labelf_colored(ctx, NK_TEXT_RIGHT, white, "%"PRIu8, msg[1]); + + nk_layout_row_push(ctx, 0.1); + _empty(ctx); + } break; + case LV2_MIDI_MSG_BENDER: + { + const int16_t bender = (((int16_t)msg[2] << 7) | msg[1]) - 0x2000; + + nk_layout_row_push(ctx, 0.1); + nk_labelf_colored(ctx, NK_TEXT_RIGHT, white, "Ch:%02"PRIu8, + (msg[0] & 0x0f) + 1); + + nk_layout_row_push(ctx, 0.2); + nk_labelf_colored(ctx, NK_TEXT_RIGHT, white, "%"PRIi16, bender); + + nk_layout_row_push(ctx, 0.1); + _empty(ctx); + } break; + case LV2_MIDI_MSG_MTC_QUARTER: + { + const uint8_t msg_type = msg[1] >> 4; + const uint8_t msg_val = msg[1] & 0xf; + + nk_layout_row_push(ctx, 0.1); + _empty(ctx); + + const midi_msg_t *timecode_msg = _search_timecode(msg_type); + const char *timecode_str = timecode_msg + ? timecode_msg->key + : "Unknown"; + nk_layout_row_push(ctx, 0.2); + nk_label_colored(ctx, timecode_str, NK_TEXT_RIGHT, white); + + nk_layout_row_push(ctx, 0.1); + nk_labelf_colored(ctx, NK_TEXT_RIGHT, white, "%"PRIu8, msg_val); + } break; + case LV2_MIDI_MSG_SONG_POS: + { + const int16_t song_pos= (((int16_t)msg[2] << 7) | msg[1]); + + nk_layout_row_push(ctx, 0.1); + _empty(ctx); + + nk_layout_row_push(ctx, 0.2); + nk_labelf_colored(ctx, NK_TEXT_RIGHT, white, "%"PRIu16, song_pos); + + nk_layout_row_push(ctx, 0.1); + _empty(ctx); + } break; + case LV2_MIDI_MSG_SONG_SELECT: + { + nk_layout_row_push(ctx, 0.1); + _empty(ctx); + + nk_layout_row_push(ctx, 0.2); + nk_labelf_colored(ctx, NK_TEXT_RIGHT, white, "%"PRIu8, msg[1]); + + nk_layout_row_push(ctx, 0.1); + _empty(ctx); + } break; + case LV2_MIDI_MSG_SYSTEM_EXCLUSIVE: + // fall-throuh + case LV2_MIDI_MSG_TUNE_REQUEST: + // fall-throuh + case LV2_MIDI_MSG_CLOCK: + // fall-throuh + case LV2_MIDI_MSG_START: + // fall-throuh + case LV2_MIDI_MSG_CONTINUE: + // fall-throuh + case LV2_MIDI_MSG_STOP: + // fall-throuh + case LV2_MIDI_MSG_ACTIVE_SENSE: + // fall-throuh + case LV2_MIDI_MSG_RESET: + { + nk_layout_row_push(ctx, 0.1); + _empty(ctx); + + nk_layout_row_push(ctx, 0.2); + _empty(ctx); + + nk_layout_row_push(ctx, 0.1); + _empty(ctx); + } break; + } + + nk_layout_row_push(ctx, 0.1); + nk_labelf_colored(ctx, NK_TEXT_RIGHT, blue, "%"PRIu32, body->size); + } + nk_layout_row_end(ctx); + + for(unsigned j=4; j<body->size; j+=4) + { + nk_layout_row_begin(ctx, NK_DYNAMIC, widget_h, 7); + { + nk_layout_row_push(ctx, 0.1); + _shadow(ctx, &handle->shadow); + _empty(ctx); + + nk_layout_row_push(ctx, 0.2); + const unsigned rem = body->size - j; + const unsigned to = rem >= 4 ? 4 : rem % 4; + for(unsigned i=0, ptr=0; i<to; i++, ptr+=3) + sprintf(&tmp[ptr], "%02"PRIX8" ", msg[j+i]); + tmp[to*3 - 1] = '\0'; + nk_label_colored(ctx, tmp, NK_TEXT_LEFT, white); + + nk_layout_row_push(ctx, 0.2); + _empty(ctx); + + nk_layout_row_push(ctx, 0.1); + _empty(ctx); + + nk_layout_row_push(ctx, 0.2); + _empty(ctx); + + nk_layout_row_push(ctx, 0.1); + _empty(ctx); + + nk_layout_row_push(ctx, 0.1); + _empty(ctx); + } + } + } break; + } + } + + nk_list_view_end(&lview); + } + + nk_layout_row_dynamic(ctx, widget_h, 3); + { + if(nk_checkbox_label(ctx, "overwrite", &handle->state.overwrite)) + _toggle(handle, handle->urid.overwrite, handle->state.overwrite, true); + if(nk_checkbox_label(ctx, "block", &handle->state.block)) + _toggle(handle, handle->urid.block, handle->state.block, true); + if(nk_checkbox_label(ctx, "follow", &handle->state.follow)) + _toggle(handle, handle->urid.follow, handle->state.follow, true); + } + + const bool max_reached = handle->n_item >= MAX_LINES; + nk_layout_row_dynamic(ctx, widget_h, 2); + if(nk_button_symbol_label(ctx, + max_reached ? NK_SYMBOL_TRIANGLE_RIGHT: NK_SYMBOL_NONE, + "clear", NK_TEXT_LEFT)) + { + _clear(handle); + } + nk_label(ctx, "Sherlock.lv2: "SHERLOCK_VERSION, NK_TEXT_RIGHT); + } + nk_end(ctx); +} diff --git a/nk_pugl/COPYING b/nk_pugl/COPYING new file mode 100644 index 0000000..ddb9a46 --- /dev/null +++ b/nk_pugl/COPYING @@ -0,0 +1,201 @@ + The Artistic License 2.0 + + Copyright (c) 2000-2006, The Perl Foundation. + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +Preamble + +This license establishes the terms under which a given free software +Package may be copied, modified, distributed, and/or redistributed. +The intent is that the Copyright Holder maintains some artistic +control over the development of that Package while still keeping the +Package available as open source and free software. + +You are always permitted to make arrangements wholly outside of this +license directly with the Copyright Holder of a given Package. If the +terms of this license do not permit the full use that you propose to +make of the Package, you should contact the Copyright Holder and seek +a different licensing arrangement. + +Definitions + + "Copyright Holder" means the individual(s) or organization(s) + named in the copyright notice for the entire Package. + + "Contributor" means any party that has contributed code or other + material to the Package, in accordance with the Copyright Holder's + procedures. + + "You" and "your" means any person who would like to copy, + distribute, or modify the Package. + + "Package" means the collection of files distributed by the + Copyright Holder, and derivatives of that collection and/or of + those files. A given Package may consist of either the Standard + Version, or a Modified Version. + + "Distribute" means providing a copy of the Package or making it + accessible to anyone else, or in the case of a company or + organization, to others outside of your company or organization. + + "Distributor Fee" means any fee that you charge for Distributing + this Package or providing support for this Package to another + party. It does not mean licensing fees. + + "Standard Version" refers to the Package if it has not been + modified, or has been modified only in ways explicitly requested + by the Copyright Holder. + + "Modified Version" means the Package, if it has been changed, and + such changes were not explicitly requested by the Copyright + Holder. + + "Original License" means this Artistic License as Distributed with + the Standard Version of the Package, in its current version or as + it may be modified by The Perl Foundation in the future. + + "Source" form means the source code, documentation source, and + configuration files for the Package. + + "Compiled" form means the compiled bytecode, object code, binary, + or any other form resulting from mechanical transformation or + translation of the Source form. + + +Permission for Use and Modification Without Distribution + +(1) You are permitted to use the Standard Version and create and use +Modified Versions for any purpose without restriction, provided that +you do not Distribute the Modified Version. + + +Permissions for Redistribution of the Standard Version + +(2) You may Distribute verbatim copies of the Source form of the +Standard Version of this Package in any medium without restriction, +either gratis or for a Distributor Fee, provided that you duplicate +all of the original copyright notices and associated disclaimers. At +your discretion, such verbatim copies may or may not include a +Compiled form of the Package. + +(3) You may apply any bug fixes, portability changes, and other +modifications made available from the Copyright Holder. The resulting +Package will still be considered the Standard Version, and as such +will be subject to the Original License. + + +Distribution of Modified Versions of the Package as Source + +(4) You may Distribute your Modified Version as Source (either gratis +or for a Distributor Fee, and with or without a Compiled form of the +Modified Version) provided that you clearly document how it differs +from the Standard Version, including, but not limited to, documenting +any non-standard features, executables, or modules, and provided that +you do at least ONE of the following: + + (a) make the Modified Version available to the Copyright Holder + of the Standard Version, under the Original License, so that the + Copyright Holder may include your modifications in the Standard + Version. + + (b) ensure that installation of your Modified Version does not + prevent the user installing or running the Standard Version. In + addition, the Modified Version must bear a name that is different + from the name of the Standard Version. + + (c) allow anyone who receives a copy of the Modified Version to + make the Source form of the Modified Version available to others + under + + (i) the Original License or + + (ii) a license that permits the licensee to freely copy, + modify and redistribute the Modified Version using the same + licensing terms that apply to the copy that the licensee + received, and requires that the Source form of the Modified + Version, and of any works derived from it, be made freely + available in that license fees are prohibited but Distributor + Fees are allowed. + + +Distribution of Compiled Forms of the Standard Version +or Modified Versions without the Source + +(5) You may Distribute Compiled forms of the Standard Version without +the Source, provided that you include complete instructions on how to +get the Source of the Standard Version. Such instructions must be +valid at the time of your distribution. If these instructions, at any +time while you are carrying out such distribution, become invalid, you +must provide new instructions on demand or cease further distribution. +If you provide valid instructions or cease distribution within thirty +days after you become aware that the instructions are invalid, then +you do not forfeit any of your rights under this license. + +(6) You may Distribute a Modified Version in Compiled form without +the Source, provided that you comply with Section 4 with respect to +the Source of the Modified Version. + + +Aggregating or Linking the Package + +(7) You may aggregate the Package (either the Standard Version or +Modified Version) with other packages and Distribute the resulting +aggregation provided that you do not charge a licensing fee for the +Package. Distributor Fees are permitted, and licensing fees for other +components in the aggregation are permitted. The terms of this license +apply to the use and Distribution of the Standard or Modified Versions +as included in the aggregation. + +(8) You are permitted to link Modified and Standard Versions with +other works, to embed the Package in a larger work of your own, or to +build stand-alone binary or bytecode versions of applications that +include the Package, and Distribute the result without restriction, +provided the result does not expose a direct interface to the Package. + + +Items That are Not Considered Part of a Modified Version + +(9) Works (including, but not limited to, modules and scripts) that +merely extend or make use of the Package, do not, by themselves, cause +the Package to be a Modified Version. In addition, such works are not +considered parts of the Package itself, and are not subject to the +terms of this license. + + +General Provisions + +(10) Any use, modification, and distribution of the Standard or +Modified Versions is governed by this Artistic License. By using, +modifying or distributing the Package, you accept this license. Do not +use, modify, or distribute the Package, if you do not accept this +license. + +(11) If your Modified Version has been derived from a Modified +Version made by someone other than you, you are nevertheless required +to ensure that your Modified Version complies with the requirements of +this license. + +(12) This license does not grant you the right to use any trademark, +service mark, tradename, or logo of the Copyright Holder. + +(13) This license includes the non-exclusive, worldwide, +free-of-charge patent license to make, have made, use, offer to sell, +sell, import and otherwise transfer the Package with respect to any +patent claims licensable by the Copyright Holder that are necessarily +infringed by the Package. If you institute patent litigation +(including a cross-claim or counterclaim) against any party alleging +that the Package constitutes direct or contributory patent +infringement, then this Artistic License to you shall terminate on the +date that such litigation is filed. + +(14) Disclaimer of Warranty: +THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS +IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR +NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL +LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/nk_pugl.h b/nk_pugl/nk_pugl.h index d876dfc..d876dfc 100644 --- a/nk_pugl.h +++ b/nk_pugl/nk_pugl.h diff --git a/nuklear/.gitattributes b/nuklear/.gitattributes new file mode 100644 index 0000000..5a5328c --- /dev/null +++ b/nuklear/.gitattributes @@ -0,0 +1,3 @@ +# Github language settings +*.h linguist-language=c +*.c linguist-language=c diff --git a/nuklear/.gitignore b/nuklear/.gitignore new file mode 100644 index 0000000..a9f3b63 --- /dev/null +++ b/nuklear/.gitignore @@ -0,0 +1,2 @@ +demo/*/*.exe +demo/*/*.obj diff --git a/nuklear/.travis.yml b/nuklear/.travis.yml new file mode 100644 index 0000000..7df45b3 --- /dev/null +++ b/nuklear/.travis.yml @@ -0,0 +1,16 @@ +language: c + +os: + - linux + - osx + +compiler: + - gcc + - clang + +before_install: + - if [ $TRAVIS_OS_NAME == linux ]; then sudo add-apt-repository -y ppa:pyglfw/pyglfw && sudo apt-get update -qq && sudo apt-get install -y --no-install-recommends libglfw3 libglfw3-dev libglew-dev; fi + - if [ $TRAVIS_OS_NAME == osx ]; then brew update && brew install glfw3 && brew install glew; fi + +script: + - make -C demo/glfw_opengl3 diff --git a/nuklear/CHANGELOG.md b/nuklear/CHANGELOG.md new file mode 100644 index 0000000..17f1858 --- /dev/null +++ b/nuklear/CHANGELOG.md @@ -0,0 +1,190 @@ +# Changelog +- 2016/12/03 (1.191)- Fixed wrapped text with no seperator and C89 error +- 2016/12/03 (1.19) - Changed text wrapping to process words not characters +- 2016/11/22 (1.184)- Fixed window minimized closing bug +- 2016/11/19 (1.184)- Fixed abstract combo box closing behavior +- 2016/11/19 (1.184)- Fixed tooltip flickering +- 2016/11/19 (1.183)- Fixed memory leak caused by popup repeated closing +- 2016/11/18 (1.182)- Fixed memory leak caused by popup panel allocation +- 2016/11/10 (1.181)- Fixed some warnings and C++ error +- 2016/11/10 (1.180)- Added additional `nk_button` versions which allows to directly + pass in a style struct to change buttons visual. +- 2016/11/10 (1.180)- Added additional 'nk_tree' versions to support external state + storage. Just like last the `nk_group` commit the main + advantage is that you optionally can minimize nuklears runtime + memory consumption or handle hash collisions. +- 2016/11/09 (1.180)- Added additional `nk_group` version to support external scrollbar + offset storage. Main advantage is that you can externalize + the memory management for the offset. It could also be helpful + if you have a hash collision in `nk_group_begin` but really + want the name. In addition I added `nk_list_view` which allows + to draw big lists inside a group without actually having to + commit the whole list to nuklear (issue #269). +- 2016/10/30 (1.171)- Fixed clipping rectangle bug inside `nk_draw_list` +- 2016/10/29 (1.170)- Pulled `nk_panel` memory management into nuklear and out of + the hands of the user. From now on users don't have to care + about panels unless they care about some information. If you + still need the panel just call `nk_window_get_panel`. +- 2016/10/21 (1.160)- Changed widget border drawing to stroked rectangle from filled + rectangle for less overdraw and widget background transparency. +- 2016/10/18 (1.160)- Added `nk_edit_focus` for manually edit widget focus control +- 2016/09/29 (1.157)- Fixed deduction of basic type in non `<stdint.h>` compilation +- 2016/09/29 (1.156)- Fixed edit widget UTF-8 text cursor drawing bug +- 2016/09/28 (1.156)- Fixed edit widget UTF-8 text appending/inserting/removing +- 2016/09/28 (1.156)- Fixed drawing bug inside edit widgets which offset all text + text in every edit widget if one of them is scrolled. +- 2016/09/28 (1.155)- Fixed small bug in edit widgets if not active. The wrong + text length is passed. It should have been in bytes but + was passed as glyphes. +- 2016/09/20 (1.154)- Fixed color button size calculation +- 2016/09/20 (1.153)- Fixed some `nk_vsnprintf` behavior bugs and removed + `<stdio.h>` again from `NK_INCLUDE_STANDARD_VARARGS`. +- 2016/09/18 (1.152)- C89 does not support vsnprintf only C99 and newer as well + as C++11 and newer. In addition to use vsnprintf you have + to include <stdio.h>. So just defining `NK_INCLUDE_STD_VAR_ARGS` + is not enough. That behavior is now fixed. By default if + both varargs as well as stdio is selected I try to use + vsnprintf if not possible I will revert to vsprintf. If + varargs but not stdio was defined I will use my own function. +- 2016/09/15 (1.151)- Fixed panel `close` behavior for deeper panel levels +- 2016/09/15 (1.151)- Fixed C++ errors and wrong argument to `nk_panel_get_xxxx` +- 2016/09/13 (1.15) - !BREAKING! Fixed nonblocking popup behavior in menu, combo, + and contextual which prevented closing in y-direction if + popup did not reach max height. + In addition the height parameter was changed into vec2 + for width and height to have more control over the popup size. +- 2016/09/13 (1.15) - Cleaned up and extended type selection +- 2016/09/13 (1.141)- Fixed slider behavior hopefully for the last time. This time + all calculation are correct so no more hackery. +- 2016/09/13 (1.141)- Internal change to divide window/panel flags into panel flags and types. + Suprisinly spend years in C and still happened to confuse types + with flags. Probably something to take note. +- 2016/09/08 (1.14)- Added additional helper function to make it easier to just + take the produced buffers from `nk_convert` and unplug the + iteration process from `nk_context`. So now you can + just use the vertex,element and command buffer + two pointer + inside the command buffer retrieved by calls `nk__draw_begin` + and `nk__draw_end` and macro `nk_draw_foreach_bounded`. +- 2016/09/08 (1.14)- Added additional asserts to make sure every `nk_xxx_begin` call + for windows, popups, combobox, menu and contextual is guarded by + `if` condition and does not produce false drawing output. +- 2016/09/08 (1.14)- Changed confusing name for `NK_SYMBOL_RECT_FILLED`, `NK_SYMBOL_RECT` + to hopefully easier to understand `NK_SYMBOL_RECT_FILLED` and + `NK_SYMBOL_RECT_OUTLINE`. +- 2016/09/08 (1.14)- Changed confusing name for `NK_SYMBOL_CIRLCE_FILLED`, `NK_SYMBOL_CIRCLE` + to hopefully easier to understand `NK_SYMBOL_CIRCLE_FILLED` and + `NK_SYMBOL_CIRCLE_OUTLINE`. +- 2016/09/08 (1.14)- Added additional checks to select correct types if `NK_INCLUDE_FIXED_TYPES` + is not defined by supporting the biggest compiler GCC, clang and MSVC. +- 2016/09/07 (1.133)- Fixed `NK_INCLUDE_COMMAND_USERDATA` define to not cause an error +- 2016/09/04 (1.132)- Fixed wrong combobox height calculation +- 2016/09/03 (1.131)- Fixed gaps inside combo boxes in OpenGL +- 2016/09/02 (1.13) - Changed nuklear to not have any default vertex layout and + instead made it user provided. The range of types to convert + to is quite limited at the moment, but I would be more than + happy to accept PRs to add additional. +- 2016/08/30 (1.12) - Removed unused variables +- 2016/08/30 (1.12) - Fixed C++ build errors +- 2016/08/30 (1.12) - Removed mouse dragging from SDL demo since it does not work correctly +- 2016/08/30 (1.12) - Tweaked some default styling variables +- 2016/08/30 (1.12) - Hopefully fixed drawing bug in slider, in general I would + refrain from using slider with a big number of steps. +- 2016/08/30 (1.12) - Fixed close and minimize button which would fire even if the + window was in Read Only Mode. +- 2016/08/30 (1.12) - Fixed popup panel padding handling which was previously just + a hack for combo box and menu. +- 2016/08/30 (1.12) - Removed `NK_WINDOW_DYNAMIC` flag from public API since + it is bugged and causes issues in window selection. +- 2016/08/30 (1.12) - Removed scaler size. The size of the scaler is now + determined by the scrollbar size +- 2016/08/30 (1.12) - Fixed some drawing bugs caused by changes from 1.11 +- 2016/08/30 (1.12) - Fixed overlapping minimized window selection +- 2016/08/30 (1.11) - Removed some internal complexity and overly complex code + handling panel padding and panel border. +- 2016/08/29 (1.10) - Added additional height parameter to `nk_combobox_xxx` +- 2016/08/29 (1.10) - Fixed drawing bug in dynamic popups +- 2016/08/29 (1.10) - Added experimental mouse scrolling to popups, menus and comboboxes +- 2016/08/26 (1.10) - Added window name string prepresentation to account for + hash collisions. Currently limited to NK_WINDOW_MAX_NAME + which in term can be redefined if not big enough. +- 2016/08/26 (1.10) - Added stacks for temporary style/UI changes in code +- 2016/08/25 (1.10) - Changed `nk_input_is_key_pressed` and 'nk_input_is_key_released' + to account for key press and release happening in one frame. +- 2016/08/25 (1.10) - Added additional nk_edit flag to directly jump to the end on activate +- 2016/08/17 (1.096)- Removed invalid check for value zero in nk_propertyx +- 2016/08/16 (1.095)- Fixed ROM mode for deeper levels of popup windows parents. +- 2016/08/15 (1.094)- Editbox are now still active if enter was pressed with flag + `NK_EDIT_SIG_ENTER`. Main reasoning is to be able to keep + typing after commiting. +- 2016/08/15 (1.094)- Removed redundant code +- 2016/08/15 (1.094)- Fixed negative numbers in `nk_strtoi` and remove unused variable +- 2016/08/15 (1.093)- Fixed `NK_WINDOW_BACKGROUND` flag behavior to select a background + window only as selected by hovering and not by clicking. +- 2016/08/14 (1.092)- Fixed a bug in font atlas which caused wrong loading + of glyphes for font with multiple ranges. +- 2016/08/12 (1.091)- Added additional function to check if window is currently + hidden and therefore not visible. +- 2016/08/12 (1.091)- nk_window_is_closed now queries the correct flag `NK_WINDOW_CLOSED` + instead of the old flag `NK_WINDOW_HIDDEN` +- 2016/08/09 (1.09) - Added additional double version to nk_property and changed + the underlying implementation to not cast to float and instead + work directly on the given values. +- 2016/08/09 (1.08) - Added additional define to overwrite library internal + floating pointer number to string conversion for additional + precision. +- 2016/08/09 (1.08) - Added additional define to overwrite library internal + string to floating point number conversion for additional + precision. +- 2016/08/08 (1.072)- Fixed compiling error without define NK_INCLUDE_FIXED_TYPE +- 2016/08/08 (1.071)- Fixed possible floating point error inside `nk_widget` leading + to wrong wiget width calculation which results in widgets falsly + becomming tagged as not inside window and cannot be accessed. +- 2016/08/08 (1.07) - Nuklear now differentiates between hiding a window (NK_WINDOW_HIDDEN) and + closing a window (NK_WINDOW_CLOSED). A window can be hidden/shown + by using `nk_window_show` and closed by either clicking the close + icon in a window or by calling `nk_window_close`. Only closed + windows get removed at the end of the frame while hidden windows + remain. +- 2016/08/08 (1.06) - Added `nk_edit_string_zero_terminated` as a second option to + `nk_edit_string` which takes, edits and outputs a '\0' terminated string. +- 2016/08/08 (1.054)- Fixed scrollbar auto hiding behavior +- 2016/08/08 (1.053)- Fixed wrong panel padding selection in `nk_layout_widget_space` +- 2016/08/07 (1.052)- Fixed old bug in dynamic immediate mode layout API, calculating + wrong item spacing and panel width. +- 2016/08/07 (1.051)- Hopefully finally fixed combobox popup drawing bug +- 2016/08/07 (1.05) - Split varargs away from NK_INCLUDE_STANDARD_IO into own + define NK_INCLUDE_STANDARD_VARARGS to allow more fine + grained controlled over library includes. +- 2016/08/06 (1.045)- Changed memset calls to NK_MEMSET +- 2016/08/04 (1.044)- Fixed fast window scaling behavior +- 2016/08/04 (1.043)- Fixed window scaling, movement bug which appears if you + move/scale a window and another window is behind it. + If you are fast enough then the window behind gets activated + and the operation is blocked. I now require activating + by hovering only if mouse is not pressed. +- 2016/08/04 (1.042)- Fixed changing fonts +- 2016/08/03 (1.041)- Fixed `NK_WINDOW_BACKGROUND` behavior +- 2016/08/03 (1.04) - Added color parameter to `nk_draw_image` +- 2016/08/03 (1.04) - Added additional window padding style attributes for + sub windows (combo, menu, ...) +- 2016/08/03 (1.04) - Added functions to show/hide software cursor +- 2016/08/03 (1.04) - Added `NK_WINDOW_BACKGROUND` flag to force a window + to be always in the background of the screen +- 2016/08/03 (1.032)- Removed invalid assert macro for NK_RGB color picker +- 2016/08/01 (1.031)- Added helper macros into header include guard +- 2016/07/29 (1.03) - Moved the window/table pool into the header part to + simplify memory management by removing the need to + allocate the pool. +- 2016/07/29 (1.03) - Added auto scrollbar hiding window flag which if enabled + will hide the window scrollbar after NK_SCROLLBAR_HIDING_TIMEOUT + seconds without window interaction. To make it work + you have to also set a delta time inside the `nk_context`. +- 2016/07/25 (1.02) - Fixed small panel and panel border drawing bugs +- 2016/07/15 (1.01) - Added software cursor to `nk_style` and `nk_context` +- 2016/07/15 (1.01) - Added const correctness to `nk_buffer_push' data argument +- 2016/07/15 (1.01) - Removed internal font baking API and simplified + font atlas memory management by converting pointer + arrays for fonts and font configurations to lists. +- 2016/07/15 (1.01) - Changed button API to use context dependend button + behavior instead of passing it for every function call. + diff --git a/nuklear/Readme.md b/nuklear/Readme.md new file mode 100644 index 0000000..29d56d3 --- /dev/null +++ b/nuklear/Readme.md @@ -0,0 +1,111 @@ +[](https://travis-ci.org/vurtun/nuklear) + +# Nuklear +This is a minimal state immediate mode graphical user interface toolkit +written in ANSI C and licensed under public domain. It was designed as a simple +embeddable user interface for application and does not have any dependencies, +a default renderbackend or OS window and input handling but instead provides a very modular +library approach by using simple input state for input and draw +commands describing primitive shapes as output. So instead of providing a +layered library that tries to abstract over a number of platform and +render backends it only focuses on the actual UI. + +## Features +- Immediate mode graphical user interface toolkit +- Single header library +- Written in C89 (ANSI C) +- Small codebase (~15kLOC) +- Focus on portability, efficiency and simplicity +- No dependencies (not even the standard library if not wanted) +- Fully skinnable and customizable +- Low memory footprint with total memory control if needed or wanted +- UTF-8 support +- No global or hidden state +- Customizable library modules (you can compile and use only what you need) +- Optional font baker and vertex buffer output + +## Building +This library is self contained in one single header file and can be used either +in header only mode or in implementation mode. The header only mode is used +by default when included and allows including this header in other headers +and does not contain the actual implementation. + +The implementation mode requires to define the preprocessor macro +`NK_IMPLEMENTATION` in *one* .c/.cpp file before `#include`ing this file, e.g.: +```c +#define NK_IMPLEMENTATION +#include "nuklear.h" +``` +IMPORTANT: Every time you include "nuklear.h" you have to define the same optional flags. +This is very important not doing it either leads to compiler errors or even worse stack corruptions. + +## Gallery + + + + + + + +## Example +```c +/* init gui state */ +struct nk_context ctx; +nk_init_fixed(&ctx, calloc(1, MAX_MEMORY), MAX_MEMORY, &font); + +enum {EASY, HARD}; +int op = EASY; +float value = 0.6f; +int i = 20; + +if (nk_begin(&ctx, "Show", nk_rect(50, 50, 220, 220), + NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_CLOSABLE)) { + /* fixed widget pixel width */ + nk_layout_row_static(&ctx, 30, 80, 1); + if (nk_button_label(&ctx, "button")) { + /* event handling */ + } + + /* fixed widget window ratio width */ + nk_layout_row_dynamic(&ctx, 30, 2); + if (nk_option_label(&ctx, "easy", op == EASY)) op = EASY; + if (nk_option_label(&ctx, "hard", op == HARD)) op = HARD; + + /* custom widget pixel width */ + nk_layout_row_begin(&ctx, NK_STATIC, 30, 2); + { + nk_layout_row_push(&ctx, 50); + nk_label(&ctx, "Volume:", NK_TEXT_LEFT); + nk_layout_row_push(&ctx, 110); + nk_slider_float(&ctx, 0, &value, 1.0f, 0.1f); + } + nk_layout_row_end(&ctx); +} +nk_end(&ctx); +``` + + +## Bindings: +Java: https://github.com/glegris/nuklear4j +Golang: https://github.com/golang-ui/nuklear +Rust: https://github.com/snuk182/nuklear-rust + +## Credits: +Developed by Micha Mettke and every direct or indirect contributor to the GitHub. + + +Embeds `stb_texedit`, `stb_truetype` and `stb_rectpack` by Sean Barret (public domain) +Embeds `ProggyClean.ttf` font by Tristan Grimmer (MIT license). + + +Big thank you to Omar Cornut (ocornut@github) for his [imgui](https://github.com/ocornut/imgui) library and +giving me the inspiration for this library, Casey Muratori for handmade hero +and his original immediate mode graphical user interface idea and Sean +Barret for his amazing single header [libraries](https://github.com/nothings/stb) which restored my faith +in libraries and brought me to create some of my own. + +## License: +This software is dual-licensed to the public domain and under the following +license: you are granted a perpetual, irrevocable license to copy, modify, +publish and distribute this file as you see fit. + diff --git a/nuklear/demo/calculator.c b/nuklear/demo/calculator.c new file mode 100644 index 0000000..b871301 --- /dev/null +++ b/nuklear/demo/calculator.c @@ -0,0 +1,64 @@ +/* nuklear - v1.00 - public domain */ +static void +calculator(struct nk_context *ctx) +{ + if (nk_begin(ctx, "Calculator", nk_rect(10, 10, 180, 250), + NK_WINDOW_BORDER|NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_MOVABLE)) + { + static int set = 0, prev = 0, op = 0; + static const char numbers[] = "789456123"; + static const char ops[] = "+-*/"; + static double a = 0, b = 0; + static double *current = &a; + + size_t i = 0; + int solve = 0; + {int len; char buffer[256]; + nk_layout_row_dynamic(ctx, 35, 1); + len = snprintf(buffer, 256, "%.2f", *current); + nk_edit_string(ctx, NK_EDIT_SIMPLE, buffer, &len, 255, nk_filter_float); + buffer[len] = 0; + *current = atof(buffer);} + + nk_layout_row_dynamic(ctx, 35, 4); + for (i = 0; i < 16; ++i) { + if (i >= 12 && i < 15) { + if (i > 12) continue; + if (nk_button_label(ctx, "C")) { + a = b = op = 0; current = &a; set = 0; + } if (nk_button_label(ctx, "0")) { + *current = *current*10.0f; set = 0; + } if (nk_button_label(ctx, "=")) { + solve = 1; prev = op; op = 0; + } + } else if (((i+1) % 4)) { + if (nk_button_text(ctx, &numbers[(i/4)*3+i%4], 1)) { + *current = *current * 10.0f + numbers[(i/4)*3+i%4] - '0'; + set = 0; + } + } else if (nk_button_text(ctx, &ops[i/4], 1)) { + if (!set) { + if (current != &b) { + current = &b; + } else { + prev = op; + solve = 1; + } + } + op = ops[i/4]; + set = 1; + } + } + if (solve) { + if (prev == '+') a = a + b; + if (prev == '-') a = a - b; + if (prev == '*') a = a * b; + if (prev == '/') a = a / b; + current = &a; + if (set) current = &b; + b = 0; set = 0; + } + } + nk_end(ctx); +} + diff --git a/nuklear/demo/d3d11/build.bat b/nuklear/demo/d3d11/build.bat new file mode 100644 index 0000000..31bd0e0 --- /dev/null +++ b/nuklear/demo/d3d11/build.bat @@ -0,0 +1,9 @@ +@echo off + +rem This will use VS2015 for compiler +call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86 + +fxc.exe /nologo /T vs_4_0_level_9_0 /E vs /O3 /Zpc /Ges /Fh nuklear_d3d11_vertex_shader.h /Vn nk_d3d11_vertex_shader /Qstrip_reflect /Qstrip_debug /Qstrip_priv nuklear_d3d11.hlsl +fxc.exe /nologo /T ps_4_0_level_9_0 /E ps /O3 /Zpc /Ges /Fh nuklear_d3d11_pixel_shader.h /Vn nk_d3d11_pixel_shader /Qstrip_reflect /Qstrip_debug /Qstrip_priv nuklear_d3d11.hlsl + +cl /D_CRT_SECURE_NO_DEPRECATE /nologo /W3 /O2 /fp:fast /Gm- /Fedemo.exe main.c user32.lib dxguid.lib d3d11.lib /link /incremental:no diff --git a/nuklear/demo/d3d11/main.c b/nuklear/demo/d3d11/main.c new file mode 100644 index 0000000..bc0dc64 --- /dev/null +++ b/nuklear/demo/d3d11/main.c @@ -0,0 +1,278 @@ +/* nuklear - v1.17 - public domain */ +#define COBJMACROS +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <d3d11.h> +#include <stdio.h> +#include <string.h> +#include <limits.h> +#include <time.h> + +#define WINDOW_WIDTH 800 +#define WINDOW_HEIGHT 600 + +#define MAX_VERTEX_BUFFER 512 * 1024 +#define MAX_INDEX_BUFFER 128 * 1024 + +#define NK_INCLUDE_FIXED_TYPES +#define NK_INCLUDE_STANDARD_IO +#define NK_INCLUDE_STANDARD_VARARGS +#define NK_INCLUDE_DEFAULT_ALLOCATOR +#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT +#define NK_INCLUDE_FONT_BAKING +#define NK_INCLUDE_DEFAULT_FONT +#define NK_IMPLEMENTATION +#define NK_D3D11_IMPLEMENTATION +#include "../../nuklear.h" +#include "nuklear_d3d11.h" + +/* =============================================================== + * + * EXAMPLE + * + * ===============================================================*/ +/* This are some code examples to provide a small overview of what can be + * done with this library. To try out an example uncomment the include + * and the corresponding function. */ + #define UNUSED(a) (void)a + #define MIN(a,b) ((a) < (b) ? (a) : (b)) + #define MAX(a,b) ((a) < (b) ? (b) : (a)) + #define LEN(a) (sizeof(a)/sizeof(a)[0]) + +/*#include "../style.c"*/ +/*#include "../calculator.c"*/ +/*#include "../overview.c"*/ +/*#include "../node_editor.c"*/ + +/* =============================================================== + * + * DEMO + * + * ===============================================================*/ +static IDXGISwapChain *swap_chain; +static ID3D11Device *device; +static ID3D11DeviceContext *context; +static ID3D11RenderTargetView* rt_view; + +static void +set_swap_chain_size(int width, int height) +{ + ID3D11Texture2D *back_buffer; + D3D11_RENDER_TARGET_VIEW_DESC desc; + HRESULT hr; + + if (rt_view) + ID3D11RenderTargetView_Release(rt_view); + + ID3D11DeviceContext_OMSetRenderTargets(context, 0, NULL, NULL); + + hr = IDXGISwapChain_ResizeBuffers(swap_chain, 0, width, height, DXGI_FORMAT_UNKNOWN, 0); + if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET || hr == DXGI_ERROR_DRIVER_INTERNAL_ERROR) + { + /* to recover from this, you'll need to recreate device and all the resources */ + MessageBoxW(NULL, L"DXGI device is removed or reset!", L"Error", 0); + exit(0); + } + assert(SUCCEEDED(hr)); + + memset(&desc, 0, sizeof(desc)); + desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + + hr = IDXGISwapChain_GetBuffer(swap_chain, 0, &IID_ID3D11Texture2D, &back_buffer); + assert(SUCCEEDED(hr)); + + hr = ID3D11Device_CreateRenderTargetView(device, (ID3D11Resource *)back_buffer, &desc, &rt_view); + assert(SUCCEEDED(hr)); + + ID3D11Texture2D_Release(back_buffer); +} + +static LRESULT CALLBACK +WindowProc(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + switch (msg) + { + case WM_DESTROY: + PostQuitMessage(0); + return 0; + + case WM_SIZE: + if (swap_chain) + { + int width = LOWORD(lparam); + int height = HIWORD(lparam); + set_swap_chain_size(width, height); + nk_d3d11_resize(context, width, height); + } + break; + } + + if (nk_d3d11_handle_event(wnd, msg, wparam, lparam)) + return 0; + + return DefWindowProcW(wnd, msg, wparam, lparam); +} + +int main(void) +{ + struct nk_context *ctx; + struct nk_color background; + + WNDCLASSW wc; + RECT rect = { 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT }; + DWORD style = WS_OVERLAPPEDWINDOW; + DWORD exstyle = WS_EX_APPWINDOW; + HWND wnd; + int running = 1; + HRESULT hr; + D3D_FEATURE_LEVEL feature_level; + DXGI_SWAP_CHAIN_DESC swap_chain_desc; + + /* Win32 */ + memset(&wc, 0, sizeof(wc)); + wc.lpfnWndProc = WindowProc; + wc.hInstance = GetModuleHandleW(0); + wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.lpszClassName = L"NuklearWindowClass"; + RegisterClassW(&wc); + + AdjustWindowRectEx(&rect, style, FALSE, exstyle); + + wnd = CreateWindowExW(exstyle, wc.lpszClassName, L"Nuklear Demo", + style | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, + rect.right - rect.left, rect.bottom - rect.top, + NULL, NULL, wc.hInstance, NULL); + + /* D3D11 setup */ + memset(&swap_chain_desc, 0, sizeof(swap_chain_desc)); + swap_chain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + swap_chain_desc.BufferDesc.RefreshRate.Numerator = 60; + swap_chain_desc.BufferDesc.RefreshRate.Denominator = 1; + swap_chain_desc.SampleDesc.Count = 1; + swap_chain_desc.SampleDesc.Quality = 0; + swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swap_chain_desc.BufferCount = 1; + swap_chain_desc.OutputWindow = wnd; + swap_chain_desc.Windowed = TRUE; + swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + swap_chain_desc.Flags = 0; + if (FAILED(D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, + NULL, 0, NULL, 0, D3D11_SDK_VERSION, &swap_chain_desc, + &swap_chain, &device, &feature_level, &context))) + { + /* if hardware device fails, then try WARP high-performance + software rasterizer, this is useful for RDP sessions */ + hr = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_WARP, + NULL, 0, NULL, 0, D3D11_SDK_VERSION, &swap_chain_desc, + &swap_chain, &device, &feature_level, &context); + assert(SUCCEEDED(hr)); + } + set_swap_chain_size(WINDOW_WIDTH, WINDOW_HEIGHT); + + /* GUI */ + ctx = nk_d3d11_init(device, WINDOW_WIDTH, WINDOW_HEIGHT, MAX_VERTEX_BUFFER, MAX_INDEX_BUFFER); + /* Load Fonts: if none of these are loaded a default font will be used */ + /* Load Cursor: if you uncomment cursor loading please hide the cursor */ + {struct nk_font_atlas *atlas; + nk_d3d11_font_stash_begin(&atlas); + /*struct nk_font *droid = nk_font_atlas_add_from_file(atlas, "../../../extra_font/DroidSans.ttf", 14, 0);*/ + /*struct nk_font *robot = nk_font_atlas_add_from_file(atlas, "../../../extra_font/Robot-Regular.ttf", 14, 0);*/ + /*struct nk_font *future = nk_font_atlas_add_from_file(atlas, "../../../extra_font/kenvector_future_thin.ttf", 13, 0);*/ + /*struct nk_font *clean = nk_font_atlas_add_from_file(atlas, "../../../extra_font/ProggyClean.ttf", 12, 0);*/ + /*struct nk_font *tiny = nk_font_atlas_add_from_file(atlas, "../../../extra_font/ProggyTiny.ttf", 10, 0);*/ + /*struct nk_font *cousine = nk_font_atlas_add_from_file(atlas, "../../../extra_font/Cousine-Regular.ttf", 13, 0);*/ + nk_d3d11_font_stash_end(); + /*nk_style_load_all_cursors(ctx, atlas->cursors);*/ + /*nk_style_set_font(ctx, &droid->handle)*/;} + + /* style.c */ + /*set_style(ctx, THEME_WHITE);*/ + /*set_style(ctx, THEME_RED);*/ + /*set_style(ctx, THEME_BLUE);*/ + /*set_style(ctx, THEME_DARK);*/ + + background = nk_rgb(28,48,62); + while (running) + { + /* Input */ + MSG msg; + nk_input_begin(ctx); + while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) + { + if (msg.message == WM_QUIT) + running = 0; + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + nk_input_end(ctx); + + /* GUI */ + if (nk_begin(ctx, "Demo", nk_rect(50, 50, 230, 250), + NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_SCALABLE| + NK_WINDOW_MINIMIZABLE|NK_WINDOW_TITLE)) + { + enum {EASY, HARD}; + static int op = EASY; + static int property = 20; + + nk_layout_row_static(ctx, 30, 80, 1); + if (nk_button_label(ctx, "button")) + fprintf(stdout, "button pressed\n"); + nk_layout_row_dynamic(ctx, 30, 2); + if (nk_option_label(ctx, "easy", op == EASY)) op = EASY; + if (nk_option_label(ctx, "hard", op == HARD)) op = HARD; + nk_layout_row_dynamic(ctx, 22, 1); + nk_property_int(ctx, "Compression:", 0, &property, 100, 10, 1); + + nk_layout_row_dynamic(ctx, 20, 1); + nk_label(ctx, "background:", NK_TEXT_LEFT); + nk_layout_row_dynamic(ctx, 25, 1); + if (nk_combo_begin_color(ctx, background, nk_vec2(nk_widget_width(ctx),400))) { + nk_layout_row_dynamic(ctx, 120, 1); + background = nk_color_picker(ctx, background, NK_RGBA); + nk_layout_row_dynamic(ctx, 25, 1); + background.r = (nk_byte)nk_propertyi(ctx, "#R:", 0, background.r, 255, 1,1); + background.g = (nk_byte)nk_propertyi(ctx, "#G:", 0, background.g, 255, 1,1); + background.b = (nk_byte)nk_propertyi(ctx, "#B:", 0, background.b, 255, 1,1); + background.a = (nk_byte)nk_propertyi(ctx, "#A:", 0, background.a, 255, 1,1); + nk_combo_end(ctx); + } + } + nk_end(ctx); + if (nk_window_is_closed(ctx, "Demo")) break; + + /* -------------- EXAMPLES ---------------- */ + /*calculator(ctx);*/ + /*overview(ctx);*/ + /*node_editor(ctx);*/ + /* ----------------------------------------- */ + + {/* Draw */ + float bg[4]; + nk_color_fv(bg, background); + ID3D11DeviceContext_ClearRenderTargetView(context, rt_view, bg); + ID3D11DeviceContext_OMSetRenderTargets(context, 1, &rt_view, NULL); + nk_d3d11_render(context, NK_ANTI_ALIASING_ON); + hr = IDXGISwapChain_Present(swap_chain, 1, 0); + if (hr == DXGI_ERROR_DEVICE_RESET || hr == DXGI_ERROR_DEVICE_REMOVED) { + /* to recover from this, you'll need to recreate device and all the resources */ + MessageBoxW(NULL, L"D3D11 device is lost or removed!", L"Error", 0); + break; + } else if (hr == DXGI_STATUS_OCCLUDED) { + /* window is not visible, so vsync won't work. Let's sleep a bit to reduce CPU usage */ + Sleep(10); + } + assert(SUCCEEDED(hr));} + } + + ID3D11DeviceContext_ClearState(context); + nk_d3d11_shutdown(); + ID3D11ShaderResourceView_Release(rt_view); + ID3D11DeviceContext_Release(context); + ID3D11Device_Release(device); + IDXGISwapChain_Release(swap_chain); + UnregisterClassW(wc.lpszClassName, wc.hInstance); + return 0; +} diff --git a/nuklear/demo/d3d11/nuklear_d3d11.h b/nuklear/demo/d3d11/nuklear_d3d11.h new file mode 100644 index 0000000..efddf0d --- /dev/null +++ b/nuklear/demo/d3d11/nuklear_d3d11.h @@ -0,0 +1,617 @@ +/* + * Nuklear - v1.17 - public domain + * no warrenty implied; use at your own risk. + * authored from 2015-2016 by Micha Mettke + */ +/* + * ============================================================== + * + * API + * + * =============================================================== + */ +#ifndef NK_D3D11_H_ +#define NK_D3D11_H_ + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +typedef struct ID3D11Device ID3D11Device; +typedef struct ID3D11DeviceContext ID3D11DeviceContext; + +NK_API struct nk_context *nk_d3d11_init(ID3D11Device *device, int width, int height, unsigned int max_vertex_buffer, unsigned int max_index_buffer); +NK_API void nk_d3d11_font_stash_begin(struct nk_font_atlas **atlas); +NK_API void nk_d3d11_font_stash_end(void); +NK_API int nk_d3d11_handle_event(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); +NK_API void nk_d3d11_render(ID3D11DeviceContext *context, enum nk_anti_aliasing); +NK_API void nk_d3d11_resize(ID3D11DeviceContext *context, int width, int height); +NK_API void nk_d3d11_shutdown(void); + +#endif +/* + * ============================================================== + * + * IMPLEMENTATION + * + * =============================================================== + */ +#ifdef NK_D3D11_IMPLEMENTATION + +#define WIN32_LEAN_AND_MEAN +#define COBJMACROS +#include <d3d11.h> + +#include <stddef.h> +#include <string.h> +#include <float.h> +#include <assert.h> + +#include "nuklear_d3d11_vertex_shader.h" +#include "nuklear_d3d11_pixel_shader.h" + +struct nk_d3d11_vertex { + float position[2]; + float uv[2]; + nk_byte col[4]; +}; + +static struct +{ + struct nk_context ctx; + struct nk_font_atlas atlas; + struct nk_buffer cmds; + + struct nk_draw_null_texture null; + unsigned int max_vertex_buffer; + unsigned int max_index_buffer; + + D3D11_VIEWPORT viewport; + ID3D11Device *device; + ID3D11RasterizerState *rasterizer_state; + ID3D11VertexShader *vertex_shader; + ID3D11InputLayout *input_layout; + ID3D11Buffer *const_buffer; + ID3D11PixelShader *pixel_shader; + ID3D11BlendState *blend_state; + ID3D11Buffer *index_buffer; + ID3D11Buffer *vertex_buffer; + ID3D11ShaderResourceView *font_texture_view; + ID3D11SamplerState *sampler_state; +} d3d11; + +NK_API void +nk_d3d11_render(ID3D11DeviceContext *context, enum nk_anti_aliasing AA) +{ + const float blend_factor[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + const UINT stride = sizeof(struct nk_d3d11_vertex); + const UINT offset = 0; + + ID3D11DeviceContext_IASetInputLayout(context, d3d11.input_layout); + ID3D11DeviceContext_IASetVertexBuffers(context, 0, 1, &d3d11.vertex_buffer, &stride, &offset); + ID3D11DeviceContext_IASetIndexBuffer(context, d3d11.index_buffer, DXGI_FORMAT_R16_UINT, 0); + ID3D11DeviceContext_IASetPrimitiveTopology(context, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + + ID3D11DeviceContext_VSSetShader(context, d3d11.vertex_shader, NULL, 0); + ID3D11DeviceContext_VSSetConstantBuffers(context, 0, 1, &d3d11.const_buffer); + + ID3D11DeviceContext_PSSetShader(context, d3d11.pixel_shader, NULL, 0); + ID3D11DeviceContext_PSSetSamplers(context, 0, 1, &d3d11.sampler_state); + + ID3D11DeviceContext_OMSetBlendState(context, d3d11.blend_state, blend_factor, 0xffffffff); + ID3D11DeviceContext_RSSetState(context, d3d11.rasterizer_state); + ID3D11DeviceContext_RSSetViewports(context, 1, &d3d11.viewport); + + /* Convert from command queue into draw list and draw to screen */ + {/* load draw vertices & elements directly into vertex + element buffer */ + D3D11_MAPPED_SUBRESOURCE vertices; + D3D11_MAPPED_SUBRESOURCE indices; + const struct nk_draw_command *cmd; + UINT offset = 0; + HRESULT hr; + + hr = ID3D11DeviceContext_Map(context, (ID3D11Resource *)d3d11.vertex_buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &vertices); + NK_ASSERT(SUCCEEDED(hr)); + hr = ID3D11DeviceContext_Map(context, (ID3D11Resource *)d3d11.index_buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &indices); + NK_ASSERT(SUCCEEDED(hr)); + + {/* fill converting configuration */ + struct nk_convert_config config; + NK_STORAGE const struct nk_draw_vertex_layout_element vertex_layout[] = { + {NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_d3d11_vertex, position)}, + {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_d3d11_vertex, uv)}, + {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, NK_OFFSETOF(struct nk_d3d11_vertex, col)}, + {NK_VERTEX_LAYOUT_END} + }; + memset(&config, 0, sizeof(config)); + config.vertex_layout = vertex_layout; + config.vertex_size = sizeof(struct nk_d3d11_vertex); + config.vertex_alignment = NK_ALIGNOF(struct nk_d3d11_vertex); + config.global_alpha = 1.0f; + config.shape_AA = AA; + config.line_AA = AA; + config.circle_segment_count = 22; + config.curve_segment_count = 22; + config.arc_segment_count = 22; + config.null = d3d11.null; + + {/* setup buffers to load vertices and elements */ + struct nk_buffer vbuf, ibuf; + nk_buffer_init_fixed(&vbuf, vertices.pData, (size_t)d3d11.max_vertex_buffer); + nk_buffer_init_fixed(&ibuf, indices.pData, (size_t)d3d11.max_index_buffer); + nk_convert(&d3d11.ctx, &d3d11.cmds, &vbuf, &ibuf, &config);} + } + + ID3D11DeviceContext_Unmap(context, (ID3D11Resource *)d3d11.vertex_buffer, 0); + ID3D11DeviceContext_Unmap(context, (ID3D11Resource *)d3d11.index_buffer, 0); + + /* iterate over and execute each draw command */ + nk_draw_foreach(cmd, &d3d11.ctx, &d3d11.cmds) + { + D3D11_RECT scissor; + ID3D11ShaderResourceView *texture_view = (ID3D11ShaderResourceView *)cmd->texture.ptr; + if (!cmd->elem_count) continue; + + scissor.left = (LONG)cmd->clip_rect.x; + scissor.right = (LONG)(cmd->clip_rect.x + cmd->clip_rect.w); + scissor.top = (LONG)cmd->clip_rect.y; + scissor.bottom = (LONG)(cmd->clip_rect.y + cmd->clip_rect.h); + + ID3D11DeviceContext_PSSetShaderResources(context, 0, 1, &texture_view); + ID3D11DeviceContext_RSSetScissorRects(context, 1, &scissor); + ID3D11DeviceContext_DrawIndexed(context, (UINT)cmd->elem_count, offset, 0); + offset += cmd->elem_count; + } + nk_clear(&d3d11.ctx);} +} + +static void +nk_d3d11_get_projection_matrix(int width, int height, float *result) +{ + const float L = 0.0f; + const float R = (float)width; + const float T = 0.0f; + const float B = (float)height; + float matrix[4][4] = + { + { 2.0f / (R - L), 0.0f, 0.0f, 0.0f }, + { 0.0f, 2.0f / (T - B), 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.5f, 0.0f }, + { (R + L) / (L - R), (T + B) / (B - T), 0.5f, 1.0f }, + }; + memcpy(result, matrix, sizeof(matrix)); +} + +NK_API void +nk_d3d11_resize(ID3D11DeviceContext *context, int width, int height) +{ + D3D11_MAPPED_SUBRESOURCE mapped; + if (SUCCEEDED(ID3D11DeviceContext_Map(context, (ID3D11Resource *)d3d11.const_buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped))) + { + nk_d3d11_get_projection_matrix(width, height, (float *)mapped.pData); + ID3D11DeviceContext_Unmap(context, (ID3D11Resource *)d3d11.const_buffer, 0); + + d3d11.viewport.Width = (float)width; + d3d11.viewport.Height = (float)height; + } +} + +NK_API int +nk_d3d11_handle_event(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + switch (msg) + { + case WM_KEYDOWN: + case WM_KEYUP: + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + { + int down = !((lparam >> 31) & 1); + int ctrl = GetKeyState(VK_CONTROL) & (1 << 15); + + switch (wparam) + { + case VK_SHIFT: + case VK_LSHIFT: + case VK_RSHIFT: + nk_input_key(&d3d11.ctx, NK_KEY_SHIFT, down); + return 1; + + case VK_DELETE: + nk_input_key(&d3d11.ctx, NK_KEY_DEL, down); + return 1; + + case VK_RETURN: + nk_input_key(&d3d11.ctx, NK_KEY_ENTER, down); + return 1; + + case VK_TAB: + nk_input_key(&d3d11.ctx, NK_KEY_TAB, down); + return 1; + + case VK_LEFT: + if (ctrl) + nk_input_key(&d3d11.ctx, NK_KEY_TEXT_WORD_LEFT, down); + else + nk_input_key(&d3d11.ctx, NK_KEY_LEFT, down); + return 1; + + case VK_RIGHT: + if (ctrl) + nk_input_key(&d3d11.ctx, NK_KEY_TEXT_WORD_RIGHT, down); + else + nk_input_key(&d3d11.ctx, NK_KEY_RIGHT, down); + return 1; + + case VK_BACK: + nk_input_key(&d3d11.ctx, NK_KEY_BACKSPACE, down); + return 1; + + case VK_HOME: + nk_input_key(&d3d11.ctx, NK_KEY_TEXT_START, down); + nk_input_key(&d3d11.ctx, NK_KEY_SCROLL_START, down); + return 1; + + case VK_END: + nk_input_key(&d3d11.ctx, NK_KEY_TEXT_END, down); + nk_input_key(&d3d11.ctx, NK_KEY_SCROLL_END, down); + return 1; + + case VK_NEXT: + nk_input_key(&d3d11.ctx, NK_KEY_SCROLL_DOWN, down); + return 1; + + case VK_PRIOR: + nk_input_key(&d3d11.ctx, NK_KEY_SCROLL_UP, down); + return 1; + + case 'C': + if (ctrl) { + nk_input_key(&d3d11.ctx, NK_KEY_COPY, down); + return 1; + } + break; + + case 'V': + if (ctrl) { + nk_input_key(&d3d11.ctx, NK_KEY_PASTE, down); + return 1; + } + break; + + case 'X': + if (ctrl) { + nk_input_key(&d3d11.ctx, NK_KEY_CUT, down); + return 1; + } + break; + + case 'Z': + if (ctrl) { + nk_input_key(&d3d11.ctx, NK_KEY_TEXT_UNDO, down); + return 1; + } + break; + + case 'R': + if (ctrl) { + nk_input_key(&d3d11.ctx, NK_KEY_TEXT_REDO, down); + return 1; + } + break; + } + return 0; + } + + case WM_CHAR: + if (wparam >= 32) + { + nk_input_unicode(&d3d11.ctx, (nk_rune)wparam); + return 1; + } + break; + + case WM_LBUTTONDOWN: + nk_input_button(&d3d11.ctx, NK_BUTTON_LEFT, (short)LOWORD(lparam), (short)HIWORD(lparam), 1); + SetCapture(wnd); + return 1; + + case WM_LBUTTONUP: + nk_input_button(&d3d11.ctx, NK_BUTTON_LEFT, (short)LOWORD(lparam), (short)HIWORD(lparam), 0); + ReleaseCapture(); + return 1; + + case WM_RBUTTONDOWN: + nk_input_button(&d3d11.ctx, NK_BUTTON_RIGHT, (short)LOWORD(lparam), (short)HIWORD(lparam), 1); + SetCapture(wnd); + return 1; + + case WM_RBUTTONUP: + nk_input_button(&d3d11.ctx, NK_BUTTON_RIGHT, (short)LOWORD(lparam), (short)HIWORD(lparam), 0); + ReleaseCapture(); + return 1; + + case WM_MBUTTONDOWN: + nk_input_button(&d3d11.ctx, NK_BUTTON_MIDDLE, (short)LOWORD(lparam), (short)HIWORD(lparam), 1); + SetCapture(wnd); + return 1; + + case WM_MBUTTONUP: + nk_input_button(&d3d11.ctx, NK_BUTTON_MIDDLE, (short)LOWORD(lparam), (short)HIWORD(lparam), 0); + ReleaseCapture(); + return 1; + + case WM_MOUSEWHEEL: + nk_input_scroll(&d3d11.ctx, (float)(short)HIWORD(wparam) / WHEEL_DELTA); + return 1; + + case WM_MOUSEMOVE: + nk_input_motion(&d3d11.ctx, (short)LOWORD(lparam), (short)HIWORD(lparam)); + return 1; + } + + return 0; +} + +static void +nk_d3d11_clipbard_paste(nk_handle usr, struct nk_text_edit *edit) +{ + (void)usr; + if (IsClipboardFormatAvailable(CF_UNICODETEXT) && OpenClipboard(NULL)) + { + HGLOBAL mem = GetClipboardData(CF_UNICODETEXT); + if (mem) + { + SIZE_T size = GlobalSize(mem) - 1; + if (size) + { + LPCWSTR wstr = (LPCWSTR)GlobalLock(mem); + if (wstr) + { + int utf8size = WideCharToMultiByte(CP_UTF8, 0, wstr, size / sizeof(wchar_t), NULL, 0, NULL, NULL); + if (utf8size) + { + char* utf8 = (char*)malloc(utf8size); + if (utf8) + { + WideCharToMultiByte(CP_UTF8, 0, wstr, size / sizeof(wchar_t), utf8, utf8size, NULL, NULL); + nk_textedit_paste(edit, utf8, utf8size); + free(utf8); + } + } + GlobalUnlock(mem); + } + } + } + CloseClipboard(); + } +} + +static void +nk_d3d11_clipbard_copy(nk_handle usr, const char *text, int len) +{ + (void)usr; + if (OpenClipboard(NULL)) + { + int wsize = MultiByteToWideChar(CP_UTF8, 0, text, len, NULL, 0); + if (wsize) + { + HGLOBAL mem = GlobalAlloc(GMEM_MOVEABLE, (wsize + 1) * sizeof(wchar_t)); + if (mem) + { + wchar_t* wstr = (wchar_t*)GlobalLock(mem); + if (wstr) + { + MultiByteToWideChar(CP_UTF8, 0, text, len, wstr, wsize); + wstr[wsize] = 0; + GlobalUnlock(mem); + SetClipboardData(CF_UNICODETEXT, mem); + } + } + } + CloseClipboard(); + } +} + +NK_API struct nk_context* +nk_d3d11_init(ID3D11Device *device, int width, int height, unsigned int max_vertex_buffer, unsigned int max_index_buffer) +{ + HRESULT hr; + d3d11.max_vertex_buffer = max_vertex_buffer; + d3d11.max_index_buffer = max_index_buffer; + d3d11.device = device; + ID3D11Device_AddRef(device); + + nk_init_default(&d3d11.ctx, 0); + d3d11.ctx.clip.copy = nk_d3d11_clipbard_copy; + d3d11.ctx.clip.paste = nk_d3d11_clipbard_paste; + d3d11.ctx.clip.userdata = nk_handle_ptr(0); + + nk_buffer_init_default(&d3d11.cmds); + + {/* rasterizer state */ + D3D11_RASTERIZER_DESC desc; + memset(&desc, 0, sizeof(desc)); + desc.FillMode = D3D11_FILL_SOLID; + desc.CullMode = D3D11_CULL_NONE; + desc.FrontCounterClockwise = FALSE; + desc.DepthBias = 0; + desc.DepthBiasClamp = 0; + desc.SlopeScaledDepthBias = 0.0f; + desc.DepthClipEnable = TRUE; + desc.ScissorEnable = TRUE; + desc.MultisampleEnable = FALSE; + desc.AntialiasedLineEnable = FALSE; + hr = ID3D11Device_CreateRasterizerState(device,&desc, &d3d11.rasterizer_state); + NK_ASSERT(SUCCEEDED(hr));} + + /* vertex shader */ + {hr = ID3D11Device_CreateVertexShader(device,nk_d3d11_vertex_shader, sizeof(nk_d3d11_vertex_shader), NULL, &d3d11.vertex_shader); + NK_ASSERT(SUCCEEDED(hr));} + + /* input layout */ + {const D3D11_INPUT_ELEMENT_DESC layout[] = { + { "POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, offsetof(struct nk_d3d11_vertex, position), D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(struct nk_d3d11_vertex, uv), D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, offsetof(struct nk_d3d11_vertex, col), D3D11_INPUT_PER_VERTEX_DATA, 0 }, + }; + hr = ID3D11Device_CreateInputLayout(device,layout, ARRAYSIZE(layout), nk_d3d11_vertex_shader, sizeof(nk_d3d11_vertex_shader), &d3d11.input_layout); + NK_ASSERT(SUCCEEDED(hr));} + + /* constant buffer */ + {float matrix[4*4]; + D3D11_BUFFER_DESC desc; + memset(&desc, 0, sizeof(desc)); + desc.ByteWidth = sizeof(matrix); + desc.Usage = D3D11_USAGE_DYNAMIC; + desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + desc.MiscFlags = 0; + + {D3D11_SUBRESOURCE_DATA data; + data.pSysMem = matrix; + data.SysMemPitch = 0; + data.SysMemSlicePitch = 0; + + nk_d3d11_get_projection_matrix(width, height, matrix); + hr = ID3D11Device_CreateBuffer(device, &desc, &data, &d3d11.const_buffer); + NK_ASSERT(SUCCEEDED(hr));}} + + /* pixel shader */ + {hr = ID3D11Device_CreatePixelShader(device, nk_d3d11_pixel_shader, sizeof(nk_d3d11_pixel_shader), NULL, &d3d11.pixel_shader); + NK_ASSERT(SUCCEEDED(hr));} + + {/* blend state */ + D3D11_BLEND_DESC desc; + memset(&desc, 0, sizeof(desc)); + desc.AlphaToCoverageEnable = FALSE; + desc.RenderTarget[0].BlendEnable = TRUE; + desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA; + desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; + desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; + desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; + desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; + desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; + desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; + hr = ID3D11Device_CreateBlendState(device, &desc, &d3d11.blend_state); + NK_ASSERT(SUCCEEDED(hr));} + + /* vertex buffer */ + {D3D11_BUFFER_DESC desc; + memset(&desc, 0, sizeof(desc)); + desc.Usage = D3D11_USAGE_DYNAMIC; + desc.ByteWidth = max_vertex_buffer; + desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + desc.MiscFlags = 0; + hr = ID3D11Device_CreateBuffer(device, &desc, NULL, &d3d11.vertex_buffer); + NK_ASSERT(SUCCEEDED(hr));} + + /* index buffer */ + {D3D11_BUFFER_DESC desc; + memset(&desc, 0, sizeof(desc)); + desc.Usage = D3D11_USAGE_DYNAMIC; + desc.ByteWidth = max_index_buffer; + desc.BindFlags = D3D11_BIND_INDEX_BUFFER; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + hr = ID3D11Device_CreateBuffer(device, &desc, NULL, &d3d11.index_buffer); + NK_ASSERT(SUCCEEDED(hr));} + + /* sampler state */ + {D3D11_SAMPLER_DESC desc; + memset(&desc, 0, sizeof(desc)); + desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; + desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; + desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; + desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP; + desc.MipLODBias = 0.0f; + desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS; + desc.MinLOD = 0.0f; + desc.MaxLOD = FLT_MAX; + hr = ID3D11Device_CreateSamplerState(device, &desc, &d3d11.sampler_state); + NK_ASSERT(SUCCEEDED(hr));} + + /* viewport */ + {d3d11.viewport.TopLeftX = 0.0f; + d3d11.viewport.TopLeftY = 0.0f; + d3d11.viewport.Width = (float)width; + d3d11.viewport.Height = (float)height; + d3d11.viewport.MinDepth = 0.0f; + d3d11.viewport.MaxDepth = 1.0f;} + return &d3d11.ctx; +} + +NK_API void +nk_d3d11_font_stash_begin(struct nk_font_atlas **atlas) +{ + nk_font_atlas_init_default(&d3d11.atlas); + nk_font_atlas_begin(&d3d11.atlas); + *atlas = &d3d11.atlas; +} + +NK_API void +nk_d3d11_font_stash_end(void) +{ + const void *image; int w, h; + image = nk_font_atlas_bake(&d3d11.atlas, &w, &h, NK_FONT_ATLAS_RGBA32); + + /* upload font to texture and create texture view */ + {ID3D11Texture2D *font_texture; + HRESULT hr; + + D3D11_TEXTURE2D_DESC desc; + memset(&desc, 0, sizeof(desc)); + desc.Width = (UINT)w; + desc.Height = (UINT)h; + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + desc.CPUAccessFlags = 0; + + {D3D11_SUBRESOURCE_DATA data; + data.pSysMem = image; + data.SysMemPitch = (UINT)(w * 4); + data.SysMemSlicePitch = 0; + hr = ID3D11Device_CreateTexture2D(d3d11.device, &desc, &data, &font_texture); + assert(SUCCEEDED(hr));} + + {D3D11_SHADER_RESOURCE_VIEW_DESC srv; + memset(&srv, 0, sizeof(srv)); + srv.Format = desc.Format; + srv.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + srv.Texture2D.MipLevels = 1; + srv.Texture2D.MostDetailedMip = 0; + hr = ID3D11Device_CreateShaderResourceView(d3d11.device, (ID3D11Resource *)font_texture, &srv, &d3d11.font_texture_view); + assert(SUCCEEDED(hr));} + ID3D11Texture2D_Release(font_texture);} + + nk_font_atlas_end(&d3d11.atlas, nk_handle_ptr(d3d11.font_texture_view), &d3d11.null); + if (d3d11.atlas.default_font) + nk_style_set_font(&d3d11.ctx, &d3d11.atlas.default_font->handle); +} + +NK_API +void nk_d3d11_shutdown(void) +{ + nk_font_atlas_clear(&d3d11.atlas); + nk_buffer_free(&d3d11.cmds); + nk_free(&d3d11.ctx); + + ID3D11SamplerState_Release(d3d11.sampler_state); + ID3D11ShaderResourceView_Release(d3d11.font_texture_view); + ID3D11Buffer_Release(d3d11.vertex_buffer); + ID3D11Buffer_Release(d3d11.index_buffer); + ID3D11BlendState_Release(d3d11.blend_state); + ID3D11PixelShader_Release(d3d11.pixel_shader); + ID3D11Buffer_Release(d3d11.const_buffer); + ID3D11VertexShader_Release(d3d11.vertex_shader); + ID3D11InputLayout_Release(d3d11.input_layout); + ID3D11RasterizerState_Release(d3d11.rasterizer_state); + ID3D11Device_Release(d3d11.device); +} + +#endif + diff --git a/nuklear/demo/d3d11/nuklear_d3d11.hlsl b/nuklear/demo/d3d11/nuklear_d3d11.hlsl new file mode 100644 index 0000000..a932dca --- /dev/null +++ b/nuklear/demo/d3d11/nuklear_d3d11.hlsl @@ -0,0 +1,36 @@ +// +cbuffer buffer0 : register(b0) +{ + float4x4 ProjectionMatrix; +}; + +sampler sampler0 : register(s0); +Texture2D<float4> texture0 : register(t0); + +struct VS_INPUT +{ + float2 pos : POSITION; + float4 col : COLOR0; + float2 uv : TEXCOORD0; +}; + +struct PS_INPUT +{ + float4 pos : SV_POSITION; + float4 col : COLOR0; + float2 uv : TEXCOORD0; +}; + +PS_INPUT vs(VS_INPUT input) +{ + PS_INPUT output; + output.pos = mul(ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f)); + output.col = input.col; + output.uv = input.uv; + return output; +} + +float4 ps(PS_INPUT input) : SV_Target +{ + return input.col * texture0.Sample(sampler0, input.uv); +} diff --git a/nuklear/demo/d3d11/nuklear_d3d11_pixel_shader.h b/nuklear/demo/d3d11/nuklear_d3d11_pixel_shader.h new file mode 100644 index 0000000..1447559 --- /dev/null +++ b/nuklear/demo/d3d11/nuklear_d3d11_pixel_shader.h @@ -0,0 +1,179 @@ +#if 0 +// +// Generated by Microsoft (R) D3D Shader Disassembler +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_POSITION 0 xyzw 0 POS float +// COLOR 0 xyzw 1 NONE float xyzw +// TEXCOORD 0 xy 2 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +// +// Sampler/Resource to DX9 shader sampler mappings: +// +// Target Sampler Source Sampler Source Resource +// -------------- --------------- ---------------- +// s0 s0 t0 +// +// +// Level9 shader bytecode: +// + ps_2_0 + dcl t0 + dcl t1.xy + dcl_2d s0 + texld r0, t1, s0 + mul r0, r0, t0 + mov oC0, r0 + +// approximately 3 instruction slots used (1 texture, 2 arithmetic) +// +// Sampler/Resource to DX9 shader sampler mappings: +// +// Target Sampler Source Sampler Source Resource +// -------------- --------------- ---------------- +// s0 s0 t0 +// +// +// XNA shader bytecode: +// + ps_2_0 + dcl t0 + dcl t1.xy + dcl_2d s0 + texld r0, r2, s0 + mul oC0, r0, r1 + +// approximately 2 instruction slots used (1 texture, 1 arithmetic) +ps_4_0 +dcl_sampler s0, mode_default +dcl_resource_texture2d (float,float,float,float) t0 +dcl_input_ps linear v1.xyzw +dcl_input_ps linear v2.xy +dcl_output o0.xyzw +dcl_temps 1 +sample r0.xyzw, v2.xyxx, t0.xyzw, s0 +mul o0.xyzw, r0.xyzw, v1.xyzw +ret +// Approximately 0 instruction slots used +#endif + +const BYTE nk_d3d11_pixel_shader[] = +{ + 68, 88, 66, 67, 249, 46, + 26, 75, 111, 182, 161, 241, + 199, 179, 191, 89, 44, 229, + 245, 103, 1, 0, 0, 0, + 124, 2, 0, 0, 5, 0, + 0, 0, 52, 0, 0, 0, + 176, 0, 0, 0, 56, 1, + 0, 0, 212, 1, 0, 0, + 72, 2, 0, 0, 88, 78, + 65, 83, 116, 0, 0, 0, + 116, 0, 0, 0, 0, 2, + 255, 255, 76, 0, 0, 0, + 40, 0, 0, 0, 0, 0, + 40, 0, 0, 0, 40, 0, + 0, 0, 40, 0, 1, 0, + 36, 0, 0, 0, 40, 0, + 0, 0, 0, 0, 0, 2, + 255, 255, 31, 0, 0, 2, + 0, 0, 0, 128, 0, 0, + 15, 176, 31, 0, 0, 2, + 0, 0, 0, 128, 1, 0, + 3, 176, 31, 0, 0, 2, + 0, 0, 0, 144, 0, 8, + 15, 160, 66, 0, 0, 3, + 0, 0, 15, 128, 2, 0, + 228, 128, 0, 8, 228, 160, + 5, 0, 0, 3, 0, 8, + 15, 128, 0, 0, 228, 128, + 1, 0, 228, 128, 255, 255, + 0, 0, 65, 111, 110, 57, + 128, 0, 0, 0, 128, 0, + 0, 0, 0, 2, 255, 255, + 88, 0, 0, 0, 40, 0, + 0, 0, 0, 0, 40, 0, + 0, 0, 40, 0, 0, 0, + 40, 0, 1, 0, 36, 0, + 0, 0, 40, 0, 0, 0, + 0, 0, 0, 2, 255, 255, + 31, 0, 0, 2, 0, 0, + 0, 128, 0, 0, 15, 176, + 31, 0, 0, 2, 0, 0, + 0, 128, 1, 0, 3, 176, + 31, 0, 0, 2, 0, 0, + 0, 144, 0, 8, 15, 160, + 66, 0, 0, 3, 0, 0, + 15, 128, 1, 0, 228, 176, + 0, 8, 228, 160, 5, 0, + 0, 3, 0, 0, 15, 128, + 0, 0, 228, 128, 0, 0, + 228, 176, 1, 0, 0, 2, + 0, 8, 15, 128, 0, 0, + 228, 128, 255, 255, 0, 0, + 83, 72, 68, 82, 148, 0, + 0, 0, 64, 0, 0, 0, + 37, 0, 0, 0, 90, 0, + 0, 3, 0, 96, 16, 0, + 0, 0, 0, 0, 88, 24, + 0, 4, 0, 112, 16, 0, + 0, 0, 0, 0, 85, 85, + 0, 0, 98, 16, 0, 3, + 242, 16, 16, 0, 1, 0, + 0, 0, 98, 16, 0, 3, + 50, 16, 16, 0, 2, 0, + 0, 0, 101, 0, 0, 3, + 242, 32, 16, 0, 0, 0, + 0, 0, 104, 0, 0, 2, + 1, 0, 0, 0, 69, 0, + 0, 9, 242, 0, 16, 0, + 0, 0, 0, 0, 70, 16, + 16, 0, 2, 0, 0, 0, + 70, 126, 16, 0, 0, 0, + 0, 0, 0, 96, 16, 0, + 0, 0, 0, 0, 56, 0, + 0, 7, 242, 32, 16, 0, + 0, 0, 0, 0, 70, 14, + 16, 0, 0, 0, 0, 0, + 70, 30, 16, 0, 1, 0, + 0, 0, 62, 0, 0, 1, + 73, 83, 71, 78, 108, 0, + 0, 0, 3, 0, 0, 0, + 8, 0, 0, 0, 80, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 3, 0, + 0, 0, 0, 0, 0, 0, + 15, 0, 0, 0, 92, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 3, 0, + 0, 0, 1, 0, 0, 0, + 15, 15, 0, 0, 98, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 3, 0, + 0, 0, 2, 0, 0, 0, + 3, 3, 0, 0, 83, 86, + 95, 80, 79, 83, 73, 84, + 73, 79, 78, 0, 67, 79, + 76, 79, 82, 0, 84, 69, + 88, 67, 79, 79, 82, 68, + 0, 171, 79, 83, 71, 78, + 44, 0, 0, 0, 1, 0, + 0, 0, 8, 0, 0, 0, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 0, 0, + 0, 0, 15, 0, 0, 0, + 83, 86, 95, 84, 97, 114, + 103, 101, 116, 0, 171, 171 +}; diff --git a/nuklear/demo/d3d11/nuklear_d3d11_vertex_shader.h b/nuklear/demo/d3d11/nuklear_d3d11_vertex_shader.h new file mode 100644 index 0000000..770d2dd --- /dev/null +++ b/nuklear/demo/d3d11/nuklear_d3d11_vertex_shader.h @@ -0,0 +1,350 @@ +#if 0 +// +// Generated by Microsoft (R) D3D Shader Disassembler +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// POSITION 0 xy 0 NONE float xy +// COLOR 0 xyzw 1 NONE float xyzw +// TEXCOORD 0 xy 2 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_POSITION 0 xyzw 0 POS float xyzw +// COLOR 0 xyzw 1 NONE float xyzw +// TEXCOORD 0 xy 2 NONE float xy +// +// +// Constant buffer to DX9 shader constant mappings: +// +// Target Reg Buffer Start Reg # of Regs Data Conversion +// ---------- ------- --------- --------- ---------------------- +// c1 cb0 0 4 ( FLT, FLT, FLT, FLT) +// +// +// Runtime generated constant mappings: +// +// Target Reg Constant Description +// ---------- -------------------------------------------------- +// c0 Vertex Shader position offset +// +// +// Level9 shader bytecode: +// + vs_2_0 + def c5, 0, 1, 0, 0 + dcl_texcoord v0 + dcl_texcoord1 v1 + dcl_texcoord2 v2 + mul r0, v0.x, c1 + mad r0, c2, v0.y, r0 + mov r1.xy, c5 + mad r0, c3, r1.x, r0 + mad r0, c4, r1.y, r0 + mul r1.xy, r0.w, c0 + add oPos.xy, r0, r1 + mov oPos.zw, r0 + mov oT0, v1 + mov oT1.xy, v2 + +// approximately 10 instruction slots used +// +// Constant buffer to DX9 shader constant mappings: +// +// Target Reg Buffer Start Reg # of Regs Data Conversion +// ---------- ------- --------- --------- ---------------------- +// c0 cb0 0 4 ( FLT, FLT, FLT, FLT) +// +// +// XNA Prepass shader bytecode: +// + vs_2_0 + def c4, 0, 1, 0, 0 + dcl_texcoord v0 + mul r1, r0.x, c0 + mad r0, c1, r0.y, r1 + mov r1.xy, c4 + mad r0, c2, r1.x, r0 + mad r0, c3, r1.y, r0 + mov oPos, r0 + +// approximately 6 instruction slots used +// +// Constant buffer to DX9 shader constant mappings: +// +// Target Reg Buffer Start Reg # of Regs Data Conversion +// ---------- ------- --------- --------- ---------------------- +// c0 cb0 0 4 ( FLT, FLT, FLT, FLT) +// +// +// XNA shader bytecode: +// + vs_2_0 + def c4, 0, 1, 0, 0 + dcl_texcoord v0 + dcl_texcoord1 v1 + dcl_texcoord2 v2 + mov oT0, r1 + mov oT1.xy, r2 + mul r1, r0.x, c0 + mad r0, c1, r0.y, r1 + mov r1.xy, c4 + mad r0, c2, r1.x, r0 + mad r0, c3, r1.y, r0 + mov oPos, r0 + +// approximately 8 instruction slots used +vs_4_0 +dcl_constantbuffer cb0[4], immediateIndexed +dcl_input v0.xy +dcl_input v1.xyzw +dcl_input v2.xy +dcl_output_siv o0.xyzw, position +dcl_output o1.xyzw +dcl_output o2.xy +dcl_temps 1 +mul r0.xyzw, v0.xxxx, cb0[0].xyzw +mad r0.xyzw, cb0[1].xyzw, v0.yyyy, r0.xyzw +mad r0.xyzw, cb0[2].xyzw, l(0.000000, 0.000000, 0.000000, 0.000000), r0.xyzw +mad o0.xyzw, cb0[3].xyzw, l(1.000000, 1.000000, 1.000000, 1.000000), r0.xyzw +mov o1.xyzw, v1.xyzw +mov o2.xy, v2.xyxx +ret +// Approximately 0 instruction slots used +#endif + +const BYTE nk_d3d11_vertex_shader[] = +{ + 68, 88, 66, 67, 215, 245, + 86, 155, 188, 117, 37, 118, + 193, 207, 209, 90, 160, 153, + 246, 188, 1, 0, 0, 0, + 72, 5, 0, 0, 6, 0, + 0, 0, 56, 0, 0, 0, + 48, 1, 0, 0, 248, 1, + 0, 0, 20, 3, 0, 0, + 100, 4, 0, 0, 212, 4, + 0, 0, 88, 78, 65, 83, + 240, 0, 0, 0, 240, 0, + 0, 0, 0, 2, 254, 255, + 192, 0, 0, 0, 48, 0, + 0, 0, 1, 0, 36, 0, + 0, 0, 48, 0, 0, 0, + 48, 0, 0, 0, 36, 0, + 0, 0, 48, 0, 0, 0, + 0, 0, 4, 0, 0, 0, + 0, 0, 0, 0, 0, 2, + 254, 255, 81, 0, 0, 5, + 4, 0, 15, 160, 0, 0, + 0, 0, 0, 0, 128, 63, + 0, 0, 0, 0, 0, 0, + 0, 0, 31, 0, 0, 2, + 5, 0, 0, 128, 0, 0, + 15, 144, 31, 0, 0, 2, + 5, 0, 1, 128, 1, 0, + 15, 144, 31, 0, 0, 2, + 5, 0, 2, 128, 2, 0, + 15, 144, 1, 0, 0, 2, + 0, 0, 15, 224, 1, 0, + 228, 128, 1, 0, 0, 2, + 1, 0, 3, 224, 2, 0, + 228, 128, 5, 0, 0, 3, + 1, 0, 15, 128, 0, 0, + 0, 128, 0, 0, 228, 160, + 4, 0, 0, 4, 0, 0, + 15, 128, 1, 0, 228, 160, + 0, 0, 85, 128, 1, 0, + 228, 128, 1, 0, 0, 2, + 1, 0, 3, 128, 4, 0, + 228, 160, 4, 0, 0, 4, + 0, 0, 15, 128, 2, 0, + 228, 160, 1, 0, 0, 128, + 0, 0, 228, 128, 4, 0, + 0, 4, 0, 0, 15, 128, + 3, 0, 228, 160, 1, 0, + 85, 128, 0, 0, 228, 128, + 1, 0, 0, 2, 0, 0, + 15, 192, 0, 0, 228, 128, + 255, 255, 0, 0, 88, 78, + 65, 80, 192, 0, 0, 0, + 192, 0, 0, 0, 0, 2, + 254, 255, 144, 0, 0, 0, + 48, 0, 0, 0, 1, 0, + 36, 0, 0, 0, 48, 0, + 0, 0, 48, 0, 0, 0, + 36, 0, 0, 0, 48, 0, + 0, 0, 0, 0, 4, 0, + 0, 0, 0, 0, 0, 0, + 0, 2, 254, 255, 81, 0, + 0, 5, 4, 0, 15, 160, + 0, 0, 0, 0, 0, 0, + 128, 63, 0, 0, 0, 0, + 0, 0, 0, 0, 31, 0, + 0, 2, 5, 0, 0, 128, + 0, 0, 15, 144, 5, 0, + 0, 3, 1, 0, 15, 128, + 0, 0, 0, 128, 0, 0, + 228, 160, 4, 0, 0, 4, + 0, 0, 15, 128, 1, 0, + 228, 160, 0, 0, 85, 128, + 1, 0, 228, 128, 1, 0, + 0, 2, 1, 0, 3, 128, + 4, 0, 228, 160, 4, 0, + 0, 4, 0, 0, 15, 128, + 2, 0, 228, 160, 1, 0, + 0, 128, 0, 0, 228, 128, + 4, 0, 0, 4, 0, 0, + 15, 128, 3, 0, 228, 160, + 1, 0, 85, 128, 0, 0, + 228, 128, 1, 0, 0, 2, + 0, 0, 15, 192, 0, 0, + 228, 128, 255, 255, 0, 0, + 65, 111, 110, 57, 20, 1, + 0, 0, 20, 1, 0, 0, + 0, 2, 254, 255, 224, 0, + 0, 0, 52, 0, 0, 0, + 1, 0, 36, 0, 0, 0, + 48, 0, 0, 0, 48, 0, + 0, 0, 36, 0, 1, 0, + 48, 0, 0, 0, 0, 0, + 4, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 2, 254, 255, 81, 0, + 0, 5, 5, 0, 15, 160, + 0, 0, 0, 0, 0, 0, + 128, 63, 0, 0, 0, 0, + 0, 0, 0, 0, 31, 0, + 0, 2, 5, 0, 0, 128, + 0, 0, 15, 144, 31, 0, + 0, 2, 5, 0, 1, 128, + 1, 0, 15, 144, 31, 0, + 0, 2, 5, 0, 2, 128, + 2, 0, 15, 144, 5, 0, + 0, 3, 0, 0, 15, 128, + 0, 0, 0, 144, 1, 0, + 228, 160, 4, 0, 0, 4, + 0, 0, 15, 128, 2, 0, + 228, 160, 0, 0, 85, 144, + 0, 0, 228, 128, 1, 0, + 0, 2, 1, 0, 3, 128, + 5, 0, 228, 160, 4, 0, + 0, 4, 0, 0, 15, 128, + 3, 0, 228, 160, 1, 0, + 0, 128, 0, 0, 228, 128, + 4, 0, 0, 4, 0, 0, + 15, 128, 4, 0, 228, 160, + 1, 0, 85, 128, 0, 0, + 228, 128, 5, 0, 0, 3, + 1, 0, 3, 128, 0, 0, + 255, 128, 0, 0, 228, 160, + 2, 0, 0, 3, 0, 0, + 3, 192, 0, 0, 228, 128, + 1, 0, 228, 128, 1, 0, + 0, 2, 0, 0, 12, 192, + 0, 0, 228, 128, 1, 0, + 0, 2, 0, 0, 15, 224, + 1, 0, 228, 144, 1, 0, + 0, 2, 1, 0, 3, 224, + 2, 0, 228, 144, 255, 255, + 0, 0, 83, 72, 68, 82, + 72, 1, 0, 0, 64, 0, + 1, 0, 82, 0, 0, 0, + 89, 0, 0, 4, 70, 142, + 32, 0, 0, 0, 0, 0, + 4, 0, 0, 0, 95, 0, + 0, 3, 50, 16, 16, 0, + 0, 0, 0, 0, 95, 0, + 0, 3, 242, 16, 16, 0, + 1, 0, 0, 0, 95, 0, + 0, 3, 50, 16, 16, 0, + 2, 0, 0, 0, 103, 0, + 0, 4, 242, 32, 16, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 101, 0, 0, 3, + 242, 32, 16, 0, 1, 0, + 0, 0, 101, 0, 0, 3, + 50, 32, 16, 0, 2, 0, + 0, 0, 104, 0, 0, 2, + 1, 0, 0, 0, 56, 0, + 0, 8, 242, 0, 16, 0, + 0, 0, 0, 0, 6, 16, + 16, 0, 0, 0, 0, 0, + 70, 142, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 50, 0, 0, 10, 242, 0, + 16, 0, 0, 0, 0, 0, + 70, 142, 32, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 86, 21, 16, 0, 0, 0, + 0, 0, 70, 14, 16, 0, + 0, 0, 0, 0, 50, 0, + 0, 13, 242, 0, 16, 0, + 0, 0, 0, 0, 70, 142, + 32, 0, 0, 0, 0, 0, + 2, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 70, 14, 16, 0, 0, 0, + 0, 0, 50, 0, 0, 13, + 242, 32, 16, 0, 0, 0, + 0, 0, 70, 142, 32, 0, + 0, 0, 0, 0, 3, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 128, 63, 0, 0, + 128, 63, 0, 0, 128, 63, + 0, 0, 128, 63, 70, 14, + 16, 0, 0, 0, 0, 0, + 54, 0, 0, 5, 242, 32, + 16, 0, 1, 0, 0, 0, + 70, 30, 16, 0, 1, 0, + 0, 0, 54, 0, 0, 5, + 50, 32, 16, 0, 2, 0, + 0, 0, 70, 16, 16, 0, + 2, 0, 0, 0, 62, 0, + 0, 1, 73, 83, 71, 78, + 104, 0, 0, 0, 3, 0, + 0, 0, 8, 0, 0, 0, + 80, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 0, 0, + 0, 0, 3, 3, 0, 0, + 89, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 1, 0, + 0, 0, 15, 15, 0, 0, + 95, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 2, 0, + 0, 0, 3, 3, 0, 0, + 80, 79, 83, 73, 84, 73, + 79, 78, 0, 67, 79, 76, + 79, 82, 0, 84, 69, 88, + 67, 79, 79, 82, 68, 0, + 79, 83, 71, 78, 108, 0, + 0, 0, 3, 0, 0, 0, + 8, 0, 0, 0, 80, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 3, 0, + 0, 0, 0, 0, 0, 0, + 15, 0, 0, 0, 92, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 3, 0, + 0, 0, 1, 0, 0, 0, + 15, 0, 0, 0, 98, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 3, 0, + 0, 0, 2, 0, 0, 0, + 3, 12, 0, 0, 83, 86, + 95, 80, 79, 83, 73, 84, + 73, 79, 78, 0, 67, 79, + 76, 79, 82, 0, 84, 69, + 88, 67, 79, 79, 82, 68, + 0, 171 +}; diff --git a/nuklear/demo/gdi/build.bat b/nuklear/demo/gdi/build.bat new file mode 100644 index 0000000..3884317 --- /dev/null +++ b/nuklear/demo/gdi/build.bat @@ -0,0 +1,6 @@ +@echo off + +rem This will use VS2015 for compiler +call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86 + +cl /nologo /W3 /O2 /fp:fast /Gm- /Fedemo.exe main.c user32.lib gdi32.lib /link /incremental:no diff --git a/nuklear/demo/gdi/main.c b/nuklear/demo/gdi/main.c new file mode 100644 index 0000000..e82cd16 --- /dev/null +++ b/nuklear/demo/gdi/main.c @@ -0,0 +1,161 @@ +/* nuklear - v1.17 - public domain */ +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <limits.h> + +#define WINDOW_WIDTH 800 +#define WINDOW_HEIGHT 600 + +#define NK_INCLUDE_FIXED_TYPES +#define NK_INCLUDE_STANDARD_IO +#define NK_INCLUDE_STANDARD_VARARGS +#define NK_INCLUDE_DEFAULT_ALLOCATOR +#define NK_IMPLEMENTATION +#define NK_GDI_IMPLEMENTATION +#include "../../nuklear.h" +#include "nuklear_gdi.h" + +/* =============================================================== + * + * EXAMPLE + * + * ===============================================================*/ +/* This are some code examples to provide a small overview of what can be + * done with this library. To try out an example uncomment the include + * and the corresponding function. */ +#define UNUSED(a) (void)a +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) < (b) ? (b) : (a)) +#define LEN(a) (sizeof(a)/sizeof(a)[0]) + +/*#include "../style.c"*/ +/*#include "../calculator.c"*/ +/*#include "../overview.c"*/ +/*#include "../node_editor.c"*/ + +/* =============================================================== + * + * DEMO + * + * ===============================================================*/ +static LRESULT CALLBACK +WindowProc(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + switch (msg) + { + case WM_DESTROY: + PostQuitMessage(0); + return 0; + } + + if (nk_gdi_handle_event(wnd, msg, wparam, lparam)) + return 0; + + return DefWindowProcW(wnd, msg, wparam, lparam); +} + +int main(void) +{ + GdiFont* font; + struct nk_context *ctx; + + WNDCLASSW wc; + ATOM atom; + RECT rect = { 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT }; + DWORD style = WS_OVERLAPPEDWINDOW; + DWORD exstyle = WS_EX_APPWINDOW; + HWND wnd; + HDC dc; + int running = 1; + int needs_refresh = 1; + + /* Win32 */ + memset(&wc, 0, sizeof(wc)); + wc.lpfnWndProc = WindowProc; + wc.hInstance = GetModuleHandleW(0); + wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.lpszClassName = L"NuklearWindowClass"; + atom = RegisterClassW(&wc); + + AdjustWindowRectEx(&rect, style, FALSE, exstyle); + wnd = CreateWindowExW(exstyle, wc.lpszClassName, L"Nuklear Demo", + style | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, + rect.right - rect.left, rect.bottom - rect.top, + NULL, NULL, wc.hInstance, NULL); + dc = GetDC(wnd); + + /* GUI */ + font = nk_gdifont_create("Arial", 14); + ctx = nk_gdi_init(font, dc, WINDOW_WIDTH, WINDOW_HEIGHT); + + /* style.c */ + /*set_style(ctx, THEME_WHITE);*/ + /*set_style(ctx, THEME_RED);*/ + /*set_style(ctx, THEME_BLUE);*/ + /*set_style(ctx, THEME_DARK);*/ + + while (running) + { + /* Input */ + MSG msg; + nk_input_begin(ctx); + if (needs_refresh == 0) { + if (GetMessageW(&msg, NULL, 0, 0) <= 0) + running = 0; + else { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + needs_refresh = 1; + } else needs_refresh = 0; + + while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { + if (msg.message == WM_QUIT) + running = 0; + TranslateMessage(&msg); + DispatchMessageW(&msg); + needs_refresh = 1; + } + nk_input_end(ctx); + + /* GUI */ + if (nk_begin(ctx, "Demo", nk_rect(50, 50, 200, 200), + NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_SCALABLE| + NK_WINDOW_CLOSABLE|NK_WINDOW_MINIMIZABLE|NK_WINDOW_TITLE)) + { + enum {EASY, HARD}; + static int op = EASY; + static int property = 20; + + nk_layout_row_static(ctx, 30, 80, 1); + if (nk_button_label(ctx, "button")) + fprintf(stdout, "button pressed\n"); + nk_layout_row_dynamic(ctx, 30, 2); + if (nk_option_label(ctx, "easy", op == EASY)) op = EASY; + if (nk_option_label(ctx, "hard", op == HARD)) op = HARD; + nk_layout_row_dynamic(ctx, 22, 1); + nk_property_int(ctx, "Compression:", 0, &property, 100, 10, 1); + } + nk_end(ctx); + if (nk_window_is_closed(ctx, "Demo")) break; + + /* -------------- EXAMPLES ---------------- */ + /*calculator(ctx);*/ + /*overview(ctx);*/ + /*node_editor(ctx);*/ + /* ----------------------------------------- */ + + /* Draw */ + nk_gdi_render(nk_rgb(30,30,30)); + } + + nk_gdifont_del(font); + ReleaseDC(wnd, dc); + UnregisterClassW(wc.lpszClassName, wc.hInstance); + return 0; +} + diff --git a/nuklear/demo/gdi/nuklear_gdi.h b/nuklear/demo/gdi/nuklear_gdi.h new file mode 100644 index 0000000..6d3a84a --- /dev/null +++ b/nuklear/demo/gdi/nuklear_gdi.h @@ -0,0 +1,761 @@ +/* + * Nuklear - v1.17 - public domain + * no warrenty implied; use at your own risk. + * authored from 2015-2016 by Micha Mettke + */ +/* + * ============================================================== + * + * API + * + * =============================================================== + */ +#ifndef NK_GDI_H_ +#define NK_GDI_H_ + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +typedef struct GdiFont GdiFont; +NK_API struct nk_context* nk_gdi_init(GdiFont *font, HDC window_dc, unsigned int width, unsigned int height); +NK_API int nk_gdi_handle_event(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); +NK_API void nk_gdi_render(struct nk_color clear); +NK_API void nk_gdi_shutdown(void); + +/* font */ +NK_API GdiFont* nk_gdifont_create(const char *name, int size); +NK_API void nk_gdifont_del(GdiFont *font); +NK_API void nk_gdi_set_font(GdiFont *font); + +#endif + +/* + * ============================================================== + * + * IMPLEMENTATION + * + * =============================================================== + */ +#ifdef NK_GDI_IMPLEMENTATION + +#include <stdlib.h> +#include <malloc.h> + +struct GdiFont { + struct nk_user_font nk; + int height; + HFONT handle; + HDC dc; +}; + +static struct { + HBITMAP bitmap; + HDC window_dc; + HDC memory_dc; + unsigned int width; + unsigned int height; + struct nk_context ctx; +} gdi; + +static COLORREF +convert_color(struct nk_color c) +{ + return c.r | (c.g << 8) | (c.b << 16); +} + +static void +nk_gdi_scissor(HDC dc, float x, float y, float w, float h) +{ + SelectClipRgn(dc, NULL); + IntersectClipRect(dc, (int)x, (int)y, (int)(x + w + 1), (int)(y + h + 1)); +} + +static void +nk_gdi_stroke_line(HDC dc, short x0, short y0, short x1, + short y1, unsigned int line_thickness, struct nk_color col) +{ + COLORREF color = convert_color(col); + + HPEN pen = NULL; + if (line_thickness == 1) { + SetDCPenColor(dc, color); + } else { + pen = CreatePen(PS_SOLID, line_thickness, color); + SelectObject(dc, pen); + } + + MoveToEx(dc, x0, y0, NULL); + LineTo(dc, x1, y1); + + if (pen) { + SelectObject(dc, GetStockObject(DC_PEN)); + DeleteObject(pen); + } +} + +static void +nk_gdi_stroke_rect(HDC dc, short x, short y, unsigned short w, + unsigned short h, unsigned short r, unsigned short line_thickness, struct nk_color col) +{ + COLORREF color = convert_color(col); + + HPEN pen = NULL; + if (line_thickness == 1) { + SetDCPenColor(dc, color); + } else { + pen = CreatePen(PS_SOLID, line_thickness, color); + SelectObject(dc, pen); + } + + SetDCBrushColor(dc, OPAQUE); + if (r == 0) { + Rectangle(dc, x, y, x + w, y + h); + } else { + RoundRect(dc, x, y, x + w, y + h, r, r); + } + + if (pen) { + SelectObject(dc, GetStockObject(DC_PEN)); + DeleteObject(pen); + } +} + +static void +nk_gdi_fill_rect(HDC dc, short x, short y, unsigned short w, + unsigned short h, unsigned short r, struct nk_color col) +{ + COLORREF color = convert_color(col); + + if (r == 0) { + RECT rect = { x, y, x + w, y + h }; + SetBkColor(dc, color); + ExtTextOutW(dc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL); + } else { + SetDCPenColor(dc, color); + SetDCBrushColor(dc, color); + RoundRect(dc, x, y, x + w, y + h, r, r); + } +} + +static void +nk_gdi_fill_triangle(HDC dc, short x0, short y0, short x1, + short y1, short x2, short y2, struct nk_color col) +{ + COLORREF color = convert_color(col); + POINT points[] = { + { x0, y0 }, + { x1, y1 }, + { x2, y2 }, + }; + + SetDCPenColor(dc, color); + SetDCBrushColor(dc, color); + Polygon(dc, points, 3); +} + +static void +nk_gdi_stroke_triangle(HDC dc, short x0, short y0, short x1, + short y1, short x2, short y2, unsigned short line_thickness, struct nk_color col) +{ + COLORREF color = convert_color(col); + POINT points[] = { + { x0, y0 }, + { x1, y1 }, + { x2, y2 }, + { x0, y0 }, + }; + + HPEN pen = NULL; + if (line_thickness == 1) { + SetDCPenColor(dc, color); + } else { + pen = CreatePen(PS_SOLID, line_thickness, color); + SelectObject(dc, pen); + } + + Polyline(dc, points, 4); + + if (pen) { + SelectObject(dc, GetStockObject(DC_PEN)); + DeleteObject(pen); + } +} + +static void +nk_gdi_fill_polygon(HDC dc, const struct nk_vec2i *pnts, int count, struct nk_color col) +{ + int i = 0; + #define MAX_POINTS 64 + POINT points[MAX_POINTS]; + COLORREF color = convert_color(col); + SetDCBrushColor(dc, color); + SetDCPenColor(dc, color); + for (i = 0; i < count && i < MAX_POINTS; ++i) { + points[i].x = pnts[i].x; + points[i].y = pnts[i].y; + } + Polygon(dc, points, i); + #undef MAX_POINTS +} + +static void +nk_gdi_stroke_polygon(HDC dc, const struct nk_vec2i *pnts, int count, + unsigned short line_thickness, struct nk_color col) +{ + COLORREF color = convert_color(col); + HPEN pen = NULL; + if (line_thickness == 1) { + SetDCPenColor(dc, color); + } else { + pen = CreatePen(PS_SOLID, line_thickness, color); + SelectObject(dc, pen); + } + + if (count > 0) { + int i; + MoveToEx(dc, pnts[0].x, pnts[0].y, NULL); + for (i = 1; i < count; ++i) + LineTo(dc, pnts[i].x, pnts[i].y); + LineTo(dc, pnts[0].x, pnts[0].y); + } + + if (pen) { + SelectObject(dc, GetStockObject(DC_PEN)); + DeleteObject(pen); + } +} + +static void +nk_gdi_stroke_polyline(HDC dc, const struct nk_vec2i *pnts, + int count, unsigned short line_thickness, struct nk_color col) +{ + COLORREF color = convert_color(col); + HPEN pen = NULL; + if (line_thickness == 1) { + SetDCPenColor(dc, color); + } else { + pen = CreatePen(PS_SOLID, line_thickness, color); + SelectObject(dc, pen); + } + + if (count > 0) { + int i; + MoveToEx(dc, pnts[0].x, pnts[0].y, NULL); + for (i = 1; i < count; ++i) + LineTo(dc, pnts[i].x, pnts[i].y); + } + + if (pen) { + SelectObject(dc, GetStockObject(DC_PEN)); + DeleteObject(pen); + } +} + +static void +nk_gdi_fill_circle(HDC dc, short x, short y, unsigned short w, + unsigned short h, struct nk_color col) +{ + COLORREF color = convert_color(col); + SetDCBrushColor(dc, color); + SetDCPenColor(dc, color); + Ellipse(dc, x, y, x + w, y + h); +} + +static void +nk_gdi_stroke_circle(HDC dc, short x, short y, unsigned short w, + unsigned short h, unsigned short line_thickness, struct nk_color col) +{ + COLORREF color = convert_color(col); + HPEN pen = NULL; + if (line_thickness == 1) { + SetDCPenColor(dc, color); + } else { + pen = CreatePen(PS_SOLID, line_thickness, color); + SelectObject(dc, pen); + } + + SetDCBrushColor(dc, OPAQUE); + Ellipse(dc, x, y, x + w, y + h); + + if (pen) { + SelectObject(dc, GetStockObject(DC_PEN)); + DeleteObject(pen); + } +} + +static void +nk_gdi_stroke_curve(HDC dc, struct nk_vec2i p1, + struct nk_vec2i p2, struct nk_vec2i p3, struct nk_vec2i p4, + unsigned short line_thickness, struct nk_color col) +{ + COLORREF color = convert_color(col); + POINT p[] = { + { p1.x, p1.y }, + { p2.x, p2.y }, + { p3.x, p3.y }, + { p4.x, p4.y }, + }; + + HPEN pen = NULL; + if (line_thickness == 1) { + SetDCPenColor(dc, color); + } else { + pen = CreatePen(PS_SOLID, line_thickness, color); + SelectObject(dc, pen); + } + + SetDCBrushColor(dc, OPAQUE); + PolyBezier(dc, p, 4); + + if (pen) { + SelectObject(dc, GetStockObject(DC_PEN)); + DeleteObject(pen); + } +} + +static void +nk_gdi_draw_text(HDC dc, short x, short y, unsigned short w, unsigned short h, + const char *text, int len, GdiFont *font, struct nk_color cbg, struct nk_color cfg) +{ + int wsize; + WCHAR* wstr; + + if(!text || !font || !len) return; + + wsize = MultiByteToWideChar(CP_UTF8, 0, text, len, NULL, 0); + wstr = (WCHAR*)_alloca(wsize * sizeof(wchar_t)); + MultiByteToWideChar(CP_UTF8, 0, text, len, wstr, wsize); + + SetBkColor(dc, convert_color(cbg)); + SetTextColor(dc, convert_color(cfg)); + + SelectObject(dc, font->handle); + ExtTextOutW(dc, x, y, ETO_OPAQUE, NULL, wstr, wsize, NULL); +} + +static void +nk_gdi_clear(HDC dc, struct nk_color col) +{ + COLORREF color = convert_color(col); + RECT rect = { 0, 0, gdi.width, gdi.height }; + SetBkColor(dc, color); + + ExtTextOutW(dc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL); +} + +static void +nk_gdi_blit(HDC dc) +{ + BitBlt(dc, 0, 0, gdi.width, gdi.height, gdi.memory_dc, 0, 0, SRCCOPY); +} + +GdiFont* +nk_gdifont_create(const char *name, int size) +{ + TEXTMETRICW metric; + GdiFont *font = (GdiFont*)calloc(1, sizeof(GdiFont)); + if (!font) + return NULL; + font->dc = CreateCompatibleDC(0); + font->handle = CreateFont(size, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_DONTCARE, name); + SelectObject(font->dc, font->handle); + GetTextMetricsW(font->dc, &metric); + font->height = metric.tmHeight; + return font; +} + +static float +nk_gdifont_get_text_width(nk_handle handle, float height, const char *text, int len) +{ + GdiFont *font = (GdiFont*)handle.ptr; + SIZE size; + int wsize; + WCHAR* wstr; + if (!font || !text) + return 0; + + wsize = MultiByteToWideChar(CP_UTF8, 0, text, len, NULL, 0); + wstr = (WCHAR*)_alloca(wsize * sizeof(wchar_t)); + MultiByteToWideChar(CP_UTF8, 0, text, len, wstr, wsize); + if (GetTextExtentPoint32W(font->dc, wstr, wsize, &size)) + return (float)size.cx; + return -1.0f; +} + +void +nk_gdifont_del(GdiFont *font) +{ + if(!font) return; + DeleteObject(font->handle); + DeleteDC(font->dc); + free(font); +} + +static void +nk_gdi_clipbard_paste(nk_handle usr, struct nk_text_edit *edit) +{ + (void)usr; + if (IsClipboardFormatAvailable(CF_UNICODETEXT) && OpenClipboard(NULL)) + { + HGLOBAL mem = GetClipboardData(CF_UNICODETEXT); + if (mem) + { + SIZE_T size = GlobalSize(mem) - 1; + if (size) + { + LPCWSTR wstr = (LPCWSTR)GlobalLock(mem); + if (wstr) + { + int utf8size = WideCharToMultiByte(CP_UTF8, 0, wstr, (int)(size / sizeof(wchar_t)), NULL, 0, NULL, NULL); + if (utf8size) + { + char* utf8 = (char*)malloc(utf8size); + if (utf8) + { + WideCharToMultiByte(CP_UTF8, 0, wstr, (int)(size / sizeof(wchar_t)), utf8, utf8size, NULL, NULL); + nk_textedit_paste(edit, utf8, utf8size); + free(utf8); + } + } + GlobalUnlock(mem); + } + } + } + CloseClipboard(); + } +} + +static void +nk_gdi_clipbard_copy(nk_handle usr, const char *text, int len) +{ + if (OpenClipboard(NULL)) + { + int wsize = MultiByteToWideChar(CP_UTF8, 0, text, len, NULL, 0); + if (wsize) + { + HGLOBAL mem = (HGLOBAL)GlobalAlloc(GMEM_MOVEABLE, (wsize + 1) * sizeof(wchar_t)); + if (mem) + { + wchar_t* wstr = (wchar_t*)GlobalLock(mem); + if (wstr) + { + MultiByteToWideChar(CP_UTF8, 0, text, len, wstr, wsize); + wstr[wsize] = 0; + GlobalUnlock(mem); + + SetClipboardData(CF_UNICODETEXT, mem); + } + } + } + CloseClipboard(); + } +} + +NK_API struct nk_context* +nk_gdi_init(GdiFont *gdifont, HDC window_dc, unsigned int width, unsigned int height) +{ + struct nk_user_font *font = &gdifont->nk; + font->userdata = nk_handle_ptr(gdifont); + font->height = (float)gdifont->height; + font->width = nk_gdifont_get_text_width; + + gdi.bitmap = CreateCompatibleBitmap(window_dc, width, height); + gdi.window_dc = window_dc; + gdi.memory_dc = CreateCompatibleDC(window_dc); + gdi.width = width; + gdi.height = height; + SelectObject(gdi.memory_dc, gdi.bitmap); + + nk_init_default(&gdi.ctx, font); + gdi.ctx.clip.copy = nk_gdi_clipbard_copy; + gdi.ctx.clip.paste = nk_gdi_clipbard_paste; + return &gdi.ctx; +} + +NK_API void +nk_gdi_set_font(GdiFont *gdifont) +{ + struct nk_user_font *font = &gdifont->nk; + font->userdata = nk_handle_ptr(gdifont); + font->height = (float)gdifont->height; + font->width = nk_gdifont_get_text_width; + nk_style_set_font(&gdi.ctx, font); +} + +NK_API int +nk_gdi_handle_event(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + switch (msg) + { + case WM_SIZE: + { + unsigned width = LOWORD(lparam); + unsigned height = LOWORD(lparam); + if (width != gdi.width || height != gdi.height) + { + DeleteObject(gdi.bitmap); + gdi.bitmap = CreateCompatibleBitmap(gdi.window_dc, width, height); + gdi.width = width; + gdi.height = height; + SelectObject(gdi.memory_dc, gdi.bitmap); + } + break; + } + + case WM_PAINT: + { + PAINTSTRUCT paint; + HDC dc = BeginPaint(wnd, &paint); + nk_gdi_blit(dc); + EndPaint(wnd, &paint); + return 1; + } + + case WM_KEYDOWN: + case WM_KEYUP: + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + { + int down = !((lparam >> 31) & 1); + int ctrl = GetKeyState(VK_CONTROL) & (1 << 15); + + switch (wparam) + { + case VK_SHIFT: + case VK_LSHIFT: + case VK_RSHIFT: + nk_input_key(&gdi.ctx, NK_KEY_SHIFT, down); + return 1; + + case VK_DELETE: + nk_input_key(&gdi.ctx, NK_KEY_DEL, down); + return 1; + + case VK_RETURN: + nk_input_key(&gdi.ctx, NK_KEY_ENTER, down); + return 1; + + case VK_TAB: + nk_input_key(&gdi.ctx, NK_KEY_TAB, down); + return 1; + + case VK_LEFT: + if (ctrl) + nk_input_key(&gdi.ctx, NK_KEY_TEXT_WORD_LEFT, down); + else + nk_input_key(&gdi.ctx, NK_KEY_LEFT, down); + return 1; + + case VK_RIGHT: + if (ctrl) + nk_input_key(&gdi.ctx, NK_KEY_TEXT_WORD_RIGHT, down); + else + nk_input_key(&gdi.ctx, NK_KEY_RIGHT, down); + return 1; + + case VK_BACK: + nk_input_key(&gdi.ctx, NK_KEY_BACKSPACE, down); + return 1; + + case VK_HOME: + nk_input_key(&gdi.ctx, NK_KEY_TEXT_START, down); + nk_input_key(&gdi.ctx, NK_KEY_SCROLL_START, down); + return 1; + + case VK_END: + nk_input_key(&gdi.ctx, NK_KEY_TEXT_END, down); + nk_input_key(&gdi.ctx, NK_KEY_SCROLL_END, down); + return 1; + + case VK_NEXT: + nk_input_key(&gdi.ctx, NK_KEY_SCROLL_DOWN, down); + return 1; + + case VK_PRIOR: + nk_input_key(&gdi.ctx, NK_KEY_SCROLL_UP, down); + return 1; + + case 'C': + if (ctrl) { + nk_input_key(&gdi.ctx, NK_KEY_COPY, down); + return 1; + } + break; + + case 'V': + if (ctrl) { + nk_input_key(&gdi.ctx, NK_KEY_PASTE, down); + return 1; + } + break; + + case 'X': + if (ctrl) { + nk_input_key(&gdi.ctx, NK_KEY_CUT, down); + return 1; + } + break; + + case 'Z': + if (ctrl) { + nk_input_key(&gdi.ctx, NK_KEY_TEXT_UNDO, down); + return 1; + } + break; + + case 'R': + if (ctrl) { + nk_input_key(&gdi.ctx, NK_KEY_TEXT_REDO, down); + return 1; + } + break; + } + return 0; + } + + case WM_CHAR: + if (wparam >= 32) + { + nk_input_unicode(&gdi.ctx, (nk_rune)wparam); + return 1; + } + break; + + case WM_LBUTTONDOWN: + nk_input_button(&gdi.ctx, NK_BUTTON_LEFT, (short)LOWORD(lparam), (short)HIWORD(lparam), 1); + SetCapture(wnd); + return 1; + + case WM_LBUTTONUP: + nk_input_button(&gdi.ctx, NK_BUTTON_LEFT, (short)LOWORD(lparam), (short)HIWORD(lparam), 0); + ReleaseCapture(); + return 1; + + case WM_RBUTTONDOWN: + nk_input_button(&gdi.ctx, NK_BUTTON_RIGHT, (short)LOWORD(lparam), (short)HIWORD(lparam), 1); + SetCapture(wnd); + return 1; + + case WM_RBUTTONUP: + nk_input_button(&gdi.ctx, NK_BUTTON_RIGHT, (short)LOWORD(lparam), (short)HIWORD(lparam), 0); + ReleaseCapture(); + return 1; + + case WM_MBUTTONDOWN: + nk_input_button(&gdi.ctx, NK_BUTTON_MIDDLE, (short)LOWORD(lparam), (short)HIWORD(lparam), 1); + SetCapture(wnd); + return 1; + + case WM_MBUTTONUP: + nk_input_button(&gdi.ctx, NK_BUTTON_MIDDLE, (short)LOWORD(lparam), (short)HIWORD(lparam), 0); + ReleaseCapture(); + return 1; + + case WM_MOUSEWHEEL: + nk_input_scroll(&gdi.ctx, (float)(short)HIWORD(wparam) / WHEEL_DELTA); + return 1; + + case WM_MOUSEMOVE: + nk_input_motion(&gdi.ctx, (short)LOWORD(lparam), (short)HIWORD(lparam)); + return 1; + } + + return 0; +} + +NK_API void +nk_gdi_shutdown(void) +{ + DeleteObject(gdi.memory_dc); + DeleteObject(gdi.bitmap); + nk_free(&gdi.ctx); +} + +NK_API void +nk_gdi_render(struct nk_color clear) +{ + const struct nk_command *cmd; + + HDC memory_dc = gdi.memory_dc; + SelectObject(memory_dc, GetStockObject(DC_PEN)); + SelectObject(memory_dc, GetStockObject(DC_BRUSH)); + nk_gdi_clear(memory_dc, clear); + + nk_foreach(cmd, &gdi.ctx) + { + switch (cmd->type) { + case NK_COMMAND_NOP: break; + case NK_COMMAND_SCISSOR: { + const struct nk_command_scissor *s =(const struct nk_command_scissor*)cmd; + nk_gdi_scissor(memory_dc, s->x, s->y, s->w, s->h); + } break; + case NK_COMMAND_LINE: { + const struct nk_command_line *l = (const struct nk_command_line *)cmd; + nk_gdi_stroke_line(memory_dc, l->begin.x, l->begin.y, l->end.x, + l->end.y, l->line_thickness, l->color); + } break; + case NK_COMMAND_RECT: { + const struct nk_command_rect *r = (const struct nk_command_rect *)cmd; + nk_gdi_stroke_rect(memory_dc, r->x, r->y, r->w, r->h, + (unsigned short)r->rounding, r->line_thickness, r->color); + } break; + case NK_COMMAND_RECT_FILLED: { + const struct nk_command_rect_filled *r = (const struct nk_command_rect_filled *)cmd; + nk_gdi_fill_rect(memory_dc, r->x, r->y, r->w, r->h, + (unsigned short)r->rounding, r->color); + } break; + case NK_COMMAND_CIRCLE: { + const struct nk_command_circle *c = (const struct nk_command_circle *)cmd; + nk_gdi_stroke_circle(memory_dc, c->x, c->y, c->w, c->h, c->line_thickness, c->color); + } break; + case NK_COMMAND_CIRCLE_FILLED: { + const struct nk_command_circle_filled *c = (const struct nk_command_circle_filled *)cmd; + nk_gdi_fill_circle(memory_dc, c->x, c->y, c->w, c->h, c->color); + } break; + case NK_COMMAND_TRIANGLE: { + const struct nk_command_triangle*t = (const struct nk_command_triangle*)cmd; + nk_gdi_stroke_triangle(memory_dc, t->a.x, t->a.y, t->b.x, t->b.y, + t->c.x, t->c.y, t->line_thickness, t->color); + } break; + case NK_COMMAND_TRIANGLE_FILLED: { + const struct nk_command_triangle_filled *t = (const struct nk_command_triangle_filled *)cmd; + nk_gdi_fill_triangle(memory_dc, t->a.x, t->a.y, t->b.x, t->b.y, + t->c.x, t->c.y, t->color); + } break; + case NK_COMMAND_POLYGON: { + const struct nk_command_polygon *p =(const struct nk_command_polygon*)cmd; + nk_gdi_stroke_polygon(memory_dc, p->points, p->point_count, p->line_thickness,p->color); + } break; + case NK_COMMAND_POLYGON_FILLED: { + const struct nk_command_polygon_filled *p = (const struct nk_command_polygon_filled *)cmd; + nk_gdi_fill_polygon(memory_dc, p->points, p->point_count, p->color); + } break; + case NK_COMMAND_POLYLINE: { + const struct nk_command_polyline *p = (const struct nk_command_polyline *)cmd; + nk_gdi_stroke_polyline(memory_dc, p->points, p->point_count, p->line_thickness, p->color); + } break; + case NK_COMMAND_TEXT: { + const struct nk_command_text *t = (const struct nk_command_text*)cmd; + nk_gdi_draw_text(memory_dc, t->x, t->y, t->w, t->h, + (const char*)t->string, t->length, + (GdiFont*)t->font->userdata.ptr, + t->background, t->foreground); + } break; + case NK_COMMAND_CURVE: { + const struct nk_command_curve *q = (const struct nk_command_curve *)cmd; + nk_gdi_stroke_curve(memory_dc, q->begin, q->ctrl[0], q->ctrl[1], + q->end, q->line_thickness, q->color); + } break; + case NK_COMMAND_RECT_MULTI_COLOR: + case NK_COMMAND_IMAGE: + case NK_COMMAND_ARC: + case NK_COMMAND_ARC_FILLED: + default: break; + } + } + nk_gdi_blit(gdi.window_dc); + nk_clear(&gdi.ctx); +} + +#endif + diff --git a/nuklear/demo/gdip/build.bat b/nuklear/demo/gdip/build.bat new file mode 100644 index 0000000..28f51a3 --- /dev/null +++ b/nuklear/demo/gdip/build.bat @@ -0,0 +1,5 @@ +@echo off + +call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86 + +cl /nologo /W3 /O2 /fp:fast /Gm- /Fedemo.exe main.c user32.lib gdiplus.lib /link /incremental:no diff --git a/nuklear/demo/gdip/main.c b/nuklear/demo/gdip/main.c new file mode 100644 index 0000000..7d623e7 --- /dev/null +++ b/nuklear/demo/gdip/main.c @@ -0,0 +1,155 @@ +/* nuklear - v1.17 - public domain */ +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <stdio.h> +#include <string.h> +#include <limits.h> +#include <time.h> + +#define WINDOW_WIDTH 800 +#define WINDOW_HEIGHT 600 + +#define NK_INCLUDE_FIXED_TYPES +#define NK_INCLUDE_STANDARD_IO +#define NK_INCLUDE_STANDARD_VARARGS +#define NK_INCLUDE_DEFAULT_ALLOCATOR +#define NK_IMPLEMENTATION +#define NK_GDIP_IMPLEMENTATION +#include "../../nuklear.h" +#include "nuklear_gdip.h" + +/* =============================================================== + * + * EXAMPLE + * + * ===============================================================*/ +/* These are some code examples to provide a small overview of what can be + * done with this library. To try out an example uncomment the include + * and the corresponding function. */ + #define UNUSED(a) (void)a + #define MIN(a,b) ((a) < (b) ? (a) : (b)) + #define MAX(a,b) ((a) < (b) ? (b) : (a)) + #define LEN(a) (sizeof(a)/sizeof(a)[0]) + +/*#include "../style.c"*/ +/*#include "../calculator.c"*/ +/*#include "../overview.c"*/ +/*#include "../node_editor.c"*/ + +/* =============================================================== + * + * DEMO + * + * ===============================================================*/ +static LRESULT CALLBACK +WindowProc(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + switch (msg) { + case WM_DESTROY: + PostQuitMessage(0); + return 0; + } + if (nk_gdip_handle_event(wnd, msg, wparam, lparam)) + return 0; + return DefWindowProcW(wnd, msg, wparam, lparam); +} + +int main(void) +{ + GdipFont* font; + struct nk_context *ctx; + + WNDCLASSW wc; + RECT rect = { 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT }; + DWORD style = WS_OVERLAPPEDWINDOW; + DWORD exstyle = WS_EX_APPWINDOW; + HWND wnd; + int running = 1; + int needs_refresh = 1; + + /* Win32 */ + memset(&wc, 0, sizeof(wc)); + wc.lpfnWndProc = WindowProc; + wc.hInstance = GetModuleHandleW(0); + wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.lpszClassName = L"NuklearWindowClass"; + RegisterClassW(&wc); + + AdjustWindowRectEx(&rect, style, FALSE, exstyle); + + wnd = CreateWindowExW(exstyle, wc.lpszClassName, L"Nuklear Demo", + style | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, + rect.right - rect.left, rect.bottom - rect.top, + NULL, NULL, wc.hInstance, NULL); + + /* GUI */ + ctx = nk_gdip_init(wnd, WINDOW_WIDTH, WINDOW_HEIGHT); + font = nk_gdipfont_create("Arial", 12); + nk_gdip_set_font(font); + + /* style.c */ + /*set_style(ctx, THEME_WHITE);*/ + /*set_style(ctx, THEME_RED);*/ + /*set_style(ctx, THEME_BLUE);*/ + /*set_style(ctx, THEME_DARK);*/ + + while (running) + { + /* Input */ + MSG msg; + nk_input_begin(ctx); + if (needs_refresh == 0) { + if (GetMessageW(&msg, NULL, 0, 0) <= 0) + running = 0; + else { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + needs_refresh = 1; + } else needs_refresh = 0; + while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { + if (msg.message == WM_QUIT) + running = 0; + TranslateMessage(&msg); + DispatchMessageW(&msg); + needs_refresh = 1; + } + nk_input_end(ctx); + + /* GUI */ + if (nk_begin(ctx, "Demo", nk_rect(50, 50, 200, 200), + NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_SCALABLE| + NK_WINDOW_CLOSABLE|NK_WINDOW_MINIMIZABLE|NK_WINDOW_TITLE)) + { + enum {EASY, HARD}; + static int op = EASY; + static int property = 20; + + nk_layout_row_static(ctx, 30, 80, 1); + if (nk_button_label(ctx, "button")) + fprintf(stdout, "button pressed\n"); + nk_layout_row_dynamic(ctx, 30, 2); + if (nk_option_label(ctx, "easy", op == EASY)) op = EASY; + if (nk_option_label(ctx, "hard", op == HARD)) op = HARD; + nk_layout_row_dynamic(ctx, 22, 1); + nk_property_int(ctx, "Compression:", 0, &property, 100, 10, 1); + } + nk_end(ctx); + if (nk_window_is_closed(ctx, "Demo")) break; + + /* -------------- EXAMPLES ---------------- */ + /*calculator(ctx);*/ + /*overview(ctx);*/ + /*node_editor(ctx);*/ + /* ----------------------------------------- */ + + /* Draw */ + nk_gdip_render(NK_ANTI_ALIASING_ON, nk_rgb(30,30,30)); + } + + nk_gdipfont_del(font); + nk_gdip_shutdown(); + UnregisterClassW(wc.lpszClassName, wc.hInstance); + return 0; +} diff --git a/nuklear/demo/gdip/nuklear_gdip.h b/nuklear/demo/gdip/nuklear_gdip.h new file mode 100644 index 0000000..66ccc0d --- /dev/null +++ b/nuklear/demo/gdip/nuklear_gdip.h @@ -0,0 +1,1006 @@ +/* + * Nuklear - v1.17 - public domain + * no warrenty implied; use at your own risk. + * authored from 2015-2016 by Micha Mettke + */ +/* + * ============================================================== + * + * API + * + * =============================================================== + */ +#ifndef NK_GDIP_H_ +#define NK_GDIP_H_ + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +/* font */ +typedef struct GdipFont GdipFont; +NK_API GdipFont* nk_gdipfont_create(const char *name, int size); +NK_API void nk_gdipfont_del(GdipFont *font); + +NK_API struct nk_context* nk_gdip_init(HWND hwnd, unsigned int width, unsigned int height); +NK_API void nk_gdip_set_font(GdipFont *font); +NK_API int nk_gdip_handle_event(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); +NK_API void nk_gdip_render(enum nk_anti_aliasing AA, struct nk_color clear); +NK_API void nk_gdip_shutdown(void); + +#endif +/* + * ============================================================== + * + * IMPLEMENTATION + * + * =============================================================== + */ +#ifdef NK_GDIP_IMPLEMENTATION + +#include <stdlib.h> +#include <malloc.h> + +/* manually declare everything GDI+ needs, because + GDI+ headers are not usable from C */ +#define WINGDIPAPI __stdcall +#define GDIPCONST const + +typedef struct GpGraphics GpGraphics; +typedef struct GpImage GpImage; +typedef struct GpPen GpPen; +typedef struct GpBrush GpBrush; +typedef struct GpStringFormat GpStringFormat; +typedef struct GpFont GpFont; +typedef struct GpFontFamily GpFontFamily; +typedef struct GpFontCollection GpFontCollection; + +typedef GpImage GpBitmap; +typedef GpBrush GpSolidFill; + +typedef int Status; +typedef Status GpStatus; + +typedef float REAL; +typedef DWORD ARGB; +typedef POINT GpPoint; + +typedef enum { + TextRenderingHintSystemDefault = 0, + TextRenderingHintSingleBitPerPixelGridFit = 1, + TextRenderingHintSingleBitPerPixel = 2, + TextRenderingHintAntiAliasGridFit = 3, + TextRenderingHintAntiAlias = 4, + TextRenderingHintClearTypeGridFit = 5 +} TextRenderingHint; + +typedef enum { + StringFormatFlagsDirectionRightToLeft = 0x00000001, + StringFormatFlagsDirectionVertical = 0x00000002, + StringFormatFlagsNoFitBlackBox = 0x00000004, + StringFormatFlagsDisplayFormatControl = 0x00000020, + StringFormatFlagsNoFontFallback = 0x00000400, + StringFormatFlagsMeasureTrailingSpaces = 0x00000800, + StringFormatFlagsNoWrap = 0x00001000, + StringFormatFlagsLineLimit = 0x00002000, + StringFormatFlagsNoClip = 0x00004000 +} StringFormatFlags; + +typedef enum +{ + QualityModeInvalid = -1, + QualityModeDefault = 0, + QualityModeLow = 1, + QualityModeHigh = 2 +} QualityMode; + +typedef enum +{ + SmoothingModeInvalid = QualityModeInvalid, + SmoothingModeDefault = QualityModeDefault, + SmoothingModeHighSpeed = QualityModeLow, + SmoothingModeHighQuality = QualityModeHigh, + SmoothingModeNone, + SmoothingModeAntiAlias, + SmoothingModeAntiAlias8x4 = SmoothingModeAntiAlias, + SmoothingModeAntiAlias8x8 +} SmoothingMode; + +typedef enum +{ + FontStyleRegular = 0, + FontStyleBold = 1, + FontStyleItalic = 2, + FontStyleBoldItalic = 3, + FontStyleUnderline = 4, + FontStyleStrikeout = 8 +} FontStyle; + +typedef enum { + FillModeAlternate, + FillModeWinding +} FillMode; + +typedef enum { + CombineModeReplace, + CombineModeIntersect, + CombineModeUnion, + CombineModeXor, + CombineModeExclude, + CombineModeComplement +} CombineMode; + +typedef enum { + UnitWorld, + UnitDisplay, + UnitPixel, + UnitPoint, + UnitInch, + UnitDocument, + UnitMillimeter +} Unit; + +typedef struct { + FLOAT X; + FLOAT Y; + FLOAT Width; + FLOAT Height; +} RectF; + +typedef enum { + DebugEventLevelFatal, + DebugEventLevelWarning +} DebugEventLevel; + +typedef VOID (WINAPI *DebugEventProc)(DebugEventLevel level, CHAR *message); + +typedef struct { + UINT32 GdiplusVersion; + DebugEventProc DebugEventCallback; + BOOL SuppressBackgroundThread; + BOOL SuppressExternalCodecs; +} GdiplusStartupInput; + +typedef Status (WINAPI *NotificationHookProc)(OUT ULONG_PTR *token); +typedef VOID (WINAPI *NotificationUnhookProc)(ULONG_PTR token); + +typedef struct { + NotificationHookProc NotificationHook; + NotificationUnhookProc NotificationUnhook; +} GdiplusStartupOutput; + +/* startup & shutdown */ + +Status WINAPI GdiplusStartup( + OUT ULONG_PTR *token, + const GdiplusStartupInput *input, + OUT GdiplusStartupOutput *output); + +VOID WINAPI GdiplusShutdown(ULONG_PTR token); + +/* image */ + +GpStatus WINGDIPAPI +GdipCreateBitmapFromGraphics(INT width, + INT height, + GpGraphics* target, + GpBitmap** bitmap); + +GpStatus WINGDIPAPI +GdipDisposeImage(GpImage *image); + +GpStatus WINGDIPAPI +GdipGetImageGraphicsContext(GpImage *image, GpGraphics **graphics); + +/* pen */ + +GpStatus WINGDIPAPI +GdipCreatePen1(ARGB color, REAL width, Unit unit, GpPen **pen); + +GpStatus WINGDIPAPI +GdipDeletePen(GpPen *pen); + +GpStatus WINGDIPAPI +GdipSetPenWidth(GpPen *pen, REAL width); + +GpStatus WINGDIPAPI +GdipSetPenColor(GpPen *pen, ARGB argb); + +/* brush */ + +GpStatus WINGDIPAPI +GdipCreateSolidFill(ARGB color, GpSolidFill **brush); + +GpStatus WINGDIPAPI +GdipDeleteBrush(GpBrush *brush); + +GpStatus WINGDIPAPI +GdipSetSolidFillColor(GpSolidFill *brush, ARGB color); + +/* font */ + +GpStatus WINGDIPAPI +GdipCreateFont( + GDIPCONST GpFontFamily *fontFamily, + REAL emSize, + INT style, + Unit unit, + GpFont **font +); + +GpStatus WINGDIPAPI +GdipDeleteFont(GpFont* font); + +GpStatus WINGDIPAPI +GdipGetFontSize(GpFont *font, REAL *size); + +GpStatus WINGDIPAPI +GdipCreateFontFamilyFromName(GDIPCONST WCHAR *name, + GpFontCollection *fontCollection, + GpFontFamily **fontFamily); + +GpStatus WINGDIPAPI +GdipDeleteFontFamily(GpFontFamily *fontFamily); + +GpStatus WINGDIPAPI +GdipStringFormatGetGenericTypographic(GpStringFormat **format); + +GpStatus WINGDIPAPI +GdipSetStringFormatFlags(GpStringFormat *format, INT flags); + +GpStatus WINGDIPAPI +GdipDeleteStringFormat(GpStringFormat *format); + +/* graphics */ + + +GpStatus WINGDIPAPI +GdipCreateFromHWND(HWND hwnd, GpGraphics **graphics); + +GpStatus WINGDIPAPI +GdipCreateFromHDC(HDC hdc, GpGraphics **graphics); + +GpStatus WINGDIPAPI +GdipDeleteGraphics(GpGraphics *graphics); + +GpStatus WINGDIPAPI +GdipSetSmoothingMode(GpGraphics *graphics, SmoothingMode smoothingMode); + +GpStatus WINGDIPAPI +GdipSetClipRectI(GpGraphics *graphics, INT x, INT y, + INT width, INT height, CombineMode combineMode); + +GpStatus WINGDIPAPI +GdipDrawLineI(GpGraphics *graphics, GpPen *pen, INT x1, INT y1, + INT x2, INT y2); + +GpStatus WINGDIPAPI +GdipDrawArcI(GpGraphics *graphics, GpPen *pen, INT x, INT y, + INT width, INT height, REAL startAngle, REAL sweepAngle); + +GpStatus WINGDIPAPI +GdipFillPieI(GpGraphics *graphics, GpBrush *brush, INT x, INT y, + INT width, INT height, REAL startAngle, REAL sweepAngle); + +GpStatus WINGDIPAPI +GdipDrawRectangleI(GpGraphics *graphics, GpPen *pen, INT x, INT y, + INT width, INT height); + +GpStatus WINGDIPAPI +GdipFillRectangleI(GpGraphics *graphics, GpBrush *brush, INT x, INT y, + INT width, INT height); + +GpStatus WINGDIPAPI +GdipFillPolygonI(GpGraphics *graphics, GpBrush *brush, + GDIPCONST GpPoint *points, INT count, FillMode fillMode); + +GpStatus WINGDIPAPI +GdipDrawPolygonI(GpGraphics *graphics, GpPen *pen, GDIPCONST GpPoint *points, + INT count); + +GpStatus WINGDIPAPI +GdipFillEllipseI(GpGraphics *graphics, GpBrush *brush, INT x, INT y, + INT width, INT height); + +GpStatus WINGDIPAPI +GdipDrawEllipseI(GpGraphics *graphics, GpPen *pen, INT x, INT y, + INT width, INT height); + +GpStatus WINGDIPAPI +GdipDrawBezierI(GpGraphics *graphics, GpPen *pen, INT x1, INT y1, + INT x2, INT y2, INT x3, INT y3, INT x4, INT y4); + +GpStatus WINGDIPAPI +GdipDrawString( + GpGraphics *graphics, + GDIPCONST WCHAR *string, + INT length, + GDIPCONST GpFont *font, + GDIPCONST RectF *layoutRect, + GDIPCONST GpStringFormat *stringFormat, + GDIPCONST GpBrush *brush +); + +GpStatus WINGDIPAPI +GdipGraphicsClear(GpGraphics *graphics, ARGB color); + +GpStatus WINGDIPAPI +GdipDrawImageI(GpGraphics *graphics, GpImage *image, INT x, INT y); + +GpStatus WINGDIPAPI +GdipMeasureString( + GpGraphics *graphics, + GDIPCONST WCHAR *string, + INT length, + GDIPCONST GpFont *font, + GDIPCONST RectF *layoutRect, + GDIPCONST GpStringFormat *stringFormat, + RectF *boundingBox, + INT *codepointsFitted, + INT *linesFilled +); + +GpStatus WINGDIPAPI +GdipSetTextRenderingHint(GpGraphics *graphics, TextRenderingHint mode); + +struct GdipFont +{ + struct nk_user_font nk; + GpFont* handle; +}; + +static struct { + ULONG_PTR token; + + GpGraphics *window; + GpGraphics *memory; + GpImage *bitmap; + GpPen *pen; + GpSolidFill *brush; + GpStringFormat *format; + + struct nk_context ctx; +} gdip; + +static ARGB convert_color(struct nk_color c) +{ + return (c.a << 24) | (c.r << 16) | (c.g << 8) | c.b; +} + +static void +nk_gdip_scissor(float x, float y, float w, float h) +{ + GdipSetClipRectI(gdip.memory, (INT)x, (INT)y, (INT)(w + 1), (INT)(h + 1), CombineModeReplace); +} + +static void +nk_gdip_stroke_line(short x0, short y0, short x1, + short y1, unsigned int line_thickness, struct nk_color col) +{ + GdipSetPenWidth(gdip.pen, (REAL)line_thickness); + GdipSetPenColor(gdip.pen, convert_color(col)); + GdipDrawLineI(gdip.memory, gdip.pen, x0, y0, x1, y1); +} + +static void +nk_gdip_stroke_rect(short x, short y, unsigned short w, + unsigned short h, unsigned short r, unsigned short line_thickness, struct nk_color col) +{ + GdipSetPenWidth(gdip.pen, (REAL)line_thickness); + GdipSetPenColor(gdip.pen, convert_color(col)); + if (r == 0) { + GdipDrawRectangleI(gdip.memory, gdip.pen, x, y, w, h); + } else { + INT d = 2 * r; + GdipDrawArcI(gdip.memory, gdip.pen, x, y, d, d, 180, 90); + GdipDrawLineI(gdip.memory, gdip.pen, x + d, y, x + w - d, y); + GdipDrawArcI(gdip.memory, gdip.pen, x + w - d, y, d, d, 270, 90); + GdipDrawLineI(gdip.memory, gdip.pen, x + w, y + d, x + w, y + h - d); + GdipDrawArcI(gdip.memory, gdip.pen, x + w - d, y + h - d, d, d, 0, 90); + GdipDrawLineI(gdip.memory, gdip.pen, x, y + d, x + w, y + h - d); + GdipDrawArcI(gdip.memory, gdip.pen, x, y + h - d, d, d, 90, 90); + GdipDrawLineI(gdip.memory, gdip.pen, x + d, y + h, x + w - d, y + h); + } +} + +static void +nk_gdip_fill_rect(short x, short y, unsigned short w, + unsigned short h, unsigned short r, struct nk_color col) +{ + GdipSetSolidFillColor(gdip.brush, convert_color(col)); + if (r == 0) { + GdipFillRectangleI(gdip.memory, gdip.brush, x, y, w, h); + } else { + INT d = 2 * r; + GdipFillRectangleI(gdip.memory, gdip.brush, x + r, y, w - d, h); + GdipFillRectangleI(gdip.memory, gdip.brush, x, y + r, w, h - d); + GdipFillPieI(gdip.memory, gdip.brush, x, y, d, d, 180, 90); + GdipFillPieI(gdip.memory, gdip.brush, x + w - d, y, d, d, 270, 90); + GdipFillPieI(gdip.memory, gdip.brush, x + w - d, y + h - d, d, d, 0, 90); + GdipFillPieI(gdip.memory, gdip.brush, x, y + h - d, d, d, 90, 90); + } +} + +static void +nk_gdip_fill_triangle(short x0, short y0, short x1, + short y1, short x2, short y2, struct nk_color col) +{ + POINT points[] = { + { x0, y0 }, + { x1, y1 }, + { x2, y2 }, + }; + + GdipSetSolidFillColor(gdip.brush, convert_color(col)); + GdipFillPolygonI(gdip.memory, gdip.brush, points, 3, FillModeAlternate); +} + +static void +nk_gdip_stroke_triangle(short x0, short y0, short x1, + short y1, short x2, short y2, unsigned short line_thickness, struct nk_color col) +{ + POINT points[] = { + { x0, y0 }, + { x1, y1 }, + { x2, y2 }, + { x0, y0 }, + }; + GdipSetPenWidth(gdip.pen, (REAL)line_thickness); + GdipSetPenColor(gdip.pen, convert_color(col)); + GdipDrawPolygonI(gdip.memory, gdip.pen, points, 4); +} + +static void +nk_gdip_fill_polygon(const struct nk_vec2i *pnts, int count, struct nk_color col) +{ + int i = 0; + #define MAX_POINTS 64 + POINT points[MAX_POINTS]; + GdipSetSolidFillColor(gdip.brush, convert_color(col)); + for (i = 0; i < count && i < MAX_POINTS; ++i) { + points[i].x = pnts[i].x; + points[i].y = pnts[i].y; + } + GdipFillPolygonI(gdip.memory, gdip.brush, points, i, FillModeAlternate); + #undef MAX_POINTS +} + +static void +nk_gdip_stroke_polygon(const struct nk_vec2i *pnts, int count, + unsigned short line_thickness, struct nk_color col) +{ + GdipSetPenWidth(gdip.pen, (REAL)line_thickness); + GdipSetPenColor(gdip.pen, convert_color(col)); + if (count > 0) { + int i; + for (i = 1; i < count; ++i) + GdipDrawLineI(gdip.memory, gdip.pen, pnts[i-1].x, pnts[i-1].y, pnts[i].x, pnts[i].y); + GdipDrawLineI(gdip.memory, gdip.pen, pnts[count-1].x, pnts[count-1].y, pnts[0].x, pnts[0].y); + } +} + +static void +nk_gdip_stroke_polyline(const struct nk_vec2i *pnts, + int count, unsigned short line_thickness, struct nk_color col) +{ + GdipSetPenWidth(gdip.pen, (REAL)line_thickness); + GdipSetPenColor(gdip.pen, convert_color(col)); + if (count > 0) { + int i; + for (i = 1; i < count; ++i) + GdipDrawLineI(gdip.memory, gdip.pen, pnts[i-1].x, pnts[i-1].y, pnts[i].x, pnts[i].y); + } +} + +static void +nk_gdip_fill_circle(short x, short y, unsigned short w, + unsigned short h, struct nk_color col) +{ + GdipSetSolidFillColor(gdip.brush, convert_color(col)); + GdipFillEllipseI(gdip.memory, gdip.brush, x, y, w, h); +} + +static void +nk_gdip_stroke_circle(short x, short y, unsigned short w, + unsigned short h, unsigned short line_thickness, struct nk_color col) +{ + GdipSetPenWidth(gdip.pen, (REAL)line_thickness); + GdipSetPenColor(gdip.pen, convert_color(col)); + GdipDrawEllipseI(gdip.memory, gdip.pen, x, y, w, h); +} + +static void +nk_gdip_stroke_curve(struct nk_vec2i p1, + struct nk_vec2i p2, struct nk_vec2i p3, struct nk_vec2i p4, + unsigned short line_thickness, struct nk_color col) +{ + GdipSetPenWidth(gdip.pen, (REAL)line_thickness); + GdipSetPenColor(gdip.pen, convert_color(col)); + GdipDrawBezierI(gdip.memory, gdip.pen, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y); +} + +static void +nk_gdip_draw_text(short x, short y, unsigned short w, unsigned short h, + const char *text, int len, GdipFont *font, struct nk_color cbg, struct nk_color cfg) +{ + int wsize; + WCHAR* wstr; + RectF layout = { (FLOAT)x, (FLOAT)y, (FLOAT)w, (FLOAT)h }; + + if(!text || !font || !len) return; + + wsize = MultiByteToWideChar(CP_UTF8, 0, text, len, NULL, 0); + wstr = (WCHAR*)_alloca(wsize * sizeof(wchar_t)); + MultiByteToWideChar(CP_UTF8, 0, text, len, wstr, wsize); + + GdipSetSolidFillColor(gdip.brush, convert_color(cbg)); + GdipFillRectangleI(gdip.memory, gdip.brush, x, y, w, h); + GdipSetSolidFillColor(gdip.brush, convert_color(cfg)); + GdipDrawString(gdip.memory, wstr, wsize, font->handle, &layout, gdip.format, gdip.brush); +} + +static void +nk_gdip_clear(struct nk_color col) +{ + GdipGraphicsClear(gdip.memory, convert_color(col)); +} + +static void +nk_gdip_blit(GpGraphics *graphics) +{ + GdipDrawImageI(graphics, gdip.bitmap, 0, 0); +} + +GdipFont* +nk_gdipfont_create(const char *name, int size) +{ + GdipFont *font = (GdipFont*)calloc(1, sizeof(GdipFont)); + GpFontFamily *family; + + int wsize = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0); + WCHAR* wname = (WCHAR*)_alloca((wsize + 1) * sizeof(wchar_t)); + MultiByteToWideChar(CP_UTF8, 0, name, -1, wname, wsize); + wname[wsize] = 0; + + GdipCreateFontFamilyFromName(wname, NULL, &family); + GdipCreateFont(family, (REAL)size, FontStyleRegular, UnitPixel, &font->handle); + GdipDeleteFontFamily(family); + + return font; +} + +static float +nk_gdipfont_get_text_width(nk_handle handle, float height, const char *text, int len) +{ + GdipFont *font = (GdipFont *)handle.ptr; + RectF layout = { 0.0f, 0.0f, 65536.0f, 65536.0f }; + RectF bbox; + int wsize; + WCHAR* wstr; + if (!font || !text) + return 0; + + (void)height; + wsize = MultiByteToWideChar(CP_UTF8, 0, text, len, NULL, 0); + wstr = (WCHAR*)_alloca(wsize * sizeof(wchar_t)); + MultiByteToWideChar(CP_UTF8, 0, text, len, wstr, wsize); + + GdipMeasureString(gdip.memory, wstr, wsize, font->handle, &layout, gdip.format, &bbox, NULL, NULL); + return bbox.Width; +} + +void +nk_gdipfont_del(GdipFont *font) +{ + if(!font) return; + GdipDeleteFont(font->handle); + free(font); +} + +static void +nk_gdip_clipbard_paste(nk_handle usr, struct nk_text_edit *edit) +{ + HGLOBAL mem; + SIZE_T size; + LPCWSTR wstr; + int utf8size; + char* utf8; + (void)usr; + + if (!IsClipboardFormatAvailable(CF_UNICODETEXT) && OpenClipboard(NULL)) + return; + + mem = (HGLOBAL)GetClipboardData(CF_UNICODETEXT); + if (!mem) { + CloseClipboard(); + return; + } + + size = GlobalSize(mem) - 1; + if (!size) { + CloseClipboard(); + return; + } + + wstr = (LPCWSTR)GlobalLock(mem); + if (!wstr) { + CloseClipboard(); + return; + } + + utf8size = WideCharToMultiByte(CP_UTF8, 0, wstr, (int)(size / sizeof(wchar_t)), NULL, 0, NULL, NULL); + if (!utf8size) { + GlobalUnlock(mem); + CloseClipboard(); + return; + } + + utf8 = (char*)malloc(utf8size); + if (!utf8) { + GlobalUnlock(mem); + CloseClipboard(); + return; + } + + WideCharToMultiByte(CP_UTF8, 0, wstr, (int)(size / sizeof(wchar_t)), utf8, utf8size, NULL, NULL); + nk_textedit_paste(edit, utf8, utf8size); + free(utf8); + GlobalUnlock(mem); + CloseClipboard(); +} + +static void +nk_gdip_clipbard_copy(nk_handle usr, const char *text, int len) +{ + HGLOBAL mem; + wchar_t* wstr; + int wsize; + (void)usr; + + if (!OpenClipboard(NULL)) + return; + + wsize = MultiByteToWideChar(CP_UTF8, 0, text, len, NULL, 0); + if (!wsize) { + CloseClipboard(); + return; + } + + mem = (HGLOBAL)GlobalAlloc(GMEM_MOVEABLE, (wsize + 1) * sizeof(wchar_t)); + if (!mem) { + CloseClipboard(); + return; + } + + wstr = (wchar_t*)GlobalLock(mem); + if (!wstr) { + GlobalFree(mem); + CloseClipboard(); + return; + } + + MultiByteToWideChar(CP_UTF8, 0, text, len, wstr, wsize); + wstr[wsize] = 0; + GlobalUnlock(mem); + if (!SetClipboardData(CF_UNICODETEXT, mem)) + GlobalFree(mem); + CloseClipboard(); +} + +NK_API struct nk_context* +nk_gdip_init(HWND hwnd, unsigned int width, unsigned int height) +{ + GdiplusStartupInput startup = { 1, NULL, FALSE, TRUE }; + GdiplusStartup(&gdip.token, &startup, NULL); + + GdipCreateFromHWND(hwnd, &gdip.window); + GdipCreateBitmapFromGraphics(width, height, gdip.window, &gdip.bitmap); + GdipGetImageGraphicsContext(gdip.bitmap, &gdip.memory); + GdipCreatePen1(0, 1.0f, UnitPixel, &gdip.pen); + GdipCreateSolidFill(0, &gdip.brush); + GdipStringFormatGetGenericTypographic(&gdip.format); + GdipSetStringFormatFlags(gdip.format, StringFormatFlagsNoFitBlackBox | + StringFormatFlagsMeasureTrailingSpaces | StringFormatFlagsNoWrap | + StringFormatFlagsNoClip); + + nk_init_default(&gdip.ctx, NULL); + gdip.ctx.clip.copy = nk_gdip_clipbard_copy; + gdip.ctx.clip.paste = nk_gdip_clipbard_paste; + return &gdip.ctx; +} + +NK_API void +nk_gdip_set_font(GdipFont *gdipfont) +{ + struct nk_user_font *font = &gdipfont->nk; + font->userdata = nk_handle_ptr(gdipfont); + GdipGetFontSize(gdipfont->handle, &font->height); + font->width = nk_gdipfont_get_text_width; + nk_style_set_font(&gdip.ctx, font); +} + +NK_API int +nk_gdip_handle_event(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + switch (msg) + { + case WM_SIZE: + if (gdip.window) + { + unsigned int width = LOWORD(lparam); + unsigned int height = HIWORD(lparam); + GdipDeleteGraphics(gdip.window); + GdipDeleteGraphics(gdip.memory); + GdipDisposeImage(gdip.bitmap); + GdipCreateFromHWND(wnd, &gdip.window); + GdipCreateBitmapFromGraphics(width, height, gdip.window, &gdip.bitmap); + GdipGetImageGraphicsContext(gdip.bitmap, &gdip.memory); + } + break; + + case WM_PAINT: + { + PAINTSTRUCT paint; + HDC dc = BeginPaint(wnd, &paint); + GpGraphics *graphics; + GdipCreateFromHDC(dc, &graphics); + nk_gdip_blit(graphics); + GdipDeleteGraphics(graphics); + EndPaint(wnd, &paint); + return 1; + } + + case WM_KEYDOWN: + case WM_KEYUP: + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + { + int down = !((lparam >> 31) & 1); + int ctrl = GetKeyState(VK_CONTROL) & (1 << 15); + + switch (wparam) + { + case VK_SHIFT: + case VK_LSHIFT: + case VK_RSHIFT: + nk_input_key(&gdip.ctx, NK_KEY_SHIFT, down); + return 1; + + case VK_DELETE: + nk_input_key(&gdip.ctx, NK_KEY_DEL, down); + return 1; + + case VK_RETURN: + nk_input_key(&gdip.ctx, NK_KEY_ENTER, down); + return 1; + + case VK_TAB: + nk_input_key(&gdip.ctx, NK_KEY_TAB, down); + return 1; + + case VK_LEFT: + if (ctrl) + nk_input_key(&gdip.ctx, NK_KEY_TEXT_WORD_LEFT, down); + else + nk_input_key(&gdip.ctx, NK_KEY_LEFT, down); + return 1; + + case VK_RIGHT: + if (ctrl) + nk_input_key(&gdip.ctx, NK_KEY_TEXT_WORD_RIGHT, down); + else + nk_input_key(&gdip.ctx, NK_KEY_RIGHT, down); + return 1; + + case VK_BACK: + nk_input_key(&gdip.ctx, NK_KEY_BACKSPACE, down); + return 1; + + case VK_HOME: + nk_input_key(&gdip.ctx, NK_KEY_TEXT_START, down); + nk_input_key(&gdip.ctx, NK_KEY_SCROLL_START, down); + return 1; + + case VK_END: + nk_input_key(&gdip.ctx, NK_KEY_TEXT_END, down); + nk_input_key(&gdip.ctx, NK_KEY_SCROLL_END, down); + return 1; + + case VK_NEXT: + nk_input_key(&gdip.ctx, NK_KEY_SCROLL_DOWN, down); + return 1; + + case VK_PRIOR: + nk_input_key(&gdip.ctx, NK_KEY_SCROLL_UP, down); + return 1; + + case 'C': + if (ctrl) { + nk_input_key(&gdip.ctx, NK_KEY_COPY, down); + return 1; + } + break; + + case 'V': + if (ctrl) { + nk_input_key(&gdip.ctx, NK_KEY_PASTE, down); + return 1; + } + break; + + case 'X': + if (ctrl) { + nk_input_key(&gdip.ctx, NK_KEY_CUT, down); + return 1; + } + break; + + case 'Z': + if (ctrl) { + nk_input_key(&gdip.ctx, NK_KEY_TEXT_UNDO, down); + return 1; + } + break; + + case 'R': + if (ctrl) { + nk_input_key(&gdip.ctx, NK_KEY_TEXT_REDO, down); + return 1; + } + break; + } + return 0; + } + + case WM_CHAR: + if (wparam >= 32) + { + nk_input_unicode(&gdip.ctx, (nk_rune)wparam); + return 1; + } + break; + + case WM_LBUTTONDOWN: + nk_input_button(&gdip.ctx, NK_BUTTON_LEFT, (short)LOWORD(lparam), (short)HIWORD(lparam), 1); + SetCapture(wnd); + return 1; + + case WM_LBUTTONUP: + nk_input_button(&gdip.ctx, NK_BUTTON_LEFT, (short)LOWORD(lparam), (short)HIWORD(lparam), 0); + ReleaseCapture(); + return 1; + + case WM_RBUTTONDOWN: + nk_input_button(&gdip.ctx, NK_BUTTON_RIGHT, (short)LOWORD(lparam), (short)HIWORD(lparam), 1); + SetCapture(wnd); + return 1; + + case WM_RBUTTONUP: + nk_input_button(&gdip.ctx, NK_BUTTON_RIGHT, (short)LOWORD(lparam), (short)HIWORD(lparam), 0); + ReleaseCapture(); + return 1; + + case WM_MBUTTONDOWN: + nk_input_button(&gdip.ctx, NK_BUTTON_MIDDLE, (short)LOWORD(lparam), (short)HIWORD(lparam), 1); + SetCapture(wnd); + return 1; + + case WM_MBUTTONUP: + nk_input_button(&gdip.ctx, NK_BUTTON_MIDDLE, (short)LOWORD(lparam), (short)HIWORD(lparam), 0); + ReleaseCapture(); + return 1; + + case WM_MOUSEWHEEL: + nk_input_scroll(&gdip.ctx, (float)(short)HIWORD(wparam) / WHEEL_DELTA); + return 1; + + case WM_MOUSEMOVE: + nk_input_motion(&gdip.ctx, (short)LOWORD(lparam), (short)HIWORD(lparam)); + return 1; + } + + return 0; +} + +NK_API void +nk_gdip_shutdown(void) +{ + GdipDeleteGraphics(gdip.window); + GdipDeleteGraphics(gdip.memory); + GdipDisposeImage(gdip.bitmap); + GdipDeletePen(gdip.pen); + GdipDeleteBrush(gdip.brush); + GdipDeleteStringFormat(gdip.format); + GdiplusShutdown(gdip.token); + + nk_free(&gdip.ctx); +} + +NK_API void +nk_gdip_render(enum nk_anti_aliasing AA, struct nk_color clear) +{ + const struct nk_command *cmd; + + GdipSetTextRenderingHint(gdip.memory, AA != NK_ANTI_ALIASING_OFF ? + TextRenderingHintClearTypeGridFit : TextRenderingHintSingleBitPerPixelGridFit); + GdipSetSmoothingMode(gdip.memory, AA != NK_ANTI_ALIASING_OFF ? + SmoothingModeHighQuality : SmoothingModeNone); + nk_gdip_clear(clear); + + nk_foreach(cmd, &gdip.ctx) + { + switch (cmd->type) { + case NK_COMMAND_NOP: break; + case NK_COMMAND_SCISSOR: { + const struct nk_command_scissor *s =(const struct nk_command_scissor*)cmd; + nk_gdip_scissor(s->x, s->y, s->w, s->h); + } break; + case NK_COMMAND_LINE: { + const struct nk_command_line *l = (const struct nk_command_line *)cmd; + nk_gdip_stroke_line(l->begin.x, l->begin.y, l->end.x, + l->end.y, l->line_thickness, l->color); + } break; + case NK_COMMAND_RECT: { + const struct nk_command_rect *r = (const struct nk_command_rect *)cmd; + nk_gdip_stroke_rect(r->x, r->y, r->w, r->h, + (unsigned short)r->rounding, r->line_thickness, r->color); + } break; + case NK_COMMAND_RECT_FILLED: { + const struct nk_command_rect_filled *r = (const struct nk_command_rect_filled *)cmd; + nk_gdip_fill_rect(r->x, r->y, r->w, r->h, + (unsigned short)r->rounding, r->color); + } break; + case NK_COMMAND_CIRCLE: { + const struct nk_command_circle *c = (const struct nk_command_circle *)cmd; + nk_gdip_stroke_circle(c->x, c->y, c->w, c->h, c->line_thickness, c->color); + } break; + case NK_COMMAND_CIRCLE_FILLED: { + const struct nk_command_circle_filled *c = (const struct nk_command_circle_filled *)cmd; + nk_gdip_fill_circle(c->x, c->y, c->w, c->h, c->color); + } break; + case NK_COMMAND_TRIANGLE: { + const struct nk_command_triangle*t = (const struct nk_command_triangle*)cmd; + nk_gdip_stroke_triangle(t->a.x, t->a.y, t->b.x, t->b.y, + t->c.x, t->c.y, t->line_thickness, t->color); + } break; + case NK_COMMAND_TRIANGLE_FILLED: { + const struct nk_command_triangle_filled *t = (const struct nk_command_triangle_filled *)cmd; + nk_gdip_fill_triangle(t->a.x, t->a.y, t->b.x, t->b.y, + t->c.x, t->c.y, t->color); + } break; + case NK_COMMAND_POLYGON: { + const struct nk_command_polygon *p =(const struct nk_command_polygon*)cmd; + nk_gdip_stroke_polygon(p->points, p->point_count, p->line_thickness,p->color); + } break; + case NK_COMMAND_POLYGON_FILLED: { + const struct nk_command_polygon_filled *p = (const struct nk_command_polygon_filled *)cmd; + nk_gdip_fill_polygon(p->points, p->point_count, p->color); + } break; + case NK_COMMAND_POLYLINE: { + const struct nk_command_polyline *p = (const struct nk_command_polyline *)cmd; + nk_gdip_stroke_polyline(p->points, p->point_count, p->line_thickness, p->color); + } break; + case NK_COMMAND_TEXT: { + const struct nk_command_text *t = (const struct nk_command_text*)cmd; + nk_gdip_draw_text(t->x, t->y, t->w, t->h, + (const char*)t->string, t->length, + (GdipFont*)t->font->userdata.ptr, + t->background, t->foreground); + } break; + case NK_COMMAND_CURVE: { + const struct nk_command_curve *q = (const struct nk_command_curve *)cmd; + nk_gdip_stroke_curve(q->begin, q->ctrl[0], q->ctrl[1], + q->end, q->line_thickness, q->color); + } break; + case NK_COMMAND_RECT_MULTI_COLOR: + case NK_COMMAND_IMAGE: + case NK_COMMAND_ARC: + case NK_COMMAND_ARC_FILLED: + default: break; + } + } + nk_gdip_blit(gdip.window); + nk_clear(&gdip.ctx); +} + +#endif + diff --git a/nuklear/demo/glfw_opengl2/Makefile b/nuklear/demo/glfw_opengl2/Makefile new file mode 100644 index 0000000..c937eb8 --- /dev/null +++ b/nuklear/demo/glfw_opengl2/Makefile @@ -0,0 +1,25 @@ +# Install +BIN = demo + +# Flags +CFLAGS = -std=c99 -pedantic -O2 + +SRC = main.c +OBJ = $(SRC:.c=.o) + +ifeq ($(OS),Windows_NT) +BIN := $(BIN).exe +LIBS = -lglfw3 -lopengl32 -lm -lGLU32 +else + UNAME_S := $(shell uname -s) + ifeq ($(UNAME_S),Darwin) + LIBS = -lglfw3 -framework OpenGL -lm + else + LIBS = -lglfw -lGL -lm -lGLU + endif +endif + +$(BIN): + @mkdir -p bin + rm -f bin/$(BIN) $(OBJS) + $(CC) $(SRC) $(CFLAGS) -o bin/$(BIN) $(LIBS) diff --git a/nuklear/demo/glfw_opengl2/main.c b/nuklear/demo/glfw_opengl2/main.c new file mode 100644 index 0000000..1c0f284 --- /dev/null +++ b/nuklear/demo/glfw_opengl2/main.c @@ -0,0 +1,165 @@ +/* nuklear - v1.17 - public domain */ +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdarg.h> +#include <string.h> +#include <math.h> +#include <assert.h> +#include <math.h> +#include <limits.h> +#include <time.h> + +#include <GLFW/glfw3.h> + +#define NK_INCLUDE_FIXED_TYPES +#define NK_INCLUDE_STANDARD_IO +#define NK_INCLUDE_STANDARD_VARARGS +#define NK_INCLUDE_DEFAULT_ALLOCATOR +#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT +#define NK_INCLUDE_FONT_BAKING +#define NK_INCLUDE_DEFAULT_FONT +#define NK_IMPLEMENTATION +#define NK_GLFW_GL2_IMPLEMENTATION +#include "../../nuklear.h" +#include "nuklear_glfw_gl2.h" + +#define WINDOW_WIDTH 1200 +#define WINDOW_HEIGHT 800 + +#define MAX_VERTEX_BUFFER 512 * 1024 +#define MAX_ELEMENT_BUFFER 128 * 1024 + +#define UNUSED(a) (void)a +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) < (b) ? (b) : (a)) +#define LEN(a) (sizeof(a)/sizeof(a)[0]) + +/* =============================================================== + * + * EXAMPLE + * + * ===============================================================*/ +/* This are some code examples to provide a small overview of what can be + * done with this library. To try out an example uncomment the include + * and the corresponding function. */ +/*#include "../style.c"*/ +/*#include "../calculator.c"*/ +/*#include "../overview.c"*/ +/*#include "../node_editor.c"*/ + +/* =============================================================== + * + * DEMO + * + * ===============================================================*/ +static void error_callback(int e, const char *d) +{printf("Error %d: %s\n", e, d);} + +int main(void) +{ + /* Platform */ + static GLFWwindow *win; + int width = 0, height = 0; + struct nk_context *ctx; + struct nk_color background; + + /* GLFW */ + glfwSetErrorCallback(error_callback); + if (!glfwInit()) { + fprintf(stdout, "[GFLW] failed to init!\n"); + exit(1); + } + win = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "Demo", NULL, NULL); + glfwMakeContextCurrent(win); + glfwGetWindowSize(win, &width, &height); + + /* GUI */ + ctx = nk_glfw3_init(win, NK_GLFW3_INSTALL_CALLBACKS); + /* Load Fonts: if none of these are loaded a default font will be used */ + /* Load Cursor: if you uncomment cursor loading please hide the cursor */ + {struct nk_font_atlas *atlas; + nk_glfw3_font_stash_begin(&atlas); + /*struct nk_font *droid = nk_font_atlas_add_from_file(atlas, "../../../extra_font/DroidSans.ttf", 14, 0);*/ + /*struct nk_font *roboto = nk_font_atlas_add_from_file(atlas, "../../../extra_font/Roboto-Regular.ttf", 14, 0);*/ + /*struct nk_font *future = nk_font_atlas_add_from_file(atlas, "../../../extra_font/kenvector_future_thin.ttf", 13, 0);*/ + /*struct nk_font *clean = nk_font_atlas_add_from_file(atlas, "../../../extra_font/ProggyClean.ttf", 12, 0);*/ + /*struct nk_font *tiny = nk_font_atlas_add_from_file(atlas, "../../../extra_font/ProggyTiny.ttf", 10, 0);*/ + /*struct nk_font *cousine = nk_font_atlas_add_from_file(atlas, "../../../extra_font/Cousine-Regular.ttf", 13, 0);*/ + nk_glfw3_font_stash_end(); + /*nk_style_load_all_cursors(ctx, atlas->cursors);*/ + /*nk_style_set_font(ctx, &droid->handle);*/} + + /* style.c */ + /*set_style(ctx, THEME_WHITE);*/ + /*set_style(ctx, THEME_RED);*/ + /*set_style(ctx, THEME_BLUE);*/ + /*set_style(ctx, THEME_DARK);*/ + + background = nk_rgb(28,48,62); + while (!glfwWindowShouldClose(win)) + { + /* Input */ + glfwPollEvents(); + nk_glfw3_new_frame(); + + /* GUI */ + if (nk_begin(ctx, "Demo", nk_rect(50, 50, 230, 250), + NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_SCALABLE| + NK_WINDOW_MINIMIZABLE|NK_WINDOW_TITLE)) + { + enum {EASY, HARD}; + static int op = EASY; + static int property = 20; + nk_layout_row_static(ctx, 30, 80, 1); + if (nk_button_label(ctx, "button")) + fprintf(stdout, "button pressed\n"); + + nk_layout_row_dynamic(ctx, 30, 2); + if (nk_option_label(ctx, "easy", op == EASY)) op = EASY; + if (nk_option_label(ctx, "hard", op == HARD)) op = HARD; + + nk_layout_row_dynamic(ctx, 25, 1); + nk_property_int(ctx, "Compression:", 0, &property, 100, 10, 1); + + nk_layout_row_dynamic(ctx, 20, 1); + nk_label(ctx, "background:", NK_TEXT_LEFT); + nk_layout_row_dynamic(ctx, 25, 1); + if (nk_combo_begin_color(ctx, background, nk_vec2(nk_widget_width(ctx),400))) { + nk_layout_row_dynamic(ctx, 120, 1); + background = nk_color_picker(ctx, background, NK_RGBA); + nk_layout_row_dynamic(ctx, 25, 1); + background.r = (nk_byte)nk_propertyi(ctx, "#R:", 0, background.r, 255, 1,1); + background.g = (nk_byte)nk_propertyi(ctx, "#G:", 0, background.g, 255, 1,1); + background.b = (nk_byte)nk_propertyi(ctx, "#B:", 0, background.b, 255, 1,1); + background.a = (nk_byte)nk_propertyi(ctx, "#A:", 0, background.a, 255, 1,1); + nk_combo_end(ctx); + } + } + nk_end(ctx); + + /* -------------- EXAMPLES ---------------- */ + /*calculator(ctx);*/ + /*overview(ctx);*/ + /*node_editor(ctx);*/ + /* ----------------------------------------- */ + + /* Draw */ + {float bg[4]; + nk_color_fv(bg, background); + glfwGetWindowSize(win, &width, &height); + glViewport(0, 0, width, height); + glClear(GL_COLOR_BUFFER_BIT); + glClearColor(bg[0], bg[1], bg[2], bg[3]); + /* IMPORTANT: `nk_glfw_render` modifies some global OpenGL state + * with blending, scissor, face culling and depth test and defaults everything + * back into a default state. Make sure to either save and restore or + * reset your own state after drawing rendering the UI. */ + nk_glfw3_render(NK_ANTI_ALIASING_ON, MAX_VERTEX_BUFFER, MAX_ELEMENT_BUFFER); + glfwSwapBuffers(win);} + } + nk_glfw3_shutdown(); + glfwTerminate(); + return 0; +} + diff --git a/nuklear/demo/glfw_opengl2/nuklear_glfw_gl2.h b/nuklear/demo/glfw_opengl2/nuklear_glfw_gl2.h new file mode 100644 index 0000000..93af773 --- /dev/null +++ b/nuklear/demo/glfw_opengl2/nuklear_glfw_gl2.h @@ -0,0 +1,350 @@ +/* + * Nuklear - v1.17 - public domain + * no warrenty implied; use at your own risk. + * authored from 2015-2016 by Micha Mettke + */ +/* + * ============================================================== + * + * API + * + * =============================================================== + */ +#ifndef NK_GLFW_GL2_H_ +#define NK_GLFW_GL2_H_ + +#include <GLFW/glfw3.h> + +enum nk_glfw_init_state{ + NK_GLFW3_DEFAULT = 0, + NK_GLFW3_INSTALL_CALLBACKS +}; +NK_API struct nk_context* nk_glfw3_init(GLFWwindow *win, enum nk_glfw_init_state); +NK_API void nk_glfw3_font_stash_begin(struct nk_font_atlas **atlas); +NK_API void nk_glfw3_font_stash_end(void); + +NK_API void nk_glfw3_new_frame(void); +NK_API void nk_glfw3_render(enum nk_anti_aliasing , int max_vertex_buffer, int max_element_buffer); +NK_API void nk_glfw3_shutdown(void); + +NK_API void nk_glfw3_char_callback(GLFWwindow *win, unsigned int codepoint); +NK_API void nk_gflw3_scroll_callback(GLFWwindow *win, double xoff, double yoff); + +#endif + +/* + * ============================================================== + * + * IMPLEMENTATION + * + * =============================================================== + */ +#ifdef NK_GLFW_GL2_IMPLEMENTATION + +#ifndef NK_GLFW_TEXT_MAX +#define NK_GLFW_TEXT_MAX 256 +#endif + +struct nk_glfw_device { + struct nk_buffer cmds; + struct nk_draw_null_texture null; + GLuint font_tex; +}; + +struct nk_glfw_vertex { + float position[2]; + float uv[2]; + nk_byte col[4]; +}; + +static struct nk_glfw { + GLFWwindow *win; + int width, height; + int display_width, display_height; + struct nk_glfw_device ogl; + struct nk_context ctx; + struct nk_font_atlas atlas; + struct nk_vec2 fb_scale; + unsigned int text[NK_GLFW_TEXT_MAX]; + int text_len; + float scroll; +} glfw; + +NK_INTERN void +nk_glfw3_device_upload_atlas(const void *image, int width, int height) +{ + struct nk_glfw_device *dev = &glfw.ogl; + glGenTextures(1, &dev->font_tex); + glBindTexture(GL_TEXTURE_2D, dev->font_tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)width, (GLsizei)height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, image); +} + +NK_API void +nk_glfw3_render(enum nk_anti_aliasing AA, int max_vertex_buffer, int max_element_buffer) +{ + /* setup global state */ + struct nk_glfw_device *dev = &glfw.ogl; + glPushAttrib(GL_ENABLE_BIT|GL_COLOR_BUFFER_BIT|GL_TRANSFORM_BIT); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glEnable(GL_SCISSOR_TEST); + glEnable(GL_BLEND); + glEnable(GL_TEXTURE_2D); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + /* setup viewport/project */ + glViewport(0,0,(GLsizei)glfw.display_width,(GLsizei)glfw.display_height); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(0.0f, glfw.width, glfw.height, 0.0f, -1.0f, 1.0f); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + { + GLsizei vs = sizeof(struct nk_glfw_vertex); + size_t vp = offsetof(struct nk_glfw_vertex, position); + size_t vt = offsetof(struct nk_glfw_vertex, uv); + size_t vc = offsetof(struct nk_glfw_vertex, col); + + /* convert from command queue into draw list and draw to screen */ + const struct nk_draw_command *cmd; + const nk_draw_index *offset = NULL; + struct nk_buffer vbuf, ebuf; + + /* fill convert configuration */ + struct nk_convert_config config; + static const struct nk_draw_vertex_layout_element vertex_layout[] = { + {NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_glfw_vertex, position)}, + {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_glfw_vertex, uv)}, + {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, NK_OFFSETOF(struct nk_glfw_vertex, col)}, + {NK_VERTEX_LAYOUT_END} + }; + NK_MEMSET(&config, 0, sizeof(config)); + config.vertex_layout = vertex_layout; + config.vertex_size = sizeof(struct nk_glfw_vertex); + config.vertex_alignment = NK_ALIGNOF(struct nk_glfw_vertex); + config.null = dev->null; + config.circle_segment_count = 22; + config.curve_segment_count = 22; + config.arc_segment_count = 22; + config.global_alpha = 1.0f; + config.shape_AA = AA; + config.line_AA = AA; + + /* convert shapes into vertexes */ + nk_buffer_init_default(&vbuf); + nk_buffer_init_default(&ebuf); + nk_convert(&glfw.ctx, &dev->cmds, &vbuf, &ebuf, &config); + + /* setup vertex buffer pointer */ + {const void *vertices = nk_buffer_memory_const(&vbuf); + glVertexPointer(2, GL_FLOAT, vs, (const void*)((const nk_byte*)vertices + vp)); + glTexCoordPointer(2, GL_FLOAT, vs, (const void*)((const nk_byte*)vertices + vt)); + glColorPointer(4, GL_UNSIGNED_BYTE, vs, (const void*)((const nk_byte*)vertices + vc));} + + /* iterate over and execute each draw command */ + offset = (const nk_draw_index*)nk_buffer_memory_const(&ebuf); + nk_draw_foreach(cmd, &glfw.ctx, &dev->cmds) + { + if (!cmd->elem_count) continue; + glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id); + glScissor( + (GLint)(cmd->clip_rect.x * glfw.fb_scale.x), + (GLint)((glfw.height - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h)) * glfw.fb_scale.y), + (GLint)(cmd->clip_rect.w * glfw.fb_scale.x), + (GLint)(cmd->clip_rect.h * glfw.fb_scale.y)); + glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset); + offset += cmd->elem_count; + } + nk_clear(&glfw.ctx); + nk_buffer_free(&vbuf); + nk_buffer_free(&ebuf); + } + + /* default OpenGL state */ + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_BLEND); + glDisable(GL_TEXTURE_2D); + + glBindTexture(GL_TEXTURE_2D, 0); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glPopAttrib(); +} + +NK_API void +nk_glfw3_char_callback(GLFWwindow *win, unsigned int codepoint) +{ + (void)win; + if (glfw.text_len < NK_GLFW_TEXT_MAX) + glfw.text[glfw.text_len++] = codepoint; +} + +NK_API void +nk_gflw3_scroll_callback(GLFWwindow *win, double xoff, double yoff) +{ + (void)win; (void)xoff; + glfw.scroll += (float)yoff; +} + +NK_INTERN void +nk_glfw3_clipbard_paste(nk_handle usr, struct nk_text_edit *edit) +{ + const char *text = glfwGetClipboardString(glfw.win); + if (text) nk_textedit_paste(edit, text, nk_strlen(text)); + (void)usr; +} + +NK_INTERN void +nk_glfw3_clipbard_copy(nk_handle usr, const char *text, int len) +{ + char *str = 0; + (void)usr; + if (!len) return; + str = (char*)malloc((size_t)len+1); + if (!str) return; + memcpy(str, text, (size_t)len); + str[len] = '\0'; + glfwSetClipboardString(glfw.win, str); + free(str); +} + +NK_API struct nk_context* +nk_glfw3_init(GLFWwindow *win, enum nk_glfw_init_state init_state) +{ + glfw.win = win; + if (init_state == NK_GLFW3_INSTALL_CALLBACKS) { + glfwSetScrollCallback(win, nk_gflw3_scroll_callback); + glfwSetCharCallback(win, nk_glfw3_char_callback); + } + + nk_init_default(&glfw.ctx, 0); + glfw.ctx.clip.copy = nk_glfw3_clipbard_copy; + glfw.ctx.clip.paste = nk_glfw3_clipbard_paste; + glfw.ctx.clip.userdata = nk_handle_ptr(0); + nk_buffer_init_default(&glfw.ogl.cmds); + return &glfw.ctx; +} + +NK_API void +nk_glfw3_font_stash_begin(struct nk_font_atlas **atlas) +{ + nk_font_atlas_init_default(&glfw.atlas); + nk_font_atlas_begin(&glfw.atlas); + *atlas = &glfw.atlas; +} + +NK_API void +nk_glfw3_font_stash_end(void) +{ + const void *image; int w, h; + image = nk_font_atlas_bake(&glfw.atlas, &w, &h, NK_FONT_ATLAS_RGBA32); + nk_glfw3_device_upload_atlas(image, w, h); + nk_font_atlas_end(&glfw.atlas, nk_handle_id((int)glfw.ogl.font_tex), &glfw.ogl.null); + if (glfw.atlas.default_font) + nk_style_set_font(&glfw.ctx, &glfw.atlas.default_font->handle); +} + +NK_API void +nk_glfw3_new_frame(void) +{ + int i; + double x, y; + struct nk_context *ctx = &glfw.ctx; + struct GLFWwindow *win = glfw.win; + + glfwGetWindowSize(win, &glfw.width, &glfw.height); + glfwGetFramebufferSize(win, &glfw.display_width, &glfw.display_height); + glfw.fb_scale.x = (float)glfw.display_width/(float)glfw.width; + glfw.fb_scale.y = (float)glfw.display_height/(float)glfw.height; + + nk_input_begin(ctx); + for (i = 0; i < glfw.text_len; ++i) + nk_input_unicode(ctx, glfw.text[i]); + + /* optional grabbing behavior */ + if (ctx->input.mouse.grab) + glfwSetInputMode(glfw.win, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); + else if (ctx->input.mouse.ungrab) + glfwSetInputMode(glfw.win, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + + nk_input_key(ctx, NK_KEY_DEL, glfwGetKey(win, GLFW_KEY_DELETE) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_ENTER, glfwGetKey(win, GLFW_KEY_ENTER) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_TAB, glfwGetKey(win, GLFW_KEY_TAB) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_BACKSPACE, glfwGetKey(win, GLFW_KEY_BACKSPACE) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_UP, glfwGetKey(win, GLFW_KEY_UP) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_DOWN, glfwGetKey(win, GLFW_KEY_DOWN) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_TEXT_START, glfwGetKey(win, GLFW_KEY_HOME) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_TEXT_END, glfwGetKey(win, GLFW_KEY_END) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_SCROLL_START, glfwGetKey(win, GLFW_KEY_HOME) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_SCROLL_END, glfwGetKey(win, GLFW_KEY_END) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_SCROLL_DOWN, glfwGetKey(win, GLFW_KEY_PAGE_DOWN) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_SCROLL_UP, glfwGetKey(win, GLFW_KEY_PAGE_UP) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_SHIFT, glfwGetKey(win, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS|| + glfwGetKey(win, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS); + + if (glfwGetKey(win, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || + glfwGetKey(win, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS) { + nk_input_key(ctx, NK_KEY_COPY, glfwGetKey(win, GLFW_KEY_C) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_PASTE, glfwGetKey(win, GLFW_KEY_P) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_CUT, glfwGetKey(win, GLFW_KEY_X) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_TEXT_UNDO, glfwGetKey(win, GLFW_KEY_Z) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_TEXT_REDO, glfwGetKey(win, GLFW_KEY_R) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_TEXT_WORD_LEFT, glfwGetKey(win, GLFW_KEY_LEFT) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_TEXT_WORD_RIGHT, glfwGetKey(win, GLFW_KEY_RIGHT) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_TEXT_LINE_START, glfwGetKey(win, GLFW_KEY_B) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_TEXT_LINE_END, glfwGetKey(win, GLFW_KEY_E) == GLFW_PRESS); + } else { + nk_input_key(ctx, NK_KEY_LEFT, glfwGetKey(win, GLFW_KEY_LEFT) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_RIGHT, glfwGetKey(win, GLFW_KEY_RIGHT) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_COPY, 0); + nk_input_key(ctx, NK_KEY_PASTE, 0); + nk_input_key(ctx, NK_KEY_CUT, 0); + nk_input_key(ctx, NK_KEY_SHIFT, 0); + } + + glfwGetCursorPos(win, &x, &y); + nk_input_motion(ctx, (int)x, (int)y); + if (ctx->input.mouse.grabbed) { + glfwSetCursorPos(glfw.win, ctx->input.mouse.prev.x, ctx->input.mouse.prev.y); + ctx->input.mouse.pos.x = ctx->input.mouse.prev.x; + ctx->input.mouse.pos.y = ctx->input.mouse.prev.y; + } + + nk_input_button(ctx, NK_BUTTON_LEFT, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS); + nk_input_button(ctx, NK_BUTTON_MIDDLE, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_MIDDLE) == GLFW_PRESS); + nk_input_button(ctx, NK_BUTTON_RIGHT, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS); + nk_input_scroll(ctx, glfw.scroll); + nk_input_end(&glfw.ctx); + glfw.text_len = 0; + glfw.scroll = 0; +} + +NK_API +void nk_glfw3_shutdown(void) +{ + struct nk_glfw_device *dev = &glfw.ogl; + nk_font_atlas_clear(&glfw.atlas); + nk_free(&glfw.ctx); + glDeleteTextures(1, &dev->font_tex); + nk_buffer_free(&dev->cmds); + memset(&glfw, 0, sizeof(glfw)); +} + +#endif diff --git a/nuklear/demo/glfw_opengl3/Makefile b/nuklear/demo/glfw_opengl3/Makefile new file mode 100644 index 0000000..7f620ea --- /dev/null +++ b/nuklear/demo/glfw_opengl3/Makefile @@ -0,0 +1,25 @@ +# Install +BIN = demo + +# Flags +CFLAGS = -std=c99 -pedantic -O2 + +SRC = main.c +OBJ = $(SRC:.c=.o) + +ifeq ($(OS),Windows_NT) +BIN := $(BIN).exe +LIBS = -lglfw3 -lopengl32 -lm -lGLU32 -lGLEW32 +else + UNAME_S := $(shell uname -s) + ifeq ($(UNAME_S),Darwin) + LIBS = -lglfw3 -framework OpenGL -lm -lGLEW + else + LIBS = -lglfw -lGL -lm -lGLU -lGLEW + endif +endif + +$(BIN): + @mkdir -p bin + rm -f bin/$(BIN) $(OBJS) + $(CC) $(SRC) $(CFLAGS) -o bin/$(BIN) $(LIBS) diff --git a/nuklear/demo/glfw_opengl3/main.c b/nuklear/demo/glfw_opengl3/main.c new file mode 100644 index 0000000..d74ee56 --- /dev/null +++ b/nuklear/demo/glfw_opengl3/main.c @@ -0,0 +1,180 @@ +/* nuklear - v1.17 - public domain */ +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdarg.h> +#include <string.h> +#include <math.h> +#include <assert.h> +#include <math.h> +#include <limits.h> +#include <time.h> + +#include <GL/glew.h> +#include <GLFW/glfw3.h> + +#define NK_INCLUDE_FIXED_TYPES +#define NK_INCLUDE_STANDARD_IO +#define NK_INCLUDE_STANDARD_VARARGS +#define NK_INCLUDE_DEFAULT_ALLOCATOR +#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT +#define NK_INCLUDE_FONT_BAKING +#define NK_INCLUDE_DEFAULT_FONT +#define NK_IMPLEMENTATION +#define NK_GLFW_GL3_IMPLEMENTATION +#include "../../nuklear.h" +#include "nuklear_glfw_gl3.h" + +#define WINDOW_WIDTH 1200 +#define WINDOW_HEIGHT 800 + +#define MAX_VERTEX_BUFFER 512 * 1024 +#define MAX_ELEMENT_BUFFER 128 * 1024 + +#define UNUSED(a) (void)a +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) < (b) ? (b) : (a)) +#define LEN(a) (sizeof(a)/sizeof(a)[0]) + +/* =============================================================== + * + * EXAMPLE + * + * ===============================================================*/ +/* This are some code examples to provide a small overview of what can be + * done with this library. To try out an example uncomment the include + * and the corresponding function. */ +/*#include "../style.c"*/ +/*#include "../calculator.c"*/ +/*#include "../overview.c"*/ +/*#include "../node_editor.c"*/ + +/* =============================================================== + * + * DEMO + * + * ===============================================================*/ +static void error_callback(int e, const char *d) +{printf("Error %d: %s\n", e, d);} + +int main(void) +{ + /* Platform */ + static GLFWwindow *win; + int width = 0, height = 0; + struct nk_context *ctx; + struct nk_color background; + + /* GLFW */ + glfwSetErrorCallback(error_callback); + if (!glfwInit()) { + fprintf(stdout, "[GFLW] failed to init!\n"); + exit(1); + } + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); +#ifdef __APPLE__ + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); +#endif + win = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "Demo", NULL, NULL); + glfwMakeContextCurrent(win); + glfwGetWindowSize(win, &width, &height); + + /* OpenGL */ + glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); + glewExperimental = 1; + if (glewInit() != GLEW_OK) { + fprintf(stderr, "Failed to setup GLEW\n"); + exit(1); + } + + ctx = nk_glfw3_init(win, NK_GLFW3_INSTALL_CALLBACKS); + /* Load Fonts: if none of these are loaded a default font will be used */ + /* Load Cursor: if you uncomment cursor loading please hide the cursor */ + {struct nk_font_atlas *atlas; + nk_glfw3_font_stash_begin(&atlas); + /*struct nk_font *droid = nk_font_atlas_add_from_file(atlas, "../../../extra_font/DroidSans.ttf", 14, 0);*/ + /*struct nk_font *roboto = nk_font_atlas_add_from_file(atlas, "../../../extra_font/Roboto-Regular.ttf", 14, 0);*/ + /*struct nk_font *future = nk_font_atlas_add_from_file(atlas, "../../../extra_font/kenvector_future_thin.ttf", 13, 0);*/ + /*struct nk_font *clean = nk_font_atlas_add_from_file(atlas, "../../../extra_font/ProggyClean.ttf", 12, 0);*/ + /*struct nk_font *tiny = nk_font_atlas_add_from_file(atlas, "../../../extra_font/ProggyTiny.ttf", 10, 0);*/ + /*struct nk_font *cousine = nk_font_atlas_add_from_file(atlas, "../../../extra_font/Cousine-Regular.ttf", 13, 0);*/ + nk_glfw3_font_stash_end(); + /*nk_style_load_all_cursors(ctx, atlas->cursors);*/ + /*nk_style_set_font(ctx, &droid->handle);*/} + + /* style.c */ + /*set_style(ctx, THEME_WHITE);*/ + /*set_style(ctx, THEME_RED);*/ + /*set_style(ctx, THEME_BLUE);*/ + /*set_style(ctx, THEME_DARK);*/ + + background = nk_rgb(28,48,62); + while (!glfwWindowShouldClose(win)) + { + /* Input */ + glfwPollEvents(); + nk_glfw3_new_frame(); + + /* GUI */ + if (nk_begin(ctx, "Demo", nk_rect(50, 50, 230, 250), + NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_SCALABLE| + NK_WINDOW_MINIMIZABLE|NK_WINDOW_TITLE)) + { + enum {EASY, HARD}; + static int op = EASY; + static int property = 20; + nk_layout_row_static(ctx, 30, 80, 1); + if (nk_button_label(ctx, "button")) + fprintf(stdout, "button pressed\n"); + + nk_layout_row_dynamic(ctx, 30, 2); + if (nk_option_label(ctx, "easy", op == EASY)) op = EASY; + if (nk_option_label(ctx, "hard", op == HARD)) op = HARD; + + nk_layout_row_dynamic(ctx, 25, 1); + nk_property_int(ctx, "Compression:", 0, &property, 100, 10, 1); + + nk_layout_row_dynamic(ctx, 20, 1); + nk_label(ctx, "background:", NK_TEXT_LEFT); + nk_layout_row_dynamic(ctx, 25, 1); + if (nk_combo_begin_color(ctx, background, nk_vec2(nk_widget_width(ctx),400))) { + nk_layout_row_dynamic(ctx, 120, 1); + background = nk_color_picker(ctx, background, NK_RGBA); + nk_layout_row_dynamic(ctx, 25, 1); + background.r = (nk_byte)nk_propertyi(ctx, "#R:", 0, background.r, 255, 1,1); + background.g = (nk_byte)nk_propertyi(ctx, "#G:", 0, background.g, 255, 1,1); + background.b = (nk_byte)nk_propertyi(ctx, "#B:", 0, background.b, 255, 1,1); + background.a = (nk_byte)nk_propertyi(ctx, "#A:", 0, background.a, 255, 1,1); + nk_combo_end(ctx); + } + } + nk_end(ctx); + + /* -------------- EXAMPLES ---------------- */ + /*calculator(ctx);*/ + /*overview(ctx);*/ + /*node_editor(ctx);*/ + /* ----------------------------------------- */ + + /* Draw */ + {float bg[4]; + nk_color_fv(bg, background); + glfwGetWindowSize(win, &width, &height); + glViewport(0, 0, width, height); + glClear(GL_COLOR_BUFFER_BIT); + glClearColor(bg[0], bg[1], bg[2], bg[3]); + /* IMPORTANT: `nk_glfw_render` modifies some global OpenGL state + * with blending, scissor, face culling, depth test and viewport and + * defaults everything back into a default state. + * Make sure to either a.) save and restore or b.) reset your own state after + * rendering the UI. */ + nk_glfw3_render(NK_ANTI_ALIASING_ON, MAX_VERTEX_BUFFER, MAX_ELEMENT_BUFFER); + glfwSwapBuffers(win);} + } + nk_glfw3_shutdown(); + glfwTerminate(); + return 0; +} + diff --git a/nuklear/demo/glfw_opengl3/nuklear_glfw_gl3.h b/nuklear/demo/glfw_opengl3/nuklear_glfw_gl3.h new file mode 100644 index 0000000..aabb365 --- /dev/null +++ b/nuklear/demo/glfw_opengl3/nuklear_glfw_gl3.h @@ -0,0 +1,456 @@ +/* + * Nuklear - v1.17 - public domain + * no warrenty implied; use at your own risk. + * authored from 2015-2016 by Micha Mettke + */ +/* + * ============================================================== + * + * API + * + * =============================================================== + */ +#ifndef NK_GLFW_GL3_H_ +#define NK_GLFW_GL3_H_ + +#include <GLFW/glfw3.h> + +enum nk_glfw_init_state{ + NK_GLFW3_DEFAULT=0, + NK_GLFW3_INSTALL_CALLBACKS +}; + +NK_API struct nk_context* nk_glfw3_init(GLFWwindow *win, enum nk_glfw_init_state); +NK_API void nk_glfw3_shutdown(void); +NK_API void nk_glfw3_font_stash_begin(struct nk_font_atlas **atlas); +NK_API void nk_glfw3_font_stash_end(void); +NK_API void nk_glfw3_new_frame(void); +NK_API void nk_glfw3_render(enum nk_anti_aliasing, int max_vertex_buffer, int max_element_buffer); + +NK_API void nk_glfw3_device_destroy(void); +NK_API void nk_glfw3_device_create(void); + +NK_API void nk_glfw3_char_callback(GLFWwindow *win, unsigned int codepoint); +NK_API void nk_gflw3_scroll_callback(GLFWwindow *win, double xoff, double yoff); + +#endif +/* + * ============================================================== + * + * IMPLEMENTATION + * + * =============================================================== + */ +#ifdef NK_GLFW_GL3_IMPLEMENTATION + +#ifndef NK_GLFW_TEXT_MAX +#define NK_GLFW_TEXT_MAX 256 +#endif + +struct nk_glfw_device { + struct nk_buffer cmds; + struct nk_draw_null_texture null; + GLuint vbo, vao, ebo; + GLuint prog; + GLuint vert_shdr; + GLuint frag_shdr; + GLint attrib_pos; + GLint attrib_uv; + GLint attrib_col; + GLint uniform_tex; + GLint uniform_proj; + GLuint font_tex; +}; + +struct nk_glfw_vertex { + float position[2]; + float uv[2]; + nk_byte col[4]; +}; + +static struct nk_glfw { + GLFWwindow *win; + int width, height; + int display_width, display_height; + struct nk_glfw_device ogl; + struct nk_context ctx; + struct nk_font_atlas atlas; + struct nk_vec2 fb_scale; + unsigned int text[NK_GLFW_TEXT_MAX]; + int text_len; + float scroll; +} glfw; + +#ifdef __APPLE__ + #define NK_SHADER_VERSION "#version 150\n" +#else + #define NK_SHADER_VERSION "#version 300 es\n" +#endif + +NK_API void +nk_glfw3_device_create(void) +{ + GLint status; + static const GLchar *vertex_shader = + NK_SHADER_VERSION + "uniform mat4 ProjMtx;\n" + "in vec2 Position;\n" + "in vec2 TexCoord;\n" + "in vec4 Color;\n" + "out vec2 Frag_UV;\n" + "out vec4 Frag_Color;\n" + "void main() {\n" + " Frag_UV = TexCoord;\n" + " Frag_Color = Color;\n" + " gl_Position = ProjMtx * vec4(Position.xy, 0, 1);\n" + "}\n"; + static const GLchar *fragment_shader = + NK_SHADER_VERSION + "precision mediump float;\n" + "uniform sampler2D Texture;\n" + "in vec2 Frag_UV;\n" + "in vec4 Frag_Color;\n" + "out vec4 Out_Color;\n" + "void main(){\n" + " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" + "}\n"; + + struct nk_glfw_device *dev = &glfw.ogl; + nk_buffer_init_default(&dev->cmds); + dev->prog = glCreateProgram(); + dev->vert_shdr = glCreateShader(GL_VERTEX_SHADER); + dev->frag_shdr = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(dev->vert_shdr, 1, &vertex_shader, 0); + glShaderSource(dev->frag_shdr, 1, &fragment_shader, 0); + glCompileShader(dev->vert_shdr); + glCompileShader(dev->frag_shdr); + glGetShaderiv(dev->vert_shdr, GL_COMPILE_STATUS, &status); + assert(status == GL_TRUE); + glGetShaderiv(dev->frag_shdr, GL_COMPILE_STATUS, &status); + assert(status == GL_TRUE); + glAttachShader(dev->prog, dev->vert_shdr); + glAttachShader(dev->prog, dev->frag_shdr); + glLinkProgram(dev->prog); + glGetProgramiv(dev->prog, GL_LINK_STATUS, &status); + assert(status == GL_TRUE); + + dev->uniform_tex = glGetUniformLocation(dev->prog, "Texture"); + dev->uniform_proj = glGetUniformLocation(dev->prog, "ProjMtx"); + dev->attrib_pos = glGetAttribLocation(dev->prog, "Position"); + dev->attrib_uv = glGetAttribLocation(dev->prog, "TexCoord"); + dev->attrib_col = glGetAttribLocation(dev->prog, "Color"); + + { + /* buffer setup */ + GLsizei vs = sizeof(struct nk_glfw_vertex); + size_t vp = offsetof(struct nk_glfw_vertex, position); + size_t vt = offsetof(struct nk_glfw_vertex, uv); + size_t vc = offsetof(struct nk_glfw_vertex, col); + + glGenBuffers(1, &dev->vbo); + glGenBuffers(1, &dev->ebo); + glGenVertexArrays(1, &dev->vao); + + glBindVertexArray(dev->vao); + glBindBuffer(GL_ARRAY_BUFFER, dev->vbo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo); + + glEnableVertexAttribArray((GLuint)dev->attrib_pos); + glEnableVertexAttribArray((GLuint)dev->attrib_uv); + glEnableVertexAttribArray((GLuint)dev->attrib_col); + + glVertexAttribPointer((GLuint)dev->attrib_pos, 2, GL_FLOAT, GL_FALSE, vs, (void*)vp); + glVertexAttribPointer((GLuint)dev->attrib_uv, 2, GL_FLOAT, GL_FALSE, vs, (void*)vt); + glVertexAttribPointer((GLuint)dev->attrib_col, 4, GL_UNSIGNED_BYTE, GL_TRUE, vs, (void*)vc); + } + + glBindTexture(GL_TEXTURE_2D, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindVertexArray(0); +} + +NK_INTERN void +nk_glfw3_device_upload_atlas(const void *image, int width, int height) +{ + struct nk_glfw_device *dev = &glfw.ogl; + glGenTextures(1, &dev->font_tex); + glBindTexture(GL_TEXTURE_2D, dev->font_tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)width, (GLsizei)height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, image); +} + +NK_API void +nk_glfw3_device_destroy(void) +{ + struct nk_glfw_device *dev = &glfw.ogl; + glDetachShader(dev->prog, dev->vert_shdr); + glDetachShader(dev->prog, dev->frag_shdr); + glDeleteShader(dev->vert_shdr); + glDeleteShader(dev->frag_shdr); + glDeleteProgram(dev->prog); + glDeleteTextures(1, &dev->font_tex); + glDeleteBuffers(1, &dev->vbo); + glDeleteBuffers(1, &dev->ebo); + nk_buffer_free(&dev->cmds); +} + +NK_API void +nk_glfw3_render(enum nk_anti_aliasing AA, int max_vertex_buffer, int max_element_buffer) +{ + struct nk_glfw_device *dev = &glfw.ogl; + GLfloat ortho[4][4] = { + {2.0f, 0.0f, 0.0f, 0.0f}, + {0.0f,-2.0f, 0.0f, 0.0f}, + {0.0f, 0.0f,-1.0f, 0.0f}, + {-1.0f,1.0f, 0.0f, 1.0f}, + }; + ortho[0][0] /= (GLfloat)glfw.width; + ortho[1][1] /= (GLfloat)glfw.height; + + /* setup global state */ + glEnable(GL_BLEND); + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glEnable(GL_SCISSOR_TEST); + glActiveTexture(GL_TEXTURE0); + + /* setup program */ + glUseProgram(dev->prog); + glUniform1i(dev->uniform_tex, 0); + glUniformMatrix4fv(dev->uniform_proj, 1, GL_FALSE, &ortho[0][0]); + glViewport(0,0,(GLsizei)glfw.display_width,(GLsizei)glfw.display_height); + { + /* convert from command queue into draw list and draw to screen */ + const struct nk_draw_command *cmd; + void *vertices, *elements; + const nk_draw_index *offset = NULL; + + /* allocate vertex and element buffer */ + glBindVertexArray(dev->vao); + glBindBuffer(GL_ARRAY_BUFFER, dev->vbo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo); + + glBufferData(GL_ARRAY_BUFFER, max_vertex_buffer, NULL, GL_STREAM_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, max_element_buffer, NULL, GL_STREAM_DRAW); + + /* load draw vertices & elements directly into vertex + element buffer */ + vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); + elements = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY); + { + /* fill convert configuration */ + struct nk_convert_config config; + static const struct nk_draw_vertex_layout_element vertex_layout[] = { + {NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_glfw_vertex, position)}, + {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_glfw_vertex, uv)}, + {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, NK_OFFSETOF(struct nk_glfw_vertex, col)}, + {NK_VERTEX_LAYOUT_END} + }; + NK_MEMSET(&config, 0, sizeof(config)); + config.vertex_layout = vertex_layout; + config.vertex_size = sizeof(struct nk_glfw_vertex); + config.vertex_alignment = NK_ALIGNOF(struct nk_glfw_vertex); + config.null = dev->null; + config.circle_segment_count = 22; + config.curve_segment_count = 22; + config.arc_segment_count = 22; + config.global_alpha = 1.0f; + config.shape_AA = AA; + config.line_AA = AA; + + /* setup buffers to load vertices and elements */ + {struct nk_buffer vbuf, ebuf; + nk_buffer_init_fixed(&vbuf, vertices, (size_t)max_vertex_buffer); + nk_buffer_init_fixed(&ebuf, elements, (size_t)max_element_buffer); + nk_convert(&glfw.ctx, &dev->cmds, &vbuf, &ebuf, &config);} + } + glUnmapBuffer(GL_ARRAY_BUFFER); + glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); + + /* iterate over and execute each draw command */ + nk_draw_foreach(cmd, &glfw.ctx, &dev->cmds) + { + if (!cmd->elem_count) continue; + glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id); + glScissor( + (GLint)(cmd->clip_rect.x * glfw.fb_scale.x), + (GLint)((glfw.height - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h)) * glfw.fb_scale.y), + (GLint)(cmd->clip_rect.w * glfw.fb_scale.x), + (GLint)(cmd->clip_rect.h * glfw.fb_scale.y)); + glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset); + offset += cmd->elem_count; + } + nk_clear(&glfw.ctx); + } + + /* default OpenGL state */ + glUseProgram(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindVertexArray(0); + glDisable(GL_BLEND); + glDisable(GL_SCISSOR_TEST); +} + +NK_API void +nk_glfw3_char_callback(GLFWwindow *win, unsigned int codepoint) +{ + (void)win; + if (glfw.text_len < NK_GLFW_TEXT_MAX) + glfw.text[glfw.text_len++] = codepoint; +} + +NK_API void +nk_gflw3_scroll_callback(GLFWwindow *win, double xoff, double yoff) +{ + (void)win; (void)xoff; + glfw.scroll += (float)yoff; +} + +NK_INTERN void +nk_glfw3_clipbard_paste(nk_handle usr, struct nk_text_edit *edit) +{ + const char *text = glfwGetClipboardString(glfw.win); + if (text) nk_textedit_paste(edit, text, nk_strlen(text)); + (void)usr; +} + +NK_INTERN void +nk_glfw3_clipbard_copy(nk_handle usr, const char *text, int len) +{ + char *str = 0; + (void)usr; + if (!len) return; + str = (char*)malloc((size_t)len+1); + if (!str) return; + memcpy(str, text, (size_t)len); + str[len] = '\0'; + glfwSetClipboardString(glfw.win, str); + free(str); +} + +NK_API struct nk_context* +nk_glfw3_init(GLFWwindow *win, enum nk_glfw_init_state init_state) +{ + glfw.win = win; + if (init_state == NK_GLFW3_INSTALL_CALLBACKS) { + glfwSetScrollCallback(win, nk_gflw3_scroll_callback); + glfwSetCharCallback(win, nk_glfw3_char_callback); + } + + nk_init_default(&glfw.ctx, 0); + glfw.ctx.clip.copy = nk_glfw3_clipbard_copy; + glfw.ctx.clip.paste = nk_glfw3_clipbard_paste; + glfw.ctx.clip.userdata = nk_handle_ptr(0); + nk_glfw3_device_create(); + return &glfw.ctx; +} + +NK_API void +nk_glfw3_font_stash_begin(struct nk_font_atlas **atlas) +{ + nk_font_atlas_init_default(&glfw.atlas); + nk_font_atlas_begin(&glfw.atlas); + *atlas = &glfw.atlas; +} + +NK_API void +nk_glfw3_font_stash_end(void) +{ + const void *image; int w, h; + image = nk_font_atlas_bake(&glfw.atlas, &w, &h, NK_FONT_ATLAS_RGBA32); + nk_glfw3_device_upload_atlas(image, w, h); + nk_font_atlas_end(&glfw.atlas, nk_handle_id((int)glfw.ogl.font_tex), &glfw.ogl.null); + if (glfw.atlas.default_font) + nk_style_set_font(&glfw.ctx, &glfw.atlas.default_font->handle); +} + +NK_API void +nk_glfw3_new_frame(void) +{ + int i; + double x, y; + struct nk_context *ctx = &glfw.ctx; + struct GLFWwindow *win = glfw.win; + + glfwGetWindowSize(win, &glfw.width, &glfw.height); + glfwGetFramebufferSize(win, &glfw.display_width, &glfw.display_height); + glfw.fb_scale.x = (float)glfw.display_width/(float)glfw.width; + glfw.fb_scale.y = (float)glfw.display_height/(float)glfw.height; + + nk_input_begin(ctx); + for (i = 0; i < glfw.text_len; ++i) + nk_input_unicode(ctx, glfw.text[i]); + + /* optional grabbing behavior */ + if (ctx->input.mouse.grab) + glfwSetInputMode(glfw.win, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); + else if (ctx->input.mouse.ungrab) + glfwSetInputMode(glfw.win, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + + nk_input_key(ctx, NK_KEY_DEL, glfwGetKey(win, GLFW_KEY_DELETE) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_ENTER, glfwGetKey(win, GLFW_KEY_ENTER) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_TAB, glfwGetKey(win, GLFW_KEY_TAB) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_BACKSPACE, glfwGetKey(win, GLFW_KEY_BACKSPACE) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_UP, glfwGetKey(win, GLFW_KEY_UP) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_DOWN, glfwGetKey(win, GLFW_KEY_DOWN) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_TEXT_START, glfwGetKey(win, GLFW_KEY_HOME) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_TEXT_END, glfwGetKey(win, GLFW_KEY_END) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_SCROLL_START, glfwGetKey(win, GLFW_KEY_HOME) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_SCROLL_END, glfwGetKey(win, GLFW_KEY_END) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_SCROLL_DOWN, glfwGetKey(win, GLFW_KEY_PAGE_DOWN) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_SCROLL_UP, glfwGetKey(win, GLFW_KEY_PAGE_UP) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_SHIFT, glfwGetKey(win, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS|| + glfwGetKey(win, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS); + + if (glfwGetKey(win, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || + glfwGetKey(win, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS) { + nk_input_key(ctx, NK_KEY_COPY, glfwGetKey(win, GLFW_KEY_C) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_PASTE, glfwGetKey(win, GLFW_KEY_P) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_CUT, glfwGetKey(win, GLFW_KEY_X) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_TEXT_UNDO, glfwGetKey(win, GLFW_KEY_Z) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_TEXT_REDO, glfwGetKey(win, GLFW_KEY_R) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_TEXT_WORD_LEFT, glfwGetKey(win, GLFW_KEY_LEFT) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_TEXT_WORD_RIGHT, glfwGetKey(win, GLFW_KEY_RIGHT) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_TEXT_LINE_START, glfwGetKey(win, GLFW_KEY_B) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_TEXT_LINE_END, glfwGetKey(win, GLFW_KEY_E) == GLFW_PRESS); + } else { + nk_input_key(ctx, NK_KEY_LEFT, glfwGetKey(win, GLFW_KEY_LEFT) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_RIGHT, glfwGetKey(win, GLFW_KEY_RIGHT) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_COPY, 0); + nk_input_key(ctx, NK_KEY_PASTE, 0); + nk_input_key(ctx, NK_KEY_CUT, 0); + nk_input_key(ctx, NK_KEY_SHIFT, 0); + } + + glfwGetCursorPos(win, &x, &y); + nk_input_motion(ctx, (int)x, (int)y); + if (ctx->input.mouse.grabbed) { + glfwSetCursorPos(glfw.win, ctx->input.mouse.prev.x, ctx->input.mouse.prev.y); + ctx->input.mouse.pos.x = ctx->input.mouse.prev.x; + ctx->input.mouse.pos.y = ctx->input.mouse.prev.y; + } + + nk_input_button(ctx, NK_BUTTON_LEFT, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS); + nk_input_button(ctx, NK_BUTTON_MIDDLE, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_MIDDLE) == GLFW_PRESS); + nk_input_button(ctx, NK_BUTTON_RIGHT, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS); + nk_input_scroll(ctx, glfw.scroll); + nk_input_end(&glfw.ctx); + glfw.text_len = 0; + glfw.scroll = 0; +} + +NK_API +void nk_glfw3_shutdown(void) +{ + nk_font_atlas_clear(&glfw.atlas); + nk_free(&glfw.ctx); + nk_glfw3_device_destroy(); + memset(&glfw, 0, sizeof(glfw)); +} + +#endif diff --git a/nuklear/demo/node_editor.c b/nuklear/demo/node_editor.c new file mode 100644 index 0000000..6949f59 --- /dev/null +++ b/nuklear/demo/node_editor.c @@ -0,0 +1,343 @@ +/* nuklear - v1.00 - public domain */ +/* This is a simple node editor just to show a simple implementation and that + * it is possible to achieve it with this library. While all nodes inside this + * example use a simple color modifier as content you could change them + * to have your custom content depending on the node time. + * Biggest difference to most usual implementation is that this example does + * not have connectors on the right position of the property that it links. + * This is mainly done out of laziness and could be implemented as well but + * requires calculating the position of all rows and add connectors. + * In addition adding and removing nodes is quite limited at the + * moment since it is based on a simple fixed array. If this is to be converted + * into something more serious it is probably best to extend it.*/ +struct node { + int ID; + char name[32]; + struct nk_rect bounds; + float value; + struct nk_color color; + int input_count; + int output_count; + struct node *next; + struct node *prev; +}; + +struct node_link { + int input_id; + int input_slot; + int output_id; + int output_slot; + struct nk_vec2 in; + struct nk_vec2 out; +}; + +struct node_linking { + int active; + struct node *node; + int input_id; + int input_slot; +}; + +struct node_editor { + int initialized; + struct node node_buf[32]; + struct node_link links[64]; + struct node *begin; + struct node *end; + int node_count; + int link_count; + struct nk_rect bounds; + struct node *selected; + int show_grid; + struct nk_vec2 scrolling; + struct node_linking linking; +}; +static struct node_editor nodeEditor; + +static void +node_editor_push(struct node_editor *editor, struct node *node) +{ + if (!editor->begin) { + node->next = NULL; + node->prev = NULL; + editor->begin = node; + editor->end = node; + } else { + node->prev = editor->end; + if (editor->end) + editor->end->next = node; + node->next = NULL; + editor->end = node; + } +} + +static void +node_editor_pop(struct node_editor *editor, struct node *node) +{ + if (node->next) + node->next->prev = node->prev; + if (node->prev) + node->prev->next = node->next; + if (editor->end == node) + editor->end = node->prev; + if (editor->begin == node) + editor->begin = node->next; + node->next = NULL; + node->prev = NULL; +} + +static struct node* +node_editor_find(struct node_editor *editor, int ID) +{ + struct node *iter = editor->begin; + while (iter) { + if (iter->ID == ID) + return iter; + iter = iter->next; + } + return NULL; +} + +static void +node_editor_add(struct node_editor *editor, const char *name, struct nk_rect bounds, + struct nk_color col, int in_count, int out_count) +{ + static int IDs = 0; + struct node *node; + assert((nk_size)editor->node_count < LEN(editor->node_buf)); + node = &editor->node_buf[editor->node_count++]; + node->ID = IDs++; + node->value = 0; + node->color = nk_rgb(255, 0, 0); + node->input_count = in_count; + node->output_count = out_count; + node->color = col; + node->bounds = bounds; + strcpy(node->name, name); + node_editor_push(editor, node); +} + +static void +node_editor_link(struct node_editor *editor, int in_id, int in_slot, + int out_id, int out_slot) +{ + struct node_link *link; + assert((nk_size)editor->link_count < LEN(editor->links)); + link = &editor->links[editor->link_count++]; + link->input_id = in_id; + link->input_slot = in_slot; + link->output_id = out_id; + link->output_slot = out_slot; +} + +static void +node_editor_init(struct node_editor *editor) +{ + memset(editor, 0, sizeof(*editor)); + editor->begin = NULL; + editor->end = NULL; + node_editor_add(editor, "Source", nk_rect(40, 10, 180, 220), nk_rgb(255, 0, 0), 0, 1); + node_editor_add(editor, "Source", nk_rect(40, 260, 180, 220), nk_rgb(0, 255, 0), 0, 1); + node_editor_add(editor, "Combine", nk_rect(400, 100, 180, 220), nk_rgb(0,0,255), 2, 2); + node_editor_link(editor, 0, 0, 2, 0); + node_editor_link(editor, 1, 0, 2, 1); + editor->show_grid = nk_true; +} + +static int +node_editor(struct nk_context *ctx) +{ + int n = 0; + struct nk_rect total_space; + const struct nk_input *in = &ctx->input; + struct nk_command_buffer *canvas; + struct node *updated = 0; + struct node_editor *nodedit = &nodeEditor; + + if (!nodeEditor.initialized) { + node_editor_init(&nodeEditor); + nodeEditor.initialized = 1; + } + + if (nk_begin(ctx, "NodeEdit", nk_rect(0, 0, 800, 600), + NK_WINDOW_BORDER|NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_MOVABLE|NK_WINDOW_CLOSABLE)) + { + /* allocate complete window space */ + canvas = nk_window_get_canvas(ctx); + total_space = nk_window_get_content_region(ctx); + nk_layout_space_begin(ctx, NK_STATIC, total_space.h, nodedit->node_count); + { + struct node *it = nodedit->begin; + struct nk_rect size = nk_layout_space_bounds(ctx); + + if (nodedit->show_grid) { + /* display grid */ + float x, y; + const float grid_size = 32.0f; + const struct nk_color grid_color = nk_rgb(50, 50, 50); + for (x = (float)fmod(size.x - nodedit->scrolling.x, grid_size); x < size.w; x += grid_size) + nk_stroke_line(canvas, x+size.x, size.y, x+size.x, size.y+size.h, 1.0f, grid_color); + for (y = (float)fmod(size.y - nodedit->scrolling.y, grid_size); y < size.h; y += grid_size) + nk_stroke_line(canvas, size.x, y+size.y, size.x+size.w, y+size.y, 1.0f, grid_color); + } + + /* execute each node as a movable group */ + struct nk_panel *node; + while (it) { + /* calculate scrolled node window position and size */ + nk_layout_space_push(ctx, nk_rect(it->bounds.x - nodedit->scrolling.x, + it->bounds.y - nodedit->scrolling.y, it->bounds.w, it->bounds.h)); + + /* execute node window */ + if (nk_group_begin(ctx, it->name, NK_WINDOW_MOVABLE|NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_BORDER|NK_WINDOW_TITLE)) + { + /* always have last selected node on top */ + + node = nk_window_get_panel(ctx); + if (nk_input_mouse_clicked(in, NK_BUTTON_LEFT, node->bounds) && + (!(it->prev && nk_input_mouse_clicked(in, NK_BUTTON_LEFT, + nk_layout_space_rect_to_screen(ctx, node->bounds)))) && + nodedit->end != it) + { + updated = it; + } + + /* ================= NODE CONTENT =====================*/ + nk_layout_row_dynamic(ctx, 25, 1); + nk_button_color(ctx, it->color); + it->color.r = (nk_byte)nk_propertyi(ctx, "#R:", 0, it->color.r, 255, 1,1); + it->color.g = (nk_byte)nk_propertyi(ctx, "#G:", 0, it->color.g, 255, 1,1); + it->color.b = (nk_byte)nk_propertyi(ctx, "#B:", 0, it->color.b, 255, 1,1); + it->color.a = (nk_byte)nk_propertyi(ctx, "#A:", 0, it->color.a, 255, 1,1); + /* ====================================================*/ + nk_group_end(ctx); + } + { + /* node connector and linking */ + float space; + struct nk_rect bounds; + bounds = nk_layout_space_rect_to_local(ctx, node->bounds); + bounds.x += nodedit->scrolling.x; + bounds.y += nodedit->scrolling.y; + it->bounds = bounds; + + /* output connector */ + space = node->bounds.h / (float)((it->output_count) + 1); + for (n = 0; n < it->output_count; ++n) { + struct nk_rect circle; + circle.x = node->bounds.x + node->bounds.w-4; + circle.y = node->bounds.y + space * (float)(n+1); + circle.w = 8; circle.h = 8; + nk_fill_circle(canvas, circle, nk_rgb(100, 100, 100)); + + /* start linking process */ + if (nk_input_has_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, circle, nk_true)) { + nodedit->linking.active = nk_true; + nodedit->linking.node = it; + nodedit->linking.input_id = it->ID; + nodedit->linking.input_slot = n; + } + + /* draw curve from linked node slot to mouse position */ + if (nodedit->linking.active && nodedit->linking.node == it && + nodedit->linking.input_slot == n) { + struct nk_vec2 l0 = nk_vec2(circle.x + 3, circle.y + 3); + struct nk_vec2 l1 = in->mouse.pos; + nk_stroke_curve(canvas, l0.x, l0.y, l0.x + 50.0f, l0.y, + l1.x - 50.0f, l1.y, l1.x, l1.y, 1.0f, nk_rgb(100, 100, 100)); + } + } + + /* input connector */ + space = node->bounds.h / (float)((it->input_count) + 1); + for (n = 0; n < it->input_count; ++n) { + struct nk_rect circle; + circle.x = node->bounds.x-4; + circle.y = node->bounds.y + space * (float)(n+1); + circle.w = 8; circle.h = 8; + nk_fill_circle(canvas, circle, nk_rgb(100, 100, 100)); + if (nk_input_is_mouse_released(in, NK_BUTTON_LEFT) && + nk_input_is_mouse_hovering_rect(in, circle) && + nodedit->linking.active && nodedit->linking.node != it) { + nodedit->linking.active = nk_false; + node_editor_link(nodedit, nodedit->linking.input_id, + nodedit->linking.input_slot, it->ID, n); + } + } + } + it = it->next; + } + + /* reset linking connection */ + if (nodedit->linking.active && nk_input_is_mouse_released(in, NK_BUTTON_LEFT)) { + nodedit->linking.active = nk_false; + nodedit->linking.node = NULL; + fprintf(stdout, "linking failed\n"); + } + + /* draw each link */ + for (n = 0; n < nodedit->link_count; ++n) { + struct node_link *link = &nodedit->links[n]; + struct node *ni = node_editor_find(nodedit, link->input_id); + struct node *no = node_editor_find(nodedit, link->output_id); + float spacei = node->bounds.h / (float)((ni->output_count) + 1); + float spaceo = node->bounds.h / (float)((no->input_count) + 1); + struct nk_vec2 l0 = nk_layout_space_to_screen(ctx, + nk_vec2(ni->bounds.x + ni->bounds.w, 3.0f + ni->bounds.y + spacei * (float)(link->input_slot+1))); + struct nk_vec2 l1 = nk_layout_space_to_screen(ctx, + nk_vec2(no->bounds.x, 3.0f + no->bounds.y + spaceo * (float)(link->output_slot+1))); + + l0.x -= nodedit->scrolling.x; + l0.y -= nodedit->scrolling.y; + l1.x -= nodedit->scrolling.x; + l1.y -= nodedit->scrolling.y; + nk_stroke_curve(canvas, l0.x, l0.y, l0.x + 50.0f, l0.y, + l1.x - 50.0f, l1.y, l1.x, l1.y, 1.0f, nk_rgb(100, 100, 100)); + } + + if (updated) { + /* reshuffle nodes to have least recently selected node on top */ + node_editor_pop(nodedit, updated); + node_editor_push(nodedit, updated); + } + + /* node selection */ + if (nk_input_mouse_clicked(in, NK_BUTTON_LEFT, nk_layout_space_bounds(ctx))) { + it = nodedit->begin; + nodedit->selected = NULL; + nodedit->bounds = nk_rect(in->mouse.pos.x, in->mouse.pos.y, 100, 200); + while (it) { + struct nk_rect b = nk_layout_space_rect_to_screen(ctx, it->bounds); + b.x -= nodedit->scrolling.x; + b.y -= nodedit->scrolling.y; + if (nk_input_is_mouse_hovering_rect(in, b)) + nodedit->selected = it; + it = it->next; + } + } + + /* contextual menu */ + if (nk_contextual_begin(ctx, 0, nk_vec2(100, 220), nk_window_get_bounds(ctx))) { + const char *grid_option[] = {"Show Grid", "Hide Grid"}; + nk_layout_row_dynamic(ctx, 25, 1); + if (nk_contextual_item_label(ctx, "New", NK_TEXT_CENTERED)) + node_editor_add(nodedit, "New", nk_rect(400, 260, 180, 220), + nk_rgb(255, 255, 255), 1, 2); + if (nk_contextual_item_label(ctx, grid_option[nodedit->show_grid],NK_TEXT_CENTERED)) + nodedit->show_grid = !nodedit->show_grid; + nk_contextual_end(ctx); + } + } + nk_layout_space_end(ctx); + + /* window content scrolling */ + if (nk_input_is_mouse_hovering_rect(in, nk_window_get_bounds(ctx)) && + nk_input_is_mouse_down(in, NK_BUTTON_MIDDLE)) { + nodedit->scrolling.x += in->mouse.delta.x; + nodedit->scrolling.y += in->mouse.delta.y; + } + } + nk_end(ctx); + return !nk_window_is_closed(ctx, "NodeEdit"); +} + diff --git a/nuklear/demo/overview.c b/nuklear/demo/overview.c new file mode 100644 index 0000000..e271318 --- /dev/null +++ b/nuklear/demo/overview.c @@ -0,0 +1,1188 @@ + +static int +overview(struct nk_context *ctx) +{ + /* window flags */ + static int show_menu = nk_true; + static int titlebar = nk_true; + static int border = nk_true; + static int resize = nk_true; + static int movable = nk_true; + static int no_scrollbar = nk_false; + static nk_flags window_flags = 0; + static int minimizable = nk_true; + + /* popups */ + static enum nk_style_header_align header_align = NK_HEADER_RIGHT; + static int show_app_about = nk_false; + + /* window flags */ + window_flags = 0; + ctx->style.window.header.align = header_align; + if (border) window_flags |= NK_WINDOW_BORDER; + if (resize) window_flags |= NK_WINDOW_SCALABLE; + if (movable) window_flags |= NK_WINDOW_MOVABLE; + if (no_scrollbar) window_flags |= NK_WINDOW_NO_SCROLLBAR; + if (minimizable) window_flags |= NK_WINDOW_MINIMIZABLE; + + if (nk_begin(ctx, "Overview", nk_rect(10, 10, 400, 600), window_flags)) + { + if (show_menu) + { + /* menubar */ + enum menu_states {MENU_DEFAULT, MENU_WINDOWS}; + static nk_size mprog = 60; + static int mslider = 10; + static int mcheck = nk_true; + + nk_menubar_begin(ctx); + nk_layout_row_begin(ctx, NK_STATIC, 25, 4); + nk_layout_row_push(ctx, 45); + if (nk_menu_begin_label(ctx, "MENU", NK_TEXT_LEFT, nk_vec2(120, 200))) + { + static size_t prog = 40; + static int slider = 10; + static int check = nk_true; + nk_layout_row_dynamic(ctx, 25, 1); + if (nk_menu_item_label(ctx, "Hide", NK_TEXT_LEFT)) + show_menu = nk_false; + if (nk_menu_item_label(ctx, "About", NK_TEXT_LEFT)) + show_app_about = nk_true; + nk_progress(ctx, &prog, 100, NK_MODIFIABLE); + nk_slider_int(ctx, 0, &slider, 16, 1); + nk_checkbox_label(ctx, "check", &check); + nk_menu_end(ctx); + } + nk_layout_row_push(ctx, 70); + nk_progress(ctx, &mprog, 100, NK_MODIFIABLE); + nk_slider_int(ctx, 0, &mslider, 16, 1); + nk_checkbox_label(ctx, "check", &mcheck); + nk_menubar_end(ctx); + } + + if (show_app_about) + { + /* about popup */ + static struct nk_rect s = {20, 100, 300, 190}; + if (nk_popup_begin(ctx, NK_POPUP_STATIC, "About", NK_WINDOW_CLOSABLE, s)) + { + nk_layout_row_dynamic(ctx, 20, 1); + nk_label(ctx, "Nuklear", NK_TEXT_LEFT); + nk_label(ctx, "By Micha Mettke", NK_TEXT_LEFT); + nk_label(ctx, "nuklear is licensed under the public domain License.", NK_TEXT_LEFT); + nk_popup_end(ctx); + } else show_app_about = nk_false; + } + + /* window flags */ + if (nk_tree_push(ctx, NK_TREE_TAB, "Window", NK_MINIMIZED)) { + nk_layout_row_dynamic(ctx, 30, 2); + nk_checkbox_label(ctx, "Titlebar", &titlebar); + nk_checkbox_label(ctx, "Menu", &show_menu); + nk_checkbox_label(ctx, "Border", &border); + nk_checkbox_label(ctx, "Resizable", &resize); + nk_checkbox_label(ctx, "Movable", &movable); + nk_checkbox_label(ctx, "No Scrollbar", &no_scrollbar); + nk_checkbox_label(ctx, "Minimizable", &minimizable); + nk_tree_pop(ctx); + } + + if (nk_tree_push(ctx, NK_TREE_TAB, "Widgets", NK_MINIMIZED)) + { + enum options {A,B,C}; + static int checkbox; + static int option; + if (nk_tree_push(ctx, NK_TREE_NODE, "Text", NK_MINIMIZED)) + { + /* Text Widgets */ + nk_layout_row_dynamic(ctx, 20, 1); + nk_label(ctx, "Label aligned left", NK_TEXT_LEFT); + nk_label(ctx, "Label aligned centered", NK_TEXT_CENTERED); + nk_label(ctx, "Label aligned right", NK_TEXT_RIGHT); + nk_label_colored(ctx, "Blue text", NK_TEXT_LEFT, nk_rgb(0,0,255)); + nk_label_colored(ctx, "Yellow text", NK_TEXT_LEFT, nk_rgb(255,255,0)); + nk_text(ctx, "Text without /0", 15, NK_TEXT_RIGHT); + + nk_layout_row_static(ctx, 100, 200, 1); + nk_label_wrap(ctx, "This is a very long line to hopefully get this text to be wrapped into multiple lines to show line wrapping"); + nk_layout_row_dynamic(ctx, 100, 1); + nk_label_wrap(ctx, "This is another long text to show dynamic window changes on multiline text"); + nk_tree_pop(ctx); + } + + if (nk_tree_push(ctx, NK_TREE_NODE, "Button", NK_MINIMIZED)) + { + /* Buttons Widgets */ + nk_layout_row_static(ctx, 30, 100, 3); + if (nk_button_label(ctx, "Button")) + fprintf(stdout, "Button pressed!\n"); + nk_button_set_behavior(ctx, NK_BUTTON_REPEATER); + if (nk_button_label(ctx, "Repeater")) + fprintf(stdout, "Repeater is being pressed!\n"); + nk_button_set_behavior(ctx, NK_BUTTON_DEFAULT); + nk_button_color(ctx, nk_rgb(0,0,255)); + + nk_layout_row_static(ctx, 25, 25, 8); + nk_button_symbol(ctx, NK_SYMBOL_CIRCLE_SOLID); + nk_button_symbol(ctx, NK_SYMBOL_CIRCLE_OUTLINE); + nk_button_symbol(ctx, NK_SYMBOL_RECT_SOLID); + nk_button_symbol(ctx, NK_SYMBOL_RECT_OUTLINE); + nk_button_symbol(ctx, NK_SYMBOL_TRIANGLE_UP); + nk_button_symbol(ctx, NK_SYMBOL_TRIANGLE_DOWN); + nk_button_symbol(ctx, NK_SYMBOL_TRIANGLE_LEFT); + nk_button_symbol(ctx, NK_SYMBOL_TRIANGLE_RIGHT); + + nk_layout_row_static(ctx, 30, 100, 2); + nk_button_symbol_label(ctx, NK_SYMBOL_TRIANGLE_LEFT, "prev", NK_TEXT_RIGHT); + nk_button_symbol_label(ctx, NK_SYMBOL_TRIANGLE_RIGHT, "next", NK_TEXT_LEFT); + nk_tree_pop(ctx); + } + + if (nk_tree_push(ctx, NK_TREE_NODE, "Basic", NK_MINIMIZED)) + { + /* Basic widgets */ + static int int_slider = 5; + static float float_slider = 2.5f; + static size_t prog_value = 40; + static float property_float = 2; + static int property_int = 10; + static int property_neg = 10; + + static float range_float_min = 0; + static float range_float_max = 100; + static float range_float_value = 50; + static int range_int_min = 0; + static int range_int_value = 2048; + static int range_int_max = 4096; + static const float ratio[] = {120, 150}; + + nk_layout_row_static(ctx, 30, 100, 1); + nk_checkbox_label(ctx, "Checkbox", &checkbox); + + nk_layout_row_static(ctx, 30, 80, 3); + option = nk_option_label(ctx, "optionA", option == A) ? A : option; + option = nk_option_label(ctx, "optionB", option == B) ? B : option; + option = nk_option_label(ctx, "optionC", option == C) ? C : option; + + + nk_layout_row(ctx, NK_STATIC, 30, 2, ratio); + nk_labelf(ctx, NK_TEXT_LEFT, "Slider int"); + nk_slider_int(ctx, 0, &int_slider, 10, 1); + + nk_label(ctx, "Slider float", NK_TEXT_LEFT); + nk_slider_float(ctx, 0, &float_slider, 5.0, 0.5f); + nk_labelf(ctx, NK_TEXT_LEFT, "Progressbar" , prog_value); + nk_progress(ctx, &prog_value, 100, NK_MODIFIABLE); + + nk_layout_row(ctx, NK_STATIC, 25, 2, ratio); + nk_label(ctx, "Property float:", NK_TEXT_LEFT); + nk_property_float(ctx, "Float:", 0, &property_float, 64.0f, 0.1f, 0.2f); + nk_label(ctx, "Property int:", NK_TEXT_LEFT); + nk_property_int(ctx, "Int:", 0, &property_int, 100.0f, 1, 1); + nk_label(ctx, "Property neg:", NK_TEXT_LEFT); + nk_property_int(ctx, "Neg:", -10, &property_neg, 10, 1, 1); + + nk_layout_row_dynamic(ctx, 25, 1); + nk_label(ctx, "Range:", NK_TEXT_LEFT); + nk_layout_row_dynamic(ctx, 25, 3); + nk_property_float(ctx, "#min:", 0, &range_float_min, range_float_max, 1.0f, 0.2f); + nk_property_float(ctx, "#float:", range_float_min, &range_float_value, range_float_max, 1.0f, 0.2f); + nk_property_float(ctx, "#max:", range_float_min, &range_float_max, 100, 1.0f, 0.2f); + + nk_property_int(ctx, "#min:", INT_MIN, &range_int_min, range_int_max, 1, 10); + nk_property_int(ctx, "#neg:", range_int_min, &range_int_value, range_int_max, 1, 10); + nk_property_int(ctx, "#max:", range_int_min, &range_int_max, INT_MAX, 1, 10); + + nk_tree_pop(ctx); + } + + if (nk_tree_push(ctx, NK_TREE_NODE, "Selectable", NK_MINIMIZED)) + { + if (nk_tree_push(ctx, NK_TREE_NODE, "List", NK_MINIMIZED)) + { + static int selected[4] = {nk_false, nk_false, nk_true, nk_false}; + nk_layout_row_static(ctx, 18, 100, 1); + nk_selectable_label(ctx, "Selectable", NK_TEXT_LEFT, &selected[0]); + nk_selectable_label(ctx, "Selectable", NK_TEXT_LEFT, &selected[1]); + nk_label(ctx, "Not Selectable", NK_TEXT_LEFT); + nk_selectable_label(ctx, "Selectable", NK_TEXT_LEFT, &selected[2]); + nk_selectable_label(ctx, "Selectable", NK_TEXT_LEFT, &selected[3]); + nk_tree_pop(ctx); + } + if (nk_tree_push(ctx, NK_TREE_NODE, "Grid", NK_MINIMIZED)) + { + int i; + static int selected[16] = {1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1}; + nk_layout_row_static(ctx, 50, 50, 4); + for (i = 0; i < 16; ++i) { + if (nk_selectable_label(ctx, "Z", NK_TEXT_CENTERED, &selected[i])) { + int x = (i % 4), y = i / 4; + if (x > 0) selected[i - 1] ^= 1; + if (x < 3) selected[i + 1] ^= 1; + if (y > 0) selected[i - 4] ^= 1; + if (y < 3) selected[i + 4] ^= 1; + } + } + nk_tree_pop(ctx); + } + nk_tree_pop(ctx); + } + + if (nk_tree_push(ctx, NK_TREE_NODE, "Combo", NK_MINIMIZED)) + { + /* Combobox Widgets + * In this library comboboxes are not limited to being a popup + * list of selectable text. Instead it is a abstract concept of + * having something that is *selected* or displayed, a popup window + * which opens if something needs to be modified and the content + * of the popup which causes the *selected* or displayed value to + * change or if wanted close the combobox. + * + * While strange at first handling comboboxes in a abstract way + * solves the problem of overloaded window content. For example + * changing a color value requires 4 value modifier (slider, property,...) + * for RGBA then you need a label and ways to display the current color. + * If you want to go fancy you even add rgb and hsv ratio boxes. + * While fine for one color if you have a lot of them it because + * tedious to look at and quite wasteful in space. You could add + * a popup which modifies the color but this does not solve the + * fact that it still requires a lot of cluttered space to do. + * + * In these kind of instance abstract comboboxes are quite handy. All + * value modifiers are hidden inside the combobox popup and only + * the color is shown if not open. This combines the clarity of the + * popup with the ease of use of just using the space for modifiers. + * + * Other instances are for example time and especially date picker, + * which only show the currently activated time/data and hide the + * selection logic inside the combobox popup. + */ + static float chart_selection = 8.0f; + static int current_weapon = 0; + static int check_values[5]; + static float position[3]; + static struct nk_color combo_color = {130, 50, 50, 255}; + static struct nk_color combo_color2 = {130, 180, 50, 255}; + static size_t prog_a = 20, prog_b = 40, prog_c = 10, prog_d = 90; + static const char *weapons[] = {"Fist","Pistol","Shotgun","Plasma","BFG"}; + + char buffer[64]; + size_t sum = 0; + + /* default combobox */ + nk_layout_row_static(ctx, 25, 200, 1); + current_weapon = nk_combo(ctx, weapons, LEN(weapons), current_weapon, 25, nk_vec2(200,200)); + + /* slider color combobox */ + if (nk_combo_begin_color(ctx, combo_color, nk_vec2(200,200))) { + float ratios[] = {0.15f, 0.85f}; + nk_layout_row(ctx, NK_DYNAMIC, 30, 2, ratios); + nk_label(ctx, "R:", NK_TEXT_LEFT); + combo_color.r = (nk_byte)nk_slide_int(ctx, 0, combo_color.r, 255, 5); + nk_label(ctx, "G:", NK_TEXT_LEFT); + combo_color.g = (nk_byte)nk_slide_int(ctx, 0, combo_color.g, 255, 5); + nk_label(ctx, "B:", NK_TEXT_LEFT); + combo_color.b = (nk_byte)nk_slide_int(ctx, 0, combo_color.b, 255, 5); + nk_label(ctx, "A:", NK_TEXT_LEFT); + combo_color.a = (nk_byte)nk_slide_int(ctx, 0, combo_color.a , 255, 5); + nk_combo_end(ctx); + } + + /* complex color combobox */ + if (nk_combo_begin_color(ctx, combo_color2, nk_vec2(200,400))) { + enum color_mode {COL_RGB, COL_HSV}; + static int col_mode = COL_RGB; + #ifndef DEMO_DO_NOT_USE_COLOR_PICKER + nk_layout_row_dynamic(ctx, 120, 1); + combo_color2 = nk_color_picker(ctx, combo_color2, NK_RGBA); + #endif + + nk_layout_row_dynamic(ctx, 25, 2); + col_mode = nk_option_label(ctx, "RGB", col_mode == COL_RGB) ? COL_RGB : col_mode; + col_mode = nk_option_label(ctx, "HSV", col_mode == COL_HSV) ? COL_HSV : col_mode; + + nk_layout_row_dynamic(ctx, 25, 1); + if (col_mode == COL_RGB) { + combo_color2.r = (nk_byte)nk_propertyi(ctx, "#R:", 0, combo_color2.r, 255, 1,1); + combo_color2.g = (nk_byte)nk_propertyi(ctx, "#G:", 0, combo_color2.g, 255, 1,1); + combo_color2.b = (nk_byte)nk_propertyi(ctx, "#B:", 0, combo_color2.b, 255, 1,1); + combo_color2.a = (nk_byte)nk_propertyi(ctx, "#A:", 0, combo_color2.a, 255, 1,1); + } else { + nk_byte tmp[4]; + nk_color_hsva_bv(tmp, combo_color2); + tmp[0] = (nk_byte)nk_propertyi(ctx, "#H:", 0, tmp[0], 255, 1,1); + tmp[1] = (nk_byte)nk_propertyi(ctx, "#S:", 0, tmp[1], 255, 1,1); + tmp[2] = (nk_byte)nk_propertyi(ctx, "#V:", 0, tmp[2], 255, 1,1); + tmp[3] = (nk_byte)nk_propertyi(ctx, "#A:", 0, tmp[3], 255, 1,1); + combo_color2 = nk_hsva_bv(tmp); + } + nk_combo_end(ctx); + } + + /* progressbar combobox */ + sum = prog_a + prog_b + prog_c + prog_d; + sprintf(buffer, "%lu", sum); + if (nk_combo_begin_label(ctx, buffer, nk_vec2(200,200))) { + nk_layout_row_dynamic(ctx, 30, 1); + nk_progress(ctx, &prog_a, 100, NK_MODIFIABLE); + nk_progress(ctx, &prog_b, 100, NK_MODIFIABLE); + nk_progress(ctx, &prog_c, 100, NK_MODIFIABLE); + nk_progress(ctx, &prog_d, 100, NK_MODIFIABLE); + nk_combo_end(ctx); + } + + /* checkbox combobox */ + sum = (size_t)(check_values[0] + check_values[1] + check_values[2] + check_values[3] + check_values[4]); + sprintf(buffer, "%lu", sum); + if (nk_combo_begin_label(ctx, buffer, nk_vec2(200,200))) { + nk_layout_row_dynamic(ctx, 30, 1); + nk_checkbox_label(ctx, weapons[0], &check_values[0]); + nk_checkbox_label(ctx, weapons[1], &check_values[1]); + nk_checkbox_label(ctx, weapons[2], &check_values[2]); + nk_checkbox_label(ctx, weapons[3], &check_values[3]); + nk_combo_end(ctx); + } + + /* complex text combobox */ + sprintf(buffer, "%.2f, %.2f, %.2f", position[0], position[1],position[2]); + if (nk_combo_begin_label(ctx, buffer, nk_vec2(200,200))) { + nk_layout_row_dynamic(ctx, 25, 1); + nk_property_float(ctx, "#X:", -1024.0f, &position[0], 1024.0f, 1,0.5f); + nk_property_float(ctx, "#Y:", -1024.0f, &position[1], 1024.0f, 1,0.5f); + nk_property_float(ctx, "#Z:", -1024.0f, &position[2], 1024.0f, 1,0.5f); + nk_combo_end(ctx); + } + + /* chart combobox */ + sprintf(buffer, "%.1f", chart_selection); + if (nk_combo_begin_label(ctx, buffer, nk_vec2(200,250))) { + size_t i = 0; + static const float values[]={26.0f,13.0f,30.0f,15.0f,25.0f,10.0f,20.0f,40.0f, 12.0f, 8.0f, 22.0f, 28.0f, 5.0f}; + nk_layout_row_dynamic(ctx, 150, 1); + nk_chart_begin(ctx, NK_CHART_COLUMN, LEN(values), 0, 50); + for (i = 0; i < LEN(values); ++i) { + nk_flags res = nk_chart_push(ctx, values[i]); + if (res & NK_CHART_CLICKED) { + chart_selection = values[i]; + nk_combo_close(ctx); + } + } + nk_chart_end(ctx); + nk_combo_end(ctx); + } + + { + static int time_selected = 0; + static int date_selected = 0; + static struct tm sel_time; + static struct tm sel_date; + if (!time_selected || !date_selected) { + /* keep time and date updated if nothing is selected */ + time_t cur_time = time(0); + struct tm *n = localtime(&cur_time); + if (!time_selected) + memcpy(&sel_time, n, sizeof(struct tm)); + if (!date_selected) + memcpy(&sel_date, n, sizeof(struct tm)); + } + + /* time combobox */ + sprintf(buffer, "%02d:%02d:%02d", sel_time.tm_hour, sel_time.tm_min, sel_time.tm_sec); + if (nk_combo_begin_label(ctx, buffer, nk_vec2(200,250))) { + time_selected = 1; + nk_layout_row_dynamic(ctx, 25, 1); + sel_time.tm_sec = nk_propertyi(ctx, "#S:", 0, sel_time.tm_sec, 60, 1, 1); + sel_time.tm_min = nk_propertyi(ctx, "#M:", 0, sel_time.tm_min, 60, 1, 1); + sel_time.tm_hour = nk_propertyi(ctx, "#H:", 0, sel_time.tm_hour, 23, 1, 1); + nk_combo_end(ctx); + } + + /* date combobox */ + sprintf(buffer, "%02d-%02d-%02d", sel_date.tm_mday, sel_date.tm_mon+1, sel_date.tm_year+1900); + if (nk_combo_begin_label(ctx, buffer, nk_vec2(350,400))) + { + int i = 0; + const char *month[] = {"January", "February", "March", "Apil", "May", "June", "July", "August", "September", "Ocotober", "November", "December"}; + const char *week_days[] = {"SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"}; + const int month_days[] = {31,28,31,30,31,30,31,31,30,31,30,31}; + int year = sel_date.tm_year+1900; + int leap_year = (!(year % 4) && ((year % 100))) || !(year % 400); + int days = (sel_date.tm_mon == 1) ? + month_days[sel_date.tm_mon] + leap_year: + month_days[sel_date.tm_mon]; + + /* header with month and year */ + date_selected = 1; + nk_layout_row_begin(ctx, NK_DYNAMIC, 20, 3); + nk_layout_row_push(ctx, 0.05f); + if (nk_button_symbol(ctx, NK_SYMBOL_TRIANGLE_LEFT)) { + if (sel_date.tm_mon == 0) { + sel_date.tm_mon = 11; + sel_date.tm_year = MAX(0, sel_date.tm_year-1); + } else sel_date.tm_mon--; + } + nk_layout_row_push(ctx, 0.9f); + sprintf(buffer, "%s %d", month[sel_date.tm_mon], year); + nk_label(ctx, buffer, NK_TEXT_CENTERED); + nk_layout_row_push(ctx, 0.05f); + if (nk_button_symbol(ctx, NK_SYMBOL_TRIANGLE_RIGHT)) { + if (sel_date.tm_mon == 11) { + sel_date.tm_mon = 0; + sel_date.tm_year++; + } else sel_date.tm_mon++; + } + nk_layout_row_end(ctx); + + /* good old week day formula (double because precision) */ + {int year_n = (sel_date.tm_mon < 2) ? year-1: year; + int y = year_n % 100; + int c = year_n / 100; + int y4 = (int)((float)y / 4); + int c4 = (int)((float)c / 4); + int m = (int)(2.6 * (double)(((sel_date.tm_mon + 10) % 12) + 1) - 0.2); + int week_day = (((1 + m + y + y4 + c4 - 2 * c) % 7) + 7) % 7; + + /* weekdays */ + nk_layout_row_dynamic(ctx, 35, 7); + for (i = 0; i < (int)LEN(week_days); ++i) + nk_label(ctx, week_days[i], NK_TEXT_CENTERED); + + /* days */ + if (week_day > 0) nk_spacing(ctx, week_day); + for (i = 1; i <= days; ++i) { + sprintf(buffer, "%d", i); + if (nk_button_label(ctx, buffer)) { + sel_date.tm_mday = i; + nk_combo_close(ctx); + } + }} + nk_combo_end(ctx); + } + } + + nk_tree_pop(ctx); + } + + if (nk_tree_push(ctx, NK_TREE_NODE, "Input", NK_MINIMIZED)) + { + static const float ratio[] = {120, 150}; + static char field_buffer[64]; + static char text[9][64]; + static int text_len[9]; + static char box_buffer[512]; + static int field_len; + static int box_len; + nk_flags active; + + nk_layout_row(ctx, NK_STATIC, 25, 2, ratio); + nk_label(ctx, "Default:", NK_TEXT_LEFT); + + nk_edit_string(ctx, NK_EDIT_SIMPLE, text[0], &text_len[0], 64, nk_filter_default); + nk_label(ctx, "Int:", NK_TEXT_LEFT); + nk_edit_string(ctx, NK_EDIT_SIMPLE, text[1], &text_len[1], 64, nk_filter_decimal); + nk_label(ctx, "Float:", NK_TEXT_LEFT); + nk_edit_string(ctx, NK_EDIT_SIMPLE, text[2], &text_len[2], 64, nk_filter_float); + nk_label(ctx, "Hex:", NK_TEXT_LEFT); + nk_edit_string(ctx, NK_EDIT_SIMPLE, text[4], &text_len[4], 64, nk_filter_hex); + nk_label(ctx, "Octal:", NK_TEXT_LEFT); + nk_edit_string(ctx, NK_EDIT_SIMPLE, text[5], &text_len[5], 64, nk_filter_oct); + nk_label(ctx, "Binary:", NK_TEXT_LEFT); + nk_edit_string(ctx, NK_EDIT_SIMPLE, text[6], &text_len[6], 64, nk_filter_binary); + + nk_label(ctx, "Password:", NK_TEXT_LEFT); + { + int i = 0; + int old_len = text_len[8]; + char buffer[64]; + for (i = 0; i < text_len[8]; ++i) buffer[i] = '*'; + nk_edit_string(ctx, NK_EDIT_FIELD, buffer, &text_len[8], 64, nk_filter_default); + if (old_len < text_len[8]) + memcpy(&text[8][old_len], &buffer[old_len], (nk_size)(text_len[8] - old_len)); + } + + nk_label(ctx, "Field:", NK_TEXT_LEFT); + nk_edit_string(ctx, NK_EDIT_FIELD, field_buffer, &field_len, 64, nk_filter_default); + + nk_label(ctx, "Box:", NK_TEXT_LEFT); + nk_layout_row_static(ctx, 180, 278, 1); + nk_edit_string(ctx, NK_EDIT_BOX, box_buffer, &box_len, 512, nk_filter_default); + + nk_layout_row(ctx, NK_STATIC, 25, 2, ratio); + active = nk_edit_string(ctx, NK_EDIT_FIELD|NK_EDIT_SIG_ENTER, text[7], &text_len[7], 64, nk_filter_ascii); + if (nk_button_label(ctx, "Submit") || + (active & NK_EDIT_COMMITED)) + { + text[7][text_len[7]] = '\n'; + text_len[7]++; + memcpy(&box_buffer[box_len], &text[7], (nk_size)text_len[7]); + box_len += text_len[7]; + text_len[7] = 0; + } + nk_layout_row_end(ctx); + nk_tree_pop(ctx); + } + nk_tree_pop(ctx); + } + + if (nk_tree_push(ctx, NK_TREE_TAB, "Chart", NK_MINIMIZED)) + { + /* Chart Widgets + * This library has two different rather simple charts. The line and the + * column chart. Both provide a simple way of visualizing values and + * have a retained mode and immediate mode API version. For the retain + * mode version `nk_plot` and `nk_plot_function` you either provide + * an array or a callback to call to handle drawing the graph. + * For the immediate mode version you start by calling `nk_chart_begin` + * and need to provide min and max values for scaling on the Y-axis. + * and then call `nk_chart_push` to push values into the chart. + * Finally `nk_chart_end` needs to be called to end the process. */ + float id = 0; + static int col_index = -1; + static int line_index = -1; + float step = (2*3.141592654f) / 32; + + int i; + int index = -1; + struct nk_rect bounds; + + /* line chart */ + id = 0; + index = -1; + nk_layout_row_dynamic(ctx, 100, 1); + bounds = nk_widget_bounds(ctx); + if (nk_chart_begin(ctx, NK_CHART_LINES, 32, -1.0f, 1.0f)) { + for (i = 0; i < 32; ++i) { + nk_flags res = nk_chart_push(ctx, (float)cos(id)); + if (res & NK_CHART_HOVERING) + index = (int)i; + if (res & NK_CHART_CLICKED) + line_index = (int)i; + id += step; + } + nk_chart_end(ctx); + } + + if (index != -1) { + char buffer[NK_MAX_NUMBER_BUFFER]; + float val = (float)cos((float)index*step); + sprintf(buffer, "Value: %.2f", val); + nk_tooltip(ctx, buffer); + } + if (line_index != -1) { + nk_layout_row_dynamic(ctx, 20, 1); + nk_labelf(ctx, NK_TEXT_LEFT, "Selected value: %.2f", (float)cos((float)index*step)); + } + + /* column chart */ + nk_layout_row_dynamic(ctx, 100, 1); + bounds = nk_widget_bounds(ctx); + if (nk_chart_begin(ctx, NK_CHART_COLUMN, 32, 0.0f, 1.0f)) { + for (i = 0; i < 32; ++i) { + nk_flags res = nk_chart_push(ctx, (float)fabs(sin(id))); + if (res & NK_CHART_HOVERING) + index = (int)i; + if (res & NK_CHART_CLICKED) + col_index = (int)i; + id += step; + } + nk_chart_end(ctx); + } + if (index != -1) { + char buffer[NK_MAX_NUMBER_BUFFER]; + sprintf(buffer, "Value: %.2f", (float)fabs(sin(step * (float)index))); + nk_tooltip(ctx, buffer); + } + if (col_index != -1) { + nk_layout_row_dynamic(ctx, 20, 1); + nk_labelf(ctx, NK_TEXT_LEFT, "Selected value: %.2f", (float)fabs(sin(step * (float)col_index))); + } + + /* mixed chart */ + nk_layout_row_dynamic(ctx, 100, 1); + bounds = nk_widget_bounds(ctx); + if (nk_chart_begin(ctx, NK_CHART_COLUMN, 32, 0.0f, 1.0f)) { + nk_chart_add_slot(ctx, NK_CHART_LINES, 32, -1.0f, 1.0f); + nk_chart_add_slot(ctx, NK_CHART_LINES, 32, -1.0f, 1.0f); + for (id = 0, i = 0; i < 32; ++i) { + nk_chart_push_slot(ctx, (float)fabs(sin(id)), 0); + nk_chart_push_slot(ctx, (float)cos(id), 1); + nk_chart_push_slot(ctx, (float)sin(id), 2); + id += step; + } + } + nk_chart_end(ctx); + + /* mixed colored chart */ + nk_layout_row_dynamic(ctx, 100, 1); + bounds = nk_widget_bounds(ctx); + if (nk_chart_begin_colored(ctx, NK_CHART_LINES, nk_rgb(255,0,0), nk_rgb(150,0,0), 32, 0.0f, 1.0f)) { + nk_chart_add_slot_colored(ctx, NK_CHART_LINES, nk_rgb(0,0,255), nk_rgb(0,0,150),32, -1.0f, 1.0f); + nk_chart_add_slot_colored(ctx, NK_CHART_LINES, nk_rgb(0,255,0), nk_rgb(0,150,0), 32, -1.0f, 1.0f); + for (id = 0, i = 0; i < 32; ++i) { + nk_chart_push_slot(ctx, (float)fabs(sin(id)), 0); + nk_chart_push_slot(ctx, (float)cos(id), 1); + nk_chart_push_slot(ctx, (float)sin(id), 2); + id += step; + } + } + nk_chart_end(ctx); + nk_tree_pop(ctx); + } + + if (nk_tree_push(ctx, NK_TREE_TAB, "Popup", NK_MINIMIZED)) + { + static struct nk_color color = {255,0,0, 255}; + static int select[4]; + static int popup_active; + const struct nk_input *in = &ctx->input; + struct nk_rect bounds; + + /* menu contextual */ + nk_layout_row_static(ctx, 30, 150, 1); + bounds = nk_widget_bounds(ctx); + nk_label(ctx, "Right click me for menu", NK_TEXT_LEFT); + + if (nk_contextual_begin(ctx, 0, nk_vec2(100, 300), bounds)) { + static size_t prog = 40; + static int slider = 10; + + nk_layout_row_dynamic(ctx, 25, 1); + nk_checkbox_label(ctx, "Menu", &show_menu); + nk_progress(ctx, &prog, 100, NK_MODIFIABLE); + nk_slider_int(ctx, 0, &slider, 16, 1); + if (nk_contextual_item_label(ctx, "About", NK_TEXT_CENTERED)) + show_app_about = nk_true; + nk_selectable_label(ctx, select[0]?"Unselect":"Select", NK_TEXT_LEFT, &select[0]); + nk_selectable_label(ctx, select[1]?"Unselect":"Select", NK_TEXT_LEFT, &select[1]); + nk_selectable_label(ctx, select[2]?"Unselect":"Select", NK_TEXT_LEFT, &select[2]); + nk_selectable_label(ctx, select[3]?"Unselect":"Select", NK_TEXT_LEFT, &select[3]); + nk_contextual_end(ctx); + } + + /* color contextual */ + nk_layout_row_begin(ctx, NK_STATIC, 30, 2); + nk_layout_row_push(ctx, 100); + nk_label(ctx, "Right Click here:", NK_TEXT_LEFT); + nk_layout_row_push(ctx, 50); + bounds = nk_widget_bounds(ctx); + nk_button_color(ctx, color); + nk_layout_row_end(ctx); + + if (nk_contextual_begin(ctx, 0, nk_vec2(350, 60), bounds)) { + nk_layout_row_dynamic(ctx, 30, 4); + color.r = (nk_byte)nk_propertyi(ctx, "#r", 0, color.r, 255, 1, 1); + color.g = (nk_byte)nk_propertyi(ctx, "#g", 0, color.g, 255, 1, 1); + color.b = (nk_byte)nk_propertyi(ctx, "#b", 0, color.b, 255, 1, 1); + color.a = (nk_byte)nk_propertyi(ctx, "#a", 0, color.a, 255, 1, 1); + nk_contextual_end(ctx); + } + + /* popup */ + nk_layout_row_begin(ctx, NK_STATIC, 30, 2); + nk_layout_row_push(ctx, 100); + nk_label(ctx, "Popup:", NK_TEXT_LEFT); + nk_layout_row_push(ctx, 50); + if (nk_button_label(ctx, "Popup")) + popup_active = 1; + nk_layout_row_end(ctx); + + if (popup_active) + { + static struct nk_rect s = {20, 100, 220, 90}; + if (nk_popup_begin(ctx, NK_POPUP_STATIC, "Error", 0, s)) + { + nk_layout_row_dynamic(ctx, 25, 1); + nk_label(ctx, "A terrible error as occured", NK_TEXT_LEFT); + nk_layout_row_dynamic(ctx, 25, 2); + if (nk_button_label(ctx, "OK")) { + popup_active = 0; + nk_popup_close(ctx); + } + if (nk_button_label(ctx, "Cancel")) { + popup_active = 0; + nk_popup_close(ctx); + } + nk_popup_end(ctx); + } else popup_active = nk_false; + } + + /* tooltip */ + nk_layout_row_static(ctx, 30, 150, 1); + bounds = nk_widget_bounds(ctx); + nk_label(ctx, "Hover me for tooltip", NK_TEXT_LEFT); + if (nk_input_is_mouse_hovering_rect(in, bounds)) + nk_tooltip(ctx, "This is a tooltip"); + + nk_tree_pop(ctx); + } + + if (nk_tree_push(ctx, NK_TREE_TAB, "Layout", NK_MINIMIZED)) + { + if (nk_tree_push(ctx, NK_TREE_NODE, "Widget", NK_MINIMIZED)) + { + float ratio_two[] = {0.2f, 0.6f, 0.2f}; + float width_two[] = {100, 200, 50}; + + nk_layout_row_dynamic(ctx, 30, 1); + nk_label(ctx, "Dynamic fixed column layout with generated position and size:", NK_TEXT_LEFT); + nk_layout_row_dynamic(ctx, 30, 3); + nk_button_label(ctx, "button"); + nk_button_label(ctx, "button"); + nk_button_label(ctx, "button"); + + nk_layout_row_dynamic(ctx, 30, 1); + nk_label(ctx, "static fixed column layout with generated position and size:", NK_TEXT_LEFT); + nk_layout_row_static(ctx, 30, 100, 3); + nk_button_label(ctx, "button"); + nk_button_label(ctx, "button"); + nk_button_label(ctx, "button"); + + nk_layout_row_dynamic(ctx, 30, 1); + nk_label(ctx, "Dynamic array-based custom column layout with generated position and custom size:",NK_TEXT_LEFT); + nk_layout_row(ctx, NK_DYNAMIC, 30, 3, ratio_two); + nk_button_label(ctx, "button"); + nk_button_label(ctx, "button"); + nk_button_label(ctx, "button"); + + nk_layout_row_dynamic(ctx, 30, 1); + nk_label(ctx, "Static array-based custom column layout with generated position and custom size:",NK_TEXT_LEFT ); + nk_layout_row(ctx, NK_STATIC, 30, 3, width_two); + nk_button_label(ctx, "button"); + nk_button_label(ctx, "button"); + nk_button_label(ctx, "button"); + + nk_layout_row_dynamic(ctx, 30, 1); + nk_label(ctx, "Dynamic immediate mode custom column layout with generated position and custom size:",NK_TEXT_LEFT); + nk_layout_row_begin(ctx, NK_DYNAMIC, 30, 3); + nk_layout_row_push(ctx, 0.2f); + nk_button_label(ctx, "button"); + nk_layout_row_push(ctx, 0.6f); + nk_button_label(ctx, "button"); + nk_layout_row_push(ctx, 0.2f); + nk_button_label(ctx, "button"); + nk_layout_row_end(ctx); + + nk_layout_row_dynamic(ctx, 30, 1); + nk_label(ctx, "Static immediate mode custom column layout with generated position and custom size:", NK_TEXT_LEFT); + nk_layout_row_begin(ctx, NK_STATIC, 30, 3); + nk_layout_row_push(ctx, 100); + nk_button_label(ctx, "button"); + nk_layout_row_push(ctx, 200); + nk_button_label(ctx, "button"); + nk_layout_row_push(ctx, 50); + nk_button_label(ctx, "button"); + nk_layout_row_end(ctx); + + nk_layout_row_dynamic(ctx, 30, 1); + nk_label(ctx, "Static free space with custom position and custom size:", NK_TEXT_LEFT); + nk_layout_space_begin(ctx, NK_STATIC, 120, 4); + nk_layout_space_push(ctx, nk_rect(100, 0, 100, 30)); + nk_button_label(ctx, "button"); + nk_layout_space_push(ctx, nk_rect(0, 15, 100, 30)); + nk_button_label(ctx, "button"); + nk_layout_space_push(ctx, nk_rect(200, 15, 100, 30)); + nk_button_label(ctx, "button"); + nk_layout_space_push(ctx, nk_rect(100, 30, 100, 30)); + nk_button_label(ctx, "button"); + nk_layout_space_end(ctx); + nk_tree_pop(ctx); + } + + if (nk_tree_push(ctx, NK_TREE_NODE, "Group", NK_MINIMIZED)) + { + static int group_titlebar = nk_false; + static int group_border = nk_true; + static int group_no_scrollbar = nk_false; + static int group_width = 320; + static int group_height = 200; + + nk_flags group_flags = 0; + if (group_border) group_flags |= NK_WINDOW_BORDER; + if (group_no_scrollbar) group_flags |= NK_WINDOW_NO_SCROLLBAR; + if (group_titlebar) group_flags |= NK_WINDOW_TITLE; + + nk_layout_row_dynamic(ctx, 30, 3); + nk_checkbox_label(ctx, "Titlebar", &group_titlebar); + nk_checkbox_label(ctx, "Border", &group_border); + nk_checkbox_label(ctx, "No Scrollbar", &group_no_scrollbar); + + nk_layout_row_begin(ctx, NK_STATIC, 22, 2); + nk_layout_row_push(ctx, 50); + nk_label(ctx, "size:", NK_TEXT_LEFT); + nk_layout_row_push(ctx, 130); + nk_property_int(ctx, "#Width:", 100, &group_width, 500, 10, 1); + nk_layout_row_push(ctx, 130); + nk_property_int(ctx, "#Height:", 100, &group_height, 500, 10, 1); + nk_layout_row_end(ctx); + + nk_layout_row_static(ctx, (float)group_height, group_width, 2); + if (nk_group_begin(ctx, "Group", group_flags)) { + int i = 0; + static int selected[16]; + nk_layout_row_static(ctx, 18, 100, 1); + for (i = 0; i < 16; ++i) + nk_selectable_label(ctx, (selected[i]) ? "Selected": "Unselected", NK_TEXT_CENTERED, &selected[i]); + nk_group_end(ctx); + } + nk_tree_pop(ctx); + } + + if (nk_tree_push(ctx, NK_TREE_NODE, "Notebook", NK_MINIMIZED)) + { + static int current_tab = 0; + struct nk_vec2 item_padding; + struct nk_rect bounds; + float step = (2*3.141592654f) / 32; + enum chart_type {CHART_LINE, CHART_HISTO, CHART_MIXED}; + const char *names[] = {"Lines", "Columns", "Mixed"}; + float id = 0; + int i; + + /* Header */ + nk_style_push_vec2(ctx, &ctx->style.window.spacing, nk_vec2(0,0)); + nk_style_push_float(ctx, &ctx->style.button.rounding, 0); + nk_layout_row_begin(ctx, NK_STATIC, 20, 3); + for (i = 0; i < 3; ++i) { + /* make sure button perfectly fits text */ + const struct nk_user_font *f = ctx->style.font; + float text_width = f->width(f->userdata, f->height, names[i], nk_strlen(names[i])); + float widget_width = text_width + 3 * ctx->style.button.padding.x; + nk_layout_row_push(ctx, widget_width); + if (current_tab == i) { + /* active tab gets highlighted */ + struct nk_style_item button_color = ctx->style.button.normal; + ctx->style.button.normal = ctx->style.button.active; + current_tab = nk_button_label(ctx, names[i]) ? i: current_tab; + ctx->style.button.normal = button_color; + } else current_tab = nk_button_label(ctx, names[i]) ? i: current_tab; + } + nk_style_pop_float(ctx); + + /* Body */ + nk_layout_row_dynamic(ctx, 140, 1); + if (nk_group_begin(ctx, "Notebook", NK_WINDOW_BORDER)) + { + nk_style_pop_vec2(ctx); + switch (current_tab) { + case CHART_LINE: + nk_layout_row_dynamic(ctx, 100, 1); + bounds = nk_widget_bounds(ctx); + if (nk_chart_begin_colored(ctx, NK_CHART_LINES, nk_rgb(255,0,0), nk_rgb(150,0,0), 32, 0.0f, 1.0f)) { + nk_chart_add_slot_colored(ctx, NK_CHART_LINES, nk_rgb(0,0,255), nk_rgb(0,0,150),32, -1.0f, 1.0f); + for (i = 0, id = 0; i < 32; ++i) { + nk_chart_push_slot(ctx, (float)fabs(sin(id)), 0); + nk_chart_push_slot(ctx, (float)cos(id), 1); + id += step; + } + } + nk_chart_end(ctx); + break; + case CHART_HISTO: + nk_layout_row_dynamic(ctx, 100, 1); + bounds = nk_widget_bounds(ctx); + if (nk_chart_begin_colored(ctx, NK_CHART_COLUMN, nk_rgb(255,0,0), nk_rgb(150,0,0), 32, 0.0f, 1.0f)) { + for (i = 0, id = 0; i < 32; ++i) { + nk_chart_push_slot(ctx, (float)fabs(sin(id)), 0); + id += step; + } + } + nk_chart_end(ctx); + break; + case CHART_MIXED: + nk_layout_row_dynamic(ctx, 100, 1); + bounds = nk_widget_bounds(ctx); + if (nk_chart_begin_colored(ctx, NK_CHART_LINES, nk_rgb(255,0,0), nk_rgb(150,0,0), 32, 0.0f, 1.0f)) { + nk_chart_add_slot_colored(ctx, NK_CHART_LINES, nk_rgb(0,0,255), nk_rgb(0,0,150),32, -1.0f, 1.0f); + nk_chart_add_slot_colored(ctx, NK_CHART_COLUMN, nk_rgb(0,255,0), nk_rgb(0,150,0), 32, 0.0f, 1.0f); + for (i = 0, id = 0; i < 32; ++i) { + nk_chart_push_slot(ctx, (float)fabs(sin(id)), 0); + nk_chart_push_slot(ctx, (float)fabs(cos(id)), 1); + nk_chart_push_slot(ctx, (float)fabs(sin(id)), 2); + id += step; + } + } + nk_chart_end(ctx); + break; + } + nk_group_end(ctx); + } else nk_style_pop_vec2(ctx); + nk_tree_pop(ctx); + } + + if (nk_tree_push(ctx, NK_TREE_NODE, "Simple", NK_MINIMIZED)) + { + nk_layout_row_dynamic(ctx, 300, 2); + if (nk_group_begin(ctx, "Group_Without_Border", 0)) { + int i = 0; + char buffer[64]; + nk_layout_row_static(ctx, 18, 150, 1); + for (i = 0; i < 64; ++i) { + sprintf(buffer, "0x%02x", i); + nk_labelf(ctx, NK_TEXT_LEFT, "%s: scrollable region", buffer); + } + nk_group_end(ctx); + } + if (nk_group_begin(ctx, "Group_With_Border", NK_WINDOW_BORDER)) { + int i = 0; + char buffer[64]; + nk_layout_row_dynamic(ctx, 25, 2); + for (i = 0; i < 64; ++i) { + sprintf(buffer, "%08d", ((((i%7)*10)^32))+(64+(i%2)*2)); + nk_button_label(ctx, buffer); + } + nk_group_end(ctx); + } + nk_tree_pop(ctx); + } + + if (nk_tree_push(ctx, NK_TREE_NODE, "Complex", NK_MINIMIZED)) + { + int i; + nk_layout_space_begin(ctx, NK_STATIC, 500, 64); + nk_layout_space_push(ctx, nk_rect(0,0,150,500)); + if (nk_group_begin(ctx, "Group_left", NK_WINDOW_BORDER)) { + static int selected[32]; + nk_layout_row_static(ctx, 18, 100, 1); + for (i = 0; i < 32; ++i) + nk_selectable_label(ctx, (selected[i]) ? "Selected": "Unselected", NK_TEXT_CENTERED, &selected[i]); + nk_group_end(ctx); + } + + nk_layout_space_push(ctx, nk_rect(160,0,150,240)); + if (nk_group_begin(ctx, "Group_top", NK_WINDOW_BORDER)) { + nk_layout_row_dynamic(ctx, 25, 1); + nk_button_label(ctx, "#FFAA"); + nk_button_label(ctx, "#FFBB"); + nk_button_label(ctx, "#FFCC"); + nk_button_label(ctx, "#FFDD"); + nk_button_label(ctx, "#FFEE"); + nk_button_label(ctx, "#FFFF"); + nk_group_end(ctx); + } + + nk_layout_space_push(ctx, nk_rect(160,250,150,250)); + if (nk_group_begin(ctx, "Group_buttom", NK_WINDOW_BORDER)) { + nk_layout_row_dynamic(ctx, 25, 1); + nk_button_label(ctx, "#FFAA"); + nk_button_label(ctx, "#FFBB"); + nk_button_label(ctx, "#FFCC"); + nk_button_label(ctx, "#FFDD"); + nk_button_label(ctx, "#FFEE"); + nk_button_label(ctx, "#FFFF"); + nk_group_end(ctx); + } + + nk_layout_space_push(ctx, nk_rect(320,0,150,150)); + if (nk_group_begin(ctx, "Group_right_top", NK_WINDOW_BORDER)) { + static int selected[4]; + nk_layout_row_static(ctx, 18, 100, 1); + for (i = 0; i < 4; ++i) + nk_selectable_label(ctx, (selected[i]) ? "Selected": "Unselected", NK_TEXT_CENTERED, &selected[i]); + nk_group_end(ctx); + } + + nk_layout_space_push(ctx, nk_rect(320,160,150,150)); + if (nk_group_begin(ctx, "Group_right_center", NK_WINDOW_BORDER)) { + static int selected[4]; + nk_layout_row_static(ctx, 18, 100, 1); + for (i = 0; i < 4; ++i) + nk_selectable_label(ctx, (selected[i]) ? "Selected": "Unselected", NK_TEXT_CENTERED, &selected[i]); + nk_group_end(ctx); + } + + nk_layout_space_push(ctx, nk_rect(320,320,150,150)); + if (nk_group_begin(ctx, "Group_right_bottom", NK_WINDOW_BORDER)) { + static int selected[4]; + nk_layout_row_static(ctx, 18, 100, 1); + for (i = 0; i < 4; ++i) + nk_selectable_label(ctx, (selected[i]) ? "Selected": "Unselected", NK_TEXT_CENTERED, &selected[i]); + nk_group_end(ctx); + } + nk_layout_space_end(ctx); + nk_tree_pop(ctx); + } + + if (nk_tree_push(ctx, NK_TREE_NODE, "Splitter", NK_MINIMIZED)) + { + const struct nk_input *in = &ctx->input; + nk_layout_row_static(ctx, 20, 320, 1); + nk_label(ctx, "Use slider and spinner to change tile size", NK_TEXT_LEFT); + nk_label(ctx, "Drag the space between tiles to change tile ratio", NK_TEXT_LEFT); + + if (nk_tree_push(ctx, NK_TREE_NODE, "Vertical", NK_MINIMIZED)) + { + static float a = 100, b = 100, c = 100; + struct nk_rect bounds; + + float row_layout[5]; + row_layout[0] = a; + row_layout[1] = 8; + row_layout[2] = b; + row_layout[3] = 8; + row_layout[4] = c; + + /* header */ + nk_layout_row_static(ctx, 30, 100, 2); + nk_label(ctx, "left:", NK_TEXT_LEFT); + nk_slider_float(ctx, 10.0f, &a, 200.0f, 10.0f); + + nk_label(ctx, "middle:", NK_TEXT_LEFT); + nk_slider_float(ctx, 10.0f, &b, 200.0f, 10.0f); + + nk_label(ctx, "right:", NK_TEXT_LEFT); + nk_slider_float(ctx, 10.0f, &c, 200.0f, 10.0f); + + /* tiles */ + nk_layout_row(ctx, NK_STATIC, 200, 5, row_layout); + + /* left space */ + if (nk_group_begin(ctx, "left", NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_BORDER|NK_WINDOW_NO_SCROLLBAR)) { + nk_layout_row_dynamic(ctx, 25, 1); + nk_button_label(ctx, "#FFAA"); + nk_button_label(ctx, "#FFBB"); + nk_button_label(ctx, "#FFCC"); + nk_button_label(ctx, "#FFDD"); + nk_button_label(ctx, "#FFEE"); + nk_button_label(ctx, "#FFFF"); + nk_group_end(ctx); + } + + /* scaler */ + bounds = nk_widget_bounds(ctx); + nk_spacing(ctx, 1); + if ((nk_input_is_mouse_hovering_rect(in, bounds) || + nk_input_is_mouse_prev_hovering_rect(in, bounds)) && + nk_input_is_mouse_down(in, NK_BUTTON_LEFT)) + { + a = row_layout[0] + in->mouse.delta.x; + b = row_layout[2] - in->mouse.delta.x; + } + + /* middle space */ + if (nk_group_begin(ctx, "center", NK_WINDOW_BORDER|NK_WINDOW_NO_SCROLLBAR)) { + nk_layout_row_dynamic(ctx, 25, 1); + nk_button_label(ctx, "#FFAA"); + nk_button_label(ctx, "#FFBB"); + nk_button_label(ctx, "#FFCC"); + nk_button_label(ctx, "#FFDD"); + nk_button_label(ctx, "#FFEE"); + nk_button_label(ctx, "#FFFF"); + nk_group_end(ctx); + } + + /* scaler */ + bounds = nk_widget_bounds(ctx); + nk_spacing(ctx, 1); + if ((nk_input_is_mouse_hovering_rect(in, bounds) || + nk_input_is_mouse_prev_hovering_rect(in, bounds)) && + nk_input_is_mouse_down(in, NK_BUTTON_LEFT)) + { + b = (row_layout[2] + in->mouse.delta.x); + c = (row_layout[4] - in->mouse.delta.x); + } + + /* right space */ + if (nk_group_begin(ctx, "right", NK_WINDOW_BORDER|NK_WINDOW_NO_SCROLLBAR)) { + nk_layout_row_dynamic(ctx, 25, 1); + nk_button_label(ctx, "#FFAA"); + nk_button_label(ctx, "#FFBB"); + nk_button_label(ctx, "#FFCC"); + nk_button_label(ctx, "#FFDD"); + nk_button_label(ctx, "#FFEE"); + nk_button_label(ctx, "#FFFF"); + nk_group_end(ctx); + } + + nk_tree_pop(ctx); + } + + if (nk_tree_push(ctx, NK_TREE_NODE, "Horizontal", NK_MINIMIZED)) + { + static float a = 100, b = 100, c = 100; + struct nk_rect bounds; + + /* header */ + nk_layout_row_static(ctx, 30, 100, 2); + nk_label(ctx, "top:", NK_TEXT_LEFT); + nk_slider_float(ctx, 10.0f, &a, 200.0f, 10.0f); + + nk_label(ctx, "middle:", NK_TEXT_LEFT); + nk_slider_float(ctx, 10.0f, &b, 200.0f, 10.0f); + + nk_label(ctx, "bottom:", NK_TEXT_LEFT); + nk_slider_float(ctx, 10.0f, &c, 200.0f, 10.0f); + + /* top space */ + nk_layout_row_dynamic(ctx, a, 1); + if (nk_group_begin(ctx, "top", NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_BORDER)) { + nk_layout_row_dynamic(ctx, 25, 3); + nk_button_label(ctx, "#FFAA"); + nk_button_label(ctx, "#FFBB"); + nk_button_label(ctx, "#FFCC"); + nk_button_label(ctx, "#FFDD"); + nk_button_label(ctx, "#FFEE"); + nk_button_label(ctx, "#FFFF"); + nk_group_end(ctx); + } + + /* scaler */ + nk_layout_row_dynamic(ctx, 8, 1); + bounds = nk_widget_bounds(ctx); + nk_spacing(ctx, 1); + if ((nk_input_is_mouse_hovering_rect(in, bounds) || + nk_input_is_mouse_prev_hovering_rect(in, bounds)) && + nk_input_is_mouse_down(in, NK_BUTTON_LEFT)) + { + a = a + in->mouse.delta.y; + b = b - in->mouse.delta.y; + } + + /* middle space */ + nk_layout_row_dynamic(ctx, b, 1); + if (nk_group_begin(ctx, "middle", NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_BORDER)) { + nk_layout_row_dynamic(ctx, 25, 3); + nk_button_label(ctx, "#FFAA"); + nk_button_label(ctx, "#FFBB"); + nk_button_label(ctx, "#FFCC"); + nk_button_label(ctx, "#FFDD"); + nk_button_label(ctx, "#FFEE"); + nk_button_label(ctx, "#FFFF"); + nk_group_end(ctx); + } + + { + /* scaler */ + nk_layout_row_dynamic(ctx, 8, 1); + bounds = nk_widget_bounds(ctx); + if ((nk_input_is_mouse_hovering_rect(in, bounds) || + nk_input_is_mouse_prev_hovering_rect(in, bounds)) && + nk_input_is_mouse_down(in, NK_BUTTON_LEFT)) + { + b = b + in->mouse.delta.y; + c = c - in->mouse.delta.y; + } + } + + /* bottom space */ + nk_layout_row_dynamic(ctx, c, 1); + if (nk_group_begin(ctx, "bottom", NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_BORDER)) { + nk_layout_row_dynamic(ctx, 25, 3); + nk_button_label(ctx, "#FFAA"); + nk_button_label(ctx, "#FFBB"); + nk_button_label(ctx, "#FFCC"); + nk_button_label(ctx, "#FFDD"); + nk_button_label(ctx, "#FFEE"); + nk_button_label(ctx, "#FFFF"); + nk_group_end(ctx); + } + nk_tree_pop(ctx); + } + nk_tree_pop(ctx); + } + nk_tree_pop(ctx); + } + } + nk_end(ctx); + return !nk_window_is_closed(ctx, "Overview"); +} + diff --git a/nuklear/demo/sdl_opengl2/Makefile b/nuklear/demo/sdl_opengl2/Makefile new file mode 100644 index 0000000..2c85a6e --- /dev/null +++ b/nuklear/demo/sdl_opengl2/Makefile @@ -0,0 +1,25 @@ +# Install +BIN = demo + +# Flags +CFLAGS = -std=c99 -pedantic -O2 + +SRC = main.c +OBJ = $(SRC:.c=.o) + +ifeq ($(OS),Windows_NT) +BIN := $(BIN).exe +LIBS = -lmingw32 -lSDL2main -lSDL2 -lopengl32 -lm -lGLU32 +else + UNAME_S := $(shell uname -s) + ifeq ($(UNAME_S),Darwin) + LIBS = -lSDL2 -framework OpenGL -lm + else + LIBS = -lSDL2 -lGL -lm -lGLU + endif +endif + +$(BIN): + @mkdir -p bin + rm -f bin/$(BIN) $(OBJS) + $(CC) $(SRC) $(CFLAGS) -o bin/$(BIN) $(LIBS) diff --git a/nuklear/demo/sdl_opengl2/main.c b/nuklear/demo/sdl_opengl2/main.c new file mode 100644 index 0000000..0d96551 --- /dev/null +++ b/nuklear/demo/sdl_opengl2/main.c @@ -0,0 +1,181 @@ +/* nuklear - v1.17 - public domain */ +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdarg.h> +#include <string.h> +#include <math.h> +#include <assert.h> +#include <math.h> +#include <limits.h> +#include <time.h> + +#include <SDL2/SDL.h> +#include <SDL2/SDL_opengl.h> + +#define NK_INCLUDE_FIXED_TYPES +#define NK_INCLUDE_STANDARD_IO +#define NK_INCLUDE_STANDARD_VARARGS +#define NK_INCLUDE_DEFAULT_ALLOCATOR +#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT +#define NK_INCLUDE_FONT_BAKING +#define NK_INCLUDE_DEFAULT_FONT +#define NK_IMPLEMENTATION +#define NK_SDL_GL2_IMPLEMENTATION +#include "../../nuklear.h" +#include "nuklear_sdl_gl2.h" + +#define WINDOW_WIDTH 1200 +#define WINDOW_HEIGHT 800 + +#define MAX_VERTEX_MEMORY 512 * 1024 +#define MAX_ELEMENT_MEMORY 128 * 1024 + +#define UNUSED(a) (void)a +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) < (b) ? (b) : (a)) +#define LEN(a) (sizeof(a)/sizeof(a)[0]) + +/* =============================================================== + * + * EXAMPLE + * + * ===============================================================*/ +/* This are some code examples to provide a small overview of what can be + * done with this library. To try out an example uncomment the include + * and the corresponding function. */ +/*#include "../style.c"*/ +/*#include "../calculator.c"*/ +/*#include "../overview.c"*/ +/*#include "../node_editor.c"*/ + +/* =============================================================== + * + * DEMO + * + * ===============================================================*/ +int +main(int argc, char* argv[]) +{ + /* Platform */ + SDL_Window *win; + SDL_GLContext glContext; + struct nk_color background; + int win_width, win_height; + int running = 1; + + /* GUI */ + struct nk_context *ctx; + + /* SDL setup */ + SDL_SetHint(SDL_HINT_VIDEO_HIGHDPI_DISABLED, "0"); + SDL_Init(SDL_INIT_VIDEO); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); + win = SDL_CreateWindow("Demo", + SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_OPENGL|SDL_WINDOW_SHOWN|SDL_WINDOW_ALLOW_HIGHDPI); + glContext = SDL_GL_CreateContext(win); + SDL_GetWindowSize(win, &win_width, &win_height); + + /* GUI */ + ctx = nk_sdl_init(win); + /* Load Fonts: if none of these are loaded a default font will be used */ + /* Load Cursor: if you uncomment cursor loading please hide the cursor */ + {struct nk_font_atlas *atlas; + nk_sdl_font_stash_begin(&atlas); + /*struct nk_font *droid = nk_font_atlas_add_from_file(atlas, "../../../extra_font/DroidSans.ttf", 14, 0);*/ + /*struct nk_font *roboto = nk_font_atlas_add_from_file(atlas, "../../../extra_font/Roboto-Regular.ttf", 16, 0);*/ + /*struct nk_font *future = nk_font_atlas_add_from_file(atlas, "../../../extra_font/kenvector_future_thin.ttf", 13, 0);*/ + /*struct nk_font *clean = nk_font_atlas_add_from_file(atlas, "../../../extra_font/ProggyClean.ttf", 12, 0);*/ + /*struct nk_font *tiny = nk_font_atlas_add_from_file(atlas, "../../../extra_font/ProggyTiny.ttf", 10, 0);*/ + /*struct nk_font *cousine = nk_font_atlas_add_from_file(atlas, "../../../extra_font/Cousine-Regular.ttf", 13, 0);*/ + nk_sdl_font_stash_end(); + /*nk_style_load_all_cursors(ctx, atlas->cursors);*/ + /*nk_style_set_font(ctx, &roboto->handle)*/;} + + /* style.c */ + /*set_style(ctx, THEME_WHITE);*/ + /*set_style(ctx, THEME_RED);*/ + /*set_style(ctx, THEME_BLUE);*/ + /*set_style(ctx, THEME_DARK);*/ + + background = nk_rgb(28,48,62); + while (running) + { + /* Input */ + SDL_Event evt; + nk_input_begin(ctx); + while (SDL_PollEvent(&evt)) { + if (evt.type == SDL_QUIT) goto cleanup; + nk_sdl_handle_event(&evt); + } + nk_input_end(ctx); + + /* GUI */ + if (nk_begin(ctx, "Demo", nk_rect(50, 50, 210, 250), + NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_SCALABLE| + NK_WINDOW_MINIMIZABLE|NK_WINDOW_TITLE)) + { + enum {EASY, HARD}; + static int op = EASY; + static int property = 20; + + nk_layout_row_static(ctx, 30, 80, 1); + if (nk_button_label(ctx, "button")) + fprintf(stdout, "button pressed\n"); + nk_layout_row_dynamic(ctx, 30, 2); + if (nk_option_label(ctx, "easy", op == EASY)) op = EASY; + if (nk_option_label(ctx, "hard", op == HARD)) op = HARD; + nk_layout_row_dynamic(ctx, 25, 1); + nk_property_int(ctx, "Compression:", 0, &property, 100, 10, 1); + + nk_layout_row_dynamic(ctx, 20, 1); + nk_label(ctx, "background:", NK_TEXT_LEFT); + nk_layout_row_dynamic(ctx, 25, 1); + if (nk_combo_begin_color(ctx, background, nk_vec2(nk_widget_width(ctx),400))) { + nk_layout_row_dynamic(ctx, 120, 1); + background = nk_color_picker(ctx, background, NK_RGBA); + nk_layout_row_dynamic(ctx, 25, 1); + background.r = (nk_byte)nk_propertyi(ctx, "#R:", 0, background.r, 255, 1,1); + background.g = (nk_byte)nk_propertyi(ctx, "#G:", 0, background.g, 255, 1,1); + background.b = (nk_byte)nk_propertyi(ctx, "#B:", 0, background.b, 255, 1,1); + background.a = (nk_byte)nk_propertyi(ctx, "#A:", 0, background.a, 255, 1,1); + nk_combo_end(ctx); + } + } + nk_end(ctx); + + /* -------------- EXAMPLES ---------------- */ + /*calculator(ctx);*/ + /*overview(ctx);*/ + /*node_editor(ctx);*/ + /* ----------------------------------------- */ + + /* Draw */ + {float bg[4]; + nk_color_fv(bg, background); + SDL_GetWindowSize(win, &win_width, &win_height); + glViewport(0, 0, win_width, win_height); + glClear(GL_COLOR_BUFFER_BIT); + glClearColor(bg[0], bg[1], bg[2], bg[3]); + /* IMPORTANT: `nk_sdl_render` modifies some global OpenGL state + * with blending, scissor, face culling, depth test and viewport and + * defaults everything back into a default state. + * Make sure to either a.) save and restore or b.) reset your own state after + * rendering the UI. */ + nk_sdl_render(NK_ANTI_ALIASING_ON, MAX_VERTEX_MEMORY, MAX_ELEMENT_MEMORY); + SDL_GL_SwapWindow(win);} + } + +cleanup: + nk_sdl_shutdown(); + SDL_GL_DeleteContext(glContext); + SDL_DestroyWindow(win); + SDL_Quit(); + return 0; +} + diff --git a/nuklear/demo/sdl_opengl2/nuklear_sdl_gl2.h b/nuklear/demo/sdl_opengl2/nuklear_sdl_gl2.h new file mode 100644 index 0000000..45c5970 --- /dev/null +++ b/nuklear/demo/sdl_opengl2/nuklear_sdl_gl2.h @@ -0,0 +1,342 @@ +/* + * Nuklear - v1.17 - public domain + * no warrenty implied; use at your own risk. + * authored from 2015-2016 by Micha Mettke + */ +/* + * ============================================================== + * + * API + * + * =============================================================== + */ +#ifndef NK_SDL_GL2_H_ +#define NK_SDL_GL2_H_ + +#include <SDL2/SDL.h> +NK_API struct nk_context* nk_sdl_init(SDL_Window *win); +NK_API void nk_sdl_font_stash_begin(struct nk_font_atlas **atlas); +NK_API void nk_sdl_font_stash_end(void); +NK_API int nk_sdl_handle_event(SDL_Event *evt); +NK_API void nk_sdl_render(enum nk_anti_aliasing , int max_vertex_buffer, int max_element_buffer); +NK_API void nk_sdl_shutdown(void); + +#endif +/* + * ============================================================== + * + * IMPLEMENTATION + * + * =============================================================== + */ +#ifdef NK_SDL_GL2_IMPLEMENTATION + +struct nk_sdl_device { + struct nk_buffer cmds; + struct nk_draw_null_texture null; + GLuint font_tex; +}; + +struct nk_sdl_vertex { + float position[2]; + float uv[2]; + nk_byte col[4]; +}; + +static struct nk_sdl { + SDL_Window *win; + struct nk_sdl_device ogl; + struct nk_context ctx; + struct nk_font_atlas atlas; +} sdl; + +NK_INTERN void +nk_sdl_device_upload_atlas(const void *image, int width, int height) +{ + struct nk_sdl_device *dev = &sdl.ogl; + glGenTextures(1, &dev->font_tex); + glBindTexture(GL_TEXTURE_2D, dev->font_tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)width, (GLsizei)height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, image); +} + +NK_API void +nk_sdl_render(enum nk_anti_aliasing AA, int max_vertex_buffer, int max_element_buffer) +{ + /* setup global state */ + struct nk_sdl_device *dev = &sdl.ogl; + int width, height; + int display_width, display_height; + struct nk_vec2 scale; + + SDL_GetWindowSize(sdl.win, &width, &height); + SDL_GL_GetDrawableSize(sdl.win, &display_width, &display_height); + scale.x = (float)display_width/(float)width; + scale.y = (float)display_height/(float)height; + + glPushAttrib(GL_ENABLE_BIT|GL_COLOR_BUFFER_BIT|GL_TRANSFORM_BIT); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glEnable(GL_SCISSOR_TEST); + glEnable(GL_BLEND); + glEnable(GL_TEXTURE_2D); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + /* setup viewport/project */ + glViewport(0,0,(GLsizei)display_width,(GLsizei)display_height); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(0.0f, width, height, 0.0f, -1.0f, 1.0f); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + { + GLsizei vs = sizeof(struct nk_sdl_vertex); + size_t vp = offsetof(struct nk_sdl_vertex, position); + size_t vt = offsetof(struct nk_sdl_vertex, uv); + size_t vc = offsetof(struct nk_sdl_vertex, col); + + /* convert from command queue into draw list and draw to screen */ + const struct nk_draw_command *cmd; + const nk_draw_index *offset = NULL; + struct nk_buffer vbuf, ebuf; + + /* fill converting configuration */ + struct nk_convert_config config; + static const struct nk_draw_vertex_layout_element vertex_layout[] = { + {NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_sdl_vertex, position)}, + {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_sdl_vertex, uv)}, + {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, NK_OFFSETOF(struct nk_sdl_vertex, col)}, + {NK_VERTEX_LAYOUT_END} + }; + NK_MEMSET(&config, 0, sizeof(config)); + config.vertex_layout = vertex_layout; + config.vertex_size = sizeof(struct nk_sdl_vertex); + config.vertex_alignment = NK_ALIGNOF(struct nk_sdl_vertex); + config.null = dev->null; + config.circle_segment_count = 22; + config.curve_segment_count = 22; + config.arc_segment_count = 22; + config.global_alpha = 1.0f; + config.shape_AA = AA; + config.line_AA = AA; + + /* convert shapes into vertexes */ + nk_buffer_init_default(&vbuf); + nk_buffer_init_default(&ebuf); + nk_convert(&sdl.ctx, &dev->cmds, &vbuf, &ebuf, &config); + + /* setup vertex buffer pointer */ + {const void *vertices = nk_buffer_memory_const(&vbuf); + glVertexPointer(2, GL_FLOAT, vs, (const void*)((const nk_byte*)vertices + vp)); + glTexCoordPointer(2, GL_FLOAT, vs, (const void*)((const nk_byte*)vertices + vt)); + glColorPointer(4, GL_UNSIGNED_BYTE, vs, (const void*)((const nk_byte*)vertices + vc));} + + /* iterate over and execute each draw command */ + offset = (const nk_draw_index*)nk_buffer_memory_const(&ebuf); + nk_draw_foreach(cmd, &sdl.ctx, &dev->cmds) + { + if (!cmd->elem_count) continue; + glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id); + glScissor( + (GLint)(cmd->clip_rect.x * scale.x), + (GLint)((height - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h)) * scale.y), + (GLint)(cmd->clip_rect.w * scale.x), + (GLint)(cmd->clip_rect.h * scale.y)); + glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset); + offset += cmd->elem_count; + } + nk_clear(&sdl.ctx); + nk_buffer_free(&vbuf); + nk_buffer_free(&ebuf); + } + + /* default OpenGL state */ + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_BLEND); + glDisable(GL_TEXTURE_2D); + + glBindTexture(GL_TEXTURE_2D, 0); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glPopAttrib(); +} + +static void +nk_sdl_clipbard_paste(nk_handle usr, struct nk_text_edit *edit) +{ + const char *text = SDL_GetClipboardText(); + if (text) nk_textedit_paste(edit, text, nk_strlen(text)); + (void)usr; +} + +static void +nk_sdl_clipbard_copy(nk_handle usr, const char *text, int len) +{ + char *str = 0; + (void)usr; + if (!len) return; + str = (char*)malloc((size_t)len+1); + if (!str) return; + memcpy(str, text, (size_t)len); + str[len] = '\0'; + SDL_SetClipboardText(str); + free(str); +} + +NK_API struct nk_context* +nk_sdl_init(SDL_Window *win) +{ + sdl.win = win; + nk_init_default(&sdl.ctx, 0); + sdl.ctx.clip.copy = nk_sdl_clipbard_copy; + sdl.ctx.clip.paste = nk_sdl_clipbard_paste; + sdl.ctx.clip.userdata = nk_handle_ptr(0); + nk_buffer_init_default(&sdl.ogl.cmds); + return &sdl.ctx; +} + +NK_API void +nk_sdl_font_stash_begin(struct nk_font_atlas **atlas) +{ + nk_font_atlas_init_default(&sdl.atlas); + nk_font_atlas_begin(&sdl.atlas); + *atlas = &sdl.atlas; +} + +NK_API void +nk_sdl_font_stash_end(void) +{ + const void *image; int w, h; + image = nk_font_atlas_bake(&sdl.atlas, &w, &h, NK_FONT_ATLAS_RGBA32); + nk_sdl_device_upload_atlas(image, w, h); + nk_font_atlas_end(&sdl.atlas, nk_handle_id((int)sdl.ogl.font_tex), &sdl.ogl.null); + if (sdl.atlas.default_font) + nk_style_set_font(&sdl.ctx, &sdl.atlas.default_font->handle); +} + +NK_API int +nk_sdl_handle_event(SDL_Event *evt) +{ + struct nk_context *ctx = &sdl.ctx; + + /* optional grabbing behavior */ + if (ctx->input.mouse.grab) { + SDL_SetRelativeMouseMode(SDL_TRUE); + ctx->input.mouse.grab = 0; + } else if (ctx->input.mouse.ungrab) { + int x = (int)ctx->input.mouse.prev.x, y = (int)ctx->input.mouse.prev.y; + SDL_SetRelativeMouseMode(SDL_FALSE); + SDL_WarpMouseInWindow(sdl.win, x, y); + ctx->input.mouse.ungrab = 0; + } + if (evt->type == SDL_KEYUP || evt->type == SDL_KEYDOWN) { + /* key events */ + int down = evt->type == SDL_KEYDOWN; + const Uint8* state = SDL_GetKeyboardState(0); + SDL_Keycode sym = evt->key.keysym.sym; + if (sym == SDLK_RSHIFT || sym == SDLK_LSHIFT) + nk_input_key(ctx, NK_KEY_SHIFT, down); + else if (sym == SDLK_DELETE) + nk_input_key(ctx, NK_KEY_DEL, down); + else if (sym == SDLK_RETURN) + nk_input_key(ctx, NK_KEY_ENTER, down); + else if (sym == SDLK_TAB) + nk_input_key(ctx, NK_KEY_TAB, down); + else if (sym == SDLK_BACKSPACE) + nk_input_key(ctx, NK_KEY_BACKSPACE, down); + else if (sym == SDLK_HOME) { + nk_input_key(ctx, NK_KEY_TEXT_START, down); + nk_input_key(ctx, NK_KEY_SCROLL_START, down); + } else if (sym == SDLK_END) { + nk_input_key(ctx, NK_KEY_TEXT_END, down); + nk_input_key(ctx, NK_KEY_SCROLL_END, down); + } else if (sym == SDLK_PAGEDOWN) { + nk_input_key(ctx, NK_KEY_SCROLL_DOWN, down); + } else if (sym == SDLK_PAGEUP) { + nk_input_key(ctx, NK_KEY_SCROLL_UP, down); + } else if (sym == SDLK_z) + nk_input_key(ctx, NK_KEY_TEXT_UNDO, down && state[SDL_SCANCODE_LCTRL]); + else if (sym == SDLK_r) + nk_input_key(ctx, NK_KEY_TEXT_REDO, down && state[SDL_SCANCODE_LCTRL]); + else if (sym == SDLK_c) + nk_input_key(ctx, NK_KEY_COPY, down && state[SDL_SCANCODE_LCTRL]); + else if (sym == SDLK_v) + nk_input_key(ctx, NK_KEY_PASTE, down && state[SDL_SCANCODE_LCTRL]); + else if (sym == SDLK_x) + nk_input_key(ctx, NK_KEY_CUT, down && state[SDL_SCANCODE_LCTRL]); + else if (sym == SDLK_b) + nk_input_key(ctx, NK_KEY_TEXT_LINE_START, down && state[SDL_SCANCODE_LCTRL]); + else if (sym == SDLK_e) + nk_input_key(ctx, NK_KEY_TEXT_LINE_END, down && state[SDL_SCANCODE_LCTRL]); + else if (sym == SDLK_UP) + nk_input_key(ctx, NK_KEY_UP, down); + else if (sym == SDLK_DOWN) + nk_input_key(ctx, NK_KEY_DOWN, down); + else if (sym == SDLK_LEFT) { + if (state[SDL_SCANCODE_LCTRL]) + nk_input_key(ctx, NK_KEY_TEXT_WORD_LEFT, down); + else nk_input_key(ctx, NK_KEY_LEFT, down); + } else if (sym == SDLK_RIGHT) { + if (state[SDL_SCANCODE_LCTRL]) + nk_input_key(ctx, NK_KEY_TEXT_WORD_RIGHT, down); + else nk_input_key(ctx, NK_KEY_RIGHT, down); + } else return 0; + return 1; + } else if (evt->type == SDL_MOUSEBUTTONDOWN || evt->type == SDL_MOUSEBUTTONUP) { + /* mouse button */ + int down = evt->type == SDL_MOUSEBUTTONDOWN; + const int x = evt->button.x, y = evt->button.y; + if (evt->button.button == SDL_BUTTON_LEFT) + nk_input_button(ctx, NK_BUTTON_LEFT, x, y, down); + if (evt->button.button == SDL_BUTTON_MIDDLE) + nk_input_button(ctx, NK_BUTTON_MIDDLE, x, y, down); + if (evt->button.button == SDL_BUTTON_RIGHT) + nk_input_button(ctx, NK_BUTTON_RIGHT, x, y, down); + else return 0; + return 1; + } else if (evt->type == SDL_MOUSEMOTION) { + if (ctx->input.mouse.grabbed) { + int x = (int)ctx->input.mouse.prev.x, y = (int)ctx->input.mouse.prev.y; + nk_input_motion(ctx, x + evt->motion.xrel, y + evt->motion.yrel); + } else nk_input_motion(ctx, evt->motion.x, evt->motion.y); + return 1; + } else if (evt->type == SDL_TEXTINPUT) { + nk_glyph glyph; + memcpy(glyph, evt->text.text, NK_UTF_SIZE); + nk_input_glyph(ctx, glyph); + return 1; + } else if (evt->type == SDL_MOUSEWHEEL) { + nk_input_scroll(ctx,(float)evt->wheel.y); + return 1; + } + return 0; +} + +NK_API +void nk_sdl_shutdown(void) +{ + struct nk_sdl_device *dev = &sdl.ogl; + nk_font_atlas_clear(&sdl.atlas); + nk_free(&sdl.ctx); + glDeleteTextures(1, &dev->font_tex); + nk_buffer_free(&dev->cmds); + memset(&sdl, 0, sizeof(sdl)); +} + +#endif diff --git a/nuklear/demo/sdl_opengl3/Makefile b/nuklear/demo/sdl_opengl3/Makefile new file mode 100644 index 0000000..4b8ccf4 --- /dev/null +++ b/nuklear/demo/sdl_opengl3/Makefile @@ -0,0 +1,25 @@ +# Install +BIN = demo + +# Flags +CFLAGS = -std=c99 -pedantic -O2 + +SRC = main.c +OBJ = $(SRC:.c=.o) + +ifeq ($(OS),Windows_NT) +BIN := $(BIN).exe +LIBS = -lmingw32 -lSDL2main -lSDL2 -lopengl32 -lm -lGLU32 -lGLEW32 +else + UNAME_S := $(shell uname -s) + ifeq ($(UNAME_S),Darwin) + LIBS = -lSDL2 -framework OpenGL -lm -lGLEW + else + LIBS = -lSDL2 -lGL -lm -lGLU -lGLEW + endif +endif + +$(BIN): + @mkdir -p bin + rm -f bin/$(BIN) $(OBJS) + $(CC) $(SRC) $(CFLAGS) -o bin/$(BIN) $(LIBS) diff --git a/nuklear/demo/sdl_opengl3/main.c b/nuklear/demo/sdl_opengl3/main.c new file mode 100644 index 0000000..aee4f82 --- /dev/null +++ b/nuklear/demo/sdl_opengl3/main.c @@ -0,0 +1,195 @@ +/* nuklear - v1.17 - public domain */ +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdarg.h> +#include <string.h> +#include <math.h> +#include <assert.h> +#include <math.h> +#include <limits.h> +#include <time.h> + +#include <GL/glew.h> +#include <SDL2/SDL.h> +#include <SDL2/SDL_opengl.h> + +#define NK_INCLUDE_FIXED_TYPES +#define NK_INCLUDE_STANDARD_IO +#define NK_INCLUDE_STANDARD_VARARGS +#define NK_INCLUDE_DEFAULT_ALLOCATOR +#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT +#define NK_INCLUDE_FONT_BAKING +#define NK_INCLUDE_DEFAULT_FONT +#define NK_IMPLEMENTATION +#define NK_SDL_GL3_IMPLEMENTATION +#include "../../nuklear.h" +#include "nuklear_sdl_gl3.h" + +#define WINDOW_WIDTH 800 +#define WINDOW_HEIGHT 600 + +#define MAX_VERTEX_MEMORY 512 * 1024 +#define MAX_ELEMENT_MEMORY 128 * 1024 + +#define UNUSED(a) (void)a +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) < (b) ? (b) : (a)) +#define LEN(a) (sizeof(a)/sizeof(a)[0]) + +/* =============================================================== + * + * EXAMPLE + * + * ===============================================================*/ +/* This are some code examples to provide a small overview of what can be + * done with this library. To try out an example uncomment the include + * and the corresponding function. */ +/*#include "../style.c"*/ +/*#include "../calculator.c"*/ +/*#include "../overview.c"*/ +/*#include "../node_editor.c"*/ + +/* =============================================================== + * + * DEMO + * + * ===============================================================*/ +int +main(int argc, char* argv[]) +{ + /* Platform */ + SDL_Window *win; + SDL_GLContext glContext; + struct nk_color background; + int win_width, win_height; + int running = 1; + + /* GUI */ + struct nk_context *ctx; + + /* SDL setup */ + SDL_SetHint(SDL_HINT_VIDEO_HIGHDPI_DISABLED, "0"); + SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_EVENTS); + SDL_GL_SetAttribute (SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); + SDL_GL_SetAttribute (SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + win = SDL_CreateWindow("Demo", + SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_OPENGL|SDL_WINDOW_SHOWN|SDL_WINDOW_ALLOW_HIGHDPI); + glContext = SDL_GL_CreateContext(win); + SDL_GetWindowSize(win, &win_width, &win_height); + + /* OpenGL setup */ + glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); + glewExperimental = 1; + if (glewInit() != GLEW_OK) { + fprintf(stderr, "Failed to setup GLEW\n"); + exit(1); + } + + ctx = nk_sdl_init(win); + /* Load Fonts: if none of these are loaded a default font will be used */ + /* Load Cursor: if you uncomment cursor loading please hide the cursor */ + {struct nk_font_atlas *atlas; + nk_sdl_font_stash_begin(&atlas); + /*struct nk_font *droid = nk_font_atlas_add_from_file(atlas, "../../../extra_font/DroidSans.ttf", 14, 0);*/ + /*struct nk_font *roboto = nk_font_atlas_add_from_file(atlas, "../../../extra_font/Roboto-Regular.ttf", 16, 0);*/ + /*struct nk_font *future = nk_font_atlas_add_from_file(atlas, "../../../extra_font/kenvector_future_thin.ttf", 13, 0);*/ + /*struct nk_font *clean = nk_font_atlas_add_from_file(atlas, "../../../extra_font/ProggyClean.ttf", 12, 0);*/ + /*struct nk_font *tiny = nk_font_atlas_add_from_file(atlas, "../../../extra_font/ProggyTiny.ttf", 10, 0);*/ + /*struct nk_font *cousine = nk_font_atlas_add_from_file(atlas, "../../../extra_font/Cousine-Regular.ttf", 13, 0);*/ + nk_sdl_font_stash_end(); + /*nk_style_load_all_cursors(ctx, atlas->cursors);*/ + /*nk_style_set_font(ctx, &roboto->handle)*/;} + + /* style.c */ + /*set_style(ctx, THEME_WHITE);*/ + /*set_style(ctx, THEME_RED);*/ + /*set_style(ctx, THEME_BLUE);*/ + /*set_style(ctx, THEME_DARK);*/ + + background = nk_rgb(28,48,62); + while (running) + { + /* Input */ + SDL_Event evt; + nk_input_begin(ctx); + while (SDL_PollEvent(&evt)) { + if (evt.type == SDL_QUIT) goto cleanup; + nk_sdl_handle_event(&evt); + } + nk_input_end(ctx); + + + /* GUI */ + if (nk_begin(ctx, "Demo", nk_rect(50, 50, 200, 200), + NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_SCALABLE| + NK_WINDOW_CLOSABLE|NK_WINDOW_MINIMIZABLE|NK_WINDOW_TITLE)) + { + nk_menubar_begin(ctx); + nk_layout_row_begin(ctx, NK_STATIC, 25, 2); + nk_layout_row_push(ctx, 45); + if (nk_menu_begin_label(ctx, "FILE", NK_TEXT_LEFT, nk_vec2(120, 200))) { + nk_layout_row_dynamic(ctx, 30, 1); + nk_menu_item_label(ctx, "OPEN", NK_TEXT_LEFT); + nk_menu_item_label(ctx, "CLOSE", NK_TEXT_LEFT); + nk_menu_end(ctx); + } + nk_layout_row_push(ctx, 45); + if (nk_menu_begin_label(ctx, "EDIT", NK_TEXT_LEFT, nk_vec2(120, 200))) { + nk_layout_row_dynamic(ctx, 30, 1); + nk_menu_item_label(ctx, "COPY", NK_TEXT_LEFT); + nk_menu_item_label(ctx, "CUT", NK_TEXT_LEFT); + nk_menu_item_label(ctx, "PASTE", NK_TEXT_LEFT); + nk_menu_end(ctx); + } + nk_layout_row_end(ctx); + nk_menubar_end(ctx); + + enum {EASY, HARD}; + static int op = EASY; + static int property = 20; + nk_layout_row_static(ctx, 30, 80, 1); + if (nk_button_label(ctx, "button")) + fprintf(stdout, "button pressed\n"); + nk_layout_row_dynamic(ctx, 30, 2); + if (nk_option_label(ctx, "easy", op == EASY)) op = EASY; + if (nk_option_label(ctx, "hard", op == HARD)) op = HARD; + nk_layout_row_dynamic(ctx, 25, 1); + nk_property_int(ctx, "Compression:", 0, &property, 100, 10, 1); + } + nk_end(ctx); + + /* -------------- EXAMPLES ---------------- */ + /*calculator(ctx);*/ + /*overview(ctx);*/ + /*node_editor(ctx);*/ + /* ----------------------------------------- */ + + /* Draw */ + {float bg[4]; + nk_color_fv(bg, background); + SDL_GetWindowSize(win, &win_width, &win_height); + glViewport(0, 0, win_width, win_height); + glClear(GL_COLOR_BUFFER_BIT); + glClearColor(bg[0], bg[1], bg[2], bg[3]); + /* IMPORTANT: `nk_sdl_render` modifies some global OpenGL state + * with blending, scissor, face culling, depth test and viewport and + * defaults everything back into a default state. + * Make sure to either a.) save and restore or b.) reset your own state after + * rendering the UI. */ + nk_sdl_render(NK_ANTI_ALIASING_ON, MAX_VERTEX_MEMORY, MAX_ELEMENT_MEMORY); + SDL_GL_SwapWindow(win);} + } + +cleanup: + nk_sdl_shutdown(); + SDL_GL_DeleteContext(glContext); + SDL_DestroyWindow(win); + SDL_Quit(); + return 0; +} + diff --git a/nuklear/demo/sdl_opengl3/nuklear_sdl_gl3.h b/nuklear/demo/sdl_opengl3/nuklear_sdl_gl3.h new file mode 100644 index 0000000..17c0899 --- /dev/null +++ b/nuklear/demo/sdl_opengl3/nuklear_sdl_gl3.h @@ -0,0 +1,437 @@ +/* + * Nuklear - v1.17 - public domain + * no warrenty implied; use at your own risk. + * authored from 2015-2016 by Micha Mettke + */ +/* + * ============================================================== + * + * API + * + * =============================================================== + */ +#ifndef NK_SDL_GL3_H_ +#define NK_SDL_GL3_H_ + +#include <SDL2/SDL.h> +#include <SDL2/SDL_opengl.h> + +NK_API struct nk_context* nk_sdl_init(SDL_Window *win); +NK_API void nk_sdl_font_stash_begin(struct nk_font_atlas **atlas); +NK_API void nk_sdl_font_stash_end(void); +NK_API int nk_sdl_handle_event(SDL_Event *evt); +NK_API void nk_sdl_render(enum nk_anti_aliasing , int max_vertex_buffer, int max_element_buffer); +NK_API void nk_sdl_shutdown(void); +NK_API void nk_sdl_device_destroy(void); +NK_API void nk_sdl_device_create(void); + +#endif + +/* + * ============================================================== + * + * IMPLEMENTATION + * + * =============================================================== + */ +#ifdef NK_SDL_GL3_IMPLEMENTATION + +#include <string.h> + +struct nk_sdl_device { + struct nk_buffer cmds; + struct nk_draw_null_texture null; + GLuint vbo, vao, ebo; + GLuint prog; + GLuint vert_shdr; + GLuint frag_shdr; + GLint attrib_pos; + GLint attrib_uv; + GLint attrib_col; + GLint uniform_tex; + GLint uniform_proj; + GLuint font_tex; +}; + +struct nk_sdl_vertex { + float position[2]; + float uv[2]; + nk_byte col[4]; +}; + +static struct nk_sdl { + SDL_Window *win; + struct nk_sdl_device ogl; + struct nk_context ctx; + struct nk_font_atlas atlas; +} sdl; + +#ifdef __APPLE__ + #define NK_SHADER_VERSION "#version 150\n" +#else + #define NK_SHADER_VERSION "#version 300 es\n" +#endif +NK_API void +nk_sdl_device_create(void) +{ + GLint status; + static const GLchar *vertex_shader = + NK_SHADER_VERSION + "uniform mat4 ProjMtx;\n" + "in vec2 Position;\n" + "in vec2 TexCoord;\n" + "in vec4 Color;\n" + "out vec2 Frag_UV;\n" + "out vec4 Frag_Color;\n" + "void main() {\n" + " Frag_UV = TexCoord;\n" + " Frag_Color = Color;\n" + " gl_Position = ProjMtx * vec4(Position.xy, 0, 1);\n" + "}\n"; + static const GLchar *fragment_shader = + NK_SHADER_VERSION + "precision mediump float;\n" + "uniform sampler2D Texture;\n" + "in vec2 Frag_UV;\n" + "in vec4 Frag_Color;\n" + "out vec4 Out_Color;\n" + "void main(){\n" + " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" + "}\n"; + + struct nk_sdl_device *dev = &sdl.ogl; + nk_buffer_init_default(&dev->cmds); + dev->prog = glCreateProgram(); + dev->vert_shdr = glCreateShader(GL_VERTEX_SHADER); + dev->frag_shdr = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(dev->vert_shdr, 1, &vertex_shader, 0); + glShaderSource(dev->frag_shdr, 1, &fragment_shader, 0); + glCompileShader(dev->vert_shdr); + glCompileShader(dev->frag_shdr); + glGetShaderiv(dev->vert_shdr, GL_COMPILE_STATUS, &status); + assert(status == GL_TRUE); + glGetShaderiv(dev->frag_shdr, GL_COMPILE_STATUS, &status); + assert(status == GL_TRUE); + glAttachShader(dev->prog, dev->vert_shdr); + glAttachShader(dev->prog, dev->frag_shdr); + glLinkProgram(dev->prog); + glGetProgramiv(dev->prog, GL_LINK_STATUS, &status); + assert(status == GL_TRUE); + + dev->uniform_tex = glGetUniformLocation(dev->prog, "Texture"); + dev->uniform_proj = glGetUniformLocation(dev->prog, "ProjMtx"); + dev->attrib_pos = glGetAttribLocation(dev->prog, "Position"); + dev->attrib_uv = glGetAttribLocation(dev->prog, "TexCoord"); + dev->attrib_col = glGetAttribLocation(dev->prog, "Color"); + + { + /* buffer setup */ + GLsizei vs = sizeof(struct nk_sdl_vertex); + size_t vp = offsetof(struct nk_sdl_vertex, position); + size_t vt = offsetof(struct nk_sdl_vertex, uv); + size_t vc = offsetof(struct nk_sdl_vertex, col); + + glGenBuffers(1, &dev->vbo); + glGenBuffers(1, &dev->ebo); + glGenVertexArrays(1, &dev->vao); + + glBindVertexArray(dev->vao); + glBindBuffer(GL_ARRAY_BUFFER, dev->vbo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo); + + glEnableVertexAttribArray((GLuint)dev->attrib_pos); + glEnableVertexAttribArray((GLuint)dev->attrib_uv); + glEnableVertexAttribArray((GLuint)dev->attrib_col); + + glVertexAttribPointer((GLuint)dev->attrib_pos, 2, GL_FLOAT, GL_FALSE, vs, (void*)vp); + glVertexAttribPointer((GLuint)dev->attrib_uv, 2, GL_FLOAT, GL_FALSE, vs, (void*)vt); + glVertexAttribPointer((GLuint)dev->attrib_col, 4, GL_UNSIGNED_BYTE, GL_TRUE, vs, (void*)vc); + } + + glBindTexture(GL_TEXTURE_2D, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindVertexArray(0); +} + +NK_INTERN void +nk_sdl_device_upload_atlas(const void *image, int width, int height) +{ + struct nk_sdl_device *dev = &sdl.ogl; + glGenTextures(1, &dev->font_tex); + glBindTexture(GL_TEXTURE_2D, dev->font_tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)width, (GLsizei)height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, image); +} + +NK_API void +nk_sdl_device_destroy(void) +{ + struct nk_sdl_device *dev = &sdl.ogl; + glDetachShader(dev->prog, dev->vert_shdr); + glDetachShader(dev->prog, dev->frag_shdr); + glDeleteShader(dev->vert_shdr); + glDeleteShader(dev->frag_shdr); + glDeleteProgram(dev->prog); + glDeleteTextures(1, &dev->font_tex); + glDeleteBuffers(1, &dev->vbo); + glDeleteBuffers(1, &dev->ebo); + nk_buffer_free(&dev->cmds); +} + +NK_API void +nk_sdl_render(enum nk_anti_aliasing AA, int max_vertex_buffer, int max_element_buffer) +{ + struct nk_sdl_device *dev = &sdl.ogl; + int width, height; + int display_width, display_height; + struct nk_vec2 scale; + GLfloat ortho[4][4] = { + {2.0f, 0.0f, 0.0f, 0.0f}, + {0.0f,-2.0f, 0.0f, 0.0f}, + {0.0f, 0.0f,-1.0f, 0.0f}, + {-1.0f,1.0f, 0.0f, 1.0f}, + }; + SDL_GetWindowSize(sdl.win, &width, &height); + SDL_GL_GetDrawableSize(sdl.win, &display_width, &display_height); + ortho[0][0] /= (GLfloat)width; + ortho[1][1] /= (GLfloat)height; + + scale.x = (float)display_width/(float)width; + scale.y = (float)display_height/(float)height; + + /* setup global state */ + glViewport(0,0,display_width,display_height); + glEnable(GL_BLEND); + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glEnable(GL_SCISSOR_TEST); + glActiveTexture(GL_TEXTURE0); + + /* setup program */ + glUseProgram(dev->prog); + glUniform1i(dev->uniform_tex, 0); + glUniformMatrix4fv(dev->uniform_proj, 1, GL_FALSE, &ortho[0][0]); + { + /* convert from command queue into draw list and draw to screen */ + const struct nk_draw_command *cmd; + void *vertices, *elements; + const nk_draw_index *offset = NULL; + + /* allocate vertex and element buffer */ + glBindVertexArray(dev->vao); + glBindBuffer(GL_ARRAY_BUFFER, dev->vbo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo); + + glBufferData(GL_ARRAY_BUFFER, max_vertex_buffer, NULL, GL_STREAM_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, max_element_buffer, NULL, GL_STREAM_DRAW); + + /* load vertices/elements directly into vertex/element buffer */ + vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); + elements = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY); + { + /* fill convert configuration */ + struct nk_convert_config config; + static const struct nk_draw_vertex_layout_element vertex_layout[] = { + {NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_sdl_vertex, position)}, + {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_sdl_vertex, uv)}, + {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, NK_OFFSETOF(struct nk_sdl_vertex, col)}, + {NK_VERTEX_LAYOUT_END} + }; + NK_MEMSET(&config, 0, sizeof(config)); + config.vertex_layout = vertex_layout; + config.vertex_size = sizeof(struct nk_sdl_vertex); + config.vertex_alignment = NK_ALIGNOF(struct nk_sdl_vertex); + config.null = dev->null; + config.circle_segment_count = 22; + config.curve_segment_count = 22; + config.arc_segment_count = 22; + config.global_alpha = 1.0f; + config.shape_AA = AA; + config.line_AA = AA; + + /* setup buffers to load vertices and elements */ + {struct nk_buffer vbuf, ebuf; + nk_buffer_init_fixed(&vbuf, vertices, (nk_size)max_vertex_buffer); + nk_buffer_init_fixed(&ebuf, elements, (nk_size)max_element_buffer); + nk_convert(&sdl.ctx, &dev->cmds, &vbuf, &ebuf, &config);} + } + glUnmapBuffer(GL_ARRAY_BUFFER); + glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); + + /* iterate over and execute each draw command */ + nk_draw_foreach(cmd, &sdl.ctx, &dev->cmds) { + if (!cmd->elem_count) continue; + glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id); + glScissor((GLint)(cmd->clip_rect.x * scale.x), + (GLint)((height - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h)) * scale.y), + (GLint)(cmd->clip_rect.w * scale.x), + (GLint)(cmd->clip_rect.h * scale.y)); + glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset); + offset += cmd->elem_count; + } + nk_clear(&sdl.ctx); + } + + glUseProgram(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindVertexArray(0); + glDisable(GL_BLEND); + glDisable(GL_SCISSOR_TEST); +} + +static void +nk_sdl_clipbard_paste(nk_handle usr, struct nk_text_edit *edit) +{ + const char *text = SDL_GetClipboardText(); + if (text) nk_textedit_paste(edit, text, nk_strlen(text)); + (void)usr; +} + +static void +nk_sdl_clipbard_copy(nk_handle usr, const char *text, int len) +{ + char *str = 0; + (void)usr; + if (!len) return; + str = (char*)malloc((size_t)len+1); + if (!str) return; + memcpy(str, text, (size_t)len); + str[len] = '\0'; + SDL_SetClipboardText(str); + free(str); +} + +NK_API struct nk_context* +nk_sdl_init(SDL_Window *win) +{ + sdl.win = win; + nk_init_default(&sdl.ctx, 0); + sdl.ctx.clip.copy = nk_sdl_clipbard_copy; + sdl.ctx.clip.paste = nk_sdl_clipbard_paste; + sdl.ctx.clip.userdata = nk_handle_ptr(0); + nk_sdl_device_create(); + return &sdl.ctx; +} + +NK_API void +nk_sdl_font_stash_begin(struct nk_font_atlas **atlas) +{ + nk_font_atlas_init_default(&sdl.atlas); + nk_font_atlas_begin(&sdl.atlas); + *atlas = &sdl.atlas; +} + +NK_API void +nk_sdl_font_stash_end(void) +{ + const void *image; int w, h; + image = nk_font_atlas_bake(&sdl.atlas, &w, &h, NK_FONT_ATLAS_RGBA32); + nk_sdl_device_upload_atlas(image, w, h); + nk_font_atlas_end(&sdl.atlas, nk_handle_id((int)sdl.ogl.font_tex), &sdl.ogl.null); + if (sdl.atlas.default_font) + nk_style_set_font(&sdl.ctx, &sdl.atlas.default_font->handle); + +} + +NK_API int +nk_sdl_handle_event(SDL_Event *evt) +{ + struct nk_context *ctx = &sdl.ctx; + if (evt->type == SDL_KEYUP || evt->type == SDL_KEYDOWN) { + /* key events */ + int down = evt->type == SDL_KEYDOWN; + const Uint8* state = SDL_GetKeyboardState(0); + SDL_Keycode sym = evt->key.keysym.sym; + if (sym == SDLK_RSHIFT || sym == SDLK_LSHIFT) + nk_input_key(ctx, NK_KEY_SHIFT, down); + else if (sym == SDLK_DELETE) + nk_input_key(ctx, NK_KEY_DEL, down); + else if (sym == SDLK_RETURN) + nk_input_key(ctx, NK_KEY_ENTER, down); + else if (sym == SDLK_TAB) + nk_input_key(ctx, NK_KEY_TAB, down); + else if (sym == SDLK_BACKSPACE) + nk_input_key(ctx, NK_KEY_BACKSPACE, down); + else if (sym == SDLK_HOME) { + nk_input_key(ctx, NK_KEY_TEXT_START, down); + nk_input_key(ctx, NK_KEY_SCROLL_START, down); + } else if (sym == SDLK_END) { + nk_input_key(ctx, NK_KEY_TEXT_END, down); + nk_input_key(ctx, NK_KEY_SCROLL_END, down); + } else if (sym == SDLK_PAGEDOWN) { + nk_input_key(ctx, NK_KEY_SCROLL_DOWN, down); + } else if (sym == SDLK_PAGEUP) { + nk_input_key(ctx, NK_KEY_SCROLL_UP, down); + } else if (sym == SDLK_z) + nk_input_key(ctx, NK_KEY_TEXT_UNDO, down && state[SDL_SCANCODE_LCTRL]); + else if (sym == SDLK_r) + nk_input_key(ctx, NK_KEY_TEXT_REDO, down && state[SDL_SCANCODE_LCTRL]); + else if (sym == SDLK_c) + nk_input_key(ctx, NK_KEY_COPY, down && state[SDL_SCANCODE_LCTRL]); + else if (sym == SDLK_v) + nk_input_key(ctx, NK_KEY_PASTE, down && state[SDL_SCANCODE_LCTRL]); + else if (sym == SDLK_x) + nk_input_key(ctx, NK_KEY_CUT, down && state[SDL_SCANCODE_LCTRL]); + else if (sym == SDLK_b) + nk_input_key(ctx, NK_KEY_TEXT_LINE_START, down && state[SDL_SCANCODE_LCTRL]); + else if (sym == SDLK_e) + nk_input_key(ctx, NK_KEY_TEXT_LINE_END, down && state[SDL_SCANCODE_LCTRL]); + else if (sym == SDLK_UP) + nk_input_key(ctx, NK_KEY_UP, down); + else if (sym == SDLK_DOWN) + nk_input_key(ctx, NK_KEY_DOWN, down); + else if (sym == SDLK_LEFT) { + if (state[SDL_SCANCODE_LCTRL]) + nk_input_key(ctx, NK_KEY_TEXT_WORD_LEFT, down); + else nk_input_key(ctx, NK_KEY_LEFT, down); + } else if (sym == SDLK_RIGHT) { + if (state[SDL_SCANCODE_LCTRL]) + nk_input_key(ctx, NK_KEY_TEXT_WORD_RIGHT, down); + else nk_input_key(ctx, NK_KEY_RIGHT, down); + } else return 0; + return 1; + } else if (evt->type == SDL_MOUSEBUTTONDOWN || evt->type == SDL_MOUSEBUTTONUP) { + /* mouse button */ + int down = evt->type == SDL_MOUSEBUTTONDOWN; + const int x = evt->button.x, y = evt->button.y; + if (evt->button.button == SDL_BUTTON_LEFT) + nk_input_button(ctx, NK_BUTTON_LEFT, x, y, down); + if (evt->button.button == SDL_BUTTON_MIDDLE) + nk_input_button(ctx, NK_BUTTON_MIDDLE, x, y, down); + if (evt->button.button == SDL_BUTTON_RIGHT) + nk_input_button(ctx, NK_BUTTON_RIGHT, x, y, down); + return 1; + } else if (evt->type == SDL_MOUSEMOTION) { + if (ctx->input.mouse.grabbed) { + int x = (int)ctx->input.mouse.prev.x, y = (int)ctx->input.mouse.prev.y; + nk_input_motion(ctx, x + evt->motion.xrel, y + evt->motion.yrel); + } else nk_input_motion(ctx, evt->motion.x, evt->motion.y); + return 1; + } else if (evt->type == SDL_TEXTINPUT) { + nk_glyph glyph; + memcpy(glyph, evt->text.text, NK_UTF_SIZE); + nk_input_glyph(ctx, glyph); + return 1; + } else if (evt->type == SDL_MOUSEWHEEL) { + nk_input_scroll(ctx,(float)evt->wheel.y); + return 1; + } + return 0; +} + +NK_API +void nk_sdl_shutdown(void) +{ + nk_font_atlas_clear(&sdl.atlas); + nk_free(&sdl.ctx); + nk_sdl_device_destroy(); + memset(&sdl, 0, sizeof(sdl)); +} + +#endif diff --git a/nuklear/demo/style.c b/nuklear/demo/style.c new file mode 100644 index 0000000..8cea152 --- /dev/null +++ b/nuklear/demo/style.c @@ -0,0 +1,132 @@ +enum theme {THEME_BLACK, THEME_WHITE, THEME_RED, THEME_BLUE, THEME_DARK}; + +void +set_style(struct nk_context *ctx, enum theme theme) +{ + struct nk_color table[NK_COLOR_COUNT]; + if (theme == THEME_WHITE) { + table[NK_COLOR_TEXT] = nk_rgba(70, 70, 70, 255); + table[NK_COLOR_WINDOW] = nk_rgba(175, 175, 175, 255); + table[NK_COLOR_HEADER] = nk_rgba(175, 175, 175, 255); + table[NK_COLOR_BORDER] = nk_rgba(0, 0, 0, 255); + table[NK_COLOR_BUTTON] = nk_rgba(185, 185, 185, 255); + table[NK_COLOR_BUTTON_HOVER] = nk_rgba(170, 170, 170, 255); + table[NK_COLOR_BUTTON_ACTIVE] = nk_rgba(160, 160, 160, 255); + table[NK_COLOR_TOGGLE] = nk_rgba(150, 150, 150, 255); + table[NK_COLOR_TOGGLE_HOVER] = nk_rgba(120, 120, 120, 255); + table[NK_COLOR_TOGGLE_CURSOR] = nk_rgba(175, 175, 175, 255); + table[NK_COLOR_SELECT] = nk_rgba(190, 190, 190, 255); + table[NK_COLOR_SELECT_ACTIVE] = nk_rgba(175, 175, 175, 255); + table[NK_COLOR_SLIDER] = nk_rgba(190, 190, 190, 255); + table[NK_COLOR_SLIDER_CURSOR] = nk_rgba(80, 80, 80, 255); + table[NK_COLOR_SLIDER_CURSOR_HOVER] = nk_rgba(70, 70, 70, 255); + table[NK_COLOR_SLIDER_CURSOR_ACTIVE] = nk_rgba(60, 60, 60, 255); + table[NK_COLOR_PROPERTY] = nk_rgba(175, 175, 175, 255); + table[NK_COLOR_EDIT] = nk_rgba(150, 150, 150, 255); + table[NK_COLOR_EDIT_CURSOR] = nk_rgba(0, 0, 0, 255); + table[NK_COLOR_COMBO] = nk_rgba(175, 175, 175, 255); + table[NK_COLOR_CHART] = nk_rgba(160, 160, 160, 255); + table[NK_COLOR_CHART_COLOR] = nk_rgba(45, 45, 45, 255); + table[NK_COLOR_CHART_COLOR_HIGHLIGHT] = nk_rgba( 255, 0, 0, 255); + table[NK_COLOR_SCROLLBAR] = nk_rgba(180, 180, 180, 255); + table[NK_COLOR_SCROLLBAR_CURSOR] = nk_rgba(140, 140, 140, 255); + table[NK_COLOR_SCROLLBAR_CURSOR_HOVER] = nk_rgba(150, 150, 150, 255); + table[NK_COLOR_SCROLLBAR_CURSOR_ACTIVE] = nk_rgba(160, 160, 160, 255); + table[NK_COLOR_TAB_HEADER] = nk_rgba(180, 180, 180, 255); + nk_style_from_table(ctx, table); + } else if (theme == THEME_RED) { + table[NK_COLOR_TEXT] = nk_rgba(190, 190, 190, 255); + table[NK_COLOR_WINDOW] = nk_rgba(30, 33, 40, 215); + table[NK_COLOR_HEADER] = nk_rgba(181, 45, 69, 220); + table[NK_COLOR_BORDER] = nk_rgba(51, 55, 67, 255); + table[NK_COLOR_BUTTON] = nk_rgba(181, 45, 69, 255); + table[NK_COLOR_BUTTON_HOVER] = nk_rgba(190, 50, 70, 255); + table[NK_COLOR_BUTTON_ACTIVE] = nk_rgba(195, 55, 75, 255); + table[NK_COLOR_TOGGLE] = nk_rgba(51, 55, 67, 255); + table[NK_COLOR_TOGGLE_HOVER] = nk_rgba(45, 60, 60, 255); + table[NK_COLOR_TOGGLE_CURSOR] = nk_rgba(181, 45, 69, 255); + table[NK_COLOR_SELECT] = nk_rgba(51, 55, 67, 255); + table[NK_COLOR_SELECT_ACTIVE] = nk_rgba(181, 45, 69, 255); + table[NK_COLOR_SLIDER] = nk_rgba(51, 55, 67, 255); + table[NK_COLOR_SLIDER_CURSOR] = nk_rgba(181, 45, 69, 255); + table[NK_COLOR_SLIDER_CURSOR_HOVER] = nk_rgba(186, 50, 74, 255); + table[NK_COLOR_SLIDER_CURSOR_ACTIVE] = nk_rgba(191, 55, 79, 255); + table[NK_COLOR_PROPERTY] = nk_rgba(51, 55, 67, 255); + table[NK_COLOR_EDIT] = nk_rgba(51, 55, 67, 225); + table[NK_COLOR_EDIT_CURSOR] = nk_rgba(190, 190, 190, 255); + table[NK_COLOR_COMBO] = nk_rgba(51, 55, 67, 255); + table[NK_COLOR_CHART] = nk_rgba(51, 55, 67, 255); + table[NK_COLOR_CHART_COLOR] = nk_rgba(170, 40, 60, 255); + table[NK_COLOR_CHART_COLOR_HIGHLIGHT] = nk_rgba( 255, 0, 0, 255); + table[NK_COLOR_SCROLLBAR] = nk_rgba(30, 33, 40, 255); + table[NK_COLOR_SCROLLBAR_CURSOR] = nk_rgba(64, 84, 95, 255); + table[NK_COLOR_SCROLLBAR_CURSOR_HOVER] = nk_rgba(70, 90, 100, 255); + table[NK_COLOR_SCROLLBAR_CURSOR_ACTIVE] = nk_rgba(75, 95, 105, 255); + table[NK_COLOR_TAB_HEADER] = nk_rgba(181, 45, 69, 220); + nk_style_from_table(ctx, table); + } else if (theme == THEME_BLUE) { + table[NK_COLOR_TEXT] = nk_rgba(20, 20, 20, 255); + table[NK_COLOR_WINDOW] = nk_rgba(202, 212, 214, 215); + table[NK_COLOR_HEADER] = nk_rgba(137, 182, 224, 220); + table[NK_COLOR_BORDER] = nk_rgba(140, 159, 173, 255); + table[NK_COLOR_BUTTON] = nk_rgba(137, 182, 224, 255); + table[NK_COLOR_BUTTON_HOVER] = nk_rgba(142, 187, 229, 255); + table[NK_COLOR_BUTTON_ACTIVE] = nk_rgba(147, 192, 234, 255); + table[NK_COLOR_TOGGLE] = nk_rgba(177, 210, 210, 255); + table[NK_COLOR_TOGGLE_HOVER] = nk_rgba(182, 215, 215, 255); + table[NK_COLOR_TOGGLE_CURSOR] = nk_rgba(137, 182, 224, 255); + table[NK_COLOR_SELECT] = nk_rgba(177, 210, 210, 255); + table[NK_COLOR_SELECT_ACTIVE] = nk_rgba(137, 182, 224, 255); + table[NK_COLOR_SLIDER] = nk_rgba(177, 210, 210, 255); + table[NK_COLOR_SLIDER_CURSOR] = nk_rgba(137, 182, 224, 245); + table[NK_COLOR_SLIDER_CURSOR_HOVER] = nk_rgba(142, 188, 229, 255); + table[NK_COLOR_SLIDER_CURSOR_ACTIVE] = nk_rgba(147, 193, 234, 255); + table[NK_COLOR_PROPERTY] = nk_rgba(210, 210, 210, 255); + table[NK_COLOR_EDIT] = nk_rgba(210, 210, 210, 225); + table[NK_COLOR_EDIT_CURSOR] = nk_rgba(20, 20, 20, 255); + table[NK_COLOR_COMBO] = nk_rgba(210, 210, 210, 255); + table[NK_COLOR_CHART] = nk_rgba(210, 210, 210, 255); + table[NK_COLOR_CHART_COLOR] = nk_rgba(137, 182, 224, 255); + table[NK_COLOR_CHART_COLOR_HIGHLIGHT] = nk_rgba( 255, 0, 0, 255); + table[NK_COLOR_SCROLLBAR] = nk_rgba(190, 200, 200, 255); + table[NK_COLOR_SCROLLBAR_CURSOR] = nk_rgba(64, 84, 95, 255); + table[NK_COLOR_SCROLLBAR_CURSOR_HOVER] = nk_rgba(70, 90, 100, 255); + table[NK_COLOR_SCROLLBAR_CURSOR_ACTIVE] = nk_rgba(75, 95, 105, 255); + table[NK_COLOR_TAB_HEADER] = nk_rgba(156, 193, 220, 255); + nk_style_from_table(ctx, table); + } else if (theme == THEME_DARK) { + table[NK_COLOR_TEXT] = nk_rgba(210, 210, 210, 255); + table[NK_COLOR_WINDOW] = nk_rgba(57, 67, 71, 215); + table[NK_COLOR_HEADER] = nk_rgba(51, 51, 56, 220); + table[NK_COLOR_BORDER] = nk_rgba(46, 46, 46, 255); + table[NK_COLOR_BUTTON] = nk_rgba(48, 83, 111, 255); + table[NK_COLOR_BUTTON_HOVER] = nk_rgba(58, 93, 121, 255); + table[NK_COLOR_BUTTON_ACTIVE] = nk_rgba(63, 98, 126, 255); + table[NK_COLOR_TOGGLE] = nk_rgba(50, 58, 61, 255); + table[NK_COLOR_TOGGLE_HOVER] = nk_rgba(45, 53, 56, 255); + table[NK_COLOR_TOGGLE_CURSOR] = nk_rgba(48, 83, 111, 255); + table[NK_COLOR_SELECT] = nk_rgba(57, 67, 61, 255); + table[NK_COLOR_SELECT_ACTIVE] = nk_rgba(48, 83, 111, 255); + table[NK_COLOR_SLIDER] = nk_rgba(50, 58, 61, 255); + table[NK_COLOR_SLIDER_CURSOR] = nk_rgba(48, 83, 111, 245); + table[NK_COLOR_SLIDER_CURSOR_HOVER] = nk_rgba(53, 88, 116, 255); + table[NK_COLOR_SLIDER_CURSOR_ACTIVE] = nk_rgba(58, 93, 121, 255); + table[NK_COLOR_PROPERTY] = nk_rgba(50, 58, 61, 255); + table[NK_COLOR_EDIT] = nk_rgba(50, 58, 61, 225); + table[NK_COLOR_EDIT_CURSOR] = nk_rgba(210, 210, 210, 255); + table[NK_COLOR_COMBO] = nk_rgba(50, 58, 61, 255); + table[NK_COLOR_CHART] = nk_rgba(50, 58, 61, 255); + table[NK_COLOR_CHART_COLOR] = nk_rgba(48, 83, 111, 255); + table[NK_COLOR_CHART_COLOR_HIGHLIGHT] = nk_rgba(255, 0, 0, 255); + table[NK_COLOR_SCROLLBAR] = nk_rgba(50, 58, 61, 255); + table[NK_COLOR_SCROLLBAR_CURSOR] = nk_rgba(48, 83, 111, 255); + table[NK_COLOR_SCROLLBAR_CURSOR_HOVER] = nk_rgba(53, 88, 116, 255); + table[NK_COLOR_SCROLLBAR_CURSOR_ACTIVE] = nk_rgba(58, 93, 121, 255); + table[NK_COLOR_TAB_HEADER] = nk_rgba(48, 83, 111, 255); + nk_style_from_table(ctx, table); + } else { + nk_style_default(ctx); + } +} + + diff --git a/nuklear/demo/x11/Makefile b/nuklear/demo/x11/Makefile new file mode 100644 index 0000000..0570089 --- /dev/null +++ b/nuklear/demo/x11/Makefile @@ -0,0 +1,13 @@ +# Install +BIN = zahnrad + +# Flags +CFLAGS = -std=c89 -pedantic -O2 + +SRC = main.c +OBJ = $(SRC:.c=.o) + +$(BIN): + @mkdir -p bin + rm -f bin/$(BIN) $(OBJS) + $(CC) $(SRC) $(CFLAGS) -D_POSIX_C_SOURCE=200809L -o bin/$(BIN) -lX11 -lm diff --git a/nuklear/demo/x11/main.c b/nuklear/demo/x11/main.c new file mode 100644 index 0000000..8f4b12c --- /dev/null +++ b/nuklear/demo/x11/main.c @@ -0,0 +1,203 @@ +/* nuklear - v1.17 - public domain */ +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <limits.h> +#include <math.h> +#include <sys/time.h> +#include <unistd.h> +#include <time.h> + +#define NK_INCLUDE_FIXED_TYPES +#define NK_INCLUDE_STANDARD_IO +#define NK_INCLUDE_STANDARD_VARARGS +#define NK_INCLUDE_DEFAULT_ALLOCATOR +#define NK_IMPLEMENTATION +#define NK_XLIB_IMPLEMENTATION +#include "../../nuklear.h" +#include "nuklear_xlib.h" + +#define DTIME 20 +#define WINDOW_WIDTH 800 +#define WINDOW_HEIGHT 600 + +#define UNUSED(a) (void)a +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) < (b) ? (b) : (a)) +#define LEN(a) (sizeof(a)/sizeof(a)[0]) + +typedef struct XWindow XWindow; +struct XWindow { + Display *dpy; + Window root; + Visual *vis; + Colormap cmap; + XWindowAttributes attr; + XSetWindowAttributes swa; + Window win; + int screen; + XFont *font; + unsigned int width; + unsigned int height; +}; + +static void +die(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fputs("\n", stderr); + exit(EXIT_FAILURE); +} + +static void* +xcalloc(size_t siz, size_t n) +{ + void *ptr = calloc(siz, n); + if (!ptr) die("Out of memory\n"); + return ptr; +} + +static long +timestamp(void) +{ + struct timeval tv; + if (gettimeofday(&tv, NULL) < 0) return 0; + return (long)((long)tv.tv_sec * 1000 + (long)tv.tv_usec/1000); +} + +static void +sleep_for(long t) +{ + struct timespec req; + const time_t sec = (int)(t/1000); + const long ms = t - (sec * 1000); + req.tv_sec = sec; + req.tv_nsec = ms * 1000000L; + while(-1 == nanosleep(&req, &req)); +} + +/* =============================================================== + * + * EXAMPLE + * + * ===============================================================*/ +/* This are some code examples to provide a small overview of what can be + * done with this library. To try out an example uncomment the include + * and the corresponding function. */ +/*#include "../style.c"*/ +/*#include "../calculator.c"*/ +#include "../overview.c" +#include "../node_editor.c" + +/* =============================================================== + * + * DEMO + * + * ===============================================================*/ +int +main(void) +{ + long dt; + long started; + int running = 1; + XWindow xw; + struct nk_context *ctx; + + /* X11 */ + memset(&xw, 0, sizeof xw); + xw.dpy = XOpenDisplay(NULL); + if (!xw.dpy) die("Could not open a display; perhaps $DISPLAY is not set?"); + + xw.root = DefaultRootWindow(xw.dpy); + xw.screen = XDefaultScreen(xw.dpy); + xw.vis = XDefaultVisual(xw.dpy, xw.screen); + xw.cmap = XCreateColormap(xw.dpy,xw.root,xw.vis,AllocNone); + xw.swa.colormap = xw.cmap; + xw.swa.event_mask = + ExposureMask | KeyPressMask | KeyReleaseMask | + ButtonPress | ButtonReleaseMask| ButtonMotionMask | + Button1MotionMask | Button3MotionMask | Button4MotionMask | Button5MotionMask| + PointerMotionMask | KeymapStateMask; + xw.win = XCreateWindow(xw.dpy, xw.root, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 0, + XDefaultDepth(xw.dpy, xw.screen), InputOutput, + xw.vis, CWEventMask | CWColormap, &xw.swa); + XStoreName(xw.dpy, xw.win, "X11"); + XMapWindow(xw.dpy, xw.win); + XGetWindowAttributes(xw.dpy, xw.win, &xw.attr); + xw.width = (unsigned int)xw.attr.width; + xw.height = (unsigned int)xw.attr.height; + + /* GUI */ + xw.font = nk_xfont_create(xw.dpy, "fixed"); + ctx = nk_xlib_init(xw.font, xw.dpy, xw.screen, xw.win, xw.width, xw.height); + + /* style.c */ + /*set_style(ctx, THEME_WHITE);*/ + /*set_style(ctx, THEME_RED);*/ + /*set_style(ctx, THEME_BLUE);*/ + /*set_style(ctx, THEME_DARK);*/ + + while (running) + { + /* Input */ + XEvent evt; + started = timestamp(); + nk_input_begin(ctx); + while (XCheckWindowEvent(xw.dpy, xw.win, xw.swa.event_mask, &evt)){ + if (XFilterEvent(&evt, xw.win)) continue; + nk_xlib_handle_event(xw.dpy, xw.screen, xw.win, &evt); + } + nk_input_end(ctx); + + /* GUI */ + if (nk_begin(ctx, "Demo", nk_rect(50, 50, 200, 200), + NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_SCALABLE| + NK_WINDOW_CLOSABLE|NK_WINDOW_MINIMIZABLE|NK_WINDOW_TITLE)) + { + enum {EASY, HARD}; + static int op = EASY; + static int property = 20; + + nk_layout_row_static(ctx, 30, 80, 1); + if (nk_button_label(ctx, "button")) + fprintf(stdout, "button pressed\n"); + nk_layout_row_dynamic(ctx, 30, 2); + if (nk_option_label(ctx, "easy", op == EASY)) op = EASY; + if (nk_option_label(ctx, "hard", op == HARD)) op = HARD; + nk_layout_row_dynamic(ctx, 25, 1); + nk_property_int(ctx, "Compression:", 0, &property, 100, 10, 1); + } + nk_end(ctx); + if (nk_window_is_closed(ctx, "Demo")) break; + + /* -------------- EXAMPLES ---------------- */ + /*calculator(ctx);*/ + overview(ctx); + node_editor(ctx); + /* ----------------------------------------- */ + + /* Draw */ + XClearWindow(xw.dpy, xw.win); + nk_xlib_render(xw.win, nk_rgb(30,30,30)); + XFlush(xw.dpy); + + /* Timing */ + dt = timestamp() - started; + if (dt < DTIME) + sleep_for(DTIME - dt); + } + + nk_xfont_del(xw.dpy, xw.font); + nk_xlib_shutdown(); + XUnmapWindow(xw.dpy, xw.win); + XFreeColormap(xw.dpy, xw.cmap); + XDestroyWindow(xw.dpy, xw.win); + XCloseDisplay(xw.dpy); + return 0; +} + diff --git a/nuklear/demo/x11/nuklear_xlib.h b/nuklear/demo/x11/nuklear_xlib.h new file mode 100644 index 0000000..df66d78 --- /dev/null +++ b/nuklear/demo/x11/nuklear_xlib.h @@ -0,0 +1,693 @@ +/* + * Nuklear - v1.17 - public domain + * no warrenty implied; use at your own risk. + * authored from 2015-2016 by Micha Mettke + */ +/* + * ============================================================== + * + * API + * + * =============================================================== + */ +#ifndef NK_XLIB_H_ +#define NK_XLIB_H_ + +#include <X11/Xlib.h> +/* Font */ +typedef struct XFont XFont; +NK_API XFont* nk_xfont_create(Display *dpy, const char *name); +NK_API void nk_xfont_del(Display *dpy, XFont *font); + +NK_API struct nk_context* nk_xlib_init(XFont *font, Display *dpy, int screen, Window root, unsigned int w, unsigned int h); +NK_API int nk_xlib_handle_event(Display *dpy, int screen, Window win, XEvent *evt); +NK_API void nk_xlib_render(Drawable screen, struct nk_color clear); +NK_API void nk_xlib_shutdown(void); +NK_API void nk_xlib_set_font(XFont *font); + +#endif +/* + * ============================================================== + * + * IMPLEMENTATION + * + * =============================================================== + */ +#ifdef NK_XLIB_IMPLEMENTATION +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xresource.h> +#include <X11/Xlocale.h> + +typedef struct XSurface XSurface; +struct XFont { + int ascent; + int descent; + int height; + XFontSet set; + XFontStruct *xfont; + struct nk_user_font handle; +}; +struct XSurface { + GC gc; + Display *dpy; + int screen; + Window root; + Drawable drawable; + unsigned int w, h; +}; +static struct { + struct nk_context ctx; + struct XSurface *surf; + Cursor cursor; + Display *dpy; + Window root; +} xlib; + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif +#ifndef MAX +#define MAX(a,b) ((a) < (b) ? (b) : (a)) +#endif + +static unsigned long +nk_color_from_byte(const nk_byte *c) +{ + unsigned long res = 0; + res |= (unsigned long)c[0] << 16; + res |= (unsigned long)c[1] << 8; + res |= (unsigned long)c[2] << 0; + return (res); +} + +static XSurface* +nk_xsurf_create(int screen, unsigned int w, unsigned int h) +{ + XSurface *surface = (XSurface*)calloc(1, sizeof(XSurface)); + surface->w = w; + surface->h = h; + surface->dpy = xlib.dpy; + surface->screen = screen; + surface->root = xlib.root; + surface->gc = XCreateGC(xlib.dpy, xlib.root, 0, NULL); + XSetLineAttributes(xlib.dpy, surface->gc, 1, LineSolid, CapButt, JoinMiter); + surface->drawable = XCreatePixmap(xlib.dpy, xlib.root, w, h, + (unsigned int)DefaultDepth(xlib.dpy, screen)); + return surface; +} + +static void +nk_xsurf_resize(XSurface *surf, unsigned int w, unsigned int h) +{ + if(!surf) return; + if (surf->w == w && surf->h == h) return; + surf->w = w; surf->h = h; + if(surf->drawable) XFreePixmap(surf->dpy, surf->drawable); + surf->drawable = XCreatePixmap(surf->dpy, surf->root, w, h, + (unsigned int)DefaultDepth(surf->dpy, surf->screen)); +} + +static void +nk_xsurf_scissor(XSurface *surf, float x, float y, float w, float h) +{ + XRectangle clip_rect; + clip_rect.x = (short)(x-1); + clip_rect.y = (short)(y-1); + clip_rect.width = (unsigned short)(w+2); + clip_rect.height = (unsigned short)(h+2); + XSetClipRectangles(surf->dpy, surf->gc, 0, 0, &clip_rect, 1, Unsorted); +} + +static void +nk_xsurf_stroke_line(XSurface *surf, short x0, short y0, short x1, + short y1, unsigned int line_thickness, struct nk_color col) +{ + unsigned long c = nk_color_from_byte(&col.r); + XSetForeground(surf->dpy, surf->gc, c); + XSetLineAttributes(surf->dpy, surf->gc, line_thickness, LineSolid, CapButt, JoinMiter); + XDrawLine(surf->dpy, surf->drawable, surf->gc, (int)x0, (int)y0, (int)x1, (int)y1); + XSetLineAttributes(surf->dpy, surf->gc, 1, LineSolid, CapButt, JoinMiter); +} + +static void +nk_xsurf_stroke_rect(XSurface* surf, short x, short y, unsigned short w, + unsigned short h, unsigned short r, unsigned short line_thickness, struct nk_color col) +{ + unsigned long c = nk_color_from_byte(&col.r); + XSetForeground(surf->dpy, surf->gc, c); + XSetLineAttributes(surf->dpy, surf->gc, line_thickness, LineSolid, CapButt, JoinMiter); + if (r == 0) { + XDrawRectangle(surf->dpy, surf->drawable, surf->gc, x, y, w, h); + } else { + short xc = x + r; + short yc = y + r; + short wc = (short)(w - 2 * r); + short hc = (short)(h - 2 * r); + + XDrawLine(surf->dpy, surf->drawable, surf->gc, xc, y, xc+wc, y); + XDrawLine(surf->dpy, surf->drawable, surf->gc, x+w, yc, x+w, yc+hc); + XDrawLine(surf->dpy, surf->drawable, surf->gc, xc, y+h, xc+wc, y+h); + XDrawLine(surf->dpy, surf->drawable, surf->gc, x, yc, x, yc+hc); + + XDrawArc(surf->dpy, surf->drawable, surf->gc, xc + wc - r, y, + (unsigned)r*2, (unsigned)r*2, 0 * 64, 90 * 64); + XDrawArc(surf->dpy, surf->drawable, surf->gc, x, y, + (unsigned)r*2, (unsigned)r*2, 90 * 64, 90 * 64); + XDrawArc(surf->dpy, surf->drawable, surf->gc, x, yc + hc - r, + (unsigned)r*2, (unsigned)2*r, 180 * 64, 90 * 64); + XDrawArc(surf->dpy, surf->drawable, surf->gc, xc + wc - r, yc + hc - r, + (unsigned)r*2, (unsigned)2*r, -90 * 64, 90 * 64); + } + XSetLineAttributes(surf->dpy, surf->gc, 1, LineSolid, CapButt, JoinMiter); +} + +static void +nk_xsurf_fill_rect(XSurface* surf, short x, short y, unsigned short w, + unsigned short h, unsigned short r, struct nk_color col) +{ + unsigned long c = nk_color_from_byte(&col.r); + XSetForeground(surf->dpy, surf->gc, c); + if (r == 0) { + XFillRectangle(surf->dpy, surf->drawable, surf->gc, x, y, w, h); + } else { + short xc = x + r; + short yc = y + r; + short wc = (short)(w - 2 * r); + short hc = (short)(h - 2 * r); + + XPoint pnts[12]; + pnts[0].x = x; + pnts[0].y = yc; + pnts[1].x = xc; + pnts[1].y = yc; + pnts[2].x = xc; + pnts[2].y = y; + + pnts[3].x = xc + wc; + pnts[3].y = y; + pnts[4].x = xc + wc; + pnts[4].y = yc; + pnts[5].x = x + w; + pnts[5].y = yc; + + pnts[6].x = x + w; + pnts[6].y = yc + hc; + pnts[7].x = xc + wc; + pnts[7].y = yc + hc; + pnts[8].x = xc + wc; + pnts[8].y = y + h; + + pnts[9].x = xc; + pnts[9].y = y + h; + pnts[10].x = xc; + pnts[10].y = yc + hc; + pnts[11].x = x; + pnts[11].y = yc + hc; + + XFillPolygon(surf->dpy, surf->drawable, surf->gc, pnts, 12, Convex, CoordModeOrigin); + XFillArc(surf->dpy, surf->drawable, surf->gc, xc + wc - r, y, + (unsigned)r*2, (unsigned)r*2, 0 * 64, 90 * 64); + XFillArc(surf->dpy, surf->drawable, surf->gc, x, y, + (unsigned)r*2, (unsigned)r*2, 90 * 64, 90 * 64); + XFillArc(surf->dpy, surf->drawable, surf->gc, x, yc + hc - r, + (unsigned)r*2, (unsigned)2*r, 180 * 64, 90 * 64); + XFillArc(surf->dpy, surf->drawable, surf->gc, xc + wc - r, yc + hc - r, + (unsigned)r*2, (unsigned)2*r, -90 * 64, 90 * 64); + } +} + +static void +nk_xsurf_fill_triangle(XSurface *surf, short x0, short y0, short x1, + short y1, short x2, short y2, struct nk_color col) +{ + XPoint pnts[3]; + unsigned long c = nk_color_from_byte(&col.r); + pnts[0].x = (short)x0; + pnts[0].y = (short)y0; + pnts[1].x = (short)x1; + pnts[1].y = (short)y1; + pnts[2].x = (short)x2; + pnts[2].y = (short)y2; + XSetForeground(surf->dpy, surf->gc, c); + XFillPolygon(surf->dpy, surf->drawable, surf->gc, pnts, 3, Convex, CoordModeOrigin); +} + +static void +nk_xsurf_stroke_triangle(XSurface *surf, short x0, short y0, short x1, + short y1, short x2, short y2, unsigned short line_thickness, struct nk_color col) +{ + XPoint pnts[3]; + unsigned long c = nk_color_from_byte(&col.r); + XSetForeground(surf->dpy, surf->gc, c); + XSetLineAttributes(surf->dpy, surf->gc, line_thickness, LineSolid, CapButt, JoinMiter); + XDrawLine(surf->dpy, surf->drawable, surf->gc, x0, y0, x1, y1); + XDrawLine(surf->dpy, surf->drawable, surf->gc, x1, y1, x2, y2); + XDrawLine(surf->dpy, surf->drawable, surf->gc, x2, y2, x0, y0); + XSetLineAttributes(surf->dpy, surf->gc, 1, LineSolid, CapButt, JoinMiter); +} + +static void +nk_xsurf_fill_polygon(XSurface *surf, const struct nk_vec2i *pnts, int count, + struct nk_color col) +{ + int i = 0; + #define MAX_POINTS 64 + XPoint xpnts[MAX_POINTS]; + unsigned long c = nk_color_from_byte(&col.r); + XSetForeground(surf->dpy, surf->gc, c); + for (i = 0; i < count && i < MAX_POINTS; ++i) { + xpnts[i].x = pnts[i].x; + xpnts[i].y = pnts[i].y; + } + XFillPolygon(surf->dpy, surf->drawable, surf->gc, xpnts, count, Convex, CoordModeOrigin); + #undef MAX_POINTS +} + +static void +nk_xsurf_stroke_polygon(XSurface *surf, const struct nk_vec2i *pnts, int count, + unsigned short line_thickness, struct nk_color col) +{ + int i = 0; + unsigned long c = nk_color_from_byte(&col.r); + XSetForeground(surf->dpy, surf->gc, c); + XSetLineAttributes(surf->dpy, surf->gc, line_thickness, LineSolid, CapButt, JoinMiter); + for (i = 1; i < count; ++i) + XDrawLine(surf->dpy, surf->drawable, surf->gc, pnts[i-1].x, pnts[i-1].y, pnts[i].x, pnts[i].y); + XDrawLine(surf->dpy, surf->drawable, surf->gc, pnts[count-1].x, pnts[count-1].y, pnts[0].x, pnts[0].y); + XSetLineAttributes(surf->dpy, surf->gc, 1, LineSolid, CapButt, JoinMiter); +} + +static void +nk_xsurf_stroke_polyline(XSurface *surf, const struct nk_vec2i *pnts, + int count, unsigned short line_thickness, struct nk_color col) +{ + int i = 0; + unsigned long c = nk_color_from_byte(&col.r); + XSetLineAttributes(surf->dpy, surf->gc, line_thickness, LineSolid, CapButt, JoinMiter); + XSetForeground(surf->dpy, surf->gc, c); + for (i = 0; i < count-1; ++i) + XDrawLine(surf->dpy, surf->drawable, surf->gc, pnts[i].x, pnts[i].y, pnts[i+1].x, pnts[i+1].y); + XSetLineAttributes(surf->dpy, surf->gc, 1, LineSolid, CapButt, JoinMiter); +} + +static void +nk_xsurf_fill_circle(XSurface *surf, short x, short y, unsigned short w, + unsigned short h, struct nk_color col) +{ + unsigned long c = nk_color_from_byte(&col.r); + XSetForeground(surf->dpy, surf->gc, c); + XFillArc(surf->dpy, surf->drawable, surf->gc, (int)x, (int)y, + (unsigned)w, (unsigned)h, 0, 360 * 64); +} + +static void +nk_xsurf_stroke_circle(XSurface *surf, short x, short y, unsigned short w, + unsigned short h, unsigned short line_thickness, struct nk_color col) +{ + unsigned long c = nk_color_from_byte(&col.r); + XSetLineAttributes(surf->dpy, surf->gc, line_thickness, LineSolid, CapButt, JoinMiter); + XSetForeground(surf->dpy, surf->gc, c); + XDrawArc(surf->dpy, surf->drawable, surf->gc, (int)x, (int)y, + (unsigned)w, (unsigned)h, 0, 360 * 64); + XSetLineAttributes(surf->dpy, surf->gc, 1, LineSolid, CapButt, JoinMiter); +} + +static void +nk_xsurf_stroke_curve(XSurface *surf, struct nk_vec2i p1, + struct nk_vec2i p2, struct nk_vec2i p3, struct nk_vec2i p4, + unsigned int num_segments, unsigned short line_thickness, struct nk_color col) +{ + unsigned int i_step; + float t_step; + struct nk_vec2i last = p1; + + XSetLineAttributes(surf->dpy, surf->gc, line_thickness, LineSolid, CapButt, JoinMiter); + num_segments = MAX(num_segments, 1); + t_step = 1.0f/(float)num_segments; + for (i_step = 1; i_step <= num_segments; ++i_step) { + float t = t_step * (float)i_step; + float u = 1.0f - t; + float w1 = u*u*u; + float w2 = 3*u*u*t; + float w3 = 3*u*t*t; + float w4 = t * t *t; + float x = w1 * p1.x + w2 * p2.x + w3 * p3.x + w4 * p4.x; + float y = w1 * p1.y + w2 * p2.y + w3 * p3.y + w4 * p4.y; + nk_xsurf_stroke_line(surf, last.x, last.y, (short)x, (short)y, line_thickness,col); + last.x = (short)x; last.y = (short)y; + } + XSetLineAttributes(surf->dpy, surf->gc, 1, LineSolid, CapButt, JoinMiter); +} + +static void +nk_xsurf_draw_text(XSurface *surf, short x, short y, unsigned short w, unsigned short h, + const char *text, int len, XFont *font, struct nk_color cbg, struct nk_color cfg) +{ + int tx, ty; + unsigned long bg = nk_color_from_byte(&cbg.r); + unsigned long fg = nk_color_from_byte(&cfg.r); + + XSetForeground(surf->dpy, surf->gc, bg); + XFillRectangle(surf->dpy, surf->drawable, surf->gc, (int)x, (int)y, (unsigned)w, (unsigned)h); + if(!text || !font || !len) return; + + tx = (int)x; + ty = (int)y + font->ascent; + XSetForeground(surf->dpy, surf->gc, fg); + if(font->set) + XmbDrawString(surf->dpy,surf->drawable,font->set,surf->gc,tx,ty,(const char*)text,(int)len); + else + XDrawString(surf->dpy, surf->drawable, surf->gc, tx, ty, (const char*)text, (int)len); +} + +static void +nk_xsurf_clear(XSurface *surf, unsigned long color) +{ + XSetForeground(surf->dpy, surf->gc, color); + XFillRectangle(surf->dpy, surf->drawable, surf->gc, 0, 0, surf->w, surf->h); +} + +static void +nk_xsurf_blit(Drawable target, XSurface *surf, unsigned int w, unsigned int h) +{ + XCopyArea(surf->dpy, surf->drawable, target, surf->gc, 0, 0, w, h, 0, 0); +} + +static void +nk_xsurf_del(XSurface *surf) +{ + XFreePixmap(surf->dpy, surf->drawable); + XFreeGC(surf->dpy, surf->gc); + free(surf); +} + +XFont* +nk_xfont_create(Display *dpy, const char *name) +{ + int n; + char *def, **missing; + XFont *font = (XFont*)calloc(1, sizeof(XFont)); + font->set = XCreateFontSet(dpy, name, &missing, &n, &def); + if(missing) { + while(n--) + fprintf(stderr, "missing fontset: %s\n", missing[n]); + XFreeStringList(missing); + } + + if(font->set) { + XFontStruct **xfonts; + char **font_names; + XExtentsOfFontSet(font->set); + n = XFontsOfFontSet(font->set, &xfonts, &font_names); + while(n--) { + font->ascent = MAX(font->ascent, (*xfonts)->ascent); + font->descent = MAX(font->descent,(*xfonts)->descent); + xfonts++; + } + } else { + if(!(font->xfont = XLoadQueryFont(dpy, name)) + && !(font->xfont = XLoadQueryFont(dpy, "fixed"))) { + free(font); + return 0; + } + font->ascent = font->xfont->ascent; + font->descent = font->xfont->descent; + } + font->height = font->ascent + font->descent; + return font; +} + +static float +nk_xfont_get_text_width(nk_handle handle, float height, const char *text, int len) +{ + XFont *font = (XFont*)handle.ptr; + XRectangle r; + if(!font || !text) + return 0; + + if(font->set) { + XmbTextExtents(font->set, (const char*)text, len, NULL, &r); + return (float)r.width; + } else{ + int w = XTextWidth(font->xfont, (const char*)text, len); + return (float)w; + } +} + +void +nk_xfont_del(Display *dpy, XFont *font) +{ + if(!font) return; + if(font->set) + XFreeFontSet(dpy, font->set); + else + XFreeFont(dpy, font->xfont); + free(font); +} + +NK_API struct nk_context* +nk_xlib_init(XFont *xfont, Display *dpy, int screen, Window root, + unsigned int w, unsigned int h) +{ + struct nk_user_font *font = &xfont->handle; + font->userdata = nk_handle_ptr(xfont); + font->height = (float)xfont->height; + font->width = nk_xfont_get_text_width; + xlib.dpy = dpy; + xlib.root = root; + + if (!setlocale(LC_ALL,"")) return 0; + if (!XSupportsLocale()) return 0; + if (!XSetLocaleModifiers("@im=none")) return 0; + + /* create invisible cursor */ + {static XColor dummy; char data[1] = {0}; + Pixmap blank = XCreateBitmapFromData(dpy, root, data, 1, 1); + if (blank == None) return 0; + xlib.cursor = XCreatePixmapCursor(dpy, blank, blank, &dummy, &dummy, 0, 0); + XFreePixmap(dpy, blank);} + + xlib.surf = nk_xsurf_create(screen, w, h); + nk_init_default(&xlib.ctx, font); + return &xlib.ctx; +} + +NK_API void +nk_xlib_set_font(XFont *xfont) +{ + struct nk_user_font *font = &xfont->handle; + font->userdata = nk_handle_ptr(xfont); + font->height = (float)xfont->height; + font->width = nk_xfont_get_text_width; + nk_style_set_font(&xlib.ctx, font); +} + +NK_API int +nk_xlib_handle_event(Display *dpy, int screen, Window win, XEvent *evt) +{ + struct nk_context *ctx = &xlib.ctx; + + /* optional grabbing behavior */ + if (ctx->input.mouse.grab) { + XDefineCursor(xlib.dpy, xlib.root, xlib.cursor); + ctx->input.mouse.grab = 0; + } else if (ctx->input.mouse.ungrab) { + XWarpPointer(xlib.dpy, None, xlib.root, 0, 0, 0, 0, + (int)ctx->input.mouse.prev.x, (int)ctx->input.mouse.prev.y); + XUndefineCursor(xlib.dpy, xlib.root); + ctx->input.mouse.ungrab = 0; + } + + if (evt->type == KeyPress || evt->type == KeyRelease) + { + /* Key handler */ + int ret, down = (evt->type == KeyPress); + KeySym *code = XGetKeyboardMapping(xlib.surf->dpy, (KeyCode)evt->xkey.keycode, 1, &ret); + if (*code == XK_Shift_L || *code == XK_Shift_R) nk_input_key(ctx, NK_KEY_SHIFT, down); + else if (*code == XK_Delete) nk_input_key(ctx, NK_KEY_DEL, down); + else if (*code == XK_Return) nk_input_key(ctx, NK_KEY_ENTER, down); + else if (*code == XK_Tab) nk_input_key(ctx, NK_KEY_TAB, down); + else if (*code == XK_Left) nk_input_key(ctx, NK_KEY_LEFT, down); + else if (*code == XK_Right) nk_input_key(ctx, NK_KEY_RIGHT, down); + else if (*code == XK_Up) nk_input_key(ctx, NK_KEY_UP, down); + else if (*code == XK_Down) nk_input_key(ctx, NK_KEY_DOWN, down); + else if (*code == XK_BackSpace) nk_input_key(ctx, NK_KEY_BACKSPACE, down); + else if (*code == XK_Escape) nk_input_key(ctx, NK_KEY_TEXT_RESET_MODE, down); + else if (*code == XK_Page_Up) nk_input_key(ctx, NK_KEY_SCROLL_UP, down); + else if (*code == XK_Page_Down) nk_input_key(ctx, NK_KEY_SCROLL_DOWN, down); + else if (*code == XK_Home) { + nk_input_key(ctx, NK_KEY_TEXT_START, down); + nk_input_key(ctx, NK_KEY_SCROLL_START, down); + } else if (*code == XK_End) { + nk_input_key(ctx, NK_KEY_TEXT_END, down); + nk_input_key(ctx, NK_KEY_SCROLL_END, down); + } else { + if (*code == 'c' && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_COPY, down); + else if (*code == 'v' && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_PASTE, down); + else if (*code == 'x' && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_CUT, down); + else if (*code == 'z' && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_TEXT_UNDO, down); + else if (*code == 'r' && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_TEXT_REDO, down); + else if (*code == XK_Left && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_TEXT_WORD_LEFT, down); + else if (*code == XK_Right && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_TEXT_WORD_RIGHT, down); + else if (*code == 'b' && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_TEXT_LINE_START, down); + else if (*code == 'e' && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_TEXT_LINE_END, down); + else { + if (*code == 'i') + nk_input_key(ctx, NK_KEY_TEXT_INSERT_MODE, down); + else if (*code == 'r') + nk_input_key(ctx, NK_KEY_TEXT_REPLACE_MODE, down); + if (down) { + char buf[32]; + KeySym keysym = 0; + if (XLookupString((XKeyEvent*)evt, buf, 32, &keysym, NULL) != NoSymbol) + nk_input_glyph(ctx, buf); + } + } + } + XFree(code); + return 1; + } else if (evt->type == ButtonPress || evt->type == ButtonRelease) { + /* Button handler */ + int down = (evt->type == ButtonPress); + const int x = evt->xbutton.x, y = evt->xbutton.y; + if (evt->xbutton.button == Button1) + nk_input_button(ctx, NK_BUTTON_LEFT, x, y, down); + if (evt->xbutton.button == Button2) + nk_input_button(ctx, NK_BUTTON_MIDDLE, x, y, down); + else if (evt->xbutton.button == Button3) + nk_input_button(ctx, NK_BUTTON_RIGHT, x, y, down); + else if (evt->xbutton.button == Button4) + nk_input_scroll(ctx, 1.0f); + else if (evt->xbutton.button == Button5) + nk_input_scroll(ctx, -1.0f); + else return 0; + return 1; + } else if (evt->type == MotionNotify) { + /* Mouse motion handler */ + const int x = evt->xmotion.x, y = evt->xmotion.y; + nk_input_motion(ctx, x, y); + if (ctx->input.mouse.grabbed) { + ctx->input.mouse.pos.x = ctx->input.mouse.prev.x; + ctx->input.mouse.pos.y = ctx->input.mouse.prev.y; + XWarpPointer(xlib.dpy, None, xlib.surf->root, 0, 0, 0, 0, (int)ctx->input.mouse.pos.x, (int)ctx->input.mouse.pos.y); + } + return 1; + } else if (evt->type == Expose || evt->type == ConfigureNotify) { + /* Window resize handler */ + unsigned int width, height; + XWindowAttributes attr; + XGetWindowAttributes(dpy, win, &attr); + width = (unsigned int)attr.width; + height = (unsigned int)attr.height; + nk_xsurf_resize(xlib.surf, width, height); + return 1; + } else if (evt->type == KeymapNotify) { + XRefreshKeyboardMapping(&evt->xmapping); + return 1; + } + return 0; +} + +NK_API void +nk_xlib_shutdown(void) +{ + nk_xsurf_del(xlib.surf); + nk_free(&xlib.ctx); + XFreeCursor(xlib.dpy, xlib.cursor); + nk_memset(&xlib, 0, sizeof(xlib)); +} + +NK_API void +nk_xlib_render(Drawable screen, struct nk_color clear) +{ + const struct nk_command *cmd; + struct nk_context *ctx = &xlib.ctx; + XSurface *surf = xlib.surf; + + nk_xsurf_clear(xlib.surf, nk_color_from_byte(&clear.r)); + nk_foreach(cmd, &xlib.ctx) + { + switch (cmd->type) { + case NK_COMMAND_NOP: break; + case NK_COMMAND_SCISSOR: { + const struct nk_command_scissor *s =(const struct nk_command_scissor*)cmd; + nk_xsurf_scissor(surf, s->x, s->y, s->w, s->h); + } break; + case NK_COMMAND_LINE: { + const struct nk_command_line *l = (const struct nk_command_line *)cmd; + nk_xsurf_stroke_line(surf, l->begin.x, l->begin.y, l->end.x, + l->end.y, l->line_thickness, l->color); + } break; + case NK_COMMAND_RECT: { + const struct nk_command_rect *r = (const struct nk_command_rect *)cmd; + nk_xsurf_stroke_rect(surf, r->x, r->y, r->w, r->h, + (unsigned short)r->rounding, r->line_thickness, r->color); + } break; + case NK_COMMAND_RECT_FILLED: { + const struct nk_command_rect_filled *r = (const struct nk_command_rect_filled *)cmd; + nk_xsurf_fill_rect(surf, r->x, r->y, r->w, r->h, + (unsigned short)r->rounding, r->color); + } break; + case NK_COMMAND_CIRCLE: { + const struct nk_command_circle *c = (const struct nk_command_circle *)cmd; + nk_xsurf_stroke_circle(surf, c->x, c->y, c->w, c->h, c->line_thickness, c->color); + } break; + case NK_COMMAND_CIRCLE_FILLED: { + const struct nk_command_circle_filled *c = (const struct nk_command_circle_filled *)cmd; + nk_xsurf_fill_circle(surf, c->x, c->y, c->w, c->h, c->color); + } break; + case NK_COMMAND_TRIANGLE: { + const struct nk_command_triangle*t = (const struct nk_command_triangle*)cmd; + nk_xsurf_stroke_triangle(surf, t->a.x, t->a.y, t->b.x, t->b.y, + t->c.x, t->c.y, t->line_thickness, t->color); + } break; + case NK_COMMAND_TRIANGLE_FILLED: { + const struct nk_command_triangle_filled *t = (const struct nk_command_triangle_filled *)cmd; + nk_xsurf_fill_triangle(surf, t->a.x, t->a.y, t->b.x, t->b.y, + t->c.x, t->c.y, t->color); + } break; + case NK_COMMAND_POLYGON: { + const struct nk_command_polygon *p =(const struct nk_command_polygon*)cmd; + nk_xsurf_stroke_polygon(surf, p->points, p->point_count, p->line_thickness,p->color); + } break; + case NK_COMMAND_POLYGON_FILLED: { + const struct nk_command_polygon_filled *p = (const struct nk_command_polygon_filled *)cmd; + nk_xsurf_fill_polygon(surf, p->points, p->point_count, p->color); + } break; + case NK_COMMAND_POLYLINE: { + const struct nk_command_polyline *p = (const struct nk_command_polyline *)cmd; + nk_xsurf_stroke_polyline(surf, p->points, p->point_count, p->line_thickness, p->color); + } break; + case NK_COMMAND_TEXT: { + const struct nk_command_text *t = (const struct nk_command_text*)cmd; + nk_xsurf_draw_text(surf, t->x, t->y, t->w, t->h, + (const char*)t->string, t->length, + (XFont*)t->font->userdata.ptr, + t->background, t->foreground); + } break; + case NK_COMMAND_CURVE: { + const struct nk_command_curve *q = (const struct nk_command_curve *)cmd; + nk_xsurf_stroke_curve(surf, q->begin, q->ctrl[0], q->ctrl[1], + q->end, 22, q->line_thickness, q->color); + } break; + case NK_COMMAND_RECT_MULTI_COLOR: + case NK_COMMAND_IMAGE: + case NK_COMMAND_ARC: + case NK_COMMAND_ARC_FILLED: + default: break; + } + } + nk_clear(ctx); + nk_xsurf_blit(screen, surf, surf->w, surf->h); +} +#endif diff --git a/nuklear/demo/x11_opengl2/Makefile b/nuklear/demo/x11_opengl2/Makefile new file mode 100644 index 0000000..1173b6c --- /dev/null +++ b/nuklear/demo/x11_opengl2/Makefile @@ -0,0 +1,26 @@ +# Install +BIN = demo + +# Compiler +CC = clang +DCC = gcc + +# Flags +CFLAGS = -std=c99 -pedantic -O2 + +SRC = main.c +OBJ = $(SRC:.c=.o) + +# Modes +.PHONY: gcc +gcc: CC = gcc +gcc: $(BIN) + +.PHONY: clang +clang: CC = clang +clang: $(BIN) + +$(BIN): + @mkdir -p bin + rm -f bin/$(BIN) $(OBJS) + $(CC) $(SRC) $(CFLAGS) -o bin/$(BIN) -lX11 -lm -lGL -lm -lGLU diff --git a/nuklear/demo/x11_opengl2/main.c b/nuklear/demo/x11_opengl2/main.c new file mode 100644 index 0000000..cfe5604 --- /dev/null +++ b/nuklear/demo/x11_opengl2/main.c @@ -0,0 +1,320 @@ +/* nuklear - v1.17 - public domain */ +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdarg.h> +#include <string.h> +#include <math.h> +#include <assert.h> +#include <math.h> +#include <time.h> +#include <limits.h> + +#include <GL/glx.h> +#include <GL/glxext.h> + +#define NK_INCLUDE_FIXED_TYPES +#define NK_INCLUDE_STANDARD_IO +#define NK_INCLUDE_STANDARD_VARARGS +#define NK_INCLUDE_DEFAULT_ALLOCATOR +#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT +#define NK_INCLUDE_FONT_BAKING +#define NK_INCLUDE_DEFAULT_FONT +#define NK_IMPLEMENTATION +#define NK_XLIB_GL2_IMPLEMENTATION +#include "../../nuklear.h" +#include "nuklear_xlib_gl2.h" + +#define WINDOW_WIDTH 1200 +#define WINDOW_HEIGHT 800 + +#define MAX_VERTEX_BUFFER 512 * 1024 +#define MAX_ELEMENT_BUFFER 128 * 1024 + +#define UNUSED(a) (void)a +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) < (b) ? (b) : (a)) +#define LEN(a) (sizeof(a)/sizeof(a)[0]) + +/* =============================================================== + * + * EXAMPLE + * + * ===============================================================*/ +/* This are some code examples to provide a small overview of what can be + * done with this library. To try out an example uncomment the include + * and the corresponding function. */ +/*#include "../style.c"*/ +/*#include "../calculator.c"*/ +/*#include "../overview.c"*/ +/*#include "../node_editor.c"*/ + +/* =============================================================== + * + * DEMO + * + * ===============================================================*/ +struct XWindow { + Display *dpy; + Window win; + XVisualInfo *vis; + Colormap cmap; + XSetWindowAttributes swa; + XWindowAttributes attr; + GLXFBConfig fbc; + int width, height; +}; +static int gl_err = FALSE; +static int gl_error_handler(Display *dpy, XErrorEvent *ev) +{UNUSED((dpy, ev)); gl_err = TRUE;return 0;} + +static void +die(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fputs("\n", stderr); + exit(EXIT_FAILURE); +} + +static int +has_extension(const char *string, const char *ext) +{ + const char *start, *where, *term; + where = strchr(ext, ' '); + if (where || *ext == '\0') + return FALSE; + + for (start = string;;) { + where = strstr((const char*)start, ext); + if (!where) break; + term = where + strlen(ext); + if (where == start || *(where - 1) == ' ') { + if (*term == ' ' || *term == '\0') + return TRUE; + } + start = term; + } + return FALSE; +} + +int main(int argc, char **argv) +{ + /* Platform */ + int running = 1; + struct XWindow win; + GLXContext glContext; + struct nk_context *ctx; + struct nk_color background; + + memset(&win, 0, sizeof(win)); + win.dpy = XOpenDisplay(NULL); + if (!win.dpy) die("Failed to open X display\n"); + { + /* check glx version */ + int glx_major, glx_minor; + if (!glXQueryVersion(win.dpy, &glx_major, &glx_minor)) + die("[X11]: Error: Failed to query OpenGL version\n"); + if ((glx_major == 1 && glx_minor < 3) || (glx_major < 1)) + die("[X11]: Error: Invalid GLX version!\n"); + } + { + /* find and pick matching framebuffer visual */ + int fb_count; + static GLint attr[] = { + GLX_X_RENDERABLE, True, + GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, + GLX_RED_SIZE, 8, + GLX_GREEN_SIZE, 8, + GLX_BLUE_SIZE, 8, + GLX_ALPHA_SIZE, 8, + GLX_DEPTH_SIZE, 24, + GLX_STENCIL_SIZE, 8, + GLX_DOUBLEBUFFER, True, + None + }; + GLXFBConfig *fbc; + fbc = glXChooseFBConfig(win.dpy, DefaultScreen(win.dpy), attr, &fb_count); + if (!fbc) die("[X11]: Error: failed to retrieve framebuffer configuration\n"); + { + /* pick framebuffer with most samples per pixel */ + int i; + int fb_best = -1, best_num_samples = -1; + for (i = 0; i < fb_count; ++i) { + XVisualInfo *vi = glXGetVisualFromFBConfig(win.dpy, fbc[i]); + if (vi) { + int sample_buffer, samples; + glXGetFBConfigAttrib(win.dpy, fbc[i], GLX_SAMPLE_BUFFERS, &sample_buffer); + glXGetFBConfigAttrib(win.dpy, fbc[i], GLX_SAMPLES, &samples); + if ((fb_best < 0) || (sample_buffer && samples > best_num_samples)) + fb_best = i; best_num_samples = samples; + } + } + win.fbc = fbc[fb_best]; + XFree(fbc); + win.vis = glXGetVisualFromFBConfig(win.dpy, win.fbc); + } + } + { + /* create window */ + win.cmap = XCreateColormap(win.dpy, RootWindow(win.dpy, win.vis->screen), win.vis->visual, AllocNone); + win.swa.colormap = win.cmap; + win.swa.background_pixmap = None; + win.swa.border_pixel = 0; + win.swa.event_mask = + ExposureMask | KeyPressMask | KeyReleaseMask | + ButtonPress | ButtonReleaseMask| ButtonMotionMask | + Button1MotionMask | Button3MotionMask | Button4MotionMask | Button5MotionMask| + PointerMotionMask| StructureNotifyMask; + win.win = XCreateWindow(win.dpy, RootWindow(win.dpy, win.vis->screen), 0, 0, + WINDOW_WIDTH, WINDOW_HEIGHT, 0, win.vis->depth, InputOutput, + win.vis->visual, CWBorderPixel|CWColormap|CWEventMask, &win.swa); + if (!win.win) die("[X11]: Failed to create window\n"); + XFree(win.vis); + XStoreName(win.dpy, win.win, "Demo"); + XMapWindow(win.dpy, win.win); + } + { + /* create opengl context */ + typedef GLXContext(*glxCreateContext)(Display*, GLXFBConfig, GLXContext, Bool, const int*); + int(*old_handler)(Display*, XErrorEvent*) = XSetErrorHandler(gl_error_handler); + const char *extensions_str = glXQueryExtensionsString(win.dpy, DefaultScreen(win.dpy)); + glxCreateContext create_context = (glxCreateContext) + glXGetProcAddressARB((const GLubyte*)"glXCreateContextAttribsARB"); + + gl_err = FALSE; + if (!has_extension(extensions_str, "GLX_ARB_create_context") || !create_context) { + fprintf(stdout, "[X11]: glXCreateContextAttribARB() not found...\n"); + fprintf(stdout, "[X11]: ... using old-style GLX context\n"); + glContext = glXCreateNewContext(win.dpy, win.fbc, GLX_RGBA_TYPE, 0, True); + } else { + GLint attr[] = { + GLX_CONTEXT_MAJOR_VERSION_ARB, 3, + GLX_CONTEXT_MINOR_VERSION_ARB, 0, + None + }; + glContext = create_context(win.dpy, win.fbc, 0, True, attr); + XSync(win.dpy, False); + if (gl_err || !glContext) { + /* Could not create GL 3.0 context. Fallback to old 2.x context. + * If a version below 3.0 is requested, implementations will + * return the newest context version compatible with OpenGL + * version less than version 3.0.*/ + attr[1] = 1; attr[3] = 0; + gl_err = FALSE; + fprintf(stdout, "[X11] Failed to create OpenGL 3.0 context\n"); + fprintf(stdout, "[X11] ... using old-style GLX context!\n"); + glContext = create_context(win.dpy, win.fbc, 0, True, attr); + } + } + XSync(win.dpy, False); + XSetErrorHandler(old_handler); + if (gl_err || !glContext) + die("[X11]: Failed to create an OpenGL context\n"); + glXMakeCurrent(win.dpy, win.win, glContext); + } + + ctx = nk_x11_init(win.dpy, win.win); + /* Load Fonts: if none of these are loaded a default font will be used */ + /* Load Cursor: if you uncomment cursor loading please hide the cursor */ + {struct nk_font_atlas *atlas; + nk_x11_font_stash_begin(&atlas); + /*struct nk_font *droid = nk_font_atlas_add_from_file(atlas, "../../../extra_font/DroidSans.ttf", 14, 0);*/ + /*struct nk_font *roboto = nk_font_atlas_add_from_file(atlas, "../../../extra_font/Roboto-Regular.ttf", 14, 0);*/ + /*struct nk_font *future = nk_font_atlas_add_from_file(atlas, "../../../extra_font/kenvector_future_thin.ttf", 13, 0);*/ + /*struct nk_font *clean = nk_font_atlas_add_from_file(atlas, "../../../extra_font/ProggyClean.ttf", 12, 0);*/ + /*struct nk_font *tiny = nk_font_atlas_add_from_file(atlas, "../../../extra_font/ProggyTiny.ttf", 10, 0);*/ + /*struct nk_font *cousine = nk_font_atlas_add_from_file(atlas, "../../../extra_font/Cousine-Regular.ttf", 13, 0);*/ + nk_x11_font_stash_end(); + /*nk_style_load_all_cursors(ctx, atlas->cursors);*/ + /*nk_style_set_font(ctx, &droid->handle);*/} + + /* style.c */ + /*set_style(ctx, THEME_WHITE);*/ + /*set_style(ctx, THEME_RED);*/ + /*set_style(ctx, THEME_BLUE);*/ + /*set_style(ctx, THEME_DARK);*/ + + background = nk_rgb(28,48,62); + while (running) + { + /* Input */ + XEvent evt; + nk_input_begin(ctx); + while (XCheckWindowEvent(win.dpy, win.win, win.swa.event_mask, &evt)){ + if (XFilterEvent(&evt, win.win)) continue; + nk_x11_handle_event(&evt); + } + nk_input_end(ctx); + + /* GUI */ + if (nk_begin(ctx, "Demo", nk_rect(50, 50, 200, 200), + NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_SCALABLE| + NK_WINDOW_CLOSABLE|NK_WINDOW_MINIMIZABLE|NK_WINDOW_TITLE)) + { + enum {EASY, HARD}; + static int op = EASY; + static int property = 20; + + nk_layout_row_static(ctx, 30, 80, 1); + if (nk_button_label(ctx, "button")) + fprintf(stdout, "button pressed\n"); + nk_layout_row_dynamic(ctx, 30, 2); + if (nk_option_label(ctx, "easy", op == EASY)) op = EASY; + if (nk_option_label(ctx, "hard", op == HARD)) op = HARD; + nk_layout_row_dynamic(ctx, 25, 1); + nk_property_int(ctx, "Compression:", 0, &property, 100, 10, 1); + + nk_layout_row_dynamic(ctx, 20, 1); + nk_label(ctx, "background:", NK_TEXT_LEFT); + nk_layout_row_dynamic(ctx, 25, 1); + if (nk_combo_begin_color(ctx, background, nk_vec2(nk_widget_width(ctx),400))) { + nk_layout_row_dynamic(ctx, 120, 1); + background = nk_color_picker(ctx, background, NK_RGBA); + nk_layout_row_dynamic(ctx, 25, 1); + background.r = (nk_byte)nk_propertyi(ctx, "#R:", 0, background.r, 255, 1,1); + background.g = (nk_byte)nk_propertyi(ctx, "#G:", 0, background.g, 255, 1,1); + background.b = (nk_byte)nk_propertyi(ctx, "#B:", 0, background.b, 255, 1,1); + background.a = (nk_byte)nk_propertyi(ctx, "#A:", 0, background.a, 255, 1,1); + nk_combo_end(ctx); + } + } + nk_end(ctx); + if (nk_window_is_closed(ctx, "Demo")) break; + + /* -------------- EXAMPLES ---------------- */ + /*calculator(ctx);*/ + /*overview(ctx);*/ + /*node_editor(ctx);*/ + /* ----------------------------------------- */ + + /* Draw */ + {float bg[4]; + nk_color_fv(bg, background); + XGetWindowAttributes(win.dpy, win.win, &win.attr); + glViewport(0, 0, win.width, win.height); + glClear(GL_COLOR_BUFFER_BIT); + glClearColor(bg[0], bg[1], bg[2], bg[3]); + /* IMPORTANT: `nk_x11_render` modifies some global OpenGL state + * with blending, scissor, face culling, depth test and viewport and + * defaults everything back into a default state. + * Make sure to either a.) save and restore or b.) reset your own state after + * rendering the UI. */ + nk_x11_render(NK_ANTI_ALIASING_ON, MAX_VERTEX_BUFFER, MAX_ELEMENT_BUFFER); + glXSwapBuffers(win.dpy, win.win);} + } + + nk_x11_shutdown(); + glXMakeCurrent(win.dpy, 0, 0); + glXDestroyContext(win.dpy, glContext); + XUnmapWindow(win.dpy, win.win); + XFreeColormap(win.dpy, win.cmap); + XDestroyWindow(win.dpy, win.win); + XCloseDisplay(win.dpy); + return 0; + +} diff --git a/nuklear/demo/x11_opengl2/nuklear_xlib_gl2.h b/nuklear/demo/x11_opengl2/nuklear_xlib_gl2.h new file mode 100644 index 0000000..a814455 --- /dev/null +++ b/nuklear/demo/x11_opengl2/nuklear_xlib_gl2.h @@ -0,0 +1,357 @@ +/* + * Nuklear - v1.17 - public domain + * no warrenty implied; use at your own risk. + * authored from 2015-2016 by Micha Mettke + */ +/* + * ============================================================== + * + * API + * + * =============================================================== + */ +#ifndef NK_XLIB_GL3_H_ +#define NK_XLIB_GL3_H_ + +#include <X11/Xlib.h> +NK_API struct nk_context* nk_x11_init(Display *dpy, Window win); +NK_API void nk_x11_font_stash_begin(struct nk_font_atlas **atlas); +NK_API void nk_x11_font_stash_end(void); +NK_API int nk_x11_handle_event(XEvent *evt); +NK_API void nk_x11_render(enum nk_anti_aliasing, int max_vertex_buffer, int max_element_buffer); +NK_API void nk_x11_shutdown(void); + +#endif +/* + * ============================================================== + * + * IMPLEMENTATION + * + * =============================================================== + */ +#ifdef NK_XLIB_GL2_IMPLEMENTATION +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xresource.h> +#include <X11/Xlocale.h> + +#include <GL/gl.h> + +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif + +struct nk_x11_vertex { + float position[2]; + float uv[2]; + nk_byte col[4]; +}; + +struct nk_x11_device { + struct nk_buffer cmds; + struct nk_draw_null_texture null; + GLuint font_tex; +}; + +static struct nk_x11 { + struct nk_x11_device ogl; + struct nk_context ctx; + struct nk_font_atlas atlas; + Cursor cursor; + Display *dpy; + Window win; +} x11; + +NK_INTERN void +nk_x11_device_upload_atlas(const void *image, int width, int height) +{ + struct nk_x11_device *dev = &x11.ogl; + glGenTextures(1, &dev->font_tex); + glBindTexture(GL_TEXTURE_2D, dev->font_tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)width, (GLsizei)height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, image); +} + +NK_API void +nk_x11_render(enum nk_anti_aliasing AA, int max_vertex_buffer, int max_element_buffer) +{ + /* setup global state */ + struct nk_x11_device *dev = &x11.ogl; + int width, height; + + XWindowAttributes attr; + XGetWindowAttributes(x11.dpy, x11.win, &attr); + width = attr.width; + height = attr.height; + + glPushAttrib(GL_ENABLE_BIT|GL_COLOR_BUFFER_BIT|GL_TRANSFORM_BIT); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glEnable(GL_SCISSOR_TEST); + glEnable(GL_BLEND); + glEnable(GL_TEXTURE_2D); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + /* setup viewport/project */ + glViewport(0,0,(GLsizei)width,(GLsizei)height); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(0.0f, width, height, 0.0f, -1.0f, 1.0f); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + { + GLsizei vs = sizeof(struct nk_x11_vertex); + size_t vp = offsetof(struct nk_x11_vertex, position); + size_t vt = offsetof(struct nk_x11_vertex, uv); + size_t vc = offsetof(struct nk_x11_vertex, col); + + /* convert from command queue into draw list and draw to screen */ + const struct nk_draw_command *cmd; + const nk_draw_index *offset = NULL; + struct nk_buffer vbuf, ebuf; + + /* fill convert configuration */ + struct nk_convert_config config; + static const struct nk_draw_vertex_layout_element vertex_layout[] = { + {NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_x11_vertex, position)}, + {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_x11_vertex, uv)}, + {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, NK_OFFSETOF(struct nk_x11_vertex, col)}, + {NK_VERTEX_LAYOUT_END} + }; + NK_MEMSET(&config, 0, sizeof(config)); + config.vertex_layout = vertex_layout; + config.vertex_size = sizeof(struct nk_x11_vertex); + config.vertex_alignment = NK_ALIGNOF(struct nk_x11_vertex); + config.null = dev->null; + config.circle_segment_count = 22; + config.curve_segment_count = 22; + config.arc_segment_count = 22; + config.global_alpha = 1.0f; + config.shape_AA = AA; + config.line_AA = AA; + + /* convert shapes into vertexes */ + nk_buffer_init_default(&vbuf); + nk_buffer_init_default(&ebuf); + nk_convert(&x11.ctx, &dev->cmds, &vbuf, &ebuf, &config); + + /* setup vertex buffer pointer */ + {const void *vertices = nk_buffer_memory_const(&vbuf); + glVertexPointer(2, GL_FLOAT, vs, (const void*)((const nk_byte*)vertices + vp)); + glTexCoordPointer(2, GL_FLOAT, vs, (const void*)((const nk_byte*)vertices + vt)); + glColorPointer(4, GL_UNSIGNED_BYTE, vs, (const void*)((const nk_byte*)vertices + vc));} + + /* iterate over and execute each draw command */ + offset = (const nk_draw_index*)nk_buffer_memory_const(&ebuf); + nk_draw_foreach(cmd, &x11.ctx, &dev->cmds) + { + if (!cmd->elem_count) continue; + glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id); + glScissor( + (GLint)(cmd->clip_rect.x), + (GLint)((height - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h))), + (GLint)(cmd->clip_rect.w), + (GLint)(cmd->clip_rect.h)); + glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset); + offset += cmd->elem_count; + } + nk_clear(&x11.ctx); + nk_buffer_free(&vbuf); + nk_buffer_free(&ebuf); + } + + /* default OpenGL state */ + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_BLEND); + glDisable(GL_TEXTURE_2D); + + glBindTexture(GL_TEXTURE_2D, 0); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glPopAttrib(); + +} + +NK_API void +nk_x11_font_stash_begin(struct nk_font_atlas **atlas) +{ + nk_font_atlas_init_default(&x11.atlas); + nk_font_atlas_begin(&x11.atlas); + *atlas = &x11.atlas; +} + +NK_API void +nk_x11_font_stash_end(void) +{ + const void *image; int w, h; + image = nk_font_atlas_bake(&x11.atlas, &w, &h, NK_FONT_ATLAS_RGBA32); + nk_x11_device_upload_atlas(image, w, h); + nk_font_atlas_end(&x11.atlas, nk_handle_id((int)x11.ogl.font_tex), &x11.ogl.null); + if (x11.atlas.default_font) + nk_style_set_font(&x11.ctx, &x11.atlas.default_font->handle); +} + +NK_API int +nk_x11_handle_event(XEvent *evt) +{ + struct nk_context *ctx = &x11.ctx; + + /* optional grabbing behavior */ + if (ctx->input.mouse.grab) { + XDefineCursor(x11.dpy, x11.win, x11.cursor); + ctx->input.mouse.grab = 0; + } else if (ctx->input.mouse.ungrab) { + XWarpPointer(x11.dpy, None, x11.win, 0, 0, 0, 0, + (int)ctx->input.mouse.pos.x, (int)ctx->input.mouse.pos.y); + XUndefineCursor(x11.dpy, x11.win); + ctx->input.mouse.ungrab = 0; + } + + if (evt->type == KeyPress || evt->type == KeyRelease) + { + /* Key handler */ + int ret, down = (evt->type == KeyPress); + KeySym *code = XGetKeyboardMapping(x11.dpy, (KeyCode)evt->xkey.keycode, 1, &ret); + if (*code == XK_Shift_L || *code == XK_Shift_R) nk_input_key(ctx, NK_KEY_SHIFT, down); + else if (*code == XK_Delete) nk_input_key(ctx, NK_KEY_DEL, down); + else if (*code == XK_Return) nk_input_key(ctx, NK_KEY_ENTER, down); + else if (*code == XK_Tab) nk_input_key(ctx, NK_KEY_TAB, down); + else if (*code == XK_Left) nk_input_key(ctx, NK_KEY_LEFT, down); + else if (*code == XK_Right) nk_input_key(ctx, NK_KEY_RIGHT, down); + else if (*code == XK_Up) nk_input_key(ctx, NK_KEY_UP, down); + else if (*code == XK_Down) nk_input_key(ctx, NK_KEY_DOWN, down); + else if (*code == XK_BackSpace) nk_input_key(ctx, NK_KEY_BACKSPACE, down); + else if (*code == XK_space && !down) nk_input_char(ctx, ' '); + else if (*code == XK_Page_Up) nk_input_key(ctx, NK_KEY_SCROLL_UP, down); + else if (*code == XK_Page_Down) nk_input_key(ctx, NK_KEY_SCROLL_DOWN, down); + else if (*code == XK_Home) { + nk_input_key(ctx, NK_KEY_TEXT_START, down); + nk_input_key(ctx, NK_KEY_SCROLL_START, down); + } else if (*code == XK_End) { + nk_input_key(ctx, NK_KEY_TEXT_END, down); + nk_input_key(ctx, NK_KEY_SCROLL_END, down); + } else { + if (*code == 'c' && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_COPY, down); + else if (*code == 'v' && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_PASTE, down); + else if (*code == 'x' && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_CUT, down); + else if (*code == 'z' && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_TEXT_UNDO, down); + else if (*code == 'r' && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_TEXT_REDO, down); + else if (*code == XK_Left && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_TEXT_WORD_LEFT, down); + else if (*code == XK_Right && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_TEXT_WORD_RIGHT, down); + else if (*code == 'b' && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_TEXT_LINE_START, down); + else if (*code == 'e' && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_TEXT_LINE_END, down); + else { + if (*code == 'i') + nk_input_key(ctx, NK_KEY_TEXT_INSERT_MODE, down); + else if (*code == 'r') + nk_input_key(ctx, NK_KEY_TEXT_REPLACE_MODE, down); + if (down) { + char buf[32]; + KeySym keysym = 0; + if (XLookupString((XKeyEvent*)evt, buf, 32, &keysym, NULL) != NoSymbol) + nk_input_glyph(ctx, buf); + } + } + } + XFree(code); + return 1; + } else if (evt->type == ButtonPress || evt->type == ButtonRelease) { + /* Button handler */ + int down = (evt->type == ButtonPress); + const int x = evt->xbutton.x, y = evt->xbutton.y; + if (evt->xbutton.button == Button1) + nk_input_button(ctx, NK_BUTTON_LEFT, x, y, down); + if (evt->xbutton.button == Button2) + nk_input_button(ctx, NK_BUTTON_MIDDLE, x, y, down); + else if (evt->xbutton.button == Button3) + nk_input_button(ctx, NK_BUTTON_RIGHT, x, y, down); + else if (evt->xbutton.button == Button4) + nk_input_scroll(ctx, 1.0f); + else if (evt->xbutton.button == Button5) + nk_input_scroll(ctx, -1.0f); + else return 0; + return 1; + } else if (evt->type == MotionNotify) { + /* Mouse motion handler */ + const int x = evt->xmotion.x, y = evt->xmotion.y; + nk_input_motion(ctx, x, y); + if (ctx->input.mouse.grabbed) { + ctx->input.mouse.pos.x = ctx->input.mouse.prev.x; + ctx->input.mouse.pos.y = ctx->input.mouse.prev.y; + XWarpPointer(x11.dpy, None, x11.win, 0, 0, 0, 0, (int)ctx->input.mouse.pos.x, (int)ctx->input.mouse.pos.y); + } + return 1; + } else if (evt->type == KeymapNotify) { + XRefreshKeyboardMapping(&evt->xmapping); + return 1; + } + return 0; +} + +NK_API struct nk_context* +nk_x11_init(Display *dpy, Window win) +{ + x11.dpy = dpy; + x11.win = win; + + if (!setlocale(LC_ALL,"")) return 0; + if (!XSupportsLocale()) return 0; + if (!XSetLocaleModifiers("@im=none")) return 0; + + /* create invisible cursor */ + {static XColor dummy; char data[1] = {0}; + Pixmap blank = XCreateBitmapFromData(dpy, win, data, 1, 1); + if (blank == None) return 0; + x11.cursor = XCreatePixmapCursor(dpy, blank, blank, &dummy, &dummy, 0, 0); + XFreePixmap(dpy, blank);} + + nk_buffer_init_default(&x11.ogl.cmds); + nk_init_default(&x11.ctx, 0); + return &x11.ctx; +} + +NK_API void +nk_x11_shutdown(void) +{ + struct nk_x11_device *dev = &x11.ogl; + nk_font_atlas_clear(&x11.atlas); + nk_free(&x11.ctx); + glDeleteTextures(1, &dev->font_tex); + nk_buffer_free(&dev->cmds); + XFreeCursor(x11.dpy, x11.cursor); + memset(&x11, 0, sizeof(x11)); +} + +#endif diff --git a/nuklear/demo/x11_opengl3/Makefile b/nuklear/demo/x11_opengl3/Makefile new file mode 100644 index 0000000..1173b6c --- /dev/null +++ b/nuklear/demo/x11_opengl3/Makefile @@ -0,0 +1,26 @@ +# Install +BIN = demo + +# Compiler +CC = clang +DCC = gcc + +# Flags +CFLAGS = -std=c99 -pedantic -O2 + +SRC = main.c +OBJ = $(SRC:.c=.o) + +# Modes +.PHONY: gcc +gcc: CC = gcc +gcc: $(BIN) + +.PHONY: clang +clang: CC = clang +clang: $(BIN) + +$(BIN): + @mkdir -p bin + rm -f bin/$(BIN) $(OBJS) + $(CC) $(SRC) $(CFLAGS) -o bin/$(BIN) -lX11 -lm -lGL -lm -lGLU diff --git a/nuklear/demo/x11_opengl3/main.c b/nuklear/demo/x11_opengl3/main.c new file mode 100644 index 0000000..cd026f9 --- /dev/null +++ b/nuklear/demo/x11_opengl3/main.c @@ -0,0 +1,317 @@ +/* nuklear - v1.17 - public domain */ +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdarg.h> +#include <string.h> +#include <math.h> +#include <assert.h> +#include <math.h> +#include <time.h> +#include <limits.h> + +#define NK_INCLUDE_FIXED_TYPES +#define NK_INCLUDE_STANDARD_IO +#define NK_INCLUDE_STANDARD_VARARGS +#define NK_INCLUDE_DEFAULT_ALLOCATOR +#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT +#define NK_INCLUDE_FONT_BAKING +#define NK_INCLUDE_DEFAULT_FONT +#define NK_IMPLEMENTATION +#define NK_XLIB_GL3_IMPLEMENTATION +#define NK_XLIB_LOAD_OPENGL_EXTENSIONS +#include "../../nuklear.h" +#include "nuklear_xlib_gl3.h" + +#define WINDOW_WIDTH 1200 +#define WINDOW_HEIGHT 800 + +#define MAX_VERTEX_BUFFER 512 * 1024 +#define MAX_ELEMENT_BUFFER 128 * 1024 + +#define UNUSED(a) (void)a +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) < (b) ? (b) : (a)) +#define LEN(a) (sizeof(a)/sizeof(a)[0]) + +/* =============================================================== + * + * EXAMPLE + * + * ===============================================================*/ +/* This are some code examples to provide a small overview of what can be + * done with this library. To try out an example uncomment the include + * and the corresponding function. */ +/*#include "../style.c"*/ +/*#include "../calculator.c"*/ +/*#include "../overview.c"*/ +/*#include "../node_editor.c"*/ + +/* =============================================================== + * + * DEMO + * + * ===============================================================*/ +struct XWindow { + Display *dpy; + Window win; + XVisualInfo *vis; + Colormap cmap; + XSetWindowAttributes swa; + XWindowAttributes attr; + GLXFBConfig fbc; + int width, height; +}; +static int gl_err = FALSE; +static int gl_error_handler(Display *dpy, XErrorEvent *ev) +{UNUSED((dpy, ev)); gl_err = TRUE;return 0;} + +static void +die(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fputs("\n", stderr); + exit(EXIT_FAILURE); +} + +static int +has_extension(const char *string, const char *ext) +{ + const char *start, *where, *term; + where = strchr(ext, ' '); + if (where || *ext == '\0') + return FALSE; + + for (start = string;;) { + where = strstr((const char*)start, ext); + if (!where) break; + term = where + strlen(ext); + if (where == start || *(where - 1) == ' ') { + if (*term == ' ' || *term == '\0') + return TRUE; + } + start = term; + } + return FALSE; +} + +int main(int argc, char **argv) +{ + /* Platform */ + int running = 1; + struct XWindow win; + GLXContext glContext; + struct nk_context *ctx; + struct nk_color background; + + memset(&win, 0, sizeof(win)); + win.dpy = XOpenDisplay(NULL); + if (!win.dpy) die("Failed to open X display\n"); + { + /* check glx version */ + int glx_major, glx_minor; + if (!glXQueryVersion(win.dpy, &glx_major, &glx_minor)) + die("[X11]: Error: Failed to query OpenGL version\n"); + if ((glx_major == 1 && glx_minor < 3) || (glx_major < 1)) + die("[X11]: Error: Invalid GLX version!\n"); + } + { + /* find and pick matching framebuffer visual */ + int fb_count; + static GLint attr[] = { + GLX_X_RENDERABLE, True, + GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, + GLX_RED_SIZE, 8, + GLX_GREEN_SIZE, 8, + GLX_BLUE_SIZE, 8, + GLX_ALPHA_SIZE, 8, + GLX_DEPTH_SIZE, 24, + GLX_STENCIL_SIZE, 8, + GLX_DOUBLEBUFFER, True, + None + }; + GLXFBConfig *fbc; + fbc = glXChooseFBConfig(win.dpy, DefaultScreen(win.dpy), attr, &fb_count); + if (!fbc) die("[X11]: Error: failed to retrieve framebuffer configuration\n"); + { + /* pick framebuffer with most samples per pixel */ + int i; + int fb_best = -1, best_num_samples = -1; + for (i = 0; i < fb_count; ++i) { + XVisualInfo *vi = glXGetVisualFromFBConfig(win.dpy, fbc[i]); + if (vi) { + int sample_buffer, samples; + glXGetFBConfigAttrib(win.dpy, fbc[i], GLX_SAMPLE_BUFFERS, &sample_buffer); + glXGetFBConfigAttrib(win.dpy, fbc[i], GLX_SAMPLES, &samples); + if ((fb_best < 0) || (sample_buffer && samples > best_num_samples)) + fb_best = i; best_num_samples = samples; + } + } + win.fbc = fbc[fb_best]; + XFree(fbc); + win.vis = glXGetVisualFromFBConfig(win.dpy, win.fbc); + } + } + { + /* create window */ + win.cmap = XCreateColormap(win.dpy, RootWindow(win.dpy, win.vis->screen), win.vis->visual, AllocNone); + win.swa.colormap = win.cmap; + win.swa.background_pixmap = None; + win.swa.border_pixel = 0; + win.swa.event_mask = + ExposureMask | KeyPressMask | KeyReleaseMask | + ButtonPress | ButtonReleaseMask| ButtonMotionMask | + Button1MotionMask | Button3MotionMask | Button4MotionMask | Button5MotionMask| + PointerMotionMask| StructureNotifyMask; + win.win = XCreateWindow(win.dpy, RootWindow(win.dpy, win.vis->screen), 0, 0, + WINDOW_WIDTH, WINDOW_HEIGHT, 0, win.vis->depth, InputOutput, + win.vis->visual, CWBorderPixel|CWColormap|CWEventMask, &win.swa); + if (!win.win) die("[X11]: Failed to create window\n"); + XFree(win.vis); + XStoreName(win.dpy, win.win, "Demo"); + XMapWindow(win.dpy, win.win); + } + { + /* create opengl context */ + typedef GLXContext(*glxCreateContext)(Display*, GLXFBConfig, GLXContext, Bool, const int*); + int(*old_handler)(Display*, XErrorEvent*) = XSetErrorHandler(gl_error_handler); + const char *extensions_str = glXQueryExtensionsString(win.dpy, DefaultScreen(win.dpy)); + glxCreateContext create_context = (glxCreateContext) + glXGetProcAddressARB((const GLubyte*)"glXCreateContextAttribsARB"); + + gl_err = FALSE; + if (!has_extension(extensions_str, "GLX_ARB_create_context") || !create_context) { + fprintf(stdout, "[X11]: glXCreateContextAttribARB() not found...\n"); + fprintf(stdout, "[X11]: ... using old-style GLX context\n"); + glContext = glXCreateNewContext(win.dpy, win.fbc, GLX_RGBA_TYPE, 0, True); + } else { + GLint attr[] = { + GLX_CONTEXT_MAJOR_VERSION_ARB, 3, + GLX_CONTEXT_MINOR_VERSION_ARB, 0, + None + }; + glContext = create_context(win.dpy, win.fbc, 0, True, attr); + XSync(win.dpy, False); + if (gl_err || !glContext) { + /* Could not create GL 3.0 context. Fallback to old 2.x context. + * If a version below 3.0 is requested, implementations will + * return the newest context version compatible with OpenGL + * version less than version 3.0.*/ + attr[1] = 1; attr[3] = 0; + gl_err = FALSE; + fprintf(stdout, "[X11] Failed to create OpenGL 3.0 context\n"); + fprintf(stdout, "[X11] ... using old-style GLX context!\n"); + glContext = create_context(win.dpy, win.fbc, 0, True, attr); + } + } + XSync(win.dpy, False); + XSetErrorHandler(old_handler); + if (gl_err || !glContext) + die("[X11]: Failed to create an OpenGL context\n"); + glXMakeCurrent(win.dpy, win.win, glContext); + } + + ctx = nk_x11_init(win.dpy, win.win); + /* Load Fonts: if none of these are loaded a default font will be used */ + {struct nk_font_atlas *atlas; + nk_x11_font_stash_begin(&atlas); + /*struct nk_font *droid = nk_font_atlas_add_from_file(atlas, "../../../extra_font/DroidSans.ttf", 14, 0);*/ + /*struct nk_font *roboto = nk_font_atlas_add_from_file(atlas, "../../../extra_font/Roboto-Regular.ttf", 14, 0);*/ + /*struct nk_font *future = nk_font_atlas_add_from_file(atlas, "../../../extra_font/kenvector_future_thin.ttf", 13, 0);*/ + /*struct nk_font *clean = nk_font_atlas_add_from_file(atlas, "../../../extra_font/ProggyClean.ttf", 12, 0);*/ + /*struct nk_font *tiny = nk_font_atlas_add_from_file(atlas, "../../../extra_font/ProggyTiny.ttf", 10, 0);*/ + /*struct nk_font *cousine = nk_font_atlas_add_from_file(atlas, "../../../extra_font/Cousine-Regular.ttf", 13, 0);*/ + nk_x11_font_stash_end(); + /*nk_style_load_all_cursors(ctx, atlas->cursors);*/ + /*nk_style_set_font(ctx, &droid->handle);*/} + + /* style.c */ + /*set_style(ctx, THEME_WHITE);*/ + /*set_style(ctx, THEME_RED);*/ + /*set_style(ctx, THEME_BLUE);*/ + /*set_style(ctx, THEME_DARK);*/ + + background = nk_rgb(28,48,62); + while (running) + { + /* Input */ + XEvent evt; + nk_input_begin(ctx); + while (XCheckWindowEvent(win.dpy, win.win, win.swa.event_mask, &evt)){ + if (XFilterEvent(&evt, win.win)) continue; + nk_x11_handle_event(&evt); + } + nk_input_end(ctx); + + /* GUI */ + if (nk_begin(ctx, "Demo", nk_rect(50, 50, 200, 200), + NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_SCALABLE| + NK_WINDOW_CLOSABLE|NK_WINDOW_MINIMIZABLE|NK_WINDOW_TITLE)) + { + enum {EASY, HARD}; + static int op = EASY; + static int property = 20; + + nk_layout_row_static(ctx, 30, 80, 1); + if (nk_button_label(ctx, "button")) + fprintf(stdout, "button pressed\n"); + nk_layout_row_dynamic(ctx, 30, 2); + if (nk_option_label(ctx, "easy", op == EASY)) op = EASY; + if (nk_option_label(ctx, "hard", op == HARD)) op = HARD; + nk_layout_row_dynamic(ctx, 25, 1); + nk_property_int(ctx, "Compression:", 0, &property, 100, 10, 1); + + nk_layout_row_dynamic(ctx, 20, 1); + nk_label(ctx, "background:", NK_TEXT_LEFT); + nk_layout_row_dynamic(ctx, 25, 1); + if (nk_combo_begin_color(ctx, background, nk_vec2(nk_widget_width(ctx),400))) { + nk_layout_row_dynamic(ctx, 120, 1); + background = nk_color_picker(ctx, background, NK_RGBA); + nk_layout_row_dynamic(ctx, 25, 1); + background.r = (nk_byte)nk_propertyi(ctx, "#R:", 0, background.r, 255, 1,1); + background.g = (nk_byte)nk_propertyi(ctx, "#G:", 0, background.g, 255, 1,1); + background.b = (nk_byte)nk_propertyi(ctx, "#B:", 0, background.b, 255, 1,1); + background.a = (nk_byte)nk_propertyi(ctx, "#A:", 0, background.a, 255, 1,1); + nk_combo_end(ctx); + } + } + nk_end(ctx); + if (nk_window_is_closed(ctx, "Demo")) break; + + /* -------------- EXAMPLES ---------------- */ + /*calculator(ctx);*/ + /*overview(ctx);*/ + /*node_editor(ctx);*/ + /* ----------------------------------------- */ + + /* Draw */ + {float bg[4]; + nk_color_fv(bg, background); + XGetWindowAttributes(win.dpy, win.win, &win.attr); + glViewport(0, 0, win.width, win.height); + glClear(GL_COLOR_BUFFER_BIT); + glClearColor(bg[0], bg[1], bg[2], bg[3]); + /* IMPORTANT: `nk_x11_render` modifies some global OpenGL state + * with blending, scissor, face culling, depth test and viewport and + * defaults everything back into a default state. + * Make sure to either a.) save and restore or b.) reset your own state after + * rendering the UI. */ + nk_x11_render(NK_ANTI_ALIASING_ON, MAX_VERTEX_BUFFER, MAX_ELEMENT_BUFFER); + glXSwapBuffers(win.dpy, win.win);} + } + + nk_x11_shutdown(); + glXMakeCurrent(win.dpy, 0, 0); + glXDestroyContext(win.dpy, glContext); + XUnmapWindow(win.dpy, win.win); + XFreeColormap(win.dpy, win.cmap); + XDestroyWindow(win.dpy, win.win); + XCloseDisplay(win.dpy); + return 0; + +} diff --git a/nuklear/demo/x11_opengl3/nuklear_xlib_gl3.h b/nuklear/demo/x11_opengl3/nuklear_xlib_gl3.h new file mode 100644 index 0000000..b0f56b9 --- /dev/null +++ b/nuklear/demo/x11_opengl3/nuklear_xlib_gl3.h @@ -0,0 +1,725 @@ +/* + * Nuklear - v1.17 - public domain + * no warrenty implied; use at your own risk. + * authored from 2015-2016 by Micha Mettke + */ +/* + * ============================================================== + * + * API + * + * =============================================================== + */ +#ifndef NK_XLIB_GL3_H_ +#define NK_XLIB_GL3_H_ + +#include <X11/Xlib.h> +NK_API struct nk_context* nk_x11_init(Display *dpy, Window win); +NK_API void nk_x11_font_stash_begin(struct nk_font_atlas **atlas); +NK_API void nk_x11_font_stash_end(void); +NK_API int nk_x11_handle_event(XEvent *evt); +NK_API void nk_x11_render(enum nk_anti_aliasing, int max_vertex_buffer, int max_element_buffer); +NK_API void nk_x11_shutdown(void); +NK_API int nk_x11_device_create(void); +NK_API void nk_x11_device_destroy(void); + +#endif +/* + * ============================================================== + * + * IMPLEMENTATION + * + * =============================================================== + */ +#ifdef NK_XLIB_GL3_IMPLEMENTATION +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xresource.h> +#include <X11/Xlocale.h> + +#include <GL/gl.h> +#include <GL/glx.h> + +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif + +#ifdef NK_XLIB_LOAD_OPENGL_EXTENSIONS +#include <GL/glxext.h> + +/* GL_ARB_vertex_buffer_object */ +typedef void(*nkglGenBuffers)(GLsizei, GLuint*); +typedef void(*nkglBindBuffer)(GLenum, GLuint); +typedef void(*nkglBufferData)(GLenum, GLsizeiptr, const GLvoid*, GLenum); +typedef void(*nkglBufferSubData)(GLenum, GLintptr, GLsizeiptr, const GLvoid*); +typedef void*(*nkglMapBuffer)(GLenum, GLenum); +typedef GLboolean(*nkglUnmapBuffer)(GLenum); +typedef void(*nkglDeleteBuffers)(GLsizei, GLuint*); +/* GL_ARB_vertex_array_object */ +typedef void (*nkglGenVertexArrays)(GLsizei, GLuint*); +typedef void (*nkglBindVertexArray)(GLuint); +typedef void (*nkglDeleteVertexArrays)(GLsizei, const GLuint*); +/* GL_ARB_vertex_program / GL_ARB_fragment_program */ +typedef void(*nkglVertexAttribPointer)(GLuint, GLint, GLenum, GLboolean, GLsizei, const GLvoid*); +typedef void(*nkglEnableVertexAttribArray)(GLuint); +typedef void(*nkglDisableVertexAttribArray)(GLuint); +/* GL_ARB_framebuffer_object */ +typedef void(*nkglGenerateMipmap)(GLenum target); +/* GLSL/OpenGL 2.0 core */ +typedef GLuint(*nkglCreateShader)(GLenum); +typedef void(*nkglShaderSource)(GLuint, GLsizei, const GLchar**, const GLint*); +typedef void(*nkglCompileShader)(GLuint); +typedef void(*nkglGetShaderiv)(GLuint, GLenum, GLint*); +typedef void(*nkglGetShaderInfoLog)(GLuint, GLsizei, GLsizei*, GLchar*); +typedef void(*nkglDeleteShader)(GLuint); +typedef GLuint(*nkglCreateProgram)(void); +typedef void(*nkglAttachShader)(GLuint, GLuint); +typedef void(*nkglDetachShader)(GLuint, GLuint); +typedef void(*nkglLinkProgram)(GLuint); +typedef void(*nkglUseProgram)(GLuint); +typedef void(*nkglGetProgramiv)(GLuint, GLenum, GLint*); +typedef void(*nkglGetProgramInfoLog)(GLuint, GLsizei, GLsizei*, GLchar*); +typedef void(*nkglDeleteProgram)(GLuint); +typedef GLint(*nkglGetUniformLocation)(GLuint, const GLchar*); +typedef GLint(*nkglGetAttribLocation)(GLuint, const GLchar*); +typedef void(*nkglUniform1i)(GLint, GLint); +typedef void(*nkglUniform1f)(GLint, GLfloat); +typedef void(*nkglUniformMatrix3fv)(GLint, GLsizei, GLboolean, const GLfloat*); +typedef void(*nkglUniformMatrix4fv)(GLint, GLsizei, GLboolean, const GLfloat*); + +static nkglGenBuffers glGenBuffers; +static nkglBindBuffer glBindBuffer; +static nkglBufferData glBufferData; +static nkglBufferSubData glBufferSubData; +static nkglMapBuffer glMapBuffer; +static nkglUnmapBuffer glUnmapBuffer; +static nkglDeleteBuffers glDeleteBuffers; +static nkglGenVertexArrays glGenVertexArrays; +static nkglBindVertexArray glBindVertexArray; +static nkglDeleteVertexArrays glDeleteVertexArrays; +static nkglVertexAttribPointer glVertexAttribPointer; +static nkglEnableVertexAttribArray glEnableVertexAttribArray; +static nkglDisableVertexAttribArray glDisableVertexAttribArray; +static nkglGenerateMipmap glGenerateMipmap; +static nkglCreateShader glCreateShader; +static nkglShaderSource glShaderSource; +static nkglCompileShader glCompileShader; +static nkglGetShaderiv glGetShaderiv; +static nkglGetShaderInfoLog glGetShaderInfoLog; +static nkglDeleteShader glDeleteShader; +static nkglCreateProgram glCreateProgram; +static nkglAttachShader glAttachShader; +static nkglDetachShader glDetachShader; +static nkglLinkProgram glLinkProgram; +static nkglUseProgram glUseProgram; +static nkglGetProgramiv glGetProgramiv; +static nkglGetProgramInfoLog glGetProgramInfoLog; +static nkglDeleteProgram glDeleteProgram; +static nkglGetUniformLocation glGetUniformLocation; +static nkglGetAttribLocation glGetAttribLocation; +static nkglUniform1i glUniform1i; +static nkglUniform1f glUniform1f; +static nkglUniformMatrix3fv glUniformMatrix3fv; +static nkglUniformMatrix4fv glUniformMatrix4fv; + +enum graphics_card_vendors { + VENDOR_UNKNOWN, + VENDOR_NVIDIA, + VENDOR_AMD, + VENDOR_INTEL +}; + +struct opengl_info { + /* info */ + const char *vendor_str; + const char *version_str; + const char *extensions_str; + const char *renderer_str; + const char *glsl_version_str; + enum graphics_card_vendors vendor; + /* version */ + float version; + int major_version; + int minor_version; + /* extensions */ + int glsl_available; + int vertex_buffer_obj_available; + int vertex_array_obj_available; + int map_buffer_range_available; + int fragment_program_available; + int frame_buffer_object_available; +}; +#endif + +struct nk_x11_vertex { + float position[2]; + float uv[2]; + nk_byte col[4]; +}; + +struct nk_x11_device { +#ifdef NK_XLIB_LOAD_OPENGL_EXTENSIONS + struct opengl_info info; +#endif + struct nk_buffer cmds; + struct nk_draw_null_texture null; + GLuint vbo, vao, ebo; + GLuint prog; + GLuint vert_shdr; + GLuint frag_shdr; + GLint attrib_pos; + GLint attrib_uv; + GLint attrib_col; + GLint uniform_tex; + GLint uniform_proj; + GLuint font_tex; +}; + +static struct nk_x11 { + struct nk_x11_device ogl; + struct nk_context ctx; + struct nk_font_atlas atlas; + Cursor cursor; + Display *dpy; + Window win; +} x11; + +#ifdef __APPLE__ + #define NK_SHADER_VERSION "#version 150\n" +#else + #define NK_SHADER_VERSION "#version 300 es\n" +#endif + +#ifdef NK_XLIB_LOAD_OPENGL_EXTENSIONS +#include <GL/glx.h> + +NK_INTERN int +nk_x11_stricmpn(const char *a, const char *b, int len) +{ + int i = 0; + for (i = 0; i < len && a[i] && b[i]; ++i) + if (a[i] != b[i]) return 1; + if (i != len) return 1; + return 0; +} + +NK_INTERN int +nk_x11_check_extension(struct opengl_info *GL, const char *ext) +{ + const char *start, *where, *term; + where = strchr(ext, ' '); + if (where || *ext == '\0') + return FALSE; + + for (start = GL->extensions_str;;) { + where = strstr((const char*)start, ext); + if (!where) break; + term = where + strlen(ext); + if (where == start || *(where - 1) == ' ') { + if (*term == ' ' || *term == '\0') + return TRUE; + } + start = term; + } + return FALSE; +} + +#define GL_EXT(name) (nk##name)nk_gl_ext(#name) +NK_INTERN __GLXextFuncPtr +nk_gl_ext(const char *name) +{ + __GLXextFuncPtr func; + func = glXGetProcAddress((const GLubyte*)name); + if (!func) { + fprintf(stdout, "[GL]: failed to load extension: %s", name); + return NULL; + } + return func; +} + +NK_INTERN int +nk_load_opengl(struct opengl_info *gl) +{ + int failed = FALSE; + gl->version_str = (const char*)glGetString(GL_VERSION); + glGetIntegerv(GL_MAJOR_VERSION, &gl->major_version); + glGetIntegerv(GL_MINOR_VERSION, &gl->minor_version); + if (gl->major_version < 2) { + fprintf(stderr, "[GL]: Graphics card does not fullfill minimum OpenGL 2.0 support\n"); + return 0; + } + + gl->version = (float)gl->major_version + (float)gl->minor_version * 0.1f; + gl->renderer_str = (const char*)glGetString(GL_RENDERER); + gl->extensions_str = (const char*)glGetString(GL_EXTENSIONS); + gl->glsl_version_str = (const char*)glGetString(GL_SHADING_LANGUAGE_VERSION); + gl->vendor_str = (const char*)glGetString(GL_VENDOR); + if (!nk_x11_stricmpn(gl->vendor_str, "ATI", 4) || + !nk_x11_stricmpn(gl->vendor_str, "AMD", 4)) + gl->vendor = VENDOR_AMD; + else if (!nk_x11_stricmpn(gl->vendor_str, "NVIDIA", 6)) + gl->vendor = VENDOR_NVIDIA; + else if (!nk_x11_stricmpn(gl->vendor_str, "Intel", 5)) + gl->vendor = VENDOR_INTEL; + else gl->vendor = VENDOR_UNKNOWN; + + /* Extensions */ + gl->glsl_available = (gl->version >= 2.0f); + if (gl->glsl_available) { + /* GLSL core in OpenGL > 2 */ + glCreateShader = GL_EXT(glCreateShader); + glShaderSource = GL_EXT(glShaderSource); + glCompileShader = GL_EXT(glCompileShader); + glGetShaderiv = GL_EXT(glGetShaderiv); + glGetShaderInfoLog = GL_EXT(glGetShaderInfoLog); + glDeleteShader = GL_EXT(glDeleteShader); + glCreateProgram = GL_EXT(glCreateProgram); + glAttachShader = GL_EXT(glAttachShader); + glDetachShader = GL_EXT(glDetachShader); + glLinkProgram = GL_EXT(glLinkProgram); + glUseProgram = GL_EXT(glUseProgram); + glGetProgramiv = GL_EXT(glGetProgramiv); + glGetProgramInfoLog = GL_EXT(glGetProgramInfoLog); + glDeleteProgram = GL_EXT(glDeleteProgram); + glGetUniformLocation = GL_EXT(glGetUniformLocation); + glGetAttribLocation = GL_EXT(glGetAttribLocation); + glUniform1i = GL_EXT(glUniform1i); + glUniform1f = GL_EXT(glUniform1f); + glUniformMatrix3fv = GL_EXT(glUniformMatrix3fv); + glUniformMatrix4fv = GL_EXT(glUniformMatrix4fv); + } + gl->vertex_buffer_obj_available = nk_x11_check_extension(gl, "GL_ARB_vertex_buffer_object"); + if (gl->vertex_buffer_obj_available) { + /* GL_ARB_vertex_buffer_object */ + glGenBuffers = GL_EXT(glGenBuffers); + glBindBuffer = GL_EXT(glBindBuffer); + glBufferData = GL_EXT(glBufferData); + glBufferSubData = GL_EXT(glBufferSubData); + glMapBuffer = GL_EXT(glMapBuffer); + glUnmapBuffer = GL_EXT(glUnmapBuffer); + glDeleteBuffers = GL_EXT(glDeleteBuffers); + } + gl->fragment_program_available = nk_x11_check_extension(gl, "GL_ARB_fragment_program"); + if (gl->fragment_program_available) { + /* GL_ARB_vertex_program / GL_ARB_fragment_program */ + glVertexAttribPointer = GL_EXT(glVertexAttribPointer); + glEnableVertexAttribArray = GL_EXT(glEnableVertexAttribArray); + glDisableVertexAttribArray = GL_EXT(glDisableVertexAttribArray); + } + gl->vertex_array_obj_available = nk_x11_check_extension(gl, "GL_ARB_vertex_array_object"); + if (gl->vertex_array_obj_available) { + /* GL_ARB_vertex_array_object */ + glGenVertexArrays = GL_EXT(glGenVertexArrays); + glBindVertexArray = GL_EXT(glBindVertexArray); + glDeleteVertexArrays = GL_EXT(glDeleteVertexArrays); + } + gl->frame_buffer_object_available = nk_x11_check_extension(gl, "GL_ARB_framebuffer_object"); + if (gl->frame_buffer_object_available) { + /* GL_ARB_framebuffer_object */ + glGenerateMipmap = GL_EXT(glGenerateMipmap); + } + if (!gl->vertex_buffer_obj_available) { + fprintf(stdout, "[GL] Error: GL_ARB_vertex_buffer_object is not available!\n"); + failed = TRUE; + } + if (!gl->fragment_program_available) { + fprintf(stdout, "[GL] Error: GL_ARB_fragment_program is not available!\n"); + failed = TRUE; + } + if (!gl->vertex_array_obj_available) { + fprintf(stdout, "[GL] Error: GL_ARB_vertex_array_object is not available!\n"); + failed = TRUE; + } + if (!gl->frame_buffer_object_available) { + fprintf(stdout, "[GL] Error: GL_ARB_framebuffer_object is not available!\n"); + failed = TRUE; + } + return !failed; +} +#endif + +NK_API int +nk_x11_device_create(void) +{ + GLint status; + static const GLchar *vertex_shader = + NK_SHADER_VERSION + "uniform mat4 ProjMtx;\n" + "in vec2 Position;\n" + "in vec2 TexCoord;\n" + "in vec4 Color;\n" + "out vec2 Frag_UV;\n" + "out vec4 Frag_Color;\n" + "void main() {\n" + " Frag_UV = TexCoord;\n" + " Frag_Color = Color;\n" + " gl_Position = ProjMtx * vec4(Position.xy, 0, 1);\n" + "}\n"; + static const GLchar *fragment_shader = + NK_SHADER_VERSION + "precision mediump float;\n" + "uniform sampler2D Texture;\n" + "in vec2 Frag_UV;\n" + "in vec4 Frag_Color;\n" + "out vec4 Out_Color;\n" + "void main(){\n" + " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" + "}\n"; + + struct nk_x11_device *dev = &x11.ogl; +#ifdef NK_XLIB_LOAD_OPENGL_EXTENSIONS + if (!nk_load_opengl(&dev->info)) return 0; +#endif + nk_buffer_init_default(&dev->cmds); + + dev->prog = glCreateProgram(); + dev->vert_shdr = glCreateShader(GL_VERTEX_SHADER); + dev->frag_shdr = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(dev->vert_shdr, 1, &vertex_shader, 0); + glShaderSource(dev->frag_shdr, 1, &fragment_shader, 0); + glCompileShader(dev->vert_shdr); + glCompileShader(dev->frag_shdr); + glGetShaderiv(dev->vert_shdr, GL_COMPILE_STATUS, &status); + assert(status == GL_TRUE); + glGetShaderiv(dev->frag_shdr, GL_COMPILE_STATUS, &status); + assert(status == GL_TRUE); + glAttachShader(dev->prog, dev->vert_shdr); + glAttachShader(dev->prog, dev->frag_shdr); + glLinkProgram(dev->prog); + glGetProgramiv(dev->prog, GL_LINK_STATUS, &status); + assert(status == GL_TRUE); + + dev->uniform_tex = glGetUniformLocation(dev->prog, "Texture"); + dev->uniform_proj = glGetUniformLocation(dev->prog, "ProjMtx"); + dev->attrib_pos = glGetAttribLocation(dev->prog, "Position"); + dev->attrib_uv = glGetAttribLocation(dev->prog, "TexCoord"); + dev->attrib_col = glGetAttribLocation(dev->prog, "Color"); + + { + /* buffer setup */ + GLsizei vs = sizeof(struct nk_x11_vertex); + size_t vp = offsetof(struct nk_x11_vertex, position); + size_t vt = offsetof(struct nk_x11_vertex, uv); + size_t vc = offsetof(struct nk_x11_vertex, col); + + glGenBuffers(1, &dev->vbo); + glGenBuffers(1, &dev->ebo); + glGenVertexArrays(1, &dev->vao); + + glBindVertexArray(dev->vao); + glBindBuffer(GL_ARRAY_BUFFER, dev->vbo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo); + + glEnableVertexAttribArray((GLuint)dev->attrib_pos); + glEnableVertexAttribArray((GLuint)dev->attrib_uv); + glEnableVertexAttribArray((GLuint)dev->attrib_col); + + glVertexAttribPointer((GLuint)dev->attrib_pos, 2, GL_FLOAT, GL_FALSE, vs, (void*)vp); + glVertexAttribPointer((GLuint)dev->attrib_uv, 2, GL_FLOAT, GL_FALSE, vs, (void*)vt); + glVertexAttribPointer((GLuint)dev->attrib_col, 4, GL_UNSIGNED_BYTE, GL_TRUE, vs, (void*)vc); + } + + glBindTexture(GL_TEXTURE_2D, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindVertexArray(0); + return 1; +} + +NK_INTERN void +nk_x11_device_upload_atlas(const void *image, int width, int height) +{ + struct nk_x11_device *dev = &x11.ogl; + glGenTextures(1, &dev->font_tex); + glBindTexture(GL_TEXTURE_2D, dev->font_tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)width, (GLsizei)height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, image); +} + +NK_API void +nk_x11_device_destroy(void) +{ + struct nk_x11_device *dev = &x11.ogl; + glDetachShader(dev->prog, dev->vert_shdr); + glDetachShader(dev->prog, dev->frag_shdr); + glDeleteShader(dev->vert_shdr); + glDeleteShader(dev->frag_shdr); + glDeleteProgram(dev->prog); + glDeleteTextures(1, &dev->font_tex); + glDeleteBuffers(1, &dev->vbo); + glDeleteBuffers(1, &dev->ebo); + nk_buffer_free(&dev->cmds); +} + +NK_API void +nk_x11_render(enum nk_anti_aliasing AA, int max_vertex_buffer, int max_element_buffer) +{ + int width, height; + XWindowAttributes attr; + struct nk_x11_device *dev = &x11.ogl; + GLfloat ortho[4][4] = { + {2.0f, 0.0f, 0.0f, 0.0f}, + {0.0f,-2.0f, 0.0f, 0.0f}, + {0.0f, 0.0f,-1.0f, 0.0f}, + {-1.0f,1.0f, 0.0f, 1.0f}, + }; + XGetWindowAttributes(x11.dpy, x11.win, &attr); + width = attr.width; + height = attr.height; + + ortho[0][0] /= (GLfloat)width; + ortho[1][1] /= (GLfloat)height; + + /* setup global state */ + glEnable(GL_BLEND); + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glEnable(GL_SCISSOR_TEST); + glActiveTexture(GL_TEXTURE0); + + /* setup program */ + glUseProgram(dev->prog); + glUniform1i(dev->uniform_tex, 0); + glUniformMatrix4fv(dev->uniform_proj, 1, GL_FALSE, &ortho[0][0]); + glViewport(0,0,(GLsizei)width,(GLsizei)height); + { + /* convert from command queue into draw list and draw to screen */ + const struct nk_draw_command *cmd; + void *vertices, *elements; + const nk_draw_index *offset = NULL; + + /* allocate vertex and element buffer */ + glBindVertexArray(dev->vao); + glBindBuffer(GL_ARRAY_BUFFER, dev->vbo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo); + + glBufferData(GL_ARRAY_BUFFER, max_vertex_buffer, NULL, GL_STREAM_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, max_element_buffer, NULL, GL_STREAM_DRAW); + + /* load draw vertices & elements directly into vertex + element buffer */ + vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); + elements = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY); + { + /* fill convert configuration */ + struct nk_convert_config config; + static const struct nk_draw_vertex_layout_element vertex_layout[] = { + {NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_x11_vertex, position)}, + {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_x11_vertex, uv)}, + {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, NK_OFFSETOF(struct nk_x11_vertex, col)}, + {NK_VERTEX_LAYOUT_END} + }; + NK_MEMSET(&config, 0, sizeof(config)); + config.vertex_layout = vertex_layout; + config.vertex_size = sizeof(struct nk_x11_vertex); + config.vertex_alignment = NK_ALIGNOF(struct nk_x11_vertex); + config.null = dev->null; + config.circle_segment_count = 22; + config.curve_segment_count = 22; + config.arc_segment_count = 22; + config.global_alpha = 1.0f; + config.shape_AA = AA; + config.line_AA = AA; + + /* setup buffers to load vertices and elements */ + {struct nk_buffer vbuf, ebuf; + nk_buffer_init_fixed(&vbuf, vertices, (size_t)max_vertex_buffer); + nk_buffer_init_fixed(&ebuf, elements, (size_t)max_element_buffer); + nk_convert(&x11.ctx, &dev->cmds, &vbuf, &ebuf, &config);} + } + glUnmapBuffer(GL_ARRAY_BUFFER); + glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); + + /* iterate over and execute each draw command */ + nk_draw_foreach(cmd, &x11.ctx, &dev->cmds) + { + if (!cmd->elem_count) continue; + glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id); + glScissor( + (GLint)(cmd->clip_rect.x), + (GLint)((height - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h))), + (GLint)(cmd->clip_rect.w), + (GLint)(cmd->clip_rect.h)); + glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset); + offset += cmd->elem_count; + } + nk_clear(&x11.ctx); + } + + /* default OpenGL state */ + glUseProgram(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindVertexArray(0); + glDisable(GL_BLEND); + glDisable(GL_SCISSOR_TEST); +} + +NK_API void +nk_x11_font_stash_begin(struct nk_font_atlas **atlas) +{ + nk_font_atlas_init_default(&x11.atlas); + nk_font_atlas_begin(&x11.atlas); + *atlas = &x11.atlas; +} + +NK_API void +nk_x11_font_stash_end(void) +{ + const void *image; int w, h; + image = nk_font_atlas_bake(&x11.atlas, &w, &h, NK_FONT_ATLAS_RGBA32); + nk_x11_device_upload_atlas(image, w, h); + nk_font_atlas_end(&x11.atlas, nk_handle_id((int)x11.ogl.font_tex), &x11.ogl.null); + if (x11.atlas.default_font) + nk_style_set_font(&x11.ctx, &x11.atlas.default_font->handle); +} + +NK_API int +nk_x11_handle_event(XEvent *evt) +{ + struct nk_context *ctx = &x11.ctx; + + /* optional grabbing behavior */ + if (ctx->input.mouse.grab) { + XDefineCursor(x11.dpy, x11.win, x11.cursor); + ctx->input.mouse.grab = 0; + } else if (ctx->input.mouse.ungrab) { + XWarpPointer(x11.dpy, None, x11.win, 0, 0, 0, 0, + (int)ctx->input.mouse.pos.x, (int)ctx->input.mouse.pos.y); + XUndefineCursor(x11.dpy, x11.win); + ctx->input.mouse.ungrab = 0; + } + + if (evt->type == KeyPress || evt->type == KeyRelease) + { + /* Key handler */ + int ret, down = (evt->type == KeyPress); + KeySym *code = XGetKeyboardMapping(x11.dpy, (KeyCode)evt->xkey.keycode, 1, &ret); + if (*code == XK_Shift_L || *code == XK_Shift_R) nk_input_key(ctx, NK_KEY_SHIFT, down); + else if (*code == XK_Delete) nk_input_key(ctx, NK_KEY_DEL, down); + else if (*code == XK_Return) nk_input_key(ctx, NK_KEY_ENTER, down); + else if (*code == XK_Tab) nk_input_key(ctx, NK_KEY_TAB, down); + else if (*code == XK_Left) nk_input_key(ctx, NK_KEY_LEFT, down); + else if (*code == XK_Right) nk_input_key(ctx, NK_KEY_RIGHT, down); + else if (*code == XK_Up) nk_input_key(ctx, NK_KEY_UP, down); + else if (*code == XK_Down) nk_input_key(ctx, NK_KEY_DOWN, down); + else if (*code == XK_BackSpace) nk_input_key(ctx, NK_KEY_BACKSPACE, down); + else if (*code == XK_space && !down) nk_input_char(ctx, ' '); + else if (*code == XK_Page_Up) nk_input_key(ctx, NK_KEY_SCROLL_UP, down); + else if (*code == XK_Page_Down) nk_input_key(ctx, NK_KEY_SCROLL_DOWN, down); + else if (*code == XK_Home) { + nk_input_key(ctx, NK_KEY_TEXT_START, down); + nk_input_key(ctx, NK_KEY_SCROLL_START, down); + } else if (*code == XK_End) { + nk_input_key(ctx, NK_KEY_TEXT_END, down); + nk_input_key(ctx, NK_KEY_SCROLL_END, down); + } else { + if (*code == 'c' && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_COPY, down); + else if (*code == 'v' && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_PASTE, down); + else if (*code == 'x' && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_CUT, down); + else if (*code == 'z' && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_TEXT_UNDO, down); + else if (*code == 'r' && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_TEXT_REDO, down); + else if (*code == XK_Left && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_TEXT_WORD_LEFT, down); + else if (*code == XK_Right && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_TEXT_WORD_RIGHT, down); + else if (*code == 'b' && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_TEXT_LINE_START, down); + else if (*code == 'e' && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_TEXT_LINE_END, down); + else { + if (*code == 'i') + nk_input_key(ctx, NK_KEY_TEXT_INSERT_MODE, down); + else if (*code == 'r') + nk_input_key(ctx, NK_KEY_TEXT_REPLACE_MODE, down); + if (down) { + char buf[32]; + KeySym keysym = 0; + if (XLookupString((XKeyEvent*)evt, buf, 32, &keysym, NULL) != NoSymbol) + nk_input_glyph(ctx, buf); + } + } + } + XFree(code); + return 1; + } else if (evt->type == ButtonPress || evt->type == ButtonRelease) { + /* Button handler */ + int down = (evt->type == ButtonPress); + const int x = evt->xbutton.x, y = evt->xbutton.y; + if (evt->xbutton.button == Button1) + nk_input_button(ctx, NK_BUTTON_LEFT, x, y, down); + if (evt->xbutton.button == Button2) + nk_input_button(ctx, NK_BUTTON_MIDDLE, x, y, down); + else if (evt->xbutton.button == Button3) + nk_input_button(ctx, NK_BUTTON_RIGHT, x, y, down); + else if (evt->xbutton.button == Button4) + nk_input_scroll(ctx, 1.0f); + else if (evt->xbutton.button == Button5) + nk_input_scroll(ctx, -1.0f); + else return 0; + return 1; + } else if (evt->type == MotionNotify) { + /* Mouse motion handler */ + const int x = evt->xmotion.x, y = evt->xmotion.y; + nk_input_motion(ctx, x, y); + if (ctx->input.mouse.grabbed) { + ctx->input.mouse.pos.x = ctx->input.mouse.prev.x; + ctx->input.mouse.pos.y = ctx->input.mouse.prev.y; + XWarpPointer(x11.dpy, None, x11.win, 0, 0, 0, 0, (int)ctx->input.mouse.pos.x, (int)ctx->input.mouse.pos.y); + } + return 1; + } else if (evt->type == KeymapNotify) { + XRefreshKeyboardMapping(&evt->xmapping); + return 1; + } + return 0; +} + +NK_API struct nk_context* +nk_x11_init(Display *dpy, Window win) +{ + if (!setlocale(LC_ALL,"")) return 0; + if (!XSupportsLocale()) return 0; + if (!XSetLocaleModifiers("@im=none")) return 0; + if (!nk_x11_device_create()) return 0; + + x11.dpy = dpy; + x11.win = win; + + /* create invisible cursor */ + {static XColor dummy; char data[1] = {0}; + Pixmap blank = XCreateBitmapFromData(dpy, win, data, 1, 1); + if (blank == None) return 0; + x11.cursor = XCreatePixmapCursor(dpy, blank, blank, &dummy, &dummy, 0, 0); + XFreePixmap(dpy, blank);} + + nk_init_default(&x11.ctx, 0); + return &x11.ctx; +} + +NK_API void +nk_x11_shutdown(void) +{ + nk_font_atlas_clear(&x11.atlas); + nk_free(&x11.ctx); + nk_x11_device_destroy(); + XFreeCursor(x11.dpy, x11.cursor); + memset(&x11, 0, sizeof(x11)); +} + +#endif diff --git a/nuklear/example/Makefile b/nuklear/example/Makefile new file mode 100644 index 0000000..22829a2 --- /dev/null +++ b/nuklear/example/Makefile @@ -0,0 +1,41 @@ +# Flags +CFLAGS = -std=c99 -pedantic -O2 +LIBS := + +ifeq ($(OS),Windows_NT) +BIN := $(BIN).exe + LIBS := -lglfw3 -lopengl32 -lm -lGLU32 -lGLEW32 +else + UNAME_S := $(shell uname -s) + ifeq ($(UNAME_S),Darwin) + LIBS := -lglfw3 -framework OpenGL -lm -lGLEW -L/usr/local/lib + CFLAGS += -I/usr/local/include + else + LIBS := -lglfw -lGL -lm -lGLU -lGLEW + endif +endif + +all: generate file_browser extended canvas skinning + +generate: clean +ifeq ($(OS),Windows_NT) + @mkdir bin 2> nul || exit 0 +else + @mkdir -p bin +endif + +clean: + @rm -rf bin + +file_browser: generate + $(CC) $(CFLAGS) -o bin/file_browser file_browser.c $(LIBS) + +extended: generate + $(CC) $(CFLAGS) -o bin/extended extended.c $(LIBS) + +canvas: generate + $(CC) $(CFLAGS) -o bin/canvas canvas.c $(LIBS) + +skinning: generate + $(CC) $(CFLAGS) -o bin/skinning skinning.c $(LIBS) + diff --git a/nuklear/example/canvas.c b/nuklear/example/canvas.c new file mode 100644 index 0000000..c5f5634 --- /dev/null +++ b/nuklear/example/canvas.c @@ -0,0 +1,489 @@ +/* nuklear - v1.05 - public domain */ +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdarg.h> +#include <string.h> +#include <math.h> +#include <assert.h> +#include <math.h> +#include <time.h> +#include <limits.h> + +#include <GL/glew.h> +#include <GLFW/glfw3.h> + +#define NK_PRIVATE +#define NK_INCLUDE_FIXED_TYPES +#define NK_INCLUDE_STANDARD_IO +#define NK_INCLUDE_DEFAULT_ALLOCATOR +#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT +#define NK_INCLUDE_FONT_BAKING +#define NK_INCLUDE_DEFAULT_FONT +#define NK_IMPLEMENTATION +#include "../nuklear.h" + +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" + +/* macros */ +#define WINDOW_WIDTH 1200 +#define WINDOW_HEIGHT 800 + +#define MAX_VERTEX_MEMORY 512 * 1024 +#define MAX_ELEMENT_MEMORY 128 * 1024 + +#define UNUSED(a) (void)a +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) < (b) ? (b) : (a)) +#define LEN(a) (sizeof(a)/sizeof(a)[0]) + +#define NK_SHADER_VERSION "#version 150\n" + +/* =============================================================== + * + * DEVICE + * + * ===============================================================*/ +struct nk_glfw_vertex { + float position[2]; + float uv[2]; + nk_byte col[4]; +}; + +struct device { + struct nk_buffer cmds; + struct nk_draw_null_texture null; + GLuint vbo, vao, ebo; + GLuint prog; + GLuint vert_shdr; + GLuint frag_shdr; + GLint attrib_pos; + GLint attrib_uv; + GLint attrib_col; + GLint uniform_tex; + GLint uniform_proj; + GLuint font_tex; +}; + +static void +die(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fputs("\n", stderr); + exit(EXIT_FAILURE); +} + +static struct nk_image +icon_load(const char *filename) +{ + int x,y,n; + GLuint tex; + unsigned char *data = stbi_load(filename, &x, &y, &n, 0); + if (!data) die("[SDL]: failed to load image: %s", filename); + + glGenTextures(1, &tex); + glBindTexture(GL_TEXTURE_2D, tex); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, x, y, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + glGenerateMipmap(GL_TEXTURE_2D); + stbi_image_free(data); + return nk_image_id((int)tex); +} + +static void +device_init(struct device *dev) +{ + GLint status; + static const GLchar *vertex_shader = + NK_SHADER_VERSION + "uniform mat4 ProjMtx;\n" + "in vec2 Position;\n" + "in vec2 TexCoord;\n" + "in vec4 Color;\n" + "out vec2 Frag_UV;\n" + "out vec4 Frag_Color;\n" + "void main() {\n" + " Frag_UV = TexCoord;\n" + " Frag_Color = Color;\n" + " gl_Position = ProjMtx * vec4(Position.xy, 0, 1);\n" + "}\n"; + static const GLchar *fragment_shader = + NK_SHADER_VERSION + "precision mediump float;\n" + "uniform sampler2D Texture;\n" + "in vec2 Frag_UV;\n" + "in vec4 Frag_Color;\n" + "out vec4 Out_Color;\n" + "void main(){\n" + " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" + "}\n"; + + nk_buffer_init_default(&dev->cmds); + dev->prog = glCreateProgram(); + dev->vert_shdr = glCreateShader(GL_VERTEX_SHADER); + dev->frag_shdr = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(dev->vert_shdr, 1, &vertex_shader, 0); + glShaderSource(dev->frag_shdr, 1, &fragment_shader, 0); + glCompileShader(dev->vert_shdr); + glCompileShader(dev->frag_shdr); + glGetShaderiv(dev->vert_shdr, GL_COMPILE_STATUS, &status); + assert(status == GL_TRUE); + glGetShaderiv(dev->frag_shdr, GL_COMPILE_STATUS, &status); + assert(status == GL_TRUE); + glAttachShader(dev->prog, dev->vert_shdr); + glAttachShader(dev->prog, dev->frag_shdr); + glLinkProgram(dev->prog); + glGetProgramiv(dev->prog, GL_LINK_STATUS, &status); + assert(status == GL_TRUE); + + dev->uniform_tex = glGetUniformLocation(dev->prog, "Texture"); + dev->uniform_proj = glGetUniformLocation(dev->prog, "ProjMtx"); + dev->attrib_pos = glGetAttribLocation(dev->prog, "Position"); + dev->attrib_uv = glGetAttribLocation(dev->prog, "TexCoord"); + dev->attrib_col = glGetAttribLocation(dev->prog, "Color"); + + { + /* buffer setup */ + GLsizei vs = sizeof(struct nk_glfw_vertex); + size_t vp = offsetof(struct nk_glfw_vertex, position); + size_t vt = offsetof(struct nk_glfw_vertex, uv); + size_t vc = offsetof(struct nk_glfw_vertex, col); + + glGenBuffers(1, &dev->vbo); + glGenBuffers(1, &dev->ebo); + glGenVertexArrays(1, &dev->vao); + + glBindVertexArray(dev->vao); + glBindBuffer(GL_ARRAY_BUFFER, dev->vbo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo); + + glEnableVertexAttribArray((GLuint)dev->attrib_pos); + glEnableVertexAttribArray((GLuint)dev->attrib_uv); + glEnableVertexAttribArray((GLuint)dev->attrib_col); + + glVertexAttribPointer((GLuint)dev->attrib_pos, 2, GL_FLOAT, GL_FALSE, vs, (void*)vp); + glVertexAttribPointer((GLuint)dev->attrib_uv, 2, GL_FLOAT, GL_FALSE, vs, (void*)vt); + glVertexAttribPointer((GLuint)dev->attrib_col, 4, GL_UNSIGNED_BYTE, GL_TRUE, vs, (void*)vc); + } + + glBindTexture(GL_TEXTURE_2D, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindVertexArray(0); +} + +static void +device_upload_atlas(struct device *dev, const void *image, int width, int height) +{ + glGenTextures(1, &dev->font_tex); + glBindTexture(GL_TEXTURE_2D, dev->font_tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)width, (GLsizei)height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, image); +} + +static void +device_shutdown(struct device *dev) +{ + glDetachShader(dev->prog, dev->vert_shdr); + glDetachShader(dev->prog, dev->frag_shdr); + glDeleteShader(dev->vert_shdr); + glDeleteShader(dev->frag_shdr); + glDeleteProgram(dev->prog); + glDeleteTextures(1, &dev->font_tex); + glDeleteBuffers(1, &dev->vbo); + glDeleteBuffers(1, &dev->ebo); + nk_buffer_free(&dev->cmds); +} + +static void +device_draw(struct device *dev, struct nk_context *ctx, int width, int height, + enum nk_anti_aliasing AA) +{ + GLfloat ortho[4][4] = { + {2.0f, 0.0f, 0.0f, 0.0f}, + {0.0f,-2.0f, 0.0f, 0.0f}, + {0.0f, 0.0f,-1.0f, 0.0f}, + {-1.0f,1.0f, 0.0f, 1.0f}, + }; + ortho[0][0] /= (GLfloat)width; + ortho[1][1] /= (GLfloat)height; + + /* setup global state */ + glEnable(GL_BLEND); + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glEnable(GL_SCISSOR_TEST); + glActiveTexture(GL_TEXTURE0); + + /* setup program */ + glUseProgram(dev->prog); + glUniform1i(dev->uniform_tex, 0); + glUniformMatrix4fv(dev->uniform_proj, 1, GL_FALSE, &ortho[0][0]); + { + /* convert from command queue into draw list and draw to screen */ + const struct nk_draw_command *cmd; + void *vertices, *elements; + const nk_draw_index *offset = NULL; + + /* allocate vertex and element buffer */ + glBindVertexArray(dev->vao); + glBindBuffer(GL_ARRAY_BUFFER, dev->vbo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo); + + glBufferData(GL_ARRAY_BUFFER, MAX_VERTEX_MEMORY, NULL, GL_STREAM_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_ELEMENT_MEMORY, NULL, GL_STREAM_DRAW); + + /* load draw vertices & elements directly into vertex + element buffer */ + vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); + elements = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY); + { + /* fill convert configuration */ + struct nk_convert_config config; + static const struct nk_draw_vertex_layout_element vertex_layout[] = { + {NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_glfw_vertex, position)}, + {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_glfw_vertex, uv)}, + {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, NK_OFFSETOF(struct nk_glfw_vertex, col)}, + {NK_VERTEX_LAYOUT_END} + }; + NK_MEMSET(&config, 0, sizeof(config)); + config.vertex_layout = vertex_layout; + config.vertex_size = sizeof(struct nk_glfw_vertex); + config.vertex_alignment = NK_ALIGNOF(struct nk_glfw_vertex); + config.null = dev->null; + config.circle_segment_count = 22; + config.curve_segment_count = 22; + config.arc_segment_count = 22; + config.global_alpha = 1.0f; + config.shape_AA = AA; + config.line_AA = AA; + + /* setup buffers to load vertices and elements */ + {struct nk_buffer vbuf, ebuf; + nk_buffer_init_fixed(&vbuf, vertices, MAX_VERTEX_MEMORY); + nk_buffer_init_fixed(&ebuf, elements, MAX_ELEMENT_MEMORY); + nk_convert(ctx, &dev->cmds, &vbuf, &ebuf, &config);} + } + glUnmapBuffer(GL_ARRAY_BUFFER); + glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); + + /* iterate over and execute each draw command */ + nk_draw_foreach(cmd, ctx, &dev->cmds) + { + if (!cmd->elem_count) continue; + glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id); + glScissor( + (GLint)(cmd->clip_rect.x), + (GLint)((height - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h))), + (GLint)(cmd->clip_rect.w), + (GLint)(cmd->clip_rect.h)); + glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset); + offset += cmd->elem_count; + } + nk_clear(ctx); + } + + /* default OpenGL state */ + glUseProgram(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindVertexArray(0); + glDisable(GL_BLEND); + glDisable(GL_SCISSOR_TEST); +} + +/* glfw callbacks (I don't know if there is a easier way to access text and scroll )*/ +static void error_callback(int e, const char *d){printf("Error %d: %s\n", e, d);} +static void text_input(GLFWwindow *win, unsigned int codepoint) +{nk_input_unicode((struct nk_context*)glfwGetWindowUserPointer(win), codepoint);} +static void scroll_input(GLFWwindow *win, double _, double yoff) +{UNUSED(_);nk_input_scroll((struct nk_context*)glfwGetWindowUserPointer(win), (float)yoff);} + +static void +pump_input(struct nk_context *ctx, GLFWwindow *win) +{ + double x, y; + nk_input_begin(ctx); + glfwPollEvents(); + + nk_input_key(ctx, NK_KEY_DEL, glfwGetKey(win, GLFW_KEY_DELETE) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_ENTER, glfwGetKey(win, GLFW_KEY_ENTER) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_TAB, glfwGetKey(win, GLFW_KEY_TAB) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_BACKSPACE, glfwGetKey(win, GLFW_KEY_BACKSPACE) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_LEFT, glfwGetKey(win, GLFW_KEY_LEFT) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_RIGHT, glfwGetKey(win, GLFW_KEY_RIGHT) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_UP, glfwGetKey(win, GLFW_KEY_UP) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_DOWN, glfwGetKey(win, GLFW_KEY_DOWN) == GLFW_PRESS); + + if (glfwGetKey(win, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || + glfwGetKey(win, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS) { + nk_input_key(ctx, NK_KEY_COPY, glfwGetKey(win, GLFW_KEY_C) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_PASTE, glfwGetKey(win, GLFW_KEY_P) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_CUT, glfwGetKey(win, GLFW_KEY_X) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_CUT, glfwGetKey(win, GLFW_KEY_E) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_SHIFT, 1); + } else { + nk_input_key(ctx, NK_KEY_COPY, 0); + nk_input_key(ctx, NK_KEY_PASTE, 0); + nk_input_key(ctx, NK_KEY_CUT, 0); + nk_input_key(ctx, NK_KEY_SHIFT, 0); + } + + glfwGetCursorPos(win, &x, &y); + nk_input_motion(ctx, (int)x, (int)y); + nk_input_button(ctx, NK_BUTTON_LEFT, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS); + nk_input_button(ctx, NK_BUTTON_MIDDLE, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_MIDDLE) == GLFW_PRESS); + nk_input_button(ctx, NK_BUTTON_RIGHT, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS); + nk_input_end(ctx); +} + +struct nk_canvas { + struct nk_command_buffer *painter; + struct nk_vec2 item_spacing; + struct nk_vec2 panel_padding; + struct nk_style_item window_background; +}; + +static void +canvas_begin(struct nk_context *ctx, struct nk_canvas *canvas, nk_flags flags, + int x, int y, int width, int height, struct nk_color background_color) +{ + /* save style properties which will be overwritten */ + canvas->panel_padding = ctx->style.window.padding; + canvas->item_spacing = ctx->style.window.spacing; + canvas->window_background = ctx->style.window.fixed_background; + + /* use the complete window space and set background */ + ctx->style.window.spacing = nk_vec2(0,0); + ctx->style.window.padding = nk_vec2(0,0); + ctx->style.window.fixed_background = nk_style_item_color(background_color); + + /* create/update window and set position + size */ + flags = flags & ~NK_WINDOW_DYNAMIC; + nk_begin(ctx, "Window", nk_rect(x, y, width, height), NK_WINDOW_NO_SCROLLBAR|flags); + nk_window_set_bounds(ctx, nk_rect(x, y, width, height)); + + /* allocate the complete window space for drawing */ + {struct nk_rect total_space; + total_space = nk_window_get_content_region(ctx); + nk_layout_row_dynamic(ctx, total_space.h, 1); + nk_widget(&total_space, ctx); + canvas->painter = nk_window_get_canvas(ctx);} +} + +static void +canvas_end(struct nk_context *ctx, struct nk_canvas *canvas) +{ + nk_end(ctx); + ctx->style.window.spacing = canvas->panel_padding; + ctx->style.window.padding = canvas->item_spacing; + ctx->style.window.fixed_background = canvas->window_background; +} + +int main(int argc, char *argv[]) +{ + /* Platform */ + static GLFWwindow *win; + int width = 0, height = 0; + + /* GUI */ + struct device device; + struct nk_font_atlas atlas; + struct nk_context ctx; + + /* GLFW */ + glfwSetErrorCallback(error_callback); + if (!glfwInit()) { + fprintf(stdout, "[GFLW] failed to init!\n"); + exit(1); + } + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + win = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "Demo", NULL, NULL); + glfwMakeContextCurrent(win); + glfwSetWindowUserPointer(win, &ctx); + glfwSetCharCallback(win, text_input); + glfwSetScrollCallback(win, scroll_input); + glfwGetWindowSize(win, &width, &height); + + /* OpenGL */ + glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); + glewExperimental = 1; + if (glewInit() != GLEW_OK) { + fprintf(stderr, "Failed to setup GLEW\n"); + exit(1); + } + + /* GUI */ + {device_init(&device); + {const void *image; int w, h; + struct nk_font *font; + nk_font_atlas_init_default(&atlas); + nk_font_atlas_begin(&atlas); + font = nk_font_atlas_add_default(&atlas, 13, 0); + image = nk_font_atlas_bake(&atlas, &w, &h, NK_FONT_ATLAS_RGBA32); + device_upload_atlas(&device, image, w, h); + nk_font_atlas_end(&atlas, nk_handle_id((int)device.font_tex), &device.null); + nk_init_default(&ctx, &font->handle); + + glEnable(GL_TEXTURE_2D); + while (!glfwWindowShouldClose(win)) + { + /* input */ + pump_input(&ctx, win); + + /* draw */ + {struct nk_canvas canvas; + canvas_begin(&ctx, &canvas, 0, 0, 0, width, height, nk_rgb(250,250,250)); + { + nk_fill_rect(canvas.painter, nk_rect(15,15,210,210), 5, nk_rgb(247, 230, 154)); + nk_fill_rect(canvas.painter, nk_rect(20,20,200,200), 5, nk_rgb(188, 174, 118)); + nk_draw_text(canvas.painter, nk_rect(30, 30, 150, 20), "Text to draw", 12, &font->handle, nk_rgb(188,174,118), nk_rgb(0,0,0)); + nk_fill_rect(canvas.painter, nk_rect(250,20,100,100), 0, nk_rgb(0,0,255)); + nk_fill_circle(canvas.painter, nk_rect(20,250,100,100), nk_rgb(255,0,0)); + nk_fill_triangle(canvas.painter, 250, 250, 350, 250, 300, 350, nk_rgb(0,255,0)); + nk_fill_arc(canvas.painter, 300, 180, 50, 0, 3.141592654f * 3.0f / 4.0f, nk_rgb(255,255,0)); + + {float points[12]; + points[0] = 200; points[1] = 250; + points[2] = 250; points[3] = 350; + points[4] = 225; points[5] = 350; + points[6] = 200; points[7] = 300; + points[8] = 175; points[9] = 350; + points[10] = 150; points[11] = 350; + nk_fill_polygon(canvas.painter, points, 6, nk_rgb(0,0,0));} + + nk_stroke_line(canvas.painter, 15, 10, 200, 10, 2.0f, nk_rgb(189,45,75)); + nk_stroke_rect(canvas.painter, nk_rect(370, 20, 100, 100), 10, 3, nk_rgb(0,0,255)); + nk_stroke_curve(canvas.painter, 380, 200, 405, 270, 455, 120, 480, 200, 2, nk_rgb(0,150,220)); + nk_stroke_circle(canvas.painter, nk_rect(20, 370, 100, 100), 5, nk_rgb(0,255,120)); + nk_stroke_triangle(canvas.painter, 370, 250, 470, 250, 420, 350, 6, nk_rgb(255,0,143)); + } + canvas_end(&ctx, &canvas);} + + /* Draw */ + glfwGetWindowSize(win, &width, &height); + glViewport(0, 0, width, height); + glClear(GL_COLOR_BUFFER_BIT); + glClearColor(0.2f, 0.2f, 0.2f, 1.0f); + device_draw(&device, &ctx, width, height, NK_ANTI_ALIASING_ON); + glfwSwapBuffers(win); + }}} + nk_font_atlas_clear(&atlas); + nk_free(&ctx); + device_shutdown(&device); + glfwTerminate(); + return 0; +} + diff --git a/nuklear/example/extended.c b/nuklear/example/extended.c new file mode 100644 index 0000000..e555d65 --- /dev/null +++ b/nuklear/example/extended.c @@ -0,0 +1,906 @@ +/* nuklear - v1.05 - public domain */ +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdarg.h> +#include <string.h> +#include <math.h> +#include <assert.h> +#include <math.h> +#include <time.h> +#include <limits.h> + +#include <GL/glew.h> +#include <GLFW/glfw3.h> + +#define NK_INCLUDE_FIXED_TYPES +#define NK_INCLUDE_STANDARD_IO +#define NK_INCLUDE_DEFAULT_ALLOCATOR +#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT +#define NK_INCLUDE_FONT_BAKING +#define NK_INCLUDE_DEFAULT_FONT +#define NK_IMPLEMENTATION +#include "../nuklear.h" + +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" + +/* macros */ +#define WINDOW_WIDTH 1200 +#define WINDOW_HEIGHT 800 + +#define MAX_VERTEX_MEMORY 512 * 1024 +#define MAX_ELEMENT_MEMORY 128 * 1024 + +#define UNUSED(a) (void)a +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) < (b) ? (b) : (a)) +#define LEN(a) (sizeof(a)/sizeof(a)[0]) + +#ifdef __APPLE__ + #define NK_SHADER_VERSION "#version 150\n" +#else + #define NK_SHADER_VERSION "#version 300 es\n" +#endif + +struct media { + struct nk_font *font_14; + struct nk_font *font_18; + struct nk_font *font_20; + struct nk_font *font_22; + + struct nk_image unchecked; + struct nk_image checked; + struct nk_image rocket; + struct nk_image cloud; + struct nk_image pen; + struct nk_image play; + struct nk_image pause; + struct nk_image stop; + struct nk_image prev; + struct nk_image next; + struct nk_image tools; + struct nk_image dir; + struct nk_image copy; + struct nk_image convert; + struct nk_image del; + struct nk_image edit; + struct nk_image images[9]; + struct nk_image menu[6]; +}; + +/* =============================================================== + * + * CUSTOM WIDGET + * + * ===============================================================*/ +static int +ui_piemenu(struct nk_context *ctx, struct nk_vec2 pos, float radius, + struct nk_image *icons, int item_count) +{ + int ret = -1; + struct nk_rect total_space; + struct nk_rect bounds; + int active_item = 0; + + /* pie menu popup */ + struct nk_color border = ctx->style.window.border_color; + struct nk_style_item background = ctx->style.window.fixed_background; + ctx->style.window.fixed_background = nk_style_item_hide(); + ctx->style.window.border_color = nk_rgba(0,0,0,0); + + total_space = nk_window_get_content_region(ctx); + ctx->style.window.spacing = nk_vec2(0,0); + ctx->style.window.padding = nk_vec2(0,0); + + if (nk_popup_begin(ctx, NK_POPUP_STATIC, "piemenu", NK_WINDOW_NO_SCROLLBAR, + nk_rect(pos.x - total_space.x - radius, pos.y - radius - total_space.y, + 2*radius,2*radius))) + { + int i = 0; + struct nk_command_buffer* out = nk_window_get_canvas(ctx); + const struct nk_input *in = &ctx->input; + + total_space = nk_window_get_content_region(ctx); + ctx->style.window.spacing = nk_vec2(4,4); + ctx->style.window.padding = nk_vec2(8,8); + nk_layout_row_dynamic(ctx, total_space.h, 1); + nk_widget(&bounds, ctx); + + /* outer circle */ + nk_fill_circle(out, bounds, nk_rgb(50,50,50)); + { + /* circle buttons */ + float step = (2 * 3.141592654f) / (float)(MAX(1,item_count)); + float a_min = 0; float a_max = step; + + struct nk_vec2 center = nk_vec2(bounds.x + bounds.w / 2.0f, bounds.y + bounds.h / 2.0f); + struct nk_vec2 drag = nk_vec2(in->mouse.pos.x - center.x, in->mouse.pos.y - center.y); + float angle = (float)atan2(drag.y, drag.x); + if (angle < -0.0f) angle += 2.0f * 3.141592654f; + active_item = (int)(angle/step); + + for (i = 0; i < item_count; ++i) { + struct nk_rect content; + float rx, ry, dx, dy, a; + nk_fill_arc(out, center.x, center.y, (bounds.w/2.0f), + a_min, a_max, (active_item == i) ? nk_rgb(45,100,255): nk_rgb(60,60,60)); + + /* separator line */ + rx = bounds.w/2.0f; ry = 0; + dx = rx * (float)cos(a_min) - ry * (float)sin(a_min); + dy = rx * (float)sin(a_min) + ry * (float)cos(a_min); + nk_stroke_line(out, center.x, center.y, + center.x + dx, center.y + dy, 1.0f, nk_rgb(50,50,50)); + + /* button content */ + a = a_min + (a_max - a_min)/2.0f; + rx = bounds.w/2.5f; ry = 0; + content.w = 30; content.h = 30; + content.x = center.x + ((rx * (float)cos(a) - ry * (float)sin(a)) - content.w/2.0f); + content.y = center.y + (rx * (float)sin(a) + ry * (float)cos(a) - content.h/2.0f); + nk_draw_image(out, content, &icons[i], nk_rgb(255,255,255)); + a_min = a_max; a_max += step; + } + } + { + /* inner circle */ + struct nk_rect inner; + inner.x = bounds.x + bounds.w/2 - bounds.w/4; + inner.y = bounds.y + bounds.h/2 - bounds.h/4; + inner.w = bounds.w/2; inner.h = bounds.h/2; + nk_fill_circle(out, inner, nk_rgb(45,45,45)); + + /* active icon content */ + bounds.w = inner.w / 2.0f; + bounds.h = inner.h / 2.0f; + bounds.x = inner.x + inner.w/2 - bounds.w/2; + bounds.y = inner.y + inner.h/2 - bounds.h/2; + nk_draw_image(out, bounds, &icons[active_item], nk_rgb(255,255,255)); + } + nk_layout_space_end(ctx); + if (!nk_input_is_mouse_down(&ctx->input, NK_BUTTON_RIGHT)) { + nk_popup_close(ctx); + ret = active_item; + } + } else ret = -2; + ctx->style.window.spacing = nk_vec2(4,4); + ctx->style.window.padding = nk_vec2(8,8); + nk_popup_end(ctx); + + ctx->style.window.fixed_background = background; + ctx->style.window.border_color = border; + return ret; +} + +/* =============================================================== + * + * GRID + * + * ===============================================================*/ +static void +grid_demo(struct nk_context *ctx, struct media *media) +{ + static char text[3][64]; + static int text_len[3]; + static const char *items[] = {"Item 0","item 1","item 2"}; + static int selected_item = 0; + static int check = 1; + + int i; + nk_style_set_font(ctx, &media->font_20->handle); + if (nk_begin(ctx, "Grid Demo", nk_rect(600, 350, 275, 250), + NK_WINDOW_TITLE|NK_WINDOW_BORDER|NK_WINDOW_MOVABLE| + NK_WINDOW_NO_SCROLLBAR)) + { + nk_style_set_font(ctx, &media->font_18->handle); + nk_layout_row_dynamic(ctx, 30, 2); + nk_label(ctx, "Floating point:", NK_TEXT_RIGHT); + nk_edit_string(ctx, NK_EDIT_FIELD, text[0], &text_len[0], 64, nk_filter_float); + nk_label(ctx, "Hexadecimal:", NK_TEXT_RIGHT); + nk_edit_string(ctx, NK_EDIT_FIELD, text[1], &text_len[1], 64, nk_filter_hex); + nk_label(ctx, "Binary:", NK_TEXT_RIGHT); + nk_edit_string(ctx, NK_EDIT_FIELD, text[2], &text_len[2], 64, nk_filter_binary); + nk_label(ctx, "Checkbox:", NK_TEXT_RIGHT); + nk_checkbox_label(ctx, "Check me", &check); + nk_label(ctx, "Combobox:", NK_TEXT_RIGHT); + if (nk_combo_begin_label(ctx, items[selected_item], nk_vec2(nk_widget_width(ctx), 200))) { + nk_layout_row_dynamic(ctx, 25, 1); + for (i = 0; i < 3; ++i) + if (nk_combo_item_label(ctx, items[i], NK_TEXT_LEFT)) + selected_item = i; + nk_combo_end(ctx); + } + } + nk_end(ctx); + nk_style_set_font(ctx, &media->font_14->handle); +} + +/* =============================================================== + * + * BUTTON DEMO + * + * ===============================================================*/ +static void +ui_header(struct nk_context *ctx, struct media *media, const char *title) +{ + nk_style_set_font(ctx, &media->font_18->handle); + nk_layout_row_dynamic(ctx, 20, 1); + nk_label(ctx, title, NK_TEXT_LEFT); +} + +static void +ui_widget(struct nk_context *ctx, struct media *media, float height) +{ + static const float ratio[] = {0.15f, 0.85f}; + nk_style_set_font(ctx, &media->font_22->handle); + nk_layout_row(ctx, NK_DYNAMIC, height, 2, ratio); + nk_spacing(ctx, 1); +} + +static void +ui_widget_centered(struct nk_context *ctx, struct media *media, float height) +{ + static const float ratio[] = {0.15f, 0.50f, 0.35f}; + nk_style_set_font(ctx, &media->font_22->handle); + nk_layout_row(ctx, NK_DYNAMIC, height, 3, ratio); + nk_spacing(ctx, 1); +} + +static void +button_demo(struct nk_context *ctx, struct media *media) +{ + static int option = 1; + static int toggle0 = 1; + static int toggle1 = 0; + static int toggle2 = 1; + + nk_style_set_font(ctx, &media->font_20->handle); + nk_begin(ctx, "Button Demo", nk_rect(50,50,255,610), + NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_TITLE); + + /*------------------------------------------------ + * MENU + *------------------------------------------------*/ + nk_menubar_begin(ctx); + { + /* toolbar */ + nk_layout_row_static(ctx, 40, 40, 4); + if (nk_menu_begin_image(ctx, "Music", media->play, nk_vec2(110,120))) + { + /* settings */ + nk_layout_row_dynamic(ctx, 25, 1); + nk_menu_item_image_label(ctx, media->play, "Play", NK_TEXT_RIGHT); + nk_menu_item_image_label(ctx, media->stop, "Stop", NK_TEXT_RIGHT); + nk_menu_item_image_label(ctx, media->pause, "Pause", NK_TEXT_RIGHT); + nk_menu_item_image_label(ctx, media->next, "Next", NK_TEXT_RIGHT); + nk_menu_item_image_label(ctx, media->prev, "Prev", NK_TEXT_RIGHT); + nk_menu_end(ctx); + } + nk_button_image(ctx, media->tools); + nk_button_image(ctx, media->cloud); + nk_button_image(ctx, media->pen); + } + nk_menubar_end(ctx); + + /*------------------------------------------------ + * BUTTON + *------------------------------------------------*/ + ui_header(ctx, media, "Push buttons"); + ui_widget(ctx, media, 35); + if (nk_button_label(ctx, "Push me")) + fprintf(stdout, "pushed!\n"); + ui_widget(ctx, media, 35); + if (nk_button_image_label(ctx, media->rocket, "Styled", NK_TEXT_CENTERED)) + fprintf(stdout, "rocket!\n"); + + /*------------------------------------------------ + * REPEATER + *------------------------------------------------*/ + ui_header(ctx, media, "Repeater"); + ui_widget(ctx, media, 35); + if (nk_button_label(ctx, "Press me")) + fprintf(stdout, "pressed!\n"); + + /*------------------------------------------------ + * TOGGLE + *------------------------------------------------*/ + ui_header(ctx, media, "Toggle buttons"); + ui_widget(ctx, media, 35); + if (nk_button_image_label(ctx, (toggle0) ? media->checked: media->unchecked, "Toggle", NK_TEXT_LEFT)) + toggle0 = !toggle0; + + ui_widget(ctx, media, 35); + if (nk_button_image_label(ctx, (toggle1) ? media->checked: media->unchecked, "Toggle", NK_TEXT_LEFT)) + toggle1 = !toggle1; + + ui_widget(ctx, media, 35); + if (nk_button_image_label(ctx, (toggle2) ? media->checked: media->unchecked, "Toggle", NK_TEXT_LEFT)) + toggle2 = !toggle2; + + /*------------------------------------------------ + * RADIO + *------------------------------------------------*/ + ui_header(ctx, media, "Radio buttons"); + ui_widget(ctx, media, 35); + if (nk_button_symbol_label(ctx, (option == 0)?NK_SYMBOL_CIRCLE_OUTLINE:NK_SYMBOL_CIRCLE_SOLID, "Select", NK_TEXT_LEFT)) + option = 0; + ui_widget(ctx, media, 35); + if (nk_button_symbol_label(ctx, (option == 1)?NK_SYMBOL_CIRCLE_OUTLINE:NK_SYMBOL_CIRCLE_SOLID, "Select", NK_TEXT_LEFT)) + option = 1; + ui_widget(ctx, media, 35); + if (nk_button_symbol_label(ctx, (option == 2)?NK_SYMBOL_CIRCLE_OUTLINE:NK_SYMBOL_CIRCLE_SOLID, "Select", NK_TEXT_LEFT)) + option = 2; + + /*------------------------------------------------ + * CONTEXTUAL + *------------------------------------------------*/ + nk_style_set_font(ctx, &media->font_18->handle); + if (nk_contextual_begin(ctx, NK_WINDOW_NO_SCROLLBAR, nk_vec2(150, 300), nk_window_get_bounds(ctx))) { + nk_layout_row_dynamic(ctx, 30, 1); + if (nk_contextual_item_image_label(ctx, media->copy, "Clone", NK_TEXT_RIGHT)) + fprintf(stdout, "pressed clone!\n"); + if (nk_contextual_item_image_label(ctx, media->del, "Delete", NK_TEXT_RIGHT)) + fprintf(stdout, "pressed delete!\n"); + if (nk_contextual_item_image_label(ctx, media->convert, "Convert", NK_TEXT_RIGHT)) + fprintf(stdout, "pressed convert!\n"); + if (nk_contextual_item_image_label(ctx, media->edit, "Edit", NK_TEXT_RIGHT)) + fprintf(stdout, "pressed edit!\n"); + nk_contextual_end(ctx); + } + nk_style_set_font(ctx, &media->font_14->handle); + nk_end(ctx); +} + +/* =============================================================== + * + * BASIC DEMO + * + * ===============================================================*/ +static void +basic_demo(struct nk_context *ctx, struct media *media) +{ + static int image_active; + static int check0 = 1; + static int check1 = 0; + static size_t prog = 80; + static int selected_item = 0; + static int selected_image = 3; + static int selected_icon = 0; + static const char *items[] = {"Item 0","item 1","item 2"}; + static int piemenu_active = 0; + static struct nk_vec2 piemenu_pos; + + int i = 0; + nk_style_set_font(ctx, &media->font_20->handle); + nk_begin(ctx, "Basic Demo", nk_rect(320, 50, 275, 610), + NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_TITLE); + + /*------------------------------------------------ + * POPUP BUTTON + *------------------------------------------------*/ + ui_header(ctx, media, "Popup & Scrollbar & Images"); + ui_widget(ctx, media, 35); + if (nk_button_image_label(ctx, media->dir, "Images", NK_TEXT_CENTERED)) + image_active = !image_active; + + /*------------------------------------------------ + * SELECTED IMAGE + *------------------------------------------------*/ + ui_header(ctx, media, "Selected Image"); + ui_widget_centered(ctx, media, 100); + nk_image(ctx, media->images[selected_image]); + + /*------------------------------------------------ + * IMAGE POPUP + *------------------------------------------------*/ + if (image_active) { + struct nk_panel popup; + if (nk_popup_begin(ctx, NK_POPUP_STATIC, "Image Popup", 0, nk_rect(265, 0, 320, 220))) { + nk_layout_row_static(ctx, 82, 82, 3); + for (i = 0; i < 9; ++i) { + if (nk_button_image(ctx, media->images[i])) { + selected_image = i; + image_active = 0; + nk_popup_close(ctx); + } + } + nk_popup_end(ctx); + } + } + /*------------------------------------------------ + * COMBOBOX + *------------------------------------------------*/ + ui_header(ctx, media, "Combo box"); + ui_widget(ctx, media, 40); + if (nk_combo_begin_label(ctx, items[selected_item], nk_vec2(nk_widget_width(ctx), 200))) { + nk_layout_row_dynamic(ctx, 35, 1); + for (i = 0; i < 3; ++i) + if (nk_combo_item_label(ctx, items[i], NK_TEXT_LEFT)) + selected_item = i; + nk_combo_end(ctx); + } + + ui_widget(ctx, media, 40); + if (nk_combo_begin_image_label(ctx, items[selected_icon], media->images[selected_icon], nk_vec2(nk_widget_width(ctx), 200))) { + nk_layout_row_dynamic(ctx, 35, 1); + for (i = 0; i < 3; ++i) + if (nk_combo_item_image_label(ctx, media->images[i], items[i], NK_TEXT_RIGHT)) + selected_icon = i; + nk_combo_end(ctx); + } + + /*------------------------------------------------ + * CHECKBOX + *------------------------------------------------*/ + ui_header(ctx, media, "Checkbox"); + ui_widget(ctx, media, 30); + nk_checkbox_label(ctx, "Flag 1", &check0); + ui_widget(ctx, media, 30); + nk_checkbox_label(ctx, "Flag 2", &check1); + + /*------------------------------------------------ + * PROGRESSBAR + *------------------------------------------------*/ + ui_header(ctx, media, "Progressbar"); + ui_widget(ctx, media, 35); + nk_progress(ctx, &prog, 100, nk_true); + + /*------------------------------------------------ + * PIEMENU + *------------------------------------------------*/ + if (nk_input_is_mouse_click_down_in_rect(&ctx->input, NK_BUTTON_RIGHT, + nk_window_get_bounds(ctx),nk_true)){ + piemenu_pos = ctx->input.mouse.pos; + piemenu_active = 1; + } + + if (piemenu_active) { + int ret = ui_piemenu(ctx, piemenu_pos, 140, &media->menu[0], 6); + if (ret == -2) piemenu_active = 0; + if (ret != -1) { + fprintf(stdout, "piemenu selected: %d\n", ret); + piemenu_active = 0; + } + } + nk_style_set_font(ctx, &media->font_14->handle); + nk_end(ctx); +} + +/* =============================================================== + * + * DEVICE + * + * ===============================================================*/ +struct nk_glfw_vertex { + float position[2]; + float uv[2]; + nk_byte col[4]; +}; + +struct device { + struct nk_buffer cmds; + struct nk_draw_null_texture null; + GLuint vbo, vao, ebo; + GLuint prog; + GLuint vert_shdr; + GLuint frag_shdr; + GLint attrib_pos; + GLint attrib_uv; + GLint attrib_col; + GLint uniform_tex; + GLint uniform_proj; + GLuint font_tex; +}; + +static void +die(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fputs("\n", stderr); + exit(EXIT_FAILURE); +} + +static struct nk_image +icon_load(const char *filename) +{ + int x,y,n; + GLuint tex; + unsigned char *data = stbi_load(filename, &x, &y, &n, 0); + if (!data) die("[SDL]: failed to load image: %s", filename); + + glGenTextures(1, &tex); + glBindTexture(GL_TEXTURE_2D, tex); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, x, y, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + glGenerateMipmap(GL_TEXTURE_2D); + stbi_image_free(data); + return nk_image_id((int)tex); +} + +static void +device_init(struct device *dev) +{ + GLint status; + static const GLchar *vertex_shader = + NK_SHADER_VERSION + "uniform mat4 ProjMtx;\n" + "in vec2 Position;\n" + "in vec2 TexCoord;\n" + "in vec4 Color;\n" + "out vec2 Frag_UV;\n" + "out vec4 Frag_Color;\n" + "void main() {\n" + " Frag_UV = TexCoord;\n" + " Frag_Color = Color;\n" + " gl_Position = ProjMtx * vec4(Position.xy, 0, 1);\n" + "}\n"; + static const GLchar *fragment_shader = + NK_SHADER_VERSION + "precision mediump float;\n" + "uniform sampler2D Texture;\n" + "in vec2 Frag_UV;\n" + "in vec4 Frag_Color;\n" + "out vec4 Out_Color;\n" + "void main(){\n" + " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" + "}\n"; + + nk_buffer_init_default(&dev->cmds); + dev->prog = glCreateProgram(); + dev->vert_shdr = glCreateShader(GL_VERTEX_SHADER); + dev->frag_shdr = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(dev->vert_shdr, 1, &vertex_shader, 0); + glShaderSource(dev->frag_shdr, 1, &fragment_shader, 0); + glCompileShader(dev->vert_shdr); + glCompileShader(dev->frag_shdr); + glGetShaderiv(dev->vert_shdr, GL_COMPILE_STATUS, &status); + assert(status == GL_TRUE); + glGetShaderiv(dev->frag_shdr, GL_COMPILE_STATUS, &status); + assert(status == GL_TRUE); + glAttachShader(dev->prog, dev->vert_shdr); + glAttachShader(dev->prog, dev->frag_shdr); + glLinkProgram(dev->prog); + glGetProgramiv(dev->prog, GL_LINK_STATUS, &status); + assert(status == GL_TRUE); + + dev->uniform_tex = glGetUniformLocation(dev->prog, "Texture"); + dev->uniform_proj = glGetUniformLocation(dev->prog, "ProjMtx"); + dev->attrib_pos = glGetAttribLocation(dev->prog, "Position"); + dev->attrib_uv = glGetAttribLocation(dev->prog, "TexCoord"); + dev->attrib_col = glGetAttribLocation(dev->prog, "Color"); + + { + /* buffer setup */ + GLsizei vs = sizeof(struct nk_glfw_vertex); + size_t vp = offsetof(struct nk_glfw_vertex, position); + size_t vt = offsetof(struct nk_glfw_vertex, uv); + size_t vc = offsetof(struct nk_glfw_vertex, col); + + glGenBuffers(1, &dev->vbo); + glGenBuffers(1, &dev->ebo); + glGenVertexArrays(1, &dev->vao); + + glBindVertexArray(dev->vao); + glBindBuffer(GL_ARRAY_BUFFER, dev->vbo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo); + + glEnableVertexAttribArray((GLuint)dev->attrib_pos); + glEnableVertexAttribArray((GLuint)dev->attrib_uv); + glEnableVertexAttribArray((GLuint)dev->attrib_col); + + glVertexAttribPointer((GLuint)dev->attrib_pos, 2, GL_FLOAT, GL_FALSE, vs, (void*)vp); + glVertexAttribPointer((GLuint)dev->attrib_uv, 2, GL_FLOAT, GL_FALSE, vs, (void*)vt); + glVertexAttribPointer((GLuint)dev->attrib_col, 4, GL_UNSIGNED_BYTE, GL_TRUE, vs, (void*)vc); + } + + glBindTexture(GL_TEXTURE_2D, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindVertexArray(0); +} + +static void +device_upload_atlas(struct device *dev, const void *image, int width, int height) +{ + glGenTextures(1, &dev->font_tex); + glBindTexture(GL_TEXTURE_2D, dev->font_tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)width, (GLsizei)height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, image); +} + +static void +device_shutdown(struct device *dev) +{ + glDetachShader(dev->prog, dev->vert_shdr); + glDetachShader(dev->prog, dev->frag_shdr); + glDeleteShader(dev->vert_shdr); + glDeleteShader(dev->frag_shdr); + glDeleteProgram(dev->prog); + glDeleteTextures(1, &dev->font_tex); + glDeleteBuffers(1, &dev->vbo); + glDeleteBuffers(1, &dev->ebo); + nk_buffer_free(&dev->cmds); +} + +static void +device_draw(struct device *dev, struct nk_context *ctx, int width, int height, + struct nk_vec2 scale, enum nk_anti_aliasing AA) +{ + GLfloat ortho[4][4] = { + {2.0f, 0.0f, 0.0f, 0.0f}, + {0.0f,-2.0f, 0.0f, 0.0f}, + {0.0f, 0.0f,-1.0f, 0.0f}, + {-1.0f,1.0f, 0.0f, 1.0f}, + }; + ortho[0][0] /= (GLfloat)width; + ortho[1][1] /= (GLfloat)height; + + /* setup global state */ + glEnable(GL_BLEND); + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glEnable(GL_SCISSOR_TEST); + glActiveTexture(GL_TEXTURE0); + + /* setup program */ + glUseProgram(dev->prog); + glUniform1i(dev->uniform_tex, 0); + glUniformMatrix4fv(dev->uniform_proj, 1, GL_FALSE, &ortho[0][0]); + { + /* convert from command queue into draw list and draw to screen */ + const struct nk_draw_command *cmd; + void *vertices, *elements; + const nk_draw_index *offset = NULL; + + /* allocate vertex and element buffer */ + glBindVertexArray(dev->vao); + glBindBuffer(GL_ARRAY_BUFFER, dev->vbo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo); + + glBufferData(GL_ARRAY_BUFFER, MAX_VERTEX_MEMORY, NULL, GL_STREAM_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_ELEMENT_MEMORY, NULL, GL_STREAM_DRAW); + + /* load draw vertices & elements directly into vertex + element buffer */ + vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); + elements = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY); + { + /* fill convert configuration */ + struct nk_convert_config config; + static const struct nk_draw_vertex_layout_element vertex_layout[] = { + {NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_glfw_vertex, position)}, + {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_glfw_vertex, uv)}, + {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, NK_OFFSETOF(struct nk_glfw_vertex, col)}, + {NK_VERTEX_LAYOUT_END} + }; + NK_MEMSET(&config, 0, sizeof(config)); + config.vertex_layout = vertex_layout; + config.vertex_size = sizeof(struct nk_glfw_vertex); + config.vertex_alignment = NK_ALIGNOF(struct nk_glfw_vertex); + config.null = dev->null; + config.circle_segment_count = 22; + config.curve_segment_count = 22; + config.arc_segment_count = 22; + config.global_alpha = 1.0f; + config.shape_AA = AA; + config.line_AA = AA; + + /* setup buffers to load vertices and elements */ + {struct nk_buffer vbuf, ebuf; + nk_buffer_init_fixed(&vbuf, vertices, MAX_VERTEX_MEMORY); + nk_buffer_init_fixed(&ebuf, elements, MAX_ELEMENT_MEMORY); + nk_convert(ctx, &dev->cmds, &vbuf, &ebuf, &config);} + } + glUnmapBuffer(GL_ARRAY_BUFFER); + glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); + + /* iterate over and execute each draw command */ + nk_draw_foreach(cmd, ctx, &dev->cmds) + { + if (!cmd->elem_count) continue; + glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id); + glScissor( + (GLint)(cmd->clip_rect.x * scale.x), + (GLint)((height - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h)) * scale.y), + (GLint)(cmd->clip_rect.w * scale.x), + (GLint)(cmd->clip_rect.h * scale.y)); + glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset); + offset += cmd->elem_count; + } + nk_clear(ctx); + } + + /* default OpenGL state */ + glUseProgram(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindVertexArray(0); + glDisable(GL_BLEND); + glDisable(GL_SCISSOR_TEST); +} + +/* glfw callbacks (I don't know if there is a easier way to access text and scroll )*/ +static void error_callback(int e, const char *d){printf("Error %d: %s\n", e, d);} +static void text_input(GLFWwindow *win, unsigned int codepoint) +{nk_input_unicode((struct nk_context*)glfwGetWindowUserPointer(win), codepoint);} +static void scroll_input(GLFWwindow *win, double _, double yoff) +{UNUSED(_);nk_input_scroll((struct nk_context*)glfwGetWindowUserPointer(win), (float)yoff);} + +int main(int argc, char *argv[]) +{ + /* Platform */ + static GLFWwindow *win; + int width = 0, height = 0; + int display_width=0, display_height=0; + + /* GUI */ + struct device device; + struct nk_font_atlas atlas; + struct media media; + struct nk_context ctx; + + /* GLFW */ + glfwSetErrorCallback(error_callback); + if (!glfwInit()) { + fprintf(stdout, "[GFLW] failed to init!\n"); + exit(1); + } + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); +#ifdef __APPLE__ + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); +#endif + win = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "Demo", NULL, NULL); + glfwMakeContextCurrent(win); + glfwSetWindowUserPointer(win, &ctx); + glfwSetCharCallback(win, text_input); + glfwSetScrollCallback(win, scroll_input); + glfwGetWindowSize(win, &width, &height); + glfwGetFramebufferSize(win, &display_width, &display_height); + + /* OpenGL */ + glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); + glewExperimental = 1; + if (glewInit() != GLEW_OK) { + fprintf(stderr, "Failed to setup GLEW\n"); + exit(1); + } + + {/* GUI */ + device_init(&device); + {const void *image; int w, h; + struct nk_font_config cfg = nk_font_config(0); + cfg.oversample_h = 3; cfg.oversample_v = 2; + /* Loading one font with different heights is only required if you want higher + * quality text otherwise you can just set the font height directly + * e.g.: ctx->style.font.height = 20. */ + nk_font_atlas_init_default(&atlas); + nk_font_atlas_begin(&atlas); + media.font_14 = nk_font_atlas_add_from_file(&atlas, "../../extra_font/Roboto-Regular.ttf", 14.0f, &cfg); + media.font_18 = nk_font_atlas_add_from_file(&atlas, "../../extra_font/Roboto-Regular.ttf", 18.0f, &cfg); + media.font_20 = nk_font_atlas_add_from_file(&atlas, "../../extra_font/Roboto-Regular.ttf", 20.0f, &cfg); + media.font_22 = nk_font_atlas_add_from_file(&atlas, "../../extra_font/Roboto-Regular.ttf", 22.0f, &cfg); + image = nk_font_atlas_bake(&atlas, &w, &h, NK_FONT_ATLAS_RGBA32); + device_upload_atlas(&device, image, w, h); + nk_font_atlas_end(&atlas, nk_handle_id((int)device.font_tex), &device.null);} + nk_init_default(&ctx, &media.font_14->handle);} + + /* icons */ + glEnable(GL_TEXTURE_2D); + media.unchecked = icon_load("../icon/unchecked.png"); + media.checked = icon_load("../icon/checked.png"); + media.rocket = icon_load("../icon/rocket.png"); + media.cloud = icon_load("../icon/cloud.png"); + media.pen = icon_load("../icon/pen.png"); + media.play = icon_load("../icon/play.png"); + media.pause = icon_load("../icon/pause.png"); + media.stop = icon_load("../icon/stop.png"); + media.next = icon_load("../icon/next.png"); + media.prev = icon_load("../icon/prev.png"); + media.tools = icon_load("../icon/tools.png"); + media.dir = icon_load("../icon/directory.png"); + media.copy = icon_load("../icon/copy.png"); + media.convert = icon_load("../icon/export.png"); + media.del = icon_load("../icon/delete.png"); + media.edit = icon_load("../icon/edit.png"); + media.menu[0] = icon_load("../icon/home.png"); + media.menu[1] = icon_load("../icon/phone.png"); + media.menu[2] = icon_load("../icon/plane.png"); + media.menu[3] = icon_load("../icon/wifi.png"); + media.menu[4] = icon_load("../icon/settings.png"); + media.menu[5] = icon_load("../icon/volume.png"); + + {int i; + for (i = 0; i < 9; ++i) { + char buffer[256]; + sprintf(buffer, "../images/image%d.png", (i+1)); + media.images[i] = icon_load(buffer); + }} + + while (!glfwWindowShouldClose(win)) + { + /* High DPI displays */ + struct nk_vec2 scale; + glfwGetWindowSize(win, &width, &height); + glfwGetFramebufferSize(win, &display_width, &display_height); + scale.x = (float)display_width/(float)width; + scale.y = (float)display_height/(float)height; + + /* Input */ + {double x, y; + nk_input_begin(&ctx); + glfwPollEvents(); + nk_input_key(&ctx, NK_KEY_DEL, glfwGetKey(win, GLFW_KEY_DELETE) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_ENTER, glfwGetKey(win, GLFW_KEY_ENTER) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_TAB, glfwGetKey(win, GLFW_KEY_TAB) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_BACKSPACE, glfwGetKey(win, GLFW_KEY_BACKSPACE) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_LEFT, glfwGetKey(win, GLFW_KEY_LEFT) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_RIGHT, glfwGetKey(win, GLFW_KEY_RIGHT) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_UP, glfwGetKey(win, GLFW_KEY_UP) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_DOWN, glfwGetKey(win, GLFW_KEY_DOWN) == GLFW_PRESS); + if (glfwGetKey(win, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || + glfwGetKey(win, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS) { + nk_input_key(&ctx, NK_KEY_COPY, glfwGetKey(win, GLFW_KEY_C) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_PASTE, glfwGetKey(win, GLFW_KEY_P) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_CUT, glfwGetKey(win, GLFW_KEY_X) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_CUT, glfwGetKey(win, GLFW_KEY_E) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_SHIFT, 1); + } else { + nk_input_key(&ctx, NK_KEY_COPY, 0); + nk_input_key(&ctx, NK_KEY_PASTE, 0); + nk_input_key(&ctx, NK_KEY_CUT, 0); + nk_input_key(&ctx, NK_KEY_SHIFT, 0); + } + glfwGetCursorPos(win, &x, &y); + nk_input_motion(&ctx, (int)x, (int)y); + nk_input_button(&ctx, NK_BUTTON_LEFT, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS); + nk_input_button(&ctx, NK_BUTTON_MIDDLE, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_MIDDLE) == GLFW_PRESS); + nk_input_button(&ctx, NK_BUTTON_RIGHT, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS); + nk_input_end(&ctx);} + + /* GUI */ + basic_demo(&ctx, &media); + button_demo(&ctx, &media); + grid_demo(&ctx, &media); + + /* Draw */ + glViewport(0, 0, display_width, display_height); + glClear(GL_COLOR_BUFFER_BIT); + glClearColor(0.3f, 0.3f, 0.3f, 1.0f); + device_draw(&device, &ctx, width, height, scale, NK_ANTI_ALIASING_ON); + glfwSwapBuffers(win); + } + + glDeleteTextures(1,(const GLuint*)&media.unchecked.handle.id); + glDeleteTextures(1,(const GLuint*)&media.checked.handle.id); + glDeleteTextures(1,(const GLuint*)&media.rocket.handle.id); + glDeleteTextures(1,(const GLuint*)&media.cloud.handle.id); + glDeleteTextures(1,(const GLuint*)&media.pen.handle.id); + glDeleteTextures(1,(const GLuint*)&media.play.handle.id); + glDeleteTextures(1,(const GLuint*)&media.pause.handle.id); + glDeleteTextures(1,(const GLuint*)&media.stop.handle.id); + glDeleteTextures(1,(const GLuint*)&media.next.handle.id); + glDeleteTextures(1,(const GLuint*)&media.prev.handle.id); + glDeleteTextures(1,(const GLuint*)&media.tools.handle.id); + glDeleteTextures(1,(const GLuint*)&media.dir.handle.id); + glDeleteTextures(1,(const GLuint*)&media.del.handle.id); + + nk_font_atlas_clear(&atlas); + nk_free(&ctx); + + device_shutdown(&device); + glfwTerminate(); + return 0; +} + diff --git a/nuklear/example/file_browser.c b/nuklear/example/file_browser.c new file mode 100644 index 0000000..ece4cb8 --- /dev/null +++ b/nuklear/example/file_browser.c @@ -0,0 +1,910 @@ +/* nuklear - v1.05 - public domain */ +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdarg.h> +#include <string.h> +#include <math.h> +#include <assert.h> +#include <math.h> +#include <time.h> +#include <limits.h> +#include <unistd.h> +#include <dirent.h> + +#include <GL/glew.h> +#include <GLFW/glfw3.h> + +#define NK_INCLUDE_FIXED_TYPES +#define NK_INCLUDE_STANDARD_IO +#define NK_INCLUDE_DEFAULT_ALLOCATOR +#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT +#define NK_INCLUDE_FONT_BAKING +#define NK_INCLUDE_DEFAULT_FONT +#define NK_IMPLEMENTATION +#include "../nuklear.h" + +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" + +/* macros */ +#define WINDOW_WIDTH 1200 +#define WINDOW_HEIGHT 800 + +#define MAX_VERTEX_MEMORY 512 * 1024 +#define MAX_ELEMENT_MEMORY 128 * 1024 + +#define UNUSED(a) (void)a +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) < (b) ? (b) : (a)) +#define LEN(a) (sizeof(a)/sizeof(a)[0]) + +#ifdef __APPLE__ + #define NK_SHADER_VERSION "#version 150\n" +#else + #define NK_SHADER_VERSION "#version 300 es\n" +#endif + +/* =============================================================== + * + * GUI + * + * ===============================================================*/ +struct icons { + struct nk_image desktop; + struct nk_image home; + struct nk_image computer; + struct nk_image directory; + + struct nk_image default_file; + struct nk_image text_file; + struct nk_image music_file; + struct nk_image font_file; + struct nk_image img_file; + struct nk_image movie_file; +}; + +enum file_groups { + FILE_GROUP_DEFAULT, + FILE_GROUP_TEXT, + FILE_GROUP_MUSIC, + FILE_GROUP_FONT, + FILE_GROUP_IMAGE, + FILE_GROUP_MOVIE, + FILE_GROUP_MAX +}; + +enum file_types { + FILE_DEFAULT, + FILE_TEXT, + FILE_C_SOURCE, + FILE_CPP_SOURCE, + FILE_HEADER, + FILE_CPP_HEADER, + FILE_MP3, + FILE_WAV, + FILE_OGG, + FILE_TTF, + FILE_BMP, + FILE_PNG, + FILE_JPEG, + FILE_PCX, + FILE_TGA, + FILE_GIF, + FILE_MAX +}; + +struct file_group { + enum file_groups group; + const char *name; + struct nk_image *icon; +}; + +struct file { + enum file_types type; + const char *suffix; + enum file_groups group; +}; + +struct media { + int font; + int icon_sheet; + struct icons icons; + struct file_group group[FILE_GROUP_MAX]; + struct file files[FILE_MAX]; +}; + +#define MAX_PATH_LEN 512 +struct file_browser { + /* path */ + char file[MAX_PATH_LEN]; + char home[MAX_PATH_LEN]; + char desktop[MAX_PATH_LEN]; + char directory[MAX_PATH_LEN]; + + /* directory content */ + char **files; + char **directories; + size_t file_count; + size_t dir_count; + struct media *media; +}; + +#ifdef __unix__ +#include <dirent.h> +#include <unistd.h> +#endif + +#ifndef _WIN32 +# include <pwd.h> +#endif + +static void +die(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fputs("\n", stderr); + exit(EXIT_FAILURE); +} + +static char* +file_load(const char* path, size_t* siz) +{ + char *buf; + FILE *fd = fopen(path, "rb"); + if (!fd) die("Failed to open file: %s\n", path); + fseek(fd, 0, SEEK_END); + *siz = (size_t)ftell(fd); + fseek(fd, 0, SEEK_SET); + buf = (char*)calloc(*siz, 1); + fread(buf, *siz, 1, fd); + fclose(fd); + return buf; +} + +static char* +str_duplicate(const char *src) +{ + char *ret; + size_t len = strlen(src); + if (!len) return 0; + ret = (char*)malloc(len+1); + if (!ret) return 0; + memcpy(ret, src, len); + ret[len] = '\0'; + return ret; +} + +static void +dir_free_list(char **list, size_t size) +{ + size_t i; + for (i = 0; i < size; ++i) + free(list[i]); + free(list); +} + +static char** +dir_list(const char *dir, int return_subdirs, size_t *count) +{ + size_t n = 0; + char buffer[MAX_PATH_LEN]; + char **results = NULL; + const DIR *none = NULL; + size_t capacity = 32; + size_t size; + DIR *z; + + assert(dir); + assert(count); + strncpy(buffer, dir, MAX_PATH_LEN); + n = strlen(buffer); + + if (n > 0 && (buffer[n-1] != '/')) + buffer[n++] = '/'; + + size = 0; + + z = opendir(dir); + if (z != none) { + int nonempty = 1; + struct dirent *data = readdir(z); + nonempty = (data != NULL); + if (!nonempty) return NULL; + + do { + DIR *y; + char *p; + int is_subdir; + if (data->d_name[0] == '.') + continue; + + strncpy(buffer + n, data->d_name, MAX_PATH_LEN-n); + y = opendir(buffer); + is_subdir = (y != NULL); + if (y != NULL) closedir(y); + + if ((return_subdirs && is_subdir) || (!is_subdir && !return_subdirs)){ + if (!size) { + results = (char**)calloc(sizeof(char*), capacity); + } else if (size >= capacity) { + void *old = results; + capacity = capacity * 2; + results = (char**)realloc(results, capacity * sizeof(char*)); + assert(results); + if (!results) free(old); + } + p = str_duplicate(data->d_name); + results[size++] = p; + } + } while ((data = readdir(z)) != NULL); + } + + if (z) closedir(z); + *count = size; + return results; +} + +static struct file_group +FILE_GROUP(enum file_groups group, const char *name, struct nk_image *icon) +{ + struct file_group fg; + fg.group = group; + fg.name = name; + fg.icon = icon; + return fg; +} + +static struct file +FILE_DEF(enum file_types type, const char *suffix, enum file_groups group) +{ + struct file fd; + fd.type = type; + fd.suffix = suffix; + fd.group = group; + return fd; +} + +static struct nk_image* +media_icon_for_file(struct media *media, const char *file) +{ + int i = 0; + const char *s = file; + char suffix[4]; + int found = 0; + memset(suffix, 0, sizeof(suffix)); + + /* extract suffix .xxx from file */ + while (*s++ != '\0') { + if (found && i < 3) + suffix[i++] = *s; + + if (*s == '.') { + if (found){ + found = 0; + break; + } + found = 1; + } + } + + /* check for all file definition of all groups for fitting suffix*/ + for (i = 0; i < FILE_MAX && found; ++i) { + struct file *d = &media->files[i]; + { + const char *f = d->suffix; + s = suffix; + while (f && *f && *s && *s == *f) { + s++; f++; + } + + /* found correct file definition so */ + if (f && *s == '\0' && *f == '\0') + return media->group[d->group].icon; + } + } + return &media->icons.default_file; +} + +static void +media_init(struct media *media) +{ + /* file groups */ + struct icons *icons = &media->icons; + media->group[FILE_GROUP_DEFAULT] = FILE_GROUP(FILE_GROUP_DEFAULT,"default",&icons->default_file); + media->group[FILE_GROUP_TEXT] = FILE_GROUP(FILE_GROUP_TEXT, "textual", &icons->text_file); + media->group[FILE_GROUP_MUSIC] = FILE_GROUP(FILE_GROUP_MUSIC, "music", &icons->music_file); + media->group[FILE_GROUP_FONT] = FILE_GROUP(FILE_GROUP_FONT, "font", &icons->font_file); + media->group[FILE_GROUP_IMAGE] = FILE_GROUP(FILE_GROUP_IMAGE, "image", &icons->img_file); + media->group[FILE_GROUP_MOVIE] = FILE_GROUP(FILE_GROUP_MOVIE, "movie", &icons->movie_file); + + /* files */ + media->files[FILE_DEFAULT] = FILE_DEF(FILE_DEFAULT, NULL, FILE_GROUP_DEFAULT); + media->files[FILE_TEXT] = FILE_DEF(FILE_TEXT, "txt", FILE_GROUP_TEXT); + media->files[FILE_C_SOURCE] = FILE_DEF(FILE_C_SOURCE, "c", FILE_GROUP_TEXT); + media->files[FILE_CPP_SOURCE] = FILE_DEF(FILE_CPP_SOURCE, "cpp", FILE_GROUP_TEXT); + media->files[FILE_HEADER] = FILE_DEF(FILE_HEADER, "h", FILE_GROUP_TEXT); + media->files[FILE_CPP_HEADER] = FILE_DEF(FILE_HEADER, "hpp", FILE_GROUP_TEXT); + media->files[FILE_MP3] = FILE_DEF(FILE_MP3, "mp3", FILE_GROUP_MUSIC); + media->files[FILE_WAV] = FILE_DEF(FILE_WAV, "wav", FILE_GROUP_MUSIC); + media->files[FILE_OGG] = FILE_DEF(FILE_OGG, "ogg", FILE_GROUP_MUSIC); + media->files[FILE_TTF] = FILE_DEF(FILE_TTF, "ttf", FILE_GROUP_FONT); + media->files[FILE_BMP] = FILE_DEF(FILE_BMP, "bmp", FILE_GROUP_IMAGE); + media->files[FILE_PNG] = FILE_DEF(FILE_PNG, "png", FILE_GROUP_IMAGE); + media->files[FILE_JPEG] = FILE_DEF(FILE_JPEG, "jpg", FILE_GROUP_IMAGE); + media->files[FILE_PCX] = FILE_DEF(FILE_PCX, "pcx", FILE_GROUP_IMAGE); + media->files[FILE_TGA] = FILE_DEF(FILE_TGA, "tga", FILE_GROUP_IMAGE); + media->files[FILE_GIF] = FILE_DEF(FILE_GIF, "gif", FILE_GROUP_IMAGE); +} + +static void +file_browser_reload_directory_content(struct file_browser *browser, const char *path) +{ + strncpy(browser->directory, path, MAX_PATH_LEN); + dir_free_list(browser->files, browser->file_count); + dir_free_list(browser->directories, browser->dir_count); + browser->files = dir_list(path, 0, &browser->file_count); + browser->directories = dir_list(path, 1, &browser->dir_count); +} + +static void +file_browser_init(struct file_browser *browser, struct media *media) +{ + memset(browser, 0, sizeof(*browser)); + browser->media = media; + { + /* load files and sub-directory list */ + const char *home = getenv("HOME"); +#ifdef _WIN32 + if (!home) home = getenv("USERPROFILE"); +#else + if (!home) home = getpwuid(getuid())->pw_dir; + { + size_t l; + strncpy(browser->home, home, MAX_PATH_LEN); + l = strlen(browser->home); + strcpy(browser->home + l, "/"); + strcpy(browser->directory, browser->home); + } +#endif + { + size_t l; + strcpy(browser->desktop, browser->home); + l = strlen(browser->desktop); + strcpy(browser->desktop + l, "desktop/"); + } + browser->files = dir_list(browser->directory, 0, &browser->file_count); + browser->directories = dir_list(browser->directory, 1, &browser->dir_count); + } +} + +static void +file_browser_free(struct file_browser *browser) +{ + if (browser->files) + dir_free_list(browser->files, browser->file_count); + if (browser->directories) + dir_free_list(browser->directories, browser->dir_count); + browser->files = NULL; + browser->directories = NULL; + memset(browser, 0, sizeof(*browser)); +} + +static int +file_browser_run(struct file_browser *browser, struct nk_context *ctx) +{ + int ret = 0; + struct media *media = browser->media; + struct nk_rect total_space; + + if (nk_begin(ctx, "File Browser", nk_rect(50, 50, 800, 600), + NK_WINDOW_BORDER|NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_MOVABLE)) + { + static float ratio[] = {0.25f, NK_UNDEFINED}; + float spacing_x = ctx->style.window.spacing.x; + + /* output path directory selector in the menubar */ + ctx->style.window.spacing.x = 0; + nk_menubar_begin(ctx); + { + char *d = browser->directory; + char *begin = d + 1; + nk_layout_row_dynamic(ctx, 25, 6); + while (*d++) { + if (*d == '/') { + *d = '\0'; + if (nk_button_label(ctx, begin)) { + *d++ = '/'; *d = '\0'; + file_browser_reload_directory_content(browser, browser->directory); + break; + } + *d = '/'; + begin = d + 1; + } + } + } + nk_menubar_end(ctx); + ctx->style.window.spacing.x = spacing_x; + + /* window layout */ + total_space = nk_window_get_content_region(ctx); + nk_layout_row(ctx, NK_DYNAMIC, total_space.h, 2, ratio); + nk_group_begin(ctx, "Special", NK_WINDOW_NO_SCROLLBAR); + { + struct nk_image home = media->icons.home; + struct nk_image desktop = media->icons.desktop; + struct nk_image computer = media->icons.computer; + + nk_layout_row_dynamic(ctx, 40, 1); + if (nk_button_image_label(ctx, home, "home", NK_TEXT_CENTERED)) + file_browser_reload_directory_content(browser, browser->home); + if (nk_button_image_label(ctx,desktop,"desktop",NK_TEXT_CENTERED)) + file_browser_reload_directory_content(browser, browser->desktop); + if (nk_button_image_label(ctx,computer,"computer",NK_TEXT_CENTERED)) + file_browser_reload_directory_content(browser, "/"); + nk_group_end(ctx); + } + + /* output directory content window */ + nk_group_begin(ctx, "Content", 0); + { + int index = -1; + size_t i = 0, j = 0, k = 0; + size_t rows = 0, cols = 0; + size_t count = browser->dir_count + browser->file_count; + + cols = 4; + rows = count / cols; + for (i = 0; i <= rows; i += 1) { + {size_t n = j + cols; + nk_layout_row_dynamic(ctx, 135, (int)cols); + for (; j < count && j < n; ++j) { + /* draw one row of icons */ + if (j < browser->dir_count) { + /* draw and execute directory buttons */ + if (nk_button_image(ctx,media->icons.directory)) + index = (int)j; + } else { + /* draw and execute files buttons */ + struct nk_image *icon; + size_t fileIndex = ((size_t)j - browser->dir_count); + icon = media_icon_for_file(media,browser->files[fileIndex]); + if (nk_button_image(ctx, *icon)) { + strncpy(browser->file, browser->directory, MAX_PATH_LEN); + n = strlen(browser->file); + strncpy(browser->file + n, browser->files[fileIndex], MAX_PATH_LEN - n); + ret = 1; + } + } + }} + {size_t n = k + cols; + nk_layout_row_dynamic(ctx, 20, (int)cols); + for (; k < count && k < n; k++) { + /* draw one row of labels */ + if (k < browser->dir_count) { + nk_label(ctx, browser->directories[k], NK_TEXT_CENTERED); + } else { + size_t t = k-browser->dir_count; + nk_label(ctx,browser->files[t],NK_TEXT_CENTERED); + } + }} + } + + if (index != -1) { + size_t n = strlen(browser->directory); + strncpy(browser->directory + n, browser->directories[index], MAX_PATH_LEN - n); + n = strlen(browser->directory); + if (n < MAX_PATH_LEN - 1) { + browser->directory[n] = '/'; + browser->directory[n+1] = '\0'; + } + file_browser_reload_directory_content(browser, browser->directory); + } + nk_group_end(ctx); + } + } + nk_end(ctx); + return ret; +} + +/* =============================================================== + * + * DEVICE + * + * ===============================================================*/ +struct nk_glfw_vertex { + float position[2]; + float uv[2]; + nk_byte col[4]; +}; + +struct device { + struct nk_buffer cmds; + struct nk_draw_null_texture null; + GLuint vbo, vao, ebo; + GLuint prog; + GLuint vert_shdr; + GLuint frag_shdr; + GLint attrib_pos; + GLint attrib_uv; + GLint attrib_col; + GLint uniform_tex; + GLint uniform_proj; + GLuint font_tex; +}; +static struct nk_image +icon_load(const char *filename) +{ + int x,y,n; + GLuint tex; + unsigned char *data = stbi_load(filename, &x, &y, &n, 0); + if (!data) die("[SDL]: failed to load image: %s", filename); + + glGenTextures(1, &tex); + glBindTexture(GL_TEXTURE_2D, tex); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, x, y, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + glGenerateMipmap(GL_TEXTURE_2D); + stbi_image_free(data); + return nk_image_id((int)tex); +} + +static void +device_init(struct device *dev) +{ + GLint status; + static const GLchar *vertex_shader = + NK_SHADER_VERSION + "uniform mat4 ProjMtx;\n" + "in vec2 Position;\n" + "in vec2 TexCoord;\n" + "in vec4 Color;\n" + "out vec2 Frag_UV;\n" + "out vec4 Frag_Color;\n" + "void main() {\n" + " Frag_UV = TexCoord;\n" + " Frag_Color = Color;\n" + " gl_Position = ProjMtx * vec4(Position.xy, 0, 1);\n" + "}\n"; + static const GLchar *fragment_shader = + NK_SHADER_VERSION + "precision mediump float;\n" + "uniform sampler2D Texture;\n" + "in vec2 Frag_UV;\n" + "in vec4 Frag_Color;\n" + "out vec4 Out_Color;\n" + "void main(){\n" + " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" + "}\n"; + + nk_buffer_init_default(&dev->cmds); + dev->prog = glCreateProgram(); + dev->vert_shdr = glCreateShader(GL_VERTEX_SHADER); + dev->frag_shdr = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(dev->vert_shdr, 1, &vertex_shader, 0); + glShaderSource(dev->frag_shdr, 1, &fragment_shader, 0); + glCompileShader(dev->vert_shdr); + glCompileShader(dev->frag_shdr); + glGetShaderiv(dev->vert_shdr, GL_COMPILE_STATUS, &status); + assert(status == GL_TRUE); + glGetShaderiv(dev->frag_shdr, GL_COMPILE_STATUS, &status); + assert(status == GL_TRUE); + glAttachShader(dev->prog, dev->vert_shdr); + glAttachShader(dev->prog, dev->frag_shdr); + glLinkProgram(dev->prog); + glGetProgramiv(dev->prog, GL_LINK_STATUS, &status); + assert(status == GL_TRUE); + + dev->uniform_tex = glGetUniformLocation(dev->prog, "Texture"); + dev->uniform_proj = glGetUniformLocation(dev->prog, "ProjMtx"); + dev->attrib_pos = glGetAttribLocation(dev->prog, "Position"); + dev->attrib_uv = glGetAttribLocation(dev->prog, "TexCoord"); + dev->attrib_col = glGetAttribLocation(dev->prog, "Color"); + + { + /* buffer setup */ + GLsizei vs = sizeof(struct nk_glfw_vertex); + size_t vp = offsetof(struct nk_glfw_vertex, position); + size_t vt = offsetof(struct nk_glfw_vertex, uv); + size_t vc = offsetof(struct nk_glfw_vertex, col); + + glGenBuffers(1, &dev->vbo); + glGenBuffers(1, &dev->ebo); + glGenVertexArrays(1, &dev->vao); + + glBindVertexArray(dev->vao); + glBindBuffer(GL_ARRAY_BUFFER, dev->vbo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo); + + glEnableVertexAttribArray((GLuint)dev->attrib_pos); + glEnableVertexAttribArray((GLuint)dev->attrib_uv); + glEnableVertexAttribArray((GLuint)dev->attrib_col); + + glVertexAttribPointer((GLuint)dev->attrib_pos, 2, GL_FLOAT, GL_FALSE, vs, (void*)vp); + glVertexAttribPointer((GLuint)dev->attrib_uv, 2, GL_FLOAT, GL_FALSE, vs, (void*)vt); + glVertexAttribPointer((GLuint)dev->attrib_col, 4, GL_UNSIGNED_BYTE, GL_TRUE, vs, (void*)vc); + } + + glBindTexture(GL_TEXTURE_2D, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindVertexArray(0); +} + +static void +device_upload_atlas(struct device *dev, const void *image, int width, int height) +{ + glGenTextures(1, &dev->font_tex); + glBindTexture(GL_TEXTURE_2D, dev->font_tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)width, (GLsizei)height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, image); +} + +static void +device_shutdown(struct device *dev) +{ + glDetachShader(dev->prog, dev->vert_shdr); + glDetachShader(dev->prog, dev->frag_shdr); + glDeleteShader(dev->vert_shdr); + glDeleteShader(dev->frag_shdr); + glDeleteProgram(dev->prog); + glDeleteTextures(1, &dev->font_tex); + glDeleteBuffers(1, &dev->vbo); + glDeleteBuffers(1, &dev->ebo); + nk_buffer_free(&dev->cmds); +} + +static void +device_draw(struct device *dev, struct nk_context *ctx, int width, int height, + struct nk_vec2 scale, enum nk_anti_aliasing AA) +{ + GLfloat ortho[4][4] = { + {2.0f, 0.0f, 0.0f, 0.0f}, + {0.0f,-2.0f, 0.0f, 0.0f}, + {0.0f, 0.0f,-1.0f, 0.0f}, + {-1.0f,1.0f, 0.0f, 1.0f}, + }; + ortho[0][0] /= (GLfloat)width; + ortho[1][1] /= (GLfloat)height; + + /* setup global state */ + glEnable(GL_BLEND); + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glEnable(GL_SCISSOR_TEST); + glActiveTexture(GL_TEXTURE0); + + /* setup program */ + glUseProgram(dev->prog); + glUniform1i(dev->uniform_tex, 0); + glUniformMatrix4fv(dev->uniform_proj, 1, GL_FALSE, &ortho[0][0]); + { + /* convert from command queue into draw list and draw to screen */ + const struct nk_draw_command *cmd; + void *vertices, *elements; + const nk_draw_index *offset = NULL; + + /* allocate vertex and element buffer */ + glBindVertexArray(dev->vao); + glBindBuffer(GL_ARRAY_BUFFER, dev->vbo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo); + + glBufferData(GL_ARRAY_BUFFER, MAX_VERTEX_MEMORY, NULL, GL_STREAM_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_ELEMENT_MEMORY, NULL, GL_STREAM_DRAW); + + /* load draw vertices & elements directly into vertex + element buffer */ + vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); + elements = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY); + { + /* fill convert configuration */ + struct nk_convert_config config; + static const struct nk_draw_vertex_layout_element vertex_layout[] = { + {NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_glfw_vertex, position)}, + {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_glfw_vertex, uv)}, + {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, NK_OFFSETOF(struct nk_glfw_vertex, col)}, + {NK_VERTEX_LAYOUT_END} + }; + NK_MEMSET(&config, 0, sizeof(config)); + config.vertex_layout = vertex_layout; + config.vertex_size = sizeof(struct nk_glfw_vertex); + config.vertex_alignment = NK_ALIGNOF(struct nk_glfw_vertex); + config.null = dev->null; + config.circle_segment_count = 22; + config.curve_segment_count = 22; + config.arc_segment_count = 22; + config.global_alpha = 1.0f; + config.shape_AA = AA; + config.line_AA = AA; + + /* setup buffers to load vertices and elements */ + {struct nk_buffer vbuf, ebuf; + nk_buffer_init_fixed(&vbuf, vertices, MAX_VERTEX_MEMORY); + nk_buffer_init_fixed(&ebuf, elements, MAX_ELEMENT_MEMORY); + nk_convert(ctx, &dev->cmds, &vbuf, &ebuf, &config);} + } + glUnmapBuffer(GL_ARRAY_BUFFER); + glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); + + /* iterate over and execute each draw command */ + nk_draw_foreach(cmd, ctx, &dev->cmds) { + if (!cmd->elem_count) continue; + glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id); + glScissor( + (GLint)(cmd->clip_rect.x * scale.x), + (GLint)((height - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h)) * scale.y), + (GLint)(cmd->clip_rect.w * scale.x), + (GLint)(cmd->clip_rect.h * scale.y)); + glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset); + offset += cmd->elem_count; + } + nk_clear(ctx); + } + + /* default OpenGL state */ + glUseProgram(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindVertexArray(0); + glDisable(GL_BLEND); + glDisable(GL_SCISSOR_TEST); +} + + +/* glfw callbacks (I don't know if there is a easier way to access text and scroll )*/ +static void error_callback(int e, const char *d){printf("Error %d: %s\n", e, d);} +static void text_input(GLFWwindow *win, unsigned int codepoint) +{nk_input_unicode((struct nk_context*)glfwGetWindowUserPointer(win), codepoint);} +static void scroll_input(GLFWwindow *win, double _, double yoff) +{UNUSED(_);nk_input_scroll((struct nk_context*)glfwGetWindowUserPointer(win), (float)yoff);} + +int main(int argc, char *argv[]) +{ + /* Platform */ + static GLFWwindow *win; + int width = 0, height = 0; + int display_width = 0, display_height = 0; + + /* GUI */ + struct device device; + struct nk_context ctx; + struct nk_font *font; + struct nk_font_atlas atlas; + struct file_browser browser; + struct media media; + + /* GLFW */ + glfwSetErrorCallback(error_callback); + if (!glfwInit()) { + fprintf(stdout, "[GFLW] failed to init!\n"); + exit(1); + } + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); +#ifdef __APPLE__ + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); +#endif + win = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "Demo", NULL, NULL); + glfwMakeContextCurrent(win); + glfwSetWindowUserPointer(win, &ctx); + glfwSetCharCallback(win, text_input); + glfwSetScrollCallback(win, scroll_input); + + /* OpenGL */ + glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); + glewExperimental = 1; + if (glewInit() != GLEW_OK) { + fprintf(stderr, "Failed to setup GLEW\n"); + exit(1); + } + + {/* GUI */ + device_init(&device); + {const void *image; int w, h; + const char *font_path = (argc > 1) ? argv[1]: 0; + nk_font_atlas_init_default(&atlas); + nk_font_atlas_begin(&atlas); + if (font_path) font = nk_font_atlas_add_from_file(&atlas, font_path, 13.0f, NULL); + else font = nk_font_atlas_add_default(&atlas, 13.0f, NULL); + image = nk_font_atlas_bake(&atlas, &w, &h, NK_FONT_ATLAS_RGBA32); + device_upload_atlas(&device, image, w, h); + nk_font_atlas_end(&atlas, nk_handle_id((int)device.font_tex), &device.null);} + nk_init_default(&ctx, &font->handle);} + + /* icons */ + glEnable(GL_TEXTURE_2D); + media.icons.home = icon_load("../icon/home.png"); + media.icons.directory = icon_load("../icon/directory.png"); + media.icons.computer = icon_load("../icon/computer.png"); + media.icons.desktop = icon_load("../icon/desktop.png"); + media.icons.default_file = icon_load("../icon/default.png"); + media.icons.text_file = icon_load("../icon/text.png"); + media.icons.music_file = icon_load("../icon/music.png"); + media.icons.font_file = icon_load("../icon/font.png"); + media.icons.img_file = icon_load("../icon/img.png"); + media.icons.movie_file = icon_load("../icon/movie.png"); + media_init(&media); + + file_browser_init(&browser, &media); + while (!glfwWindowShouldClose(win)) + { + /* High DPI displays */ + struct nk_vec2 scale; + glfwGetWindowSize(win, &width, &height); + glfwGetFramebufferSize(win, &display_width, &display_height); + scale.x = (float)display_width/(float)width; + scale.y = (float)display_height/(float)height; + + /* Input */ + {double x, y; + nk_input_begin(&ctx); + glfwPollEvents(); + nk_input_key(&ctx, NK_KEY_DEL, glfwGetKey(win, GLFW_KEY_DELETE) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_ENTER, glfwGetKey(win, GLFW_KEY_ENTER) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_TAB, glfwGetKey(win, GLFW_KEY_TAB) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_BACKSPACE, glfwGetKey(win, GLFW_KEY_BACKSPACE) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_LEFT, glfwGetKey(win, GLFW_KEY_LEFT) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_RIGHT, glfwGetKey(win, GLFW_KEY_RIGHT) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_UP, glfwGetKey(win, GLFW_KEY_UP) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_DOWN, glfwGetKey(win, GLFW_KEY_DOWN) == GLFW_PRESS); + if (glfwGetKey(win, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || + glfwGetKey(win, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS) { + nk_input_key(&ctx, NK_KEY_COPY, glfwGetKey(win, GLFW_KEY_C) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_PASTE, glfwGetKey(win, GLFW_KEY_P) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_CUT, glfwGetKey(win, GLFW_KEY_X) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_CUT, glfwGetKey(win, GLFW_KEY_E) == GLFW_PRESS); + nk_input_key(&ctx, NK_KEY_SHIFT, 1); + } else { + nk_input_key(&ctx, NK_KEY_COPY, 0); + nk_input_key(&ctx, NK_KEY_PASTE, 0); + nk_input_key(&ctx, NK_KEY_CUT, 0); + nk_input_key(&ctx, NK_KEY_SHIFT, 0); + } + glfwGetCursorPos(win, &x, &y); + nk_input_motion(&ctx, (int)x, (int)y); + nk_input_button(&ctx, NK_BUTTON_LEFT, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS); + nk_input_button(&ctx, NK_BUTTON_MIDDLE, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_MIDDLE) == GLFW_PRESS); + nk_input_button(&ctx, NK_BUTTON_RIGHT, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS); + nk_input_end(&ctx);} + + /* GUI */ + file_browser_run(&browser, &ctx); + + /* Draw */ + glViewport(0, 0, display_width, display_height); + glClear(GL_COLOR_BUFFER_BIT); + glClearColor(0.2f, 0.2f, 0.2f, 1.0f); + device_draw(&device, &ctx, width, height, scale, NK_ANTI_ALIASING_ON); + glfwSwapBuffers(win); + } + + glDeleteTextures(1,(const GLuint*)&media.icons.home.handle.id); + glDeleteTextures(1,(const GLuint*)&media.icons.directory.handle.id); + glDeleteTextures(1,(const GLuint*)&media.icons.computer.handle.id); + glDeleteTextures(1,(const GLuint*)&media.icons.desktop.handle.id); + glDeleteTextures(1,(const GLuint*)&media.icons.default_file.handle.id); + glDeleteTextures(1,(const GLuint*)&media.icons.text_file.handle.id); + glDeleteTextures(1,(const GLuint*)&media.icons.music_file.handle.id); + glDeleteTextures(1,(const GLuint*)&media.icons.font_file.handle.id); + glDeleteTextures(1,(const GLuint*)&media.icons.img_file.handle.id); + glDeleteTextures(1,(const GLuint*)&media.icons.movie_file.handle.id); + + file_browser_free(&browser); + nk_font_atlas_clear(&atlas); + nk_free(&ctx); + device_shutdown(&device); + glfwTerminate(); + return 0; +} + + diff --git a/nuklear/example/icon/checked.png b/nuklear/example/icon/checked.png Binary files differnew file mode 100644 index 0000000..e4e05b2 --- /dev/null +++ b/nuklear/example/icon/checked.png diff --git a/nuklear/example/icon/cloud.png b/nuklear/example/icon/cloud.png Binary files differnew file mode 100644 index 0000000..ecc5791 --- /dev/null +++ b/nuklear/example/icon/cloud.png diff --git a/nuklear/example/icon/computer.png b/nuklear/example/icon/computer.png Binary files differnew file mode 100644 index 0000000..29db8fc --- /dev/null +++ b/nuklear/example/icon/computer.png diff --git a/nuklear/example/icon/copy.png b/nuklear/example/icon/copy.png Binary files differnew file mode 100644 index 0000000..0a6e979 --- /dev/null +++ b/nuklear/example/icon/copy.png diff --git a/nuklear/example/icon/default.png b/nuklear/example/icon/default.png Binary files differnew file mode 100644 index 0000000..c11145a --- /dev/null +++ b/nuklear/example/icon/default.png diff --git a/nuklear/example/icon/delete.png b/nuklear/example/icon/delete.png Binary files differnew file mode 100644 index 0000000..7bc6dde --- /dev/null +++ b/nuklear/example/icon/delete.png diff --git a/nuklear/example/icon/desktop.png b/nuklear/example/icon/desktop.png Binary files differnew file mode 100644 index 0000000..b4abcfd --- /dev/null +++ b/nuklear/example/icon/desktop.png diff --git a/nuklear/example/icon/directory.png b/nuklear/example/icon/directory.png Binary files differnew file mode 100644 index 0000000..4c73d37 --- /dev/null +++ b/nuklear/example/icon/directory.png diff --git a/nuklear/example/icon/edit.png b/nuklear/example/icon/edit.png Binary files differnew file mode 100644 index 0000000..62ce0b4 --- /dev/null +++ b/nuklear/example/icon/edit.png diff --git a/nuklear/example/icon/export.png b/nuklear/example/icon/export.png Binary files differnew file mode 100644 index 0000000..ff6b5aa --- /dev/null +++ b/nuklear/example/icon/export.png diff --git a/nuklear/example/icon/font.png b/nuklear/example/icon/font.png Binary files differnew file mode 100644 index 0000000..918e9bf --- /dev/null +++ b/nuklear/example/icon/font.png diff --git a/nuklear/example/icon/home.png b/nuklear/example/icon/home.png Binary files differnew file mode 100644 index 0000000..8560626 --- /dev/null +++ b/nuklear/example/icon/home.png diff --git a/nuklear/example/icon/img.png b/nuklear/example/icon/img.png Binary files differnew file mode 100644 index 0000000..1985957 --- /dev/null +++ b/nuklear/example/icon/img.png diff --git a/nuklear/example/icon/movie.png b/nuklear/example/icon/movie.png Binary files differnew file mode 100644 index 0000000..5227883 --- /dev/null +++ b/nuklear/example/icon/movie.png diff --git a/nuklear/example/icon/music.png b/nuklear/example/icon/music.png Binary files differnew file mode 100644 index 0000000..0f1415c --- /dev/null +++ b/nuklear/example/icon/music.png diff --git a/nuklear/example/icon/next.png b/nuklear/example/icon/next.png Binary files differnew file mode 100644 index 0000000..af0b98d --- /dev/null +++ b/nuklear/example/icon/next.png diff --git a/nuklear/example/icon/pause.png b/nuklear/example/icon/pause.png Binary files differnew file mode 100644 index 0000000..7d6367e --- /dev/null +++ b/nuklear/example/icon/pause.png diff --git a/nuklear/example/icon/pen.png b/nuklear/example/icon/pen.png Binary files differnew file mode 100644 index 0000000..10c851c --- /dev/null +++ b/nuklear/example/icon/pen.png diff --git a/nuklear/example/icon/phone.png b/nuklear/example/icon/phone.png Binary files differnew file mode 100644 index 0000000..5e6f613 --- /dev/null +++ b/nuklear/example/icon/phone.png diff --git a/nuklear/example/icon/plane.png b/nuklear/example/icon/plane.png Binary files differnew file mode 100644 index 0000000..3a98489 --- /dev/null +++ b/nuklear/example/icon/plane.png diff --git a/nuklear/example/icon/play.png b/nuklear/example/icon/play.png Binary files differnew file mode 100644 index 0000000..9c9e8f0 --- /dev/null +++ b/nuklear/example/icon/play.png diff --git a/nuklear/example/icon/prev.png b/nuklear/example/icon/prev.png Binary files differnew file mode 100644 index 0000000..0eecc2e --- /dev/null +++ b/nuklear/example/icon/prev.png diff --git a/nuklear/example/icon/rocket.png b/nuklear/example/icon/rocket.png Binary files differnew file mode 100644 index 0000000..ea8e187 --- /dev/null +++ b/nuklear/example/icon/rocket.png diff --git a/nuklear/example/icon/settings.png b/nuklear/example/icon/settings.png Binary files differnew file mode 100644 index 0000000..e6e13f8 --- /dev/null +++ b/nuklear/example/icon/settings.png diff --git a/nuklear/example/icon/stop.png b/nuklear/example/icon/stop.png Binary files differnew file mode 100644 index 0000000..6742baf --- /dev/null +++ b/nuklear/example/icon/stop.png diff --git a/nuklear/example/icon/text.png b/nuklear/example/icon/text.png Binary files differnew file mode 100644 index 0000000..136e534 --- /dev/null +++ b/nuklear/example/icon/text.png diff --git a/nuklear/example/icon/tools.png b/nuklear/example/icon/tools.png Binary files differnew file mode 100644 index 0000000..412ff85 --- /dev/null +++ b/nuklear/example/icon/tools.png diff --git a/nuklear/example/icon/unchecked.png b/nuklear/example/icon/unchecked.png Binary files differnew file mode 100644 index 0000000..fca94d2 --- /dev/null +++ b/nuklear/example/icon/unchecked.png diff --git a/nuklear/example/icon/volume.png b/nuklear/example/icon/volume.png Binary files differnew file mode 100644 index 0000000..8e86fa9 --- /dev/null +++ b/nuklear/example/icon/volume.png diff --git a/nuklear/example/icon/wifi.png b/nuklear/example/icon/wifi.png Binary files differnew file mode 100644 index 0000000..270d55d --- /dev/null +++ b/nuklear/example/icon/wifi.png diff --git a/nuklear/example/images/image1.png b/nuklear/example/images/image1.png Binary files differnew file mode 100644 index 0000000..66a2b63 --- /dev/null +++ b/nuklear/example/images/image1.png diff --git a/nuklear/example/images/image2.png b/nuklear/example/images/image2.png Binary files differnew file mode 100644 index 0000000..4acafe5 --- /dev/null +++ b/nuklear/example/images/image2.png diff --git a/nuklear/example/images/image3.png b/nuklear/example/images/image3.png Binary files differnew file mode 100644 index 0000000..4dfe664 --- /dev/null +++ b/nuklear/example/images/image3.png diff --git a/nuklear/example/images/image4.png b/nuklear/example/images/image4.png Binary files differnew file mode 100644 index 0000000..d2f16d0 --- /dev/null +++ b/nuklear/example/images/image4.png diff --git a/nuklear/example/images/image5.png b/nuklear/example/images/image5.png Binary files differnew file mode 100644 index 0000000..852fd70 --- /dev/null +++ b/nuklear/example/images/image5.png diff --git a/nuklear/example/images/image6.png b/nuklear/example/images/image6.png Binary files differnew file mode 100644 index 0000000..0e261cb --- /dev/null +++ b/nuklear/example/images/image6.png diff --git a/nuklear/example/images/image7.png b/nuklear/example/images/image7.png Binary files differnew file mode 100644 index 0000000..f61325b --- /dev/null +++ b/nuklear/example/images/image7.png diff --git a/nuklear/example/images/image8.png b/nuklear/example/images/image8.png Binary files differnew file mode 100644 index 0000000..6b27cb8 --- /dev/null +++ b/nuklear/example/images/image8.png diff --git a/nuklear/example/images/image9.png b/nuklear/example/images/image9.png Binary files differnew file mode 100644 index 0000000..516929e --- /dev/null +++ b/nuklear/example/images/image9.png diff --git a/nuklear/example/skinning.c b/nuklear/example/skinning.c new file mode 100644 index 0000000..4634b09 --- /dev/null +++ b/nuklear/example/skinning.c @@ -0,0 +1,825 @@ +/* nuklear - v1.05 - public domain */ +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdarg.h> +#include <string.h> +#include <math.h> +#include <assert.h> +#include <math.h> +#include <time.h> +#include <limits.h> + +#include <GL/glew.h> +#include <GLFW/glfw3.h> + +#define NK_INCLUDE_FIXED_TYPES +#define NK_INCLUDE_STANDARD_IO +#define NK_INCLUDE_DEFAULT_ALLOCATOR +#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT +#define NK_INCLUDE_FONT_BAKING +#define NK_INCLUDE_DEFAULT_FONT +#define NK_IMPLEMENTATION +#include "../nuklear.h" + +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" + +/* macros */ +#define WINDOW_WIDTH 1200 +#define WINDOW_HEIGHT 800 + +#define MAX_VERTEX_MEMORY 512 * 1024 +#define MAX_ELEMENT_MEMORY 128 * 1024 + +#define UNUSED(a) (void)a +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) < (b) ? (b) : (a)) +#define LEN(a) (sizeof(a)/sizeof(a)[0]) + +#ifdef __APPLE__ + #define NK_SHADER_VERSION "#version 150\n" +#else + #define NK_SHADER_VERSION "#version 300 es\n" +#endif + +struct media { + GLint skin; + struct nk_image menu; + struct nk_image check; + struct nk_image check_cursor; + struct nk_image option; + struct nk_image option_cursor; + struct nk_image header; + struct nk_image window; + struct nk_image scrollbar_inc_button; + struct nk_image scrollbar_inc_button_hover; + struct nk_image scrollbar_dec_button; + struct nk_image scrollbar_dec_button_hover; + struct nk_image button; + struct nk_image button_hover; + struct nk_image button_active; + struct nk_image tab_minimize; + struct nk_image tab_maximize; + struct nk_image slider; + struct nk_image slider_hover; + struct nk_image slider_active; +}; + + +/* =============================================================== + * + * DEVICE + * + * ===============================================================*/ +struct nk_glfw_vertex { + float position[2]; + float uv[2]; + nk_byte col[4]; +}; + +struct device { + struct nk_buffer cmds; + struct nk_draw_null_texture null; + GLuint vbo, vao, ebo; + GLuint prog; + GLuint vert_shdr; + GLuint frag_shdr; + GLint attrib_pos; + GLint attrib_uv; + GLint attrib_col; + GLint uniform_tex; + GLint uniform_proj; + GLuint font_tex; +}; + +static void +die(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fputs("\n", stderr); + exit(EXIT_FAILURE); +} + +static GLuint +image_load(const char *filename) +{ + int x,y,n; + GLuint tex; + unsigned char *data = stbi_load(filename, &x, &y, &n, 0); + if (!data) die("failed to load image: %s", filename); + + glGenTextures(1, &tex); + glBindTexture(GL_TEXTURE_2D, tex); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, x, y, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + glGenerateMipmap(GL_TEXTURE_2D); + stbi_image_free(data); + return tex; +} + +static void +device_init(struct device *dev) +{ + GLint status; + static const GLchar *vertex_shader = + NK_SHADER_VERSION + "uniform mat4 ProjMtx;\n" + "in vec2 Position;\n" + "in vec2 TexCoord;\n" + "in vec4 Color;\n" + "out vec2 Frag_UV;\n" + "out vec4 Frag_Color;\n" + "void main() {\n" + " Frag_UV = TexCoord;\n" + " Frag_Color = Color;\n" + " gl_Position = ProjMtx * vec4(Position.xy, 0, 1);\n" + "}\n"; + static const GLchar *fragment_shader = + NK_SHADER_VERSION + "precision mediump float;\n" + "uniform sampler2D Texture;\n" + "in vec2 Frag_UV;\n" + "in vec4 Frag_Color;\n" + "out vec4 Out_Color;\n" + "void main(){\n" + " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" + "}\n"; + + nk_buffer_init_default(&dev->cmds); + dev->prog = glCreateProgram(); + dev->vert_shdr = glCreateShader(GL_VERTEX_SHADER); + dev->frag_shdr = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(dev->vert_shdr, 1, &vertex_shader, 0); + glShaderSource(dev->frag_shdr, 1, &fragment_shader, 0); + glCompileShader(dev->vert_shdr); + glCompileShader(dev->frag_shdr); + glGetShaderiv(dev->vert_shdr, GL_COMPILE_STATUS, &status); + assert(status == GL_TRUE); + glGetShaderiv(dev->frag_shdr, GL_COMPILE_STATUS, &status); + assert(status == GL_TRUE); + glAttachShader(dev->prog, dev->vert_shdr); + glAttachShader(dev->prog, dev->frag_shdr); + glLinkProgram(dev->prog); + glGetProgramiv(dev->prog, GL_LINK_STATUS, &status); + assert(status == GL_TRUE); + + dev->uniform_tex = glGetUniformLocation(dev->prog, "Texture"); + dev->uniform_proj = glGetUniformLocation(dev->prog, "ProjMtx"); + dev->attrib_pos = glGetAttribLocation(dev->prog, "Position"); + dev->attrib_uv = glGetAttribLocation(dev->prog, "TexCoord"); + dev->attrib_col = glGetAttribLocation(dev->prog, "Color"); + + { + /* buffer setup */ + GLsizei vs = sizeof(struct nk_glfw_vertex); + size_t vp = offsetof(struct nk_glfw_vertex, position); + size_t vt = offsetof(struct nk_glfw_vertex, uv); + size_t vc = offsetof(struct nk_glfw_vertex, col); + + glGenBuffers(1, &dev->vbo); + glGenBuffers(1, &dev->ebo); + glGenVertexArrays(1, &dev->vao); + + glBindVertexArray(dev->vao); + glBindBuffer(GL_ARRAY_BUFFER, dev->vbo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo); + + glEnableVertexAttribArray((GLuint)dev->attrib_pos); + glEnableVertexAttribArray((GLuint)dev->attrib_uv); + glEnableVertexAttribArray((GLuint)dev->attrib_col); + + glVertexAttribPointer((GLuint)dev->attrib_pos, 2, GL_FLOAT, GL_FALSE, vs, (void*)vp); + glVertexAttribPointer((GLuint)dev->attrib_uv, 2, GL_FLOAT, GL_FALSE, vs, (void*)vt); + glVertexAttribPointer((GLuint)dev->attrib_col, 4, GL_UNSIGNED_BYTE, GL_TRUE, vs, (void*)vc); + } + + glBindTexture(GL_TEXTURE_2D, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindVertexArray(0); +} + +static void +device_upload_atlas(struct device *dev, const void *image, int width, int height) +{ + glGenTextures(1, &dev->font_tex); + glBindTexture(GL_TEXTURE_2D, dev->font_tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)width, (GLsizei)height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, image); +} + +static void +device_shutdown(struct device *dev) +{ + glDetachShader(dev->prog, dev->vert_shdr); + glDetachShader(dev->prog, dev->frag_shdr); + glDeleteShader(dev->vert_shdr); + glDeleteShader(dev->frag_shdr); + glDeleteProgram(dev->prog); + glDeleteTextures(1, &dev->font_tex); + glDeleteBuffers(1, &dev->vbo); + glDeleteBuffers(1, &dev->ebo); + nk_buffer_free(&dev->cmds); +} + +static void +device_draw(struct device *dev, struct nk_context *ctx, int width, int height, + struct nk_vec2 scale, enum nk_anti_aliasing AA) +{ + GLfloat ortho[4][4] = { + {2.0f, 0.0f, 0.0f, 0.0f}, + {0.0f,-2.0f, 0.0f, 0.0f}, + {0.0f, 0.0f,-1.0f, 0.0f}, + {-1.0f,1.0f, 0.0f, 1.0f}, + }; + ortho[0][0] /= (GLfloat)width; + ortho[1][1] /= (GLfloat)height; + + /* setup global state */ + glEnable(GL_BLEND); + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glEnable(GL_SCISSOR_TEST); + glActiveTexture(GL_TEXTURE0); + + /* setup program */ + glUseProgram(dev->prog); + glUniform1i(dev->uniform_tex, 0); + glUniformMatrix4fv(dev->uniform_proj, 1, GL_FALSE, &ortho[0][0]); + { + /* convert from command queue into draw list and draw to screen */ + const struct nk_draw_command *cmd; + void *vertices, *elements; + const nk_draw_index *offset = NULL; + + /* allocate vertex and element buffer */ + glBindVertexArray(dev->vao); + glBindBuffer(GL_ARRAY_BUFFER, dev->vbo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo); + + glBufferData(GL_ARRAY_BUFFER, MAX_VERTEX_MEMORY, NULL, GL_STREAM_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_ELEMENT_MEMORY, NULL, GL_STREAM_DRAW); + + /* load draw vertices & elements directly into vertex + element buffer */ + vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); + elements = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY); + { + /* fill convert configuration */ + struct nk_convert_config config; + static const struct nk_draw_vertex_layout_element vertex_layout[] = { + {NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_glfw_vertex, position)}, + {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_glfw_vertex, uv)}, + {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, NK_OFFSETOF(struct nk_glfw_vertex, col)}, + {NK_VERTEX_LAYOUT_END} + }; + NK_MEMSET(&config, 0, sizeof(config)); + config.vertex_layout = vertex_layout; + config.vertex_size = sizeof(struct nk_glfw_vertex); + config.vertex_alignment = NK_ALIGNOF(struct nk_glfw_vertex); + config.null = dev->null; + config.circle_segment_count = 22; + config.curve_segment_count = 22; + config.arc_segment_count = 22; + config.global_alpha = 1.0f; + config.shape_AA = AA; + config.line_AA = AA; + + /* setup buffers to load vertices and elements */ + {struct nk_buffer vbuf, ebuf; + nk_buffer_init_fixed(&vbuf, vertices, MAX_VERTEX_MEMORY); + nk_buffer_init_fixed(&ebuf, elements, MAX_ELEMENT_MEMORY); + nk_convert(ctx, &dev->cmds, &vbuf, &ebuf, &config);} + } + glUnmapBuffer(GL_ARRAY_BUFFER); + glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); + + /* iterate over and execute each draw command */ + nk_draw_foreach(cmd, ctx, &dev->cmds) + { + if (!cmd->elem_count) continue; + glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id); + glScissor( + (GLint)(cmd->clip_rect.x * scale.x), + (GLint)((height - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h)) * scale.y), + (GLint)(cmd->clip_rect.w * scale.x), + (GLint)(cmd->clip_rect.h * scale.y)); + glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset); + offset += cmd->elem_count; + } + nk_clear(ctx); + } + + /* default OpenGL state */ + glUseProgram(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindVertexArray(0); + glDisable(GL_BLEND); + glDisable(GL_SCISSOR_TEST); +} + +/* glfw callbacks (I don't know if there is a easier way to access text and scroll )*/ +static void error_callback(int e, const char *d){printf("Error %d: %s\n", e, d);} +static void text_input(GLFWwindow *win, unsigned int codepoint) +{nk_input_unicode((struct nk_context*)glfwGetWindowUserPointer(win), codepoint);} +static void scroll_input(GLFWwindow *win, double _, double yoff) +{UNUSED(_);nk_input_scroll((struct nk_context*)glfwGetWindowUserPointer(win), (float)yoff);} + +int main(int argc, char *argv[]) +{ + /* Platform */ + static GLFWwindow *win; + int width = 0, height = 0; + int display_width=0, display_height=0; + + /* GUI */ + struct device device; + struct nk_font_atlas atlas; + struct media media; + struct nk_context ctx; + struct nk_font *font; + + /* GLFW */ + glfwSetErrorCallback(error_callback); + if (!glfwInit()) { + fprintf(stdout, "[GFLW] failed to init!\n"); + exit(1); + } + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); +#ifdef __APPLE__ + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); +#endif + win = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "Demo", NULL, NULL); + glfwMakeContextCurrent(win); + glfwSetWindowUserPointer(win, &ctx); + glfwSetCharCallback(win, text_input); + glfwSetScrollCallback(win, scroll_input); + glfwGetWindowSize(win, &width, &height); + glfwGetFramebufferSize(win, &display_width, &display_height); + + /* OpenGL */ + glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); + glewExperimental = 1; + if (glewInit() != GLEW_OK) { + fprintf(stderr, "Failed to setup GLEW\n"); + exit(1); + } + + /* GUI */ + {device_init(&device); + {const void *image; int w, h; + const char *font_path = (argc > 1) ? argv[1]: 0; + nk_font_atlas_init_default(&atlas); + nk_font_atlas_begin(&atlas); + if (font_path) font = nk_font_atlas_add_from_file(&atlas, font_path, 13.0f, NULL); + else font = nk_font_atlas_add_default(&atlas, 13.0f, NULL); + image = nk_font_atlas_bake(&atlas, &w, &h, NK_FONT_ATLAS_RGBA32); + device_upload_atlas(&device, image, w, h); + nk_font_atlas_end(&atlas, nk_handle_id((int)device.font_tex), &device.null);} + nk_init_default(&ctx, &font->handle);} + + { /* skin */ + glEnable(GL_TEXTURE_2D); + media.skin = image_load("../skins/gwen.png"); + media.check = nk_subimage_id(media.skin, 512,512, nk_rect(464,32,15,15)); + media.check_cursor = nk_subimage_id(media.skin, 512,512, nk_rect(450,34,11,11)); + media.option = nk_subimage_id(media.skin, 512,512, nk_rect(464,64,15,15)); + media.option_cursor = nk_subimage_id(media.skin, 512,512, nk_rect(451,67,9,9)); + media.header = nk_subimage_id(media.skin, 512,512, nk_rect(128,0,127,24)); + media.window = nk_subimage_id(media.skin, 512,512, nk_rect(128,23,127,104)); + media.scrollbar_inc_button = nk_subimage_id(media.skin, 512,512, nk_rect(464,256,15,15)); + media.scrollbar_inc_button_hover = nk_subimage_id(media.skin, 512,512, nk_rect(464,320,15,15)); + media.scrollbar_dec_button = nk_subimage_id(media.skin, 512,512, nk_rect(464,224,15,15)); + media.scrollbar_dec_button_hover = nk_subimage_id(media.skin, 512,512, nk_rect(464,288,15,15)); + media.button = nk_subimage_id(media.skin, 512,512, nk_rect(384,336,127,31)); + media.button_hover = nk_subimage_id(media.skin, 512,512, nk_rect(384,368,127,31)); + media.button_active = nk_subimage_id(media.skin, 512,512, nk_rect(384,400,127,31)); + media.tab_minimize = nk_subimage_id(media.skin, 512,512, nk_rect(451, 99, 9, 9)); + media.tab_maximize = nk_subimage_id(media.skin, 512,512, nk_rect(467,99,9,9)); + media.slider = nk_subimage_id(media.skin, 512,512, nk_rect(418,33,11,14)); + media.slider_hover = nk_subimage_id(media.skin, 512,512, nk_rect(418,49,11,14)); + media.slider_active = nk_subimage_id(media.skin, 512,512, nk_rect(418,64,11,14)); + + /* window */ + ctx.style.window.background = nk_rgb(204,204,204); + ctx.style.window.fixed_background = nk_style_item_image(media.window); + ctx.style.window.border_color = nk_rgb(67,67,67); + ctx.style.window.combo_border_color = nk_rgb(67,67,67); + ctx.style.window.contextual_border_color = nk_rgb(67,67,67); + ctx.style.window.menu_border_color = nk_rgb(67 |