diff --git a/demo/demo.c b/demo/demo.c index 0d1124d..577d0c1 100644 --- a/demo/demo.c +++ b/demo/demo.c @@ -1,61 +1,7 @@ #define MAX_BUFFER 64 #define MAX_MEMORY (64 * 1024) -#define WINDOW_WIDTH 1200 -#define WINDOW_HEIGHT 800 - -struct menubar_window { - struct gui_panel_hook hook; - int current; - gui_size selection; -}; - -struct status_window { - struct gui_panel_hook hook; - gui_char mel_buffer[MAX_BUFFER]; - gui_size mel_length; - gui_bool mel_active; - gui_char py_buffer[MAX_BUFFER]; - gui_size py_length; - gui_bool py_active; -}; - -struct toolbar_window {struct gui_panel_hook hook;}; -struct settings_window { - struct gui_panel_hook hook; - - /* brush tab */ - gui_bool brush_tab; - gui_bool radius_u_active; - gui_bool radius_l_active; - gui_float radiusu; - gui_float radiusl; - gui_bool rotate_to_stroke; - - /* color tab */ - gui_bool color_tab; - struct gui_color color; - gui_float opacity; - gui_bool opacity_active; - - /* floot tab */ - gui_bool flood_tab; - struct gui_color flood_color; - gui_float flood_opacity; - gui_size flood_type; - - /* paint tabs */ - gui_bool paint_tab; - gui_size artisan_type; - gui_size effect_type; - gui_size brush_mode; - - /* texture tab */ - gui_bool texture_tab; - gui_size attribute; - gui_bool update_on_stroke; - gui_bool save_on_stroke; - gui_bool extend_seam_color; -}; +#define WINDOW_WIDTH 800 +#define WINDOW_HEIGHT 600 struct show_window { struct gui_panel_hook hook; @@ -104,34 +50,15 @@ struct control_window { struct gui_color color; }; -struct demo_img { - void *select; - void *lasso; - void *paint; - void *move; - void *rotate; - void *scale; -}; - struct demo_gui { gui_bool running; struct show_window show; struct control_window control; - struct settings_window settings; - struct menubar_window menu; - struct toolbar_window tool; - struct status_window status; - struct gui_memory memory; struct gui_command_buffer buffer; - struct gui_layout_config conf; - struct gui_panel_stack background; - struct gui_layout layout; - struct gui_panel_stack floating; + struct gui_panel_stack stack; struct gui_panel_layout tab; - - struct demo_img images; struct gui_config config; struct gui_font font; gui_size width, height; @@ -228,7 +155,7 @@ static void init_show(struct show_window *win, struct gui_config *config, struct gui_font *font, struct gui_panel_stack *stack) { - gui_panel_hook_init(&win->hook, 120, 160, 300, 545, + gui_panel_hook_init(&win->hook, 20, 20, 300, 545, GUI_PANEL_BORDER|GUI_PANEL_MOVEABLE| GUI_PANEL_CLOSEABLE|GUI_PANEL_SCALEABLE| GUI_PANEL_MINIMIZABLE|GUI_PANEL_HIDDEN, config, font); @@ -401,7 +328,7 @@ static void init_control(struct control_window *win, struct gui_config *config, struct gui_font *font, struct gui_panel_stack *stack) { - gui_panel_hook_init(&win->hook, 430, 180, 320, 500, + gui_panel_hook_init(&win->hook, 380, 20, 320, 500, GUI_PANEL_BORDER|GUI_PANEL_MOVEABLE|GUI_PANEL_CLOSEABLE|GUI_PANEL_SCALEABLE, config, font); gui_stack_push(stack, &win->hook); win->show_flags = gui_hook_panel(&win->hook)->flags; @@ -433,276 +360,6 @@ update_control(struct control_window *control, struct gui_panel_stack *stack, return running; } - -static void -brush_tab(struct gui_panel_layout *panel, struct settings_window *win) -{ - gui_panel_row(panel, 20, 2); - gui_panel_label(panel, "Radius (U):", GUI_TEXT_RIGHT); - win->radiusu = gui_panel_slider(panel, 0, win->radiusu, 20, 1); - gui_panel_label(panel, "Radius (L):", GUI_TEXT_RIGHT); - win->radiusl = gui_panel_slider(panel, 0, win->radiusl, 20, 1); - - gui_panel_row(panel, 25, 3); - gui_panel_seperator(panel, 1); - win->rotate_to_stroke = gui_panel_check(panel, "Rotate to stroke", win->rotate_to_stroke); -} - -static struct gui_color -scolor_tab(struct gui_panel_layout *panel, struct settings_window *win) -{ - char buffer[256]; - struct gui_color color = win->color; - gui_float c = color.r; - - gui_panel_row(panel, 25, 3); - gui_panel_label(panel, "Color:", GUI_TEXT_RIGHT); - c = gui_panel_slider(panel, 0, c, 250, 10); - color.r = (gui_byte)c; - color.g = (gui_byte)c; - color.b = (gui_byte)c; - color.a = 255; - gui_panel_button_color(panel, color, GUI_BUTTON_DEFAULT); - - gui_panel_label(panel, "Opacity:", GUI_TEXT_RIGHT); - win->flood_opacity = gui_panel_slider(panel, 0, win->flood_opacity, 250, 10); - sprintf(buffer, "%.2f", win->flood_opacity); - gui_panel_label(panel, buffer, GUI_TEXT_LEFT); - return color; -} - -static struct gui_color -flood_tab(struct gui_panel_layout *panel, struct settings_window *win) -{ - char buffer[256]; - const char *flood_types[] = {"All", "Selected"}; - struct gui_color color = win->flood_color; - gui_float c = color.r; - - gui_panel_row(panel, 25, 3); - gui_panel_label(panel, "Color:", GUI_TEXT_RIGHT); - c = gui_panel_slider(panel, 0, c, 250, 10); - color.r = (gui_byte)c; - color.g = (gui_byte)c; - color.b = (gui_byte)c; - color.a = 255; - gui_panel_button_color(panel, color, GUI_BUTTON_DEFAULT); - - gui_panel_label(panel, "Opacity:", GUI_TEXT_RIGHT); - win->opacity = gui_panel_slider(panel, 0, win->opacity, 250, 10); - sprintf(buffer, "%.2f", win->opacity); - gui_panel_label(panel, buffer, GUI_TEXT_LEFT); - - gui_panel_row(panel, 25, 2); - if (gui_panel_button_text(panel, "Flood Paint", GUI_BUTTON_DEFAULT)) - fprintf(stdout, "flood paint pressed!\n"); - if (gui_panel_button_text(panel, "Flood Erease", GUI_BUTTON_DEFAULT)) - fprintf(stdout, "flood erase pressed!\n"); - - gui_panel_row(panel, 25, LEN(flood_types) + 1); - gui_panel_label(panel, "Flood:", GUI_TEXT_RIGHT); - win->flood_type = gui_panel_option_group(panel, flood_types, LEN(flood_types), win->flood_type); - return color; -} - -static void -paint_tab(struct gui_panel_layout *panel, struct settings_window *win) -{ - const char *brush_mode[] = {"Dynamic", "Static"}; - const char *artisan_types[] = {"Paint", "Erase", "Clone"}; - const char *paint_effects[] = {"Paint", "Smear", "Blur"}; - gui_panel_row(panel, 25, LEN(artisan_types) + 1); - gui_panel_label(panel, "Artisan:", GUI_TEXT_RIGHT); - win->artisan_type = gui_panel_option_group(panel, artisan_types, LEN(artisan_types), win->artisan_type); - gui_panel_row(panel, 25, LEN(paint_effects) + 1); - gui_panel_label(panel, "Effects:", GUI_TEXT_RIGHT); - win->effect_type = gui_panel_option_group(panel, paint_effects, LEN(paint_effects), win->effect_type); - - gui_panel_row(panel, 25, 3); - gui_panel_seperator(panel, 1); - if (gui_panel_button_text(panel, "Erase", GUI_BUTTON_DEFAULT)) - fprintf(stdout, "set erase image button pressed!\n"); - - gui_panel_row(panel, 25, 3); - gui_panel_seperator(panel, 1); - if (gui_panel_button_text(panel, "Reset", GUI_BUTTON_DEFAULT)) - fprintf(stdout, "reset brushes button pressed!\n"); - - gui_panel_row(panel, 25, LEN(brush_mode) + 1); - gui_panel_label(panel, "Brush mode:", GUI_TEXT_RIGHT); - win->brush_mode = gui_panel_option_group(panel, brush_mode, LEN(brush_mode), win->brush_mode); -} - -static void -texture_tab(struct gui_panel_layout *panel, struct settings_window *win) -{ - const char *attributes[] = {"Color", "Normals", "Depth"}; - gui_panel_row(panel, 25, 1); - win->attribute = gui_panel_selector(panel, attributes, LEN(attributes), win->attribute); - if (gui_panel_button_text(panel, "Assign", GUI_BUTTON_DEFAULT)) - fprintf(stdout, "assign/edit textures button pressed!\n"); - if (gui_panel_button_text(panel, "Save", GUI_BUTTON_DEFAULT)) - fprintf(stdout, "save textures button pressed!\n"); - if (gui_panel_button_text(panel, "Reload", GUI_BUTTON_DEFAULT)) - fprintf(stdout, "reload textures button pressed!\n"); - - win->update_on_stroke = gui_panel_check(panel, "Update on stroke", win->update_on_stroke); - win->save_on_stroke = gui_panel_check(panel, "Save on stroke", win->save_on_stroke); - win->extend_seam_color = gui_panel_check(panel, "Extend seam color", win->extend_seam_color); -} - -static void -update_settings(struct settings_window *win, struct gui_layout *layout, - struct gui_input *in, struct gui_canvas *canvas) -{ - struct gui_panel_layout panel; - struct gui_panel_layout tab; - gui_panel_hook_begin_tiled(&panel, &win->hook, layout, GUI_SLOT_RIGHT, 0, - "Tool Settings", canvas, in); - - win->brush_tab = gui_panel_tab_begin(&panel, &tab, "Brush", win->brush_tab); - brush_tab(&tab, win); - gui_panel_tab_end(&panel, &tab); - - win->color_tab = gui_panel_tab_begin(&panel, &tab, "Color", win->color_tab); - win->color = scolor_tab(&tab, win); - gui_panel_tab_end(&panel, &tab); - - win->flood_tab = gui_panel_tab_begin(&panel, &tab, "Flood", win->flood_tab); - win->flood_color = flood_tab(&tab, win); - gui_panel_tab_end(&panel, &tab); - - win->paint_tab = gui_panel_tab_begin(&panel, &tab, "Paint", win->paint_tab); - paint_tab(&tab, win); - gui_panel_tab_end(&panel, &tab); - - win->texture_tab = gui_panel_tab_begin(&panel, &tab, "Textures", win->texture_tab); - texture_tab(&tab, win); - gui_panel_tab_end(&panel, &tab); - - gui_panel_hook_end(&panel, &win->hook); -} - -static void -update_menu(struct menubar_window *win, struct gui_layout *layout, - struct gui_input *in, struct gui_canvas *canvas, struct demo_gui *demo) -{ - /* TODO(vurtun): probably want this to be a little bit more expressive */ - struct level {const char *name; const int next;}; - static const struct level levels[][32] = { - {{"File", 1}, {"Edit", 2}, {"Tools", 3}, {"Create", 4}, {"Window", 5}, {NULL, -1}}, - {{"Back", 0}, {"New", 0}, {"Open", 0}, {"Quit", 0}, {NULL, -1}}, - {{"Back", 0}, {"Undo", 0}, {"Redo", 0}, {"Copy", 0}, {"Paste", 0}, {NULL, -1}}, - {{"Back", 0}, {"Selection", 6}, {"Transform", 7}, {NULL, -1}}, - {{"Back", 0}, {"Sphere", 0}, {"Cube", 0}, {"Cylinder", 0}, {NULL, -1}}, - {{"Back", 0}, {"General Editor", 0}, {"Node Editor", 0}, {NULL, -1}}, - {{"Home", 0}, {"Back", 3}, {"Rectangle", 0}, {"Elipse", 0}, {NULL, -1}}, - {{"Home", 0}, {"Back", 3}, {"Align", 0}, {"Move", 0}, {NULL, -1}} - }; - const char *tabs[] = { - "General", "Curves", "Surfaces", "Polygons", "Deformation", "Animation", "Dynamics", - "Rendering", "PaintEffects", "Toon", "Muscle", "Fluids", "Fur", "nHair", "nCloth" - }; - - gui_size cols; - const struct level *iter = levels[win->current]; - struct gui_panel_layout panel; - struct gui_panel_layout tab; - struct demo_img *images = &demo->images; - - /* menus */ - gui_panel_hook_begin_tiled(&panel, &win->hook, layout, GUI_SLOT_TOP, 0, NULL, canvas, in); - gui_panel_row(&panel, 20, 12); - while (iter->name) { - if (gui_panel_button_text(&panel, iter->name, GUI_BUTTON_DEFAULT)) { - fprintf(stdout, "button: %s pressed!\n", iter->name); - if (iter->next >= 0) - win->current = iter->next; - if (!strcmp(iter->name, "Quit")) - demo->running = gui_false; - } - iter++; - } - - /* toolbar */ - gui_panel_row(&panel, 85, 1); - gui_config_push_property(&demo->config, GUI_PROPERTY_PADDING, 15, 5); - gui_config_push_property(&demo->config, GUI_PROPERTY_ITEM_PADDING, 1, 1); - win->selection = gui_panel_shelf_begin(&panel, &tab, tabs, LEN(tabs), win->selection, 0); - - cols = gui_panel_row_columns(&tab, 40); - gui_panel_row(&tab, 40, cols); - if (win->selection % 2) { - if (gui_panel_button_image(&tab, images->select, GUI_BUTTON_DEFAULT)) - fprintf(stdout, "select button pressed!\n"); - if (gui_panel_button_image(&tab, images->lasso, GUI_BUTTON_DEFAULT)) - fprintf(stdout, "lasso button pressed!\n"); - if (gui_panel_button_image(&tab, images->paint, GUI_BUTTON_DEFAULT)) - fprintf(stdout, "paint button pressed!\n"); - if (gui_panel_button_image(&tab, images->move, GUI_BUTTON_DEFAULT)) - fprintf(stdout, "move button pressed!\n"); - if (gui_panel_button_image(&tab, images->rotate, GUI_BUTTON_DEFAULT)) - fprintf(stdout, "rotate button pressed!\n"); - if (gui_panel_button_image(&tab, images->scale, GUI_BUTTON_DEFAULT)) - fprintf(stdout, "scale button pressed!\n"); - } else { - if (gui_panel_button_image(&tab, images->move, GUI_BUTTON_DEFAULT)) - fprintf(stdout, "move button pressed!\n"); - if (gui_panel_button_image(&tab, images->rotate, GUI_BUTTON_DEFAULT)) - fprintf(stdout, "rotate button pressed!\n"); - if (gui_panel_button_image(&tab, images->scale, GUI_BUTTON_DEFAULT)) - fprintf(stdout, "scale button pressed!\n"); - if (gui_panel_button_image(&tab, images->select, GUI_BUTTON_DEFAULT)) - fprintf(stdout, "select button pressed!\n"); - if (gui_panel_button_image(&tab, images->lasso, GUI_BUTTON_DEFAULT)) - fprintf(stdout, "lasso button pressed!\n"); - if (gui_panel_button_image(&tab, images->paint, GUI_BUTTON_DEFAULT)) - fprintf(stdout, "paint button pressed!\n"); - } - gui_panel_shelf_end(&panel, &tab); - gui_config_pop_property(&demo->config); - gui_config_pop_property(&demo->config); - gui_panel_hook_end(&panel, &win->hook); -} - -static void -update_status(struct status_window *win, struct gui_layout *layout, - struct gui_input *in, struct gui_canvas *canvas) -{ - struct gui_panel_layout panel; - gui_panel_hook_begin_tiled(&panel, &win->hook, layout, GUI_SLOT_BOTTOM, 0, NULL, canvas, in); - gui_panel_row(&panel, 20, 2); - if (gui_panel_shell(&panel, win->mel_buffer, &win->mel_length, MAX_BUFFER, &win->mel_active)) - win->mel_length = 0; - if (gui_panel_shell(&panel, win->py_buffer, &win->py_length, MAX_BUFFER, &win->py_active)) - win->py_length = 0; - gui_panel_hook_end(&panel, &win->hook); -} - -static void -update_toolbar(struct toolbar_window *win, struct gui_layout *layout, struct demo_img *images, - struct gui_input *in, struct gui_canvas *canvas, struct gui_config *config) -{ - struct gui_panel_layout panel; - gui_config_push_property(config, GUI_PROPERTY_PADDING, 5, 10); - gui_panel_hook_begin_tiled(&panel, &win->hook, layout, GUI_SLOT_LEFT, 0, NULL, canvas, in); - gui_panel_row(&panel, 45, 1); - if (gui_panel_button_image(&panel, images->select, GUI_BUTTON_DEFAULT)) - fprintf(stdout, "select button pressed!\n"); - if (gui_panel_button_image(&panel, images->lasso, GUI_BUTTON_DEFAULT)) - fprintf(stdout, "lasso button pressed!\n"); - if (gui_panel_button_image(&panel, images->paint, GUI_BUTTON_DEFAULT)) - fprintf(stdout, "paint button pressed!\n"); - if (gui_panel_button_image(&panel, images->move, GUI_BUTTON_DEFAULT)) - fprintf(stdout, "move button pressed!\n"); - if (gui_panel_button_image(&panel, images->rotate, GUI_BUTTON_DEFAULT)) - fprintf(stdout, "rotate button pressed!\n"); - if (gui_panel_button_image(&panel, images->scale, GUI_BUTTON_DEFAULT)) - fprintf(stdout, "scale button pressed!\n"); - gui_panel_hook_end(&panel, &win->hook); - gui_config_pop_property(config); -} - static void init_demo(struct demo_gui *gui, struct gui_font *font) { @@ -717,101 +374,34 @@ init_demo(struct demo_gui *gui, struct gui_font *font) gui_buffer_init_fixed(buffer, memory, GUI_BUFFER_CLIPPING); gui_config_default(config); - /* background panels */ - gui_panel_hook_init(&gui->settings.hook, 0, 0, 0, 0, 0, config, font); - gui_panel_hook_init(&gui->status.hook, 0, 0, 0, 0, GUI_PANEL_NO_HEADER, config, font); - gui_panel_hook_init(&gui->tool.hook, 0, 0, 0, 0, GUI_PANEL_NO_HEADER, config, font); - gui_panel_hook_init(&gui->menu.hook, 0, 0, 0, 0, GUI_PANEL_NO_HEADER, config, font); - gui->settings.brush_tab = GUI_MINIMIZED; - gui->settings.color_tab = GUI_MINIMIZED; - gui->settings.texture_tab = GUI_MINIMIZED; - /* floating windows */ - gui_stack_clear(&gui->floating); - init_show(&gui->show, config, font, &gui->floating); - init_control(&gui->control, config, font, &gui->floating); -} - -static void -background_demo(struct demo_gui *gui, struct gui_input *input, struct gui_command_buffer *buffer, - gui_bool active) -{ - struct gui_layout_config ratio; - struct gui_command_buffer sub; - struct gui_canvas canvas; - struct menubar_window *menu = &gui->menu; - struct toolbar_window *tool = &gui->tool; - struct status_window *status = &gui->status; - struct settings_window *settings = &gui->settings; - - /* setup layout split to fit screen */ - ratio.right = 350.0f / gui->width; - ratio.top = 140.0f / gui->height; - ratio.left = 70.0f / gui->width; - ratio.bottom = 48.0f / gui->height; - ratio.centerv = 1.0f - (ratio.top + ratio.bottom); - ratio.centerh = 1.0f - (ratio.right + ratio.left); - gui_layout_init(&gui->layout, &ratio); - - /* activate each used layout slot */ - gui_layout_begin(&gui->layout, gui->width, gui->height, active); - gui_layout_slot(&gui->layout, GUI_SLOT_RIGHT, GUI_LAYOUT_VERTICAL, 1); - gui_layout_slot(&gui->layout, GUI_SLOT_LEFT, GUI_LAYOUT_VERTICAL, 1); - gui_layout_slot(&gui->layout, GUI_SLOT_TOP, GUI_LAYOUT_VERTICAL, 1); - gui_layout_slot(&gui->layout, GUI_SLOT_BOTTOM, GUI_LAYOUT_VERTICAL, 1); - - /* settings window */ - gui_buffer_lock(&canvas, buffer, &sub, 0, gui->width, gui->height); - update_settings(settings, &gui->layout, input, &canvas); - gui_buffer_unlock(gui_hook_output(&settings->hook), buffer, &sub, &canvas, NULL); - - /* menubar window */ - gui_buffer_lock(&canvas, buffer, &sub, 0, gui->width, gui->height); - update_menu(menu, &gui->layout, input, &canvas, gui); - gui_buffer_unlock(gui_hook_output(&menu->hook), buffer, &sub, &canvas, NULL); - - /* statusbar window */ - gui_buffer_lock(&canvas, buffer, &sub, 0, gui->width, gui->height); - update_status(status, &gui->layout, input, &canvas); - gui_buffer_unlock(gui_hook_output(&status->hook), buffer, &sub, &canvas, NULL); - - /* toolbar window */ - gui_buffer_lock(&canvas, buffer, &sub, 0, gui->width, gui->height); - update_toolbar(tool, &gui->layout, &gui->images, input, &canvas, &gui->config); - gui_buffer_unlock(gui_hook_output(&tool->hook), buffer, &sub, &canvas, NULL); - - gui_layout_end(&gui->background, &gui->layout); -} - -static void -floating_demo(struct demo_gui *gui, struct gui_input *input, struct gui_command_buffer *buffer) -{ - struct show_window *show = &gui->show; - struct control_window *control = &gui->control; - struct gui_command_buffer sub; - struct gui_canvas canvas; - - /* Show window */ - gui_buffer_lock(&canvas, buffer, &sub, 0, gui->width, gui->height); - update_control(control, &gui->floating, input, &canvas, &gui->config); - gui_buffer_unlock(gui_hook_output(&control->hook), buffer, &sub, &canvas, NULL); - - /* control window */ - gui_hook_panel(&show->hook)->flags = control->show_flags; - gui_buffer_lock(&canvas, buffer, &sub, 0, gui->width, gui->height); - update_show(show, &gui->floating, input, &canvas); - if (gui_hook_panel(&show->hook)->flags & GUI_PANEL_HIDDEN) - control->show_flags |= GUI_PANEL_HIDDEN; - gui_buffer_unlock(gui_hook_output(&show->hook), buffer, &sub, &canvas, NULL); + gui_stack_clear(&gui->stack); + init_show(&gui->show, config, font, &gui->stack); + init_control(&gui->control, config, font, &gui->stack); } static void run_demo(struct demo_gui *gui, struct gui_input *input) { struct gui_command_buffer *buffer = &gui->buffer; + struct show_window *show = &gui->show; + struct control_window *control = &gui->control; + struct gui_command_buffer sub; + struct gui_canvas canvas; + + /* control window */ gui_buffer_begin(NULL, buffer, gui->width, gui->height); - background_demo(gui, input, buffer, gui->control.show_flags & GUI_PANEL_HIDDEN); - floating_demo(gui, input, buffer); + gui_buffer_lock(&canvas, buffer, &sub, 0, gui->width, gui->height); + gui->running = update_control(control, &gui->stack, input, &canvas, &gui->config); + gui_buffer_unlock(gui_hook_output(&control->hook), buffer, &sub, &canvas, NULL); + + /* Show window */ + gui_hook_panel(&show->hook)->flags = control->show_flags; + gui_buffer_lock(&canvas, buffer, &sub, 0, gui->width, gui->height); + update_show(show, &gui->stack, input, &canvas); + if (gui_hook_panel(&show->hook)->flags & GUI_PANEL_HIDDEN) + control->show_flags |= GUI_PANEL_HIDDEN; + gui_buffer_unlock(gui_hook_output(&show->hook), buffer, &sub, &canvas, NULL); gui_buffer_end(NULL, buffer, NULL, NULL); } diff --git a/demo/icon/Thumbs.db b/demo/icon/Thumbs.db deleted file mode 100644 index b5aeb55..0000000 Binary files a/demo/icon/Thumbs.db and /dev/null differ diff --git a/demo/icon/lasso.bmp b/demo/icon/lasso.bmp deleted file mode 100644 index 43b587f..0000000 Binary files a/demo/icon/lasso.bmp and /dev/null differ diff --git a/demo/icon/move.bmp b/demo/icon/move.bmp deleted file mode 100644 index 8004824..0000000 Binary files a/demo/icon/move.bmp and /dev/null differ diff --git a/demo/icon/paint.bmp b/demo/icon/paint.bmp deleted file mode 100644 index 8690915..0000000 Binary files a/demo/icon/paint.bmp and /dev/null differ diff --git a/demo/icon/rotate.bmp b/demo/icon/rotate.bmp deleted file mode 100644 index 278c163..0000000 Binary files a/demo/icon/rotate.bmp and /dev/null differ diff --git a/demo/icon/scale.bmp b/demo/icon/scale.bmp deleted file mode 100644 index 4e437f5..0000000 Binary files a/demo/icon/scale.bmp and /dev/null differ diff --git a/demo/icon/select.bmp b/demo/icon/select.bmp deleted file mode 100644 index 6580910..0000000 Binary files a/demo/icon/select.bmp and /dev/null differ diff --git a/demo/opengl.c b/demo/opengl.c new file mode 100644 index 0000000..9ad215e --- /dev/null +++ b/demo/opengl.c @@ -0,0 +1,630 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include FT_FREETYPE_H +#include FT_GLYPH_H + +#include "../gui.h" + +/* macros */ +#define DTIME 16 +#define FONT_ATLAS_DEPTH 4 +#define CIRCLE_SEGMENTS 22 + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) < (b) ? (b) : (a)) +#define CLAMP(i,v,x) (MAX(MIN(v,x), i)) +#define LEN(a) (sizeof(a)/sizeof(a)[0]) +#define UNUSED(a) ((void)(a)) + +#include "demo.c" + +struct texCoord { + float u; + float v; +}; + +enum font_atlas_dimension { + FONT_ATLAS_DIM_64 = 64, + FONT_ATLAS_DIM_128 = 128, + FONT_ATLAS_DIM_256 = 256, + FONT_ATLAS_DIM_512 = 512, + FONT_ATLAS_DIM_1024 = 1024, + FONT_ATLAS_DIM_2048 = 2048 +}; + +struct font_atlas { + enum font_atlas_dimension dim; + gui_size range; + gui_size size; + gui_byte *memory; +}; + +struct font_glyph { + unsigned int code; + float xadvance; + short width, height; + float xoff, yoff; + struct texCoord uv[2]; +}; + +struct font { + float height; + float scale; + GLuint texture; + unsigned int glyph_count; + struct font_glyph *glyphes; + const struct font_glyph *fallback; +}; + +struct demo { + gui_char in_buf[MAX_BUFFER]; + gui_size in_len; + gui_bool in_act; + gui_bool check; + gui_int option; + gui_float slider; + gui_size prog; + gui_int spinner; + gui_bool spin_act; + gui_size item_cur; + gui_size cur; + gui_bool tab_min; + gui_float group_off; + gui_float shelf_off; + gui_bool toggle; +}; + +static void +die(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + exit(EXIT_FAILURE); +} + +static char* +file_load(const char* path, size_t* siz) +{ + char *buf; + FILE *fd = fopen(path, "rb"); + if (!fd) { + fprintf(stderr, "Failed to open file: %s\n", path); + exit(EXIT_FAILURE); + } + fseek(fd, 0, SEEK_END); + *siz = (size_t)ftell(fd); + fseek(fd, 0, SEEK_SET); + buf = calloc(*siz, 1); + fread(buf, *siz, 1, fd); + fclose(fd); + return buf; +} + +static void +font_load_glyph(unsigned int code, struct font_glyph *glyph, FT_GlyphSlot slot) +{ + glyph->code = code; + glyph->width = (short)slot->bitmap.width; + glyph->height = (short)slot->bitmap.rows; + glyph->xoff = (float)slot->bitmap_left; + glyph->yoff = (float)slot->bitmap_top; + glyph->xadvance = (float)(slot->advance.x >> 6); +} + +static void +font_load_glyphes(FT_Face face, struct font *font, size_t range) +{ + size_t i; + int ft_err; + for (i = 0; i < range; ++i) { + unsigned int index = FT_Get_Char_Index(face, i); + if (!index) continue; + ft_err = FT_Load_Glyph(face, index, 0); + if (ft_err) continue; + ft_err = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); + if (ft_err) continue; + font_load_glyph(index, &font->glyphes[i], face->glyph); + } +} + +static void +font_pack_glyphes(struct font *font, float width, float height, size_t range) +{ + size_t i; + float xoff = 0, yoff = 0; + float max_height = 0.0f; + for (i = 0; i < range; ++i) { + struct font_glyph *glyph = &font->glyphes[i]; + if ((xoff + glyph->width) > width) { + yoff += max_height; + max_height = 0.0f; + xoff = 0.0f; + } + + glyph->uv[0].u = xoff / width; + glyph->uv[0].v = yoff / height; + glyph->uv[1].u = (xoff + glyph->width) / width; + glyph->uv[1].v = (yoff + glyph->height) / height; + if (glyph->height > max_height) + max_height = glyph->height; + xoff += glyph->width; + } +} + +static void +font_atlas_blit(struct font_atlas *atlas, FT_GlyphSlot glyph, + size_t off_x, size_t off_y) +{ + size_t y, x; + size_t width = glyph->bitmap.width; + size_t height = glyph->bitmap.rows; + const size_t pitch = atlas->dim * FONT_ATLAS_DEPTH; + for (y = 0; y < height; y++) { + size_t x_off = off_x * FONT_ATLAS_DEPTH; + size_t y_off = (off_y + y) * pitch; + unsigned char *dst = &atlas->memory[y_off + x_off]; + for (x = 0; x < width; ++x) { + dst[0] = 255; + dst[1] = 255; + dst[2] = 255; + dst[3] = glyph->bitmap.buffer[y * width + x]; + dst += FONT_ATLAS_DEPTH; + } + } +} + +static void +font_bake_glyphes(FT_Face face, struct font_atlas *atlas, + const struct font *font) +{ + size_t i; + int ft_err; + for (i = 0; i < atlas->range; ++i) { + size_t x, y; + struct font_glyph *glyph = &font->glyphes[i]; + unsigned int index = FT_Get_Char_Index(face, i); + + if (!index) continue; + ft_err = FT_Load_Glyph(face, index, 0); + if (ft_err) continue; + ft_err = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); + if (ft_err) continue; + + x = (gui_size)(glyph->uv[0].u * (gui_float)atlas->dim); + y = (gui_size)(glyph->uv[0].v * (gui_float)atlas->dim); + font_atlas_blit(atlas, face->glyph, x, y); + } +} + +static int +font_load(struct font *font, struct font_atlas *atlas, unsigned int height, + const unsigned char *data, size_t size) +{ + int ret = 1; + FT_Library ft_lib; + FT_Face ft_face; + + assert(font); + assert(atlas); + assert(font->glyphes); + assert(atlas->memory); + + if (!font || !atlas) + return gui_false; + if (!font->glyphes || !atlas->memory) + return gui_false; + if (FT_Init_FreeType(&ft_lib)) + return gui_false; + if (FT_New_Memory_Face(ft_lib, data, (FT_Long)size, 0, &ft_face)) + goto cleanup; + if (FT_Select_Charmap(ft_face, FT_ENCODING_UNICODE)) + goto failed; + if (FT_Set_Char_Size(ft_face, height << 6, height << 6, 96, 96)) + goto failed; + + font_load_glyphes(ft_face, font, atlas->range); + font_pack_glyphes(font, atlas->dim, atlas->dim, atlas->range); + font_bake_glyphes(ft_face, atlas, font); + +failed: + FT_Done_Face(ft_face); +cleanup: + FT_Done_FreeType(ft_lib); + return ret; +} + +static gui_size +font_get_text_width(void *handle, const gui_char *t, gui_size l) +{ + long unicode; + size_t text_width = 0; + const struct font_glyph *glyph; + size_t text_len = 0; + size_t glyph_len; + struct font *font = handle; + assert(font); + if (!t || !l) return 0; + + glyph_len = gui_utf_decode(t, &unicode, l); + while (text_len <= l && glyph_len) { + if (unicode == GUI_UTF_INVALID) return 0; + glyph = (unicode < font->glyph_count) ? &font->glyphes[unicode] : font->fallback; + glyph = (glyph->code == 0) ? font->fallback : glyph; + + text_width += (gui_size)((float)glyph->xadvance * font->scale); + glyph_len = gui_utf_decode(t + text_len, &unicode, l - text_len); + text_len += glyph_len; + } + return text_width; +} + +static void +font_draw_text(const struct font *font, float x, float y, float h, + struct gui_color color, const unsigned char *text, size_t len) +{ + struct gui_rect clip; + size_t text_len; + long unicode; + const struct font_glyph *g; + + text_len = gui_utf_decode(text, &unicode, len); + glBindTexture(GL_TEXTURE_2D, font->texture); + glColor4ub(color.r, color.g, color.b, color.a); + glBegin(GL_QUADS); + while (text_len <= len) { + float gx, gy, gh, gw, char_width = 0; + if (unicode == GUI_UTF_INVALID) break; + g = (unicode < font->glyph_count) ? + &font->glyphes[unicode] : + font->fallback; + g = (g->code == 0) ? font->fallback : g; + + + gw = (float)g->width * font->scale; + gh = (float)g->height * font->scale; + gx = x + g->xoff * font->scale; + gy = y + (h / 2) - (g->yoff * font->scale); + char_width = g->xadvance * font->scale; + + glTexCoord2f(g->uv[0].u, g->uv[0].v); + glVertex2f(gx, gy); + glTexCoord2f(g->uv[1].u, g->uv[0].v); + glVertex2f(gx + gw, gy); + glTexCoord2f(g->uv[1].u, g->uv[1].v); + glVertex2f(gx + gw, gy + gh); + glTexCoord2f(g->uv[0].u, g->uv[1].v); + glVertex2f(gx, gy + gh); + text_len += gui_utf_decode(text + text_len, &unicode, len - text_len); + x += char_width; + } + glEnd(); + glBindTexture(GL_TEXTURE_2D, 0); +} + +static void +font_del(struct font *font) +{ + glDeleteTextures(1, &font->texture); + free(font->glyphes); + free(font); +} + +static struct font* +font_new(const char *path, unsigned int font_height, unsigned int bake_height, + size_t range, enum font_atlas_dimension dim) +{ + gui_byte *ttf_blob; + gui_size ttf_blob_size; + struct font_atlas atlas; + struct font *font = calloc(sizeof(struct font), 1); + + atlas.dim = dim; + atlas.range = range; + atlas.size = atlas.dim * atlas.dim * FONT_ATLAS_DEPTH; + atlas.memory = calloc((gui_size)atlas.size, 1); + + font->glyph_count = (unsigned int)atlas.range; + font->glyphes = calloc(atlas.range, sizeof(struct font_glyph)); + font->fallback = &font->glyphes['?']; + font->scale = (float)font_height / (gui_float)bake_height; + font->height = (float)font_height; + + ttf_blob = (unsigned char*)file_load(path, &ttf_blob_size); + if (!font_load(font, &atlas, bake_height, ttf_blob, ttf_blob_size)) + goto failed; + + glGenTextures(1, &font->texture); + glBindTexture(GL_TEXTURE_2D, font->texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, dim, dim, 0, + GL_RGBA, GL_UNSIGNED_BYTE, atlas.memory); + + free(atlas.memory); + free(ttf_blob); + return font; + +failed: + free(atlas.memory); + free(ttf_blob); + font_del(font); + return NULL; +} + +static void +draw_line(float x0, float y0, float x1, float y1, struct gui_color color) +{ + glColor4ub(color.r, color.g, color.b, color.a); + glBegin(GL_LINES); + glVertex2f(x0, y0); + glVertex2f(x1, y1); + glEnd(); +} + +static void +draw_rect(float x, float y, float w, float h, struct gui_color color) +{ + glColor4ub(color.r, color.g, color.b, color.a); + glBegin(GL_QUADS); + glVertex2f(x, y); + glVertex2f(x + w, y); + glVertex2f(x + w, y + h); + glVertex2f(x, y + h); + glEnd(); +} + +static void +draw_circle(float x, float y, float r, struct gui_color color) +{ + int i; + float a0 = 0.0f; + const float a_step = (2 * 3.141592654f)/22.0f; + x += r; y += r; + glColor4ub(color.r, color.g, color.b, color.a); + glBegin(GL_TRIANGLES); + for (i = 0; i < CIRCLE_SEGMENTS; i++) { + const float a1 = ((i + 1) == CIRCLE_SEGMENTS) ? 0.0f : a0 + a_step; + const float p0x = x + (float)cos(a0) * r; + const float p0y = y + (float)sin(a0) * r; + const float p1x = x + (float)cos(a1) * r; + const float p1y = y + (float)sin(a1) * r; + + glVertex2f(p0x, p0y); + glVertex2f(p1x, p1y); + glVertex2f(x, y); + a0 = a1; + } + glEnd(); +} + +static void +execute(struct gui_command_list *list, int width, int height) +{ + static const struct gui_color col = {255, 0, 0, 255}; + const struct gui_command *cmd; + if (!list->count) return; + + glPushAttrib(GL_ENABLE_BIT|GL_COLOR_BUFFER_BIT|GL_TRANSFORM_BIT); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glEnable(GL_SCISSOR_TEST); + glEnable(GL_TEXTURE_2D); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(0.0f, width, height, 0.0f, 0.0f, 1.0f); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + gui_list_for_each(cmd, list) { + switch (cmd->type) { + case GUI_COMMAND_NOP: break; + case GUI_COMMAND_SCISSOR: { + const struct gui_command_scissor *s = GUI_FETCH(scissor, cmd); + glScissor(s->x, height - (s->y + s->h), s->w, s->h); + } break; + case GUI_COMMAND_LINE: { + const struct gui_command_line *l = GUI_FETCH(line, cmd); + draw_line(l->begin[0], l->begin[1], l->end[0], l->end[1], l->color); + } break; + case GUI_COMMAND_RECT: { + const struct gui_command_rect *r = GUI_FETCH(rect, cmd); + draw_rect(r->x, r->y, r->w, r->h, r->color); + } break; + case GUI_COMMAND_CIRCLE: { + const struct gui_command_circle *c = GUI_FETCH(circle, cmd); + draw_circle(c->x, c->y, (float)c->w / 2.0f, c->color); + } break; + case GUI_COMMAND_TRIANGLE: { + const struct gui_command_triangle *t = GUI_FETCH(triangle, cmd); + glColor4ub(t->color.r, t->color.g, t->color.b, t->color.a); + glBegin(GL_TRIANGLES); + glVertex2f(t->a[0], t->a[1]); + glVertex2f(t->b[0], t->b[1]); + glVertex2f(t->c[0], t->c[1]); + glEnd(); + } break; + case GUI_COMMAND_TEXT: { + const struct gui_command_text *t = GUI_FETCH(text, cmd); + font_draw_text(t->font, t->x, t->y, t->h, t->fg, t->string, t->length); + } break; + case GUI_COMMAND_IMAGE: + case GUI_COMMAND_MAX: + default: break; + } + } + + glBindTexture(GL_TEXTURE_2D, 0); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glPopAttrib(); +} + +static void +draw(struct gui_panel_stack *stack, int width, int height) +{ + struct gui_panel_hook *iter; + gui_stack_for_each(iter, stack) + execute(gui_hook_output(iter), width, height); +} + +static void +key(struct gui_input *in, SDL_Event *evt, gui_bool down) +{ + SDL_Keycode sym = evt->key.keysym.sym; + if (sym == SDLK_LCTRL || sym == SDLK_RCTRL) + gui_input_key(in, GUI_KEY_CTRL, down); + else if (sym == SDLK_RSHIFT || sym == SDLK_LSHIFT) + gui_input_key(in, GUI_KEY_SHIFT, down); + else if (sym == SDLK_DELETE) + gui_input_key(in, GUI_KEY_DEL, down); + else if (sym == SDLK_RETURN) + gui_input_key(in, GUI_KEY_ENTER, down); + else if (sym == SDLK_SPACE) + gui_input_key(in, GUI_KEY_SPACE, down); + else if (sym == SDLK_BACKSPACE) + gui_input_key(in, GUI_KEY_BACKSPACE, down); +} + +static void +motion(struct gui_input *in, SDL_Event *evt) +{ + const gui_int x = evt->motion.x; + const gui_int y = evt->motion.y; + gui_input_motion(in, x, y); +} + +static void +btn(struct gui_input *in, SDL_Event *evt, gui_bool down) +{ + const gui_int x = evt->button.x; + const gui_int y = evt->button.y; + if (evt->button.button == SDL_BUTTON_LEFT) + gui_input_button(in, x, y, down); +} + +static void +text(struct gui_input *in, SDL_Event *evt) +{ + gui_glyph glyph; + memcpy(glyph, evt->text.text, GUI_UTF_SIZE); + gui_input_char(in, glyph); +} + +static void +resize(SDL_Event *evt) +{ + if (evt->window.event != SDL_WINDOWEVENT_RESIZED) return; + glViewport(0, 0, evt->window.data1, evt->window.data2); +} + +int +main(int argc, char *argv[]) +{ + /* Platform */ + const char *font_path; + SDL_Window *win; + SDL_GLContext glContext; + struct font *glfont; + int win_width, win_height; + unsigned int started; + unsigned int dt; + int width = 0, height = 0; + + /* GUI */ + struct gui_input in; + struct gui_font font; + struct demo_gui gui; + + font_path = argv[1]; + if (argc < 2) { + fprintf(stdout, "Missing TTF Font file argument!"); + exit(EXIT_FAILURE); + } + + /* SDL */ + SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_EVENTS); + 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); + glContext = SDL_GL_CreateContext(win); + SDL_GetWindowSize(win, &win_width, &win_height); + glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); + glfont = font_new(font_path, 10, 12, 255, FONT_ATLAS_DIM_256); + + /* GUI */ + memset(&in, 0, sizeof in); + memset(&gui, 0, sizeof gui); + font.userdata = glfont; + font.height = glfont->height; + font.width = font_get_text_width; + init_demo(&gui, &font); + + while (gui.running) { + /* Input */ + SDL_Event evt; + started = SDL_GetTicks(); + gui_input_begin(&in); + while (SDL_PollEvent(&evt)) { + if (evt.type == SDL_WINDOWEVENT) resize(&evt); + else if (evt.type == SDL_QUIT) goto cleanup; + else if (evt.type == SDL_KEYUP) key(&in, &evt, gui_false); + else if (evt.type == SDL_KEYDOWN) key(&in, &evt, gui_true); + else if (evt.type == SDL_MOUSEBUTTONDOWN) btn(&in, &evt, gui_true); + else if (evt.type == SDL_MOUSEBUTTONUP) btn(&in, &evt, gui_false); + else if (evt.type == SDL_MOUSEMOTION) motion(&in, &evt); + else if (evt.type == SDL_TEXTINPUT) text(&in, &evt); + } + gui_input_end(&in); + + /* GUI */ + SDL_GetWindowSize(win, &width, &height); + gui.width = (gui_size)width; + gui.height = (gui_size)height; + run_demo(&gui, &in); + + /* Draw */ + glClearColor(0.4f, 0.4f, 0.4f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + draw(&gui.stack, width, height); + SDL_GL_SwapWindow(win); + + /* Timing */ + dt = SDL_GetTicks() - started; + if (dt < DTIME) + SDL_Delay(DTIME - dt); + } + +cleanup: + /* Cleanup */ + free(gui.memory.memory); + font_del(glfont); + SDL_GL_DeleteContext(glContext); + SDL_DestroyWindow(win); + SDL_Quit(); + return 0; +} + diff --git a/demo/win32.c b/demo/win32.c index c235097..6790ed4 100644 --- a/demo/win32.c +++ b/demo/win32.c @@ -247,21 +247,6 @@ surface_draw_text(XSurface *surf, XFont *font, short x, short y, unsigned short SelectObject(surf->hdc, old); } -static void -surface_draw_image(XSurface *surf, HBITMAP img, short x, short y, unsigned short w, unsigned short h) -{ - HDC mem; - BITMAP bitmap; - HGDIOBJ old; - - mem = CreateCompatibleDC(surf->hdc); - old = SelectObject(mem, img); - GetObject(img, sizeof(bitmap), &bitmap); - StretchBlt(surf->hdc, x, y, w, h, mem, 0, 0, bitmap.bmWidth, bitmap.bmHeight, SRCCOPY); - SelectObject(mem, old); - DeleteDC(mem); -} - static void surface_clear(XSurface *surf, unsigned char r, unsigned char g, unsigned char b) {surface_draw_rect(surf, 0, 0, (unsigned short)surf->width, (unsigned short)surf->height, r, g, b);} @@ -317,11 +302,7 @@ execute(XSurface *surf, struct gui_command_list *list) surface_draw_text(surf, win->font, t->x, t->y, t->w, t->h, (const char*)t->string, t->length, t->bg.r, t->bg.g, t->bg.b, t->fg.r, t->fg.g, t->fg.b); } break; - case GUI_COMMAND_IMAGE: { - const struct gui_command_image *i = GUI_FETCH(image, cmd); - HBITMAP bitmap = i->img; - surface_draw_image(surf, bitmap, i->x, i->y, i->w, i->h); - } break; + case GUI_COMMAND_IMAGE: default: break; } } @@ -398,19 +379,6 @@ wnd_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) return 0; } -HBITMAP -loadimg(const char *path) -{ - HBITMAP bitmap; - DWORD ret = 0; - TCHAR buffer[4 *1024]; - TCHAR ** lppPart = {NULL}; - ret = GetFullPathName(path, 4 * 1024, buffer, lppPart); - bitmap = (HBITMAP)LoadImage(NULL, buffer, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); - assert(bitmap); - return bitmap; -} - INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prev, LPSTR lpCmdLine, int shown) { @@ -453,13 +421,6 @@ WinMain(HINSTANCE hInstance, HINSTANCE prev, LPSTR lpCmdLine, int shown) gui.running = gui_true; init_demo(&gui, &font); - gui.images.select = loadimg("icon/select.bmp"); - gui.images.lasso = loadimg("icon/lasso.bmp"); - gui.images.paint = loadimg("icon/paint.bmp"); - gui.images.move = loadimg("icon/move.bmp"); - gui.images.rotate = loadimg("icon/rotate.bmp"); - gui.images.scale = loadimg("icon/scale.bmp"); - while (gui.running && !quit) { /* Input */ MSG msg; @@ -485,8 +446,7 @@ WinMain(HINSTANCE hInstance, HINSTANCE prev, LPSTR lpCmdLine, int shown) /* Draw */ surface_begin(xw.backbuffer); surface_clear(xw.backbuffer, 100, 100, 100); - draw(xw.backbuffer, &gui.background); - draw(xw.backbuffer, &gui.floating); + draw(xw.backbuffer, &gui.stack); surface_end(xw.backbuffer, xw.hdc); /* Timing */ @@ -495,11 +455,6 @@ WinMain(HINSTANCE hInstance, HINSTANCE prev, LPSTR lpCmdLine, int shown) } free(gui.memory.memory); - DeleteObject(gui.images.select); - DeleteObject(gui.images.paint); - DeleteObject(gui.images.move); - DeleteObject(gui.images.rotate); - DeleteObject(gui.images.scale); font_del(xw.font); surface_del(xw.backbuffer); ReleaseDC(xw.hWnd, xw.hdc); diff --git a/demo/xlib.c b/demo/xlib.c new file mode 100644 index 0000000..64aa107 --- /dev/null +++ b/demo/xlib.c @@ -0,0 +1,484 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../gui.h" + +/* macros */ +#define DTIME 16 +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) < (b) ? (b) : (a)) +#define CLAMP(i,v,x) (MAX(MIN(v,x), i)) +#define LEN(a) (sizeof(a)/sizeof(a)[0]) +#define UNUSED(a) ((void)(a)) + +#include "demo.c" + +typedef struct XFont XFont; +typedef struct XSurface XSurface; +typedef struct XWindow XWindow; + +struct XFont { + int ascent; + int descent; + int height; + XFontSet set; + XFontStruct *xfont; +}; + +struct XSurface { + GC gc; + Display *dpy; + int screen; + Window root; + Drawable drawable; + unsigned int w, h; + gui_size clip_depth; +}; + +struct XWindow { + Display *dpy; + Window root; + Visual *vis; + XFont *font; + XSurface *surf; + Colormap cmap; + XWindowAttributes attr; + XSetWindowAttributes swa; + Window win; + int screen; + 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); + 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)); +} + +static XFont* +font_create(Display *dpy, const char *name) +{ + int n; + char *def, **missing; + XFont *font = xcalloc(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"))) + die("error, cannot load font: '%s'\n", name); + font->ascent = font->xfont->ascent; + font->descent = font->xfont->descent; + } + font->height = font->ascent + font->descent; + return font; +} + +static gui_size +font_get_text_width(void *handle, const gui_char *text, gui_size len) +{ + XFont *font = handle; + XRectangle r; + gui_size width; + if(!font || !text) + return 0; + + if(font->set) { + XmbTextExtents(font->set, (const char*)text, (int)len, NULL, &r); + return r.width; + } + else { + return (gui_size)XTextWidth(font->xfont, (const char*)text, (int)len); + } + return width; +} + +static void +font_del(Display *dpy, XFont *font) +{ + if(!font) return; + if(font->set) + XFreeFontSet(dpy, font->set); + else + XFreeFont(dpy, font->xfont); + free(font); +} + +static unsigned long +color_from_byte(struct gui_color col) +{ + /* NOTE(vurtun): this only works for little-endian */ + unsigned long res = 0; + res |= (unsigned long)col.r << 16; + res |= (unsigned long)col.g << 8; + res |= (unsigned long)col.b << 0; + return (res); +} + +static XSurface* +surface_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) +{ + XSurface *surface = xcalloc(1, sizeof(XSurface)); + surface->w = w; + surface->h = h; + surface->dpy = dpy; + surface->screen = screen; + surface->root = root; + surface->gc = XCreateGC(dpy, root, 0, NULL); + XSetLineAttributes(dpy, surface->gc, 1, LineSolid, CapButt, JoinMiter); + surface->drawable = XCreatePixmap(dpy, root, w, h, (unsigned int)DefaultDepth(dpy, screen)); + return surface; +} + +static void +surface_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 +surface_scissor(XSurface *surf, float x, float y, float w, float h) +{ + XRectangle clip_rect; + clip_rect.x = (short)x; + clip_rect.y = (short)y; + clip_rect.width = (unsigned short)w; + clip_rect.height = (unsigned short)h; + clip_rect.width = (unsigned short)MIN(surf->w, clip_rect.width); + clip_rect.height = (unsigned short)MIN(surf->h, clip_rect.height); + XSetClipRectangles(surf->dpy, surf->gc, 0, 0, &clip_rect, 1, Unsorted); +} + +static void +surface_draw_line(XSurface *surf, gui_short x0, gui_short y0, gui_short x1, + gui_short y1, struct gui_color col) +{ + unsigned long c = color_from_byte(col); + XSetForeground(surf->dpy, surf->gc, c); + XDrawLine(surf->dpy, surf->drawable, surf->gc, (int)x0, (int)y0, (int)x1, (int)y1); +} + +static void +surface_draw_rect(XSurface* surf, gui_short x, gui_short y, gui_ushort w, + gui_ushort h, struct gui_color col) +{ + unsigned long c = color_from_byte(col); + XSetForeground(surf->dpy, surf->gc, c); + XFillRectangle(surf->dpy, surf->drawable, surf->gc, x, y, w, h); +} + +static void +surface_draw_triangle(XSurface *surf, gui_short x0, gui_short y0, gui_short x1, + gui_short y1, gui_short x2, gui_short y2, struct gui_color col) +{ + XPoint pnts[3]; + unsigned long c = color_from_byte(col); + 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 +surface_draw_circle(XSurface *surf, gui_short x, gui_short y, gui_ushort w, + gui_ushort h, struct gui_color col) +{ + unsigned long c = color_from_byte(col); + 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 +surface_draw_text(XSurface *surf, gui_short x, gui_short y, gui_ushort w, gui_ushort h, + const char *text, size_t len, XFont *font, struct gui_color cbg, struct gui_color cfg) +{ + int i, tx, ty, th, olen; + unsigned long bg = color_from_byte(cbg); + unsigned long fg = color_from_byte(cfg); + + 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; + th = font->ascent + font->descent; + ty = (int)y + ((int)h / 2) - (th / 2) + 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 +surface_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 +surface_blit(Drawable target, XSurface *surf, unsigned int width, unsigned int height) +{ + XCopyArea(surf->dpy, surf->drawable, target, surf->gc, 0, 0, width, height, 0, 0); +} + +static void +surface_del(XSurface *surf) +{ + XFreePixmap(surf->dpy, surf->drawable); + XFreeGC(surf->dpy, surf->gc); + free(surf); +} + +static void +execute(XSurface *surf, struct gui_command_list *list) +{ + const struct gui_command *cmd; + gui_list_for_each(cmd, list) { + switch (cmd->type) { + case GUI_COMMAND_NOP: break; + case GUI_COMMAND_SCISSOR: { + const struct gui_command_scissor *s = GUI_FETCH(scissor, cmd); + surface_scissor(surf, s->x, s->y, s->w, s->h); + } break; + case GUI_COMMAND_LINE: { + const struct gui_command_line *l = GUI_FETCH(line, cmd); + surface_draw_line(surf, l->begin[0], l->begin[1], l->end[0], + l->end[1], l->color); + } break; + case GUI_COMMAND_RECT: { + const struct gui_command_rect *r = GUI_FETCH(rect, cmd); + surface_draw_rect(surf, r->x, r->y, r->w, r->h, r->color); + } break; + case GUI_COMMAND_CIRCLE: { + const struct gui_command_circle *c = GUI_FETCH(circle, cmd); + surface_draw_circle(surf, c->x, c->y, c->w, c->h, c->color); + } break; + case GUI_COMMAND_TRIANGLE: { + const struct gui_command_triangle *t = GUI_FETCH(triangle, cmd); + surface_draw_triangle(surf, t->a[0], t->a[1], t->b[0], t->b[1], + t->c[0], t->c[1], t->color); + } break; + case GUI_COMMAND_TEXT: { + const struct gui_command_text *t = GUI_FETCH(text, cmd); + surface_draw_text(surf, t->x, t->y, t->w, t->h, (const char*)t->string, + t->length, t->font, t->bg, t->fg); + } break; + case GUI_COMMAND_IMAGE: + case GUI_COMMAND_MAX: + default: break; + } + } +} + +static void +draw(XSurface *surf, struct gui_panel_stack *stack) +{ + struct gui_panel_hook *iter; + gui_stack_for_each(iter, stack) + execute(surf, gui_hook_output(iter)); +} + +static void +key(struct XWindow *xw, struct gui_input *in, XEvent *evt, gui_bool down) +{ + int ret; + KeySym *code = XGetKeyboardMapping(xw->dpy, (KeyCode)evt->xkey.keycode, 1, &ret); + if (*code == XK_Control_L || *code == XK_Control_R) + gui_input_key(in, GUI_KEY_CTRL, down); + else if (*code == XK_Shift_L || *code == XK_Shift_R) + gui_input_key(in, GUI_KEY_SHIFT, down); + else if (*code == XK_Delete) + gui_input_key(in, GUI_KEY_DEL, down); + else if (*code == XK_Return) + gui_input_key(in, GUI_KEY_ENTER, down); + else if (*code == XK_space) + gui_input_key(in, GUI_KEY_SPACE, down); + else if (*code == XK_BackSpace) + gui_input_key(in, GUI_KEY_BACKSPACE, down); + else if (*code > 32 && *code < 128 && !down) { + gui_glyph glyph; + glyph[0] = (gui_char)*code; + gui_input_char(in, glyph); + } + XFree(code); +} + +static void +motion(struct gui_input *in, XEvent *evt) +{ + const gui_int x = evt->xmotion.x; + const gui_int y = evt->xmotion.y; + gui_input_motion(in, x, y); +} + +static void +btn(struct gui_input *in, XEvent *evt, gui_bool down) +{ + const gui_int x = evt->xbutton.x; + const gui_int y = evt->xbutton.y; + if (evt->xbutton.button == Button1) + gui_input_button(in, x, y, down); +} + +static void +resize(struct XWindow *xw, XSurface *surf) +{ + XGetWindowAttributes(xw->dpy, xw->win, &xw->attr); + xw->width = (unsigned int)xw->attr.width; + xw->height = (unsigned int)xw->attr.height; + surface_resize(surf, xw->width, xw->height); +} + +int +main(int argc, char *argv[]) +{ + long dt; + long started; + XWindow xw; + + struct gui_input in; + struct gui_font font; + struct demo_gui gui; + + /* Platform */ + UNUSED(argc); UNUSED(argv); + memset(&xw, 0, sizeof xw); + xw.dpy = XOpenDisplay(NULL); + 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 | PointerMotionMask; + 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; + xw.surf = surface_create(xw.dpy, xw.screen, xw.win, xw.width, xw.height); + xw.font = font_create(xw.dpy, "fixed"); + + /* GUI */ + memset(&in, 0, sizeof in); + memset(&gui, 0, sizeof gui); + font.userdata = xw.font; + font.height = (gui_float)xw.font->height; + font.width = font_get_text_width; + init_demo(&gui, &font); + + while (gui.running) { + /* Input */ + XEvent evt; + started = timestamp(); + gui_input_begin(&in); + while (XCheckWindowEvent(xw.dpy, xw.win, xw.swa.event_mask, &evt)) { + if (evt.type == KeyPress) key(&xw, &in, &evt, gui_true); + else if (evt.type == KeyRelease) key(&xw, &in, &evt, gui_false); + else if (evt.type == ButtonPress) btn(&in, &evt, gui_true); + else if (evt.type == ButtonRelease) btn(&in, &evt, gui_false); + else if (evt.type == MotionNotify) motion(&in, &evt); + else if (evt.type == Expose || evt.type == ConfigureNotify) + resize(&xw, xw.surf); + } + gui_input_end(&in); + + /* GUI */ + gui.width = xw.width; + gui.height = xw.height; + run_demo(&gui, &in); + + /* Draw */ + XClearWindow(xw.dpy, xw.win); + surface_clear(xw.surf, 0x00646464); + draw(xw.surf, &gui.stack); + surface_blit(xw.win, xw.surf, xw.width, xw.height); + XFlush(xw.dpy); + + /* Timing */ + dt = timestamp() - started; + if (dt < DTIME) + sleep_for(DTIME - dt); + } + +cleanup: + free(gui.memory.memory); + font_del(xw.dpy, xw.font); + surface_del(xw.surf); + XUnmapWindow(xw.dpy, xw.win); + XFreeColormap(xw.dpy, xw.cmap); + XDestroyWindow(xw.dpy, xw.win); + XCloseDisplay(xw.dpy); + return 0; +} +