ltk

Socket-based GUI for X11 (WIP)
git clone git://lumidify.org/ltk.git (fast, but not encrypted)
git clone https://lumidify.org/git/ltk.git (encrypted, but very slow)
Log | Files | Refs | README | LICENSE

commit dfedb3c646b7a664329ae1453e58d07451f9c200
parent 8427dcdb18de419d9d158dfa8eeedefe5baafc61
Author: lumidify <nobody@lumidify.org>
Date:   Wed,  3 Jun 2020 21:44:17 +0200

Rework command system

Diffstat:
MLICENSE | 3+++
MMakefile | 8+++++---
MREADME.md | 8++++++++
Mbutton.c | 62+++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Mbutton.h | 9+++++----
Mgrid.c | 312+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Mgrid.h | 79++-----------------------------------------------------------------------------
Mltk.c | 147+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Mltk.h | 22++++++++++++++++++++--
Astrtonum.c | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest.gui | 14++++++++++----
Autil.h | 5+++++
12 files changed, 557 insertions(+), 178 deletions(-)

diff --git a/LICENSE b/LICENSE @@ -1,3 +1,6 @@ +See khash.h, ini.*, and strtonum.c for third-party licenses. + + MIT/X Consortium License The Lumidify ToolKit (LTK) diff --git a/Makefile b/Makefile @@ -2,9 +2,11 @@ LIBS = -lm `pkg-config --libs x11` STD = -std=c99 CFLAGS = -g -w -fcommon -Wall -Werror -Wextra `pkg-config --cflags x11` -pedantic OBJ = ltk.o ini.o grid.o button.o +COMPATOBJ = +#COMPATOBJ = strtonum.o -ltk: $(OBJ) - $(CC) $(STD) -o $@ $(OBJ) $(LIBS) +ltk: $(OBJ) $(COMPATOBJ) + $(CC) $(STD) -o $@ $(OBJ) $(COMPATOBJ) $(LIBS) %.o: %.c $(CC) -c -o $@ $< $(CFLAGS) @@ -12,4 +14,4 @@ ltk: $(OBJ) .PHONY: clean clean: - rm -f $(OBJ) ltk ltk_in + rm -f $(OBJ) ltk *.core diff --git a/README.md b/README.md @@ -1 +1,9 @@ Not much to see here. + +To test: + +make +./ltk < test.gui + +Note: you need to uncomment "COMPATOBJ = strtonum.c" in Makefile +if you're not using OpenBSD. diff --git a/button.c b/button.c @@ -1,6 +1,6 @@ /* * This file is part of the Lumidify ToolKit (LTK) - * Copyright (c) 2016, 2017, 2018 lumidify <nobody@lumidify.org> + * Copyright (c) 2016, 2017, 2018, 2020 lumidify <nobody@lumidify.org> * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -26,10 +26,22 @@ #include <string.h> #include <X11/Xlib.h> #include <X11/Xutil.h> +#include "util.h" #include "khash.h" #include "ltk.h" #include "button.h" +static void ltk_button_draw(ltk_button *button); +static void ltk_button_mouse_release(ltk_button *button, XEvent event); +static ltk_button *ltk_button_create(ltk_window *window, + const char *id, const char *text); +static void ltk_button_destroy(ltk_button *button, int shallow); + +static void ltk_grid_cmd_create( + ltk_window *window, + char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH], + size_t num_tokens); + void ltk_button_ini_handler(ltk_window *window, const char *prop, const char *value) { ltk_theme *theme = window->theme; @@ -64,7 +76,7 @@ ltk_button_ini_handler(ltk_window *window, const char *prop, const char *value) } } -void +static void ltk_button_draw(ltk_button *button) { ltk_window *window = button->widget.window; ltk_button_theme *theme = window->theme->button; @@ -102,30 +114,66 @@ ltk_button_draw(ltk_button *button) { } -void +static void ltk_button_mouse_release(ltk_button *button, XEvent event) { ltk_queue_event(button->widget.window, button->widget.id, "button_click"); } -ltk_button * +static ltk_button * ltk_button_create(ltk_window *window, const char *id, const char *text) { ltk_button *button = malloc(sizeof(ltk_button)); if (!button) ltk_fatal("ERROR: Unable to allocate memory for ltk_button.\n"); - ltk_fill_widget_defaults(&button->widget, id, window, &ltk_button_draw, &ltk_button_destroy, 1); + ltk_fill_widget_defaults(&button->widget, id, window, + &ltk_button_draw, &ltk_button_destroy, 1, LTK_BUTTON); button->widget.mouse_release = &ltk_button_mouse_release; button->text = strdup(text); return button; } -void -ltk_button_destroy(ltk_button *button) { +static void +ltk_button_destroy(ltk_button *button, int shallow) { if (!button) { (void)printf("WARNING: Tried to destroy NULL button.\n"); return; } free(button->text); + ltk_remove_widget(button->widget.window, button->widget.id); free(button->widget.id); free(button); } + +/* button <button id> create */ +static void +ltk_button_cmd_create( + ltk_window *window, + char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH], + size_t num_tokens) { + ltk_button *button; + if (num_tokens != 3) { + (void)fprintf(stderr, "button create: Invalid number of arguments.\n"); + return; + } + if (!ltk_check_widget_id_free(window, tokens[1], "button create")) + return; + button = ltk_button_create(window, tokens[1], "tmp"); + ltk_set_widget(window, button, tokens[1]); +} + +/* button <button id> <command> ... */ +void +ltk_button_cmd( + ltk_window *window, + char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH], + size_t num_tokens) { + if (num_tokens < 3) { + (void)fprintf(stderr, "button: Invalid number of arguments.\n"); + return; + } + if (strcmp(tokens[2], "create") == 0) { + ltk_button_cmd_create(window, tokens, num_tokens); + } else { + (void)fprintf(stderr, "button: Invalid command.\n"); + } +} diff --git a/button.h b/button.h @@ -49,10 +49,11 @@ typedef struct ltk_button_theme { XColor fill_disabled; } ltk_button_theme; -void ltk_button_draw(ltk_button *button); +void ltk_button_ini_handler(ltk_window *window, const char *prop, const char *value); -ltk_button *ltk_button_create(ltk_window *window, const char *id, const char *text); - -void ltk_button_destroy(ltk_button *button); +void ltk_button_cmd( + ltk_window *window, + char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH], + size_t num_tokens); #endif diff --git a/grid.c b/grid.c @@ -28,21 +28,62 @@ #include <stdlib.h> #include <X11/Xlib.h> #include <X11/Xutil.h> +#include "util.h" #include "khash.h" #include "ltk.h" #include "grid.h" -void ltk_set_row_weight(ltk_grid * grid, int row, int weight) { +static void ltk_grid_set_row_weight(ltk_grid *grid, int row, int weight); +static void ltk_grid_set_column_weight(ltk_grid *grid, int column, int weight); +static void ltk_grid_draw(ltk_grid *grid); +static ltk_grid *ltk_grid_create(ltk_window *window, const char *id, + int rows, int columns); +static void ltk_grid_destroy(ltk_grid *grid, int shallow); +static void ltk_recalculate_grid(ltk_grid *grid); +static void ltk_grid_add(ltk_window *window, ltk_widget *widget, ltk_grid *grid, + int row, int column, int row_span, int column_span, unsigned short sticky); +static void ltk_grid_ungrid(ltk_window *window, ltk_widget *widget, ltk_grid *grid); +static int ltk_grid_find_nearest_column(ltk_grid *grid, int x); +static int ltk_grid_find_nearest_row(ltk_grid *grid, int y); +static void ltk_grid_mouse_press(ltk_grid *grid, XEvent event); +static void ltk_grid_mouse_release(ltk_grid *grid, XEvent event); +static void ltk_grid_motion_notify(ltk_grid *grid, XEvent event); + +static void ltk_grid_cmd_add( + ltk_window *window, + char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH], + size_t num_tokens); +static void ltk_grid_cmd_ungrid( + ltk_window *window, + char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH], + size_t num_tokens); +static void ltk_grid_cmd_create( + ltk_window *window, + char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH], + size_t num_tokens); +static void ltk_grid_cmd_set_row_weight( + ltk_window *window, + char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH], + size_t num_tokens); +static void ltk_grid_cmd_set_column_weight( + ltk_window *window, + char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH], + size_t num_tokens); + +static void +ltk_grid_set_row_weight(ltk_grid *grid, int row, int weight) { grid->row_weights[row] = weight; ltk_recalculate_grid(grid); } -void ltk_set_column_weight(ltk_grid * grid, int column, int weight) { +static void +ltk_grid_set_column_weight(ltk_grid *grid, int column, int weight) { grid->column_weights[column] = weight; ltk_recalculate_grid(grid); } -void ltk_draw_grid(ltk_grid *grid) { +static void +ltk_grid_draw(ltk_grid *grid) { int i; for (i = 0; i < grid->rows * grid->columns; i++) { if (!grid->widget_grid[i]) @@ -52,10 +93,12 @@ void ltk_draw_grid(ltk_grid *grid) { } } -ltk_grid *ltk_create_grid(ltk_window *window, const char *id, int rows, int columns) { +static ltk_grid * +ltk_grid_create(ltk_window *window, const char *id, int rows, int columns) { ltk_grid *grid = malloc(sizeof(ltk_grid)); - ltk_fill_widget_defaults(&grid->widget, id, window, &ltk_draw_grid, &ltk_destroy_grid, 0); + ltk_fill_widget_defaults(&grid->widget, id, window, &ltk_grid_draw, + &ltk_grid_destroy, 0, LTK_GRID); grid->widget.mouse_press = &ltk_grid_mouse_press; grid->widget.mouse_release = &ltk_grid_mouse_release; grid->widget.motion_notify = &ltk_grid_motion_notify; @@ -92,13 +135,22 @@ ltk_grid *ltk_create_grid(ltk_window *window, const char *id, int rows, int colu return grid; } -void ltk_destroy_grid(ltk_grid *grid) { +static void +ltk_grid_destroy(ltk_grid *grid, int shallow) { ltk_widget *ptr; - int i; - for (i = 0; i < grid->rows * grid->columns; i++) { - if (grid->widget_grid[i]) { - ptr = grid->widget_grid[i]; - ptr->destroy(ptr); + if (!shallow) { + for (int i = 0; i < grid->rows * grid->columns; i++) { + if (grid->widget_grid[i]) { + ptr = grid->widget_grid[i]; + /* required to avoid freeing a widget multiple times + if row_span or column_span is not 1 */ + for (int r = ptr->row; r < ptr->row + ptr->row_span; r++) { + for (int c = ptr->column; c < ptr->column + ptr->column_span; c++) { + grid->widget_grid[r * grid->columns + c] = NULL; + } + } + ptr->destroy(ptr, shallow); + } } } free(grid->widget_grid); @@ -108,10 +160,13 @@ void ltk_destroy_grid(ltk_grid *grid) { free(grid->column_weights); free(grid->row_pos); free(grid->column_pos); + ltk_remove_widget(grid->widget.window, grid->widget.id); + free(grid->widget.id); free(grid); } -void ltk_recalculate_grid(ltk_grid *grid) { +static void +ltk_recalculate_grid(ltk_grid *grid) { unsigned int height_static = 0, width_static = 0; unsigned int total_row_weight = 0, total_column_weight = 0; float height_unit = 0, width_unit = 0; @@ -155,10 +210,11 @@ void ltk_recalculate_grid(ltk_grid *grid) { int end_column, end_row; for (i = 0; i < grid->rows; i++) { for (j = 0; j < grid->columns; j++) { - if (!grid->widget_grid[i * grid->columns + j]) { + if (!grid->widget_grid[i * grid->columns + j]) continue; - } ltk_widget *ptr = grid->widget_grid[i * grid->columns + j]; + if (ptr->row != i || ptr->column != j) + continue; orig_width = ptr->rect.w; orig_height = ptr->rect.h; end_row = i + ptr->row_span; @@ -194,8 +250,10 @@ void ltk_recalculate_grid(ltk_grid *grid) { } } -void ltk_grid_widget(ltk_widget *widget, ltk_grid *grid, int row, int column, int row_span, int column_span, unsigned short sticky) { - if (row >= grid->rows || column >= grid->columns) { +static void +ltk_grid_add(ltk_window *window, ltk_widget *widget, ltk_grid *grid, + int row, int column, int row_span, int column_span, unsigned short sticky) { + if (row + row_span > grid->rows || column + column_span > grid->columns) { (void)fprintf(stderr, "Invalid row or column.\n"); return; } @@ -210,12 +268,33 @@ void ltk_grid_widget(ltk_widget *widget, ltk_grid *grid, int row, int column, in if (grid->row_weights[row] == 0 && widget->rect.h > grid->row_heights[row]) { grid->row_heights[row] = widget->rect.h; } - grid->widget_grid[widget->row * grid->columns + widget->column] = widget; + for (int i = row; i < row + row_span; i++) { + for (int j = column; j < column + column_span; j++) { + grid->widget_grid[i * grid->columns + j] = widget; + } + } widget->parent = grid; ltk_recalculate_grid(grid); + ltk_window_invalidate_rect(window, grid->widget.rect); +} + +static void +ltk_grid_ungrid(ltk_window *window, ltk_widget *widget, ltk_grid *grid) { + if (widget->parent != grid) { + (void)fprintf(stderr, "grid remove: Widget isn't gridded in given grid.\n"); + return; + } + widget->parent = NULL; + for (int i = widget->row; i < widget->row + widget->row_span; i++) { + for (int j = widget->column; j < widget->column + widget->column_span; j++) { + grid->widget_grid[i * grid->columns + j] = NULL; + } + } + ltk_window_invalidate_rect(window, grid->widget.rect); } -static int ltk_grid_find_nearest_column(ltk_grid *grid, int x) { +static int +ltk_grid_find_nearest_column(ltk_grid *grid, int x) { int i; for (i = 0; i < grid->columns; i++) { if (grid->column_pos[i] <= x && grid->column_pos[i + 1] >= x) { @@ -225,7 +304,8 @@ static int ltk_grid_find_nearest_column(ltk_grid *grid, int x) { return -1; } -static int ltk_grid_find_nearest_row(ltk_grid *grid, int y) { +static int +ltk_grid_find_nearest_row(ltk_grid *grid, int y) { int i; for (i = 0; i < grid->rows; i++) { if (grid->row_pos[i] <= y && grid->row_pos[i + 1] >= y) { @@ -235,7 +315,8 @@ static int ltk_grid_find_nearest_row(ltk_grid *grid, int y) { return -1; } -void ltk_grid_mouse_press(ltk_grid *grid, XEvent event) { +static void +ltk_grid_mouse_press(ltk_grid *grid, XEvent event) { int x = event.xbutton.x; int y = event.xbutton.y; int row = ltk_grid_find_nearest_row(grid, y); @@ -247,7 +328,8 @@ void ltk_grid_mouse_press(ltk_grid *grid, XEvent event) { ltk_widget_mouse_press_event(ptr, event); } -void ltk_grid_mouse_release(ltk_grid *grid, XEvent event) { +static void +ltk_grid_mouse_release(ltk_grid *grid, XEvent event) { int x = event.xbutton.x; int y = event.xbutton.y; int row = ltk_grid_find_nearest_row(grid, y); @@ -259,7 +341,8 @@ void ltk_grid_mouse_release(ltk_grid *grid, XEvent event) { ltk_widget_mouse_release_event(ptr, event); } -void ltk_grid_motion_notify(ltk_grid *grid, XEvent event) { +static void +ltk_grid_motion_notify(ltk_grid *grid, XEvent event) { short pressed = (event.xmotion.state & Button1Mask) == Button1Mask; if (pressed) return; @@ -273,3 +356,188 @@ void ltk_grid_motion_notify(ltk_grid *grid, XEvent event) { if (ptr && ltk_collide_rect(ptr->rect, x, y)) ltk_widget_motion_notify_event(ptr, event); } + +/* grid <grid id> add <widget id> <row> <column> <row_span> <column_span> <sticky> */ +static void +ltk_grid_cmd_add( + ltk_window *window, + char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH], + size_t num_tokens) { + ltk_grid *grid; + ltk_widget *widget; + const char *errstr; + + int row, column, row_span, column_span, sticky; + if (num_tokens != 9) { + (void)fprintf(stderr, "grid: Invalid number of arguments.\n"); + return; + } + grid = ltk_get_widget(window, tokens[1], LTK_GRID, "grid add"); + widget = ltk_get_widget(window, tokens[3], LTK_WIDGET, "grid add"); + if (!grid || !widget) return; + row = strtonum(tokens[4], 0, grid->rows - 1, &errstr); + if (errstr) { + (void)fprintf("grid add: Invalid row number: %s\n", errstr); + free(errstr); + return; + } + column = strtonum(tokens[5], 0, grid->columns - 1, &errstr); + if (errstr) { + (void)fprintf("grid add: Invalid column number: %s\n", errstr); + free(errstr); + return; + } + row_span = strtonum(tokens[6], 1, grid->rows, &errstr); + if (errstr) { + (void)fprintf("grid add: Invalid row_span: %s\n", errstr); + free(errstr); + return; + } + column_span = strtonum(tokens[7], 1, grid->columns, &errstr); + if (errstr) { + (void)fprintf("grid add: Invalid column_span: %s\n", errstr); + free(errstr); + return; + } + sticky = strtonum(tokens[8], 0, 15, &errstr); + if (errstr) { + (void)fprintf("grid add: Invalid sticky setting: %s\n", errstr); + free(errstr); + return; + } + ltk_grid_add(window, widget, grid, row, column, row_span, column_span, sticky); +} + +/* grid <grid id> remove <widget id> */ +static void +ltk_grid_cmd_ungrid( + ltk_window *window, + char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH], + size_t num_tokens) { + ltk_grid *grid; + ltk_widget *widget; + if (num_tokens != 4) { + (void)fprintf(stderr, "grid ungrid: Invalid number of arguments.\n"); + return; + } + grid = ltk_get_widget(window, tokens[1], LTK_GRID, "grid remove"); + widget = ltk_get_widget(window, tokens[3], LTK_WIDGET, "grid ungrid"); + if (!grid || !widget) return; + ltk_grid_ungrid(window, widget, grid); +} + +/* grid <grid id> create <rows> <columns> */ +static void +ltk_grid_cmd_create( + ltk_window *window, + char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH], + size_t num_tokens) { + int rows, columns; + ltk_grid *grid; + const char *errstr; + if (num_tokens != 5) { + (void)fprintf(stderr, "grid create: Invalid number of arguments.\n"); + return; + } + if (!ltk_check_widget_id_free(window, tokens[1], "grid create")) + return; + rows = strtonum(tokens[3], 1, 64, &errstr); + if (errstr) { + (void)fprintf("grid create: Invalid number of rows: %s\n ", errstr); + free(errstr); + return; + } + columns = strtonum(tokens[4], 1, 64, &errstr); + if (errstr) { + (void)fprintf("grid create: Invalid number of columns: %s\n ", errstr); + free(errstr); + return; + } + grid = ltk_grid_create(window, tokens[1], rows, columns); + ltk_set_widget(window, grid, tokens[1]); +} + +/* grid <grid id> set-row-weight <row> <weight> */ +static void +ltk_grid_cmd_set_row_weight( + ltk_window *window, + char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH], + size_t num_tokens) { + ltk_grid *grid; + int row, weight; + const char *errstr; + if (num_tokens != 5) { + (void)fprintf(stderr, "grid set-row-weight: Invalid number of arguments.\n"); + return; + } + grid = ltk_get_widget(window, tokens[1], LTK_GRID, "grid set-row-weight"); + if (!grid) return; + row = strtonum(tokens[3], 0, grid->rows, &errstr); + if (errstr) { + (void)fprintf("grid set-row-weight: Invalid row number: %s\n ", errstr); + free(errstr); + return; + } + weight = strtonum(tokens[4], 0, 64, &errstr); + if (errstr) { + (void)fprintf("grid set-row-weight: Invalid weight: %s\n ", errstr); + free(errstr); + return; + } + ltk_grid_set_row_weight(grid, row, weight); +} + +/* grid <grid id> set-column-weight <column> <weight> */ +static void +ltk_grid_cmd_set_column_weight( + ltk_window *window, + char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH], + size_t num_tokens) { + ltk_grid *grid; + int column, weight; + const char *errstr; + if (num_tokens != 5) { + (void)fprintf(stderr, "grid set-column-weight: Invalid number of arguments.\n"); + return; + } + grid = ltk_get_widget(window, tokens[1], LTK_GRID, "grid set-column-weight"); + if (!grid) return; + column = strtonum(tokens[3], 0, grid->columns, &errstr); + if (errstr) { + (void)fprintf("grid set-column-weight: Invalid column number: %s\n ", errstr); + free(errstr); + return; + } + weight = strtonum(tokens[4], 0, 64, &errstr); + if (errstr) { + (void)fprintf("grid set-column-weight: Invalid weight: %s\n ", errstr); + free(errstr); + return; + } + ltk_grid_set_column_weight(grid, column, weight); +} + +/* grid <grid id> <command> ... */ +void +ltk_grid_cmd( + ltk_window *window, + char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH], + size_t num_tokens) { + if (num_tokens < 3) { + (void)fprintf(stderr, "grid: Invalid number of arguments.\n"); + return; + } + if (strcmp(tokens[2], "add") == 0) { + ltk_grid_cmd_add(window, tokens, num_tokens); + } else if (strcmp(tokens[2], "ungrid") == 0) { + ltk_grid_cmd_ungrid(window, tokens, num_tokens); + } else if (strcmp(tokens[2], "create") == 0) { + ltk_grid_cmd_create(window, tokens, num_tokens); + } else if (strcmp(tokens[2], "set-row-weight") == 0) { + ltk_grid_cmd_set_row_weight(window, tokens, num_tokens); + } else if (strcmp(tokens[2], "set-column-weight") == 0) { + ltk_grid_cmd_set_column_weight(window, tokens, num_tokens); + } else { + (void)fprintf(stderr, "button: Invalid command.\n"); + } +} diff --git a/grid.h b/grid.h @@ -24,7 +24,7 @@ #ifndef _LTK_GRID_H_ #define _LTK_GRID_H_ -/* Requires the following includes: <X11/Xlib.h>, "ltk.h" */ +/* Requires the following includes: "ltk.h" */ /* * Struct to represent a grid widget. @@ -42,81 +42,6 @@ typedef struct { unsigned int *column_pos; } ltk_grid; -/* - * Set the weight of a row in a grid. - * grid: The grid. - * row: The row. - * weight: The weight to set the row to. - */ -void ltk_set_row_weight(ltk_grid *grid, int row, int weight); - -/* - * Set the weight of a column in a grid. - * grid: The grid. - * column: The column. - * weight: The weight to set the row to. - */ -void ltk_set_column_weight(ltk_grid *grid, int column, int weight); - -/* - * Draw all the widgets in a grid. - * grid: The grid to draw the widgets of. - */ -void ltk_draw_grid(ltk_grid *grid); - -/* - * Create a grid. - * window: The window the grid will displayed on. - * rows: The number of rows in the grid. - * columns: The number of columns in the grid. - */ -ltk_grid *ltk_create_grid(ltk_window *window, const char *id, int rows, int columns); - -/* - * Destroy a grid. - * grid: Pointer to the grid. - */ -void ltk_destroy_grid(ltk_grid *grid); - -/* - * Recalculate the positions and dimensions of the - * columns, rows, and widgets in a grid. - * grid: Pointer to the grid. - */ -void ltk_recalculate_grid(ltk_grid *grid); - -/* - * Grid a widget. - * widget: Pointer to the widget. - * grid: The grid. - * row: The row to grid the widget in. - * column: The column to grid the widget in. - * rowspan: The amount of rows the widget should span. - * columnspan: The amount of columns the widget should span. - * sticky: Mask of the sticky values (LTK_STICKY_*). - */ -void ltk_grid_widget(ltk_widget *widget, ltk_grid *grid, int row, int column, - int rowspan, int columnspan, unsigned short sticky); - -/* - * Delegate a mouse press event on the grid to the proper widget. - * grid: The grid. - * event: The event to be handled. - */ -void ltk_grid_mouse_press(ltk_grid *grid, XEvent event); - -/* - * Delegate a mouse release event on the grid to the proper widget. - * grid: The grid. - * event: The event to be handled. - */ -void ltk_grid_mouse_release(ltk_grid *grid, XEvent event); - -/* - * Delegate a mouse motion event on the grid to the proper widget. - * grid: The grid. - * event: The event to be handled. - */ -void ltk_grid_motion_notify(ltk_grid *grid, XEvent event); +void ltk_grid_cmd(ltk_window *window, char tokens[10][20], size_t num_tokens); #endif diff --git a/ltk.c b/ltk.c @@ -31,6 +31,7 @@ #include <sys/select.h> #include <X11/Xlib.h> #include <X11/Xutil.h> +#include "util.h" #include "khash.h" #include "ini.h" #include "ltk.h" @@ -105,50 +106,25 @@ ltk_queue_event(ltk_window *window, const char *id, const char *name) { } static void -create_widget(ltk_window *window, char tokens[10][20], size_t num_tokens) { - if (num_tokens < 3) { - (void)fprintf(stderr, "Invalid number of arguments.\n"); - return; - } +ltk_set_root_widget_cmd( + ltk_window *window, + char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH], + int num_tokens) { ltk_widget *widget; - khint_t k = kh_get(widget, window->widget_hash, tokens[2]); - if (k != kh_end(window->widget_hash)) { - (void)fprintf(stderr, "Widget id already exists.\n"); - return; - } - if (strcmp(tokens[1], "button") == 0) { - /* yeah, text is currently ignored... */ - widget = ltk_button_create(window, tokens[2], "I'm a button!"); - } else { - (void)fprintf(stderr, "Invalid widget type.\n"); + if (num_tokens != 2) { + (void)fprintf(stderr, "set-root-widget: Invalid number of arguments.\n"); return; } - int ret; - /* apparently, khash requires the string to stay accessible */ - /* FIXME: actually free this hash table in the end... */ - char *tmp = strdup(tokens[2]); - k = kh_put(widget, window->widget_hash, tmp, &ret); - kh_value(window->widget_hash, k) = widget; -} - -static void -grid_widget(ltk_window *window, char tokens[10][20], size_t num_tokens) { - if (num_tokens < 4) { - (void)fprintf(stderr, "Invalid number of arguments.\n"); - return; - } - ltk_widget *widget; - khint_t k = kh_get(widget, window->widget_hash, tokens[1]); - if (k == kh_end(window->widget_hash)) { - (void)fprintf(stderr, "Widget with given id doesn't exist.\n"); - return; + widget = ltk_get_widget(window, tokens[1], LTK_WIDGET, "set-root-widget"); + if (!widget) return; + window->root_widget = widget; + int w = widget->rect.w; + int h = widget->rect.h; + widget->rect.w = window->rect.w; + widget->rect.h = window->rect.h; + if (widget->resize) { + widget->resize(widget, w, h); } - widget = kh_value(window->widget_hash, k); - /* FIXME: error checking */ - int row = atoi(tokens[2]); - int column = atoi(tokens[3]); - ltk_grid_widget(widget, window->root_widget, row, column, 1, 1, LTK_STICKY_LEFT | LTK_STICKY_RIGHT); - ltk_window_invalidate_rect(window, window->root_widget->rect); } /* copied from suckless ii */ @@ -167,7 +143,7 @@ read_line(int fd, char *buf, size_t bufsiz) } static size_t -tokenize(char tokens[10][20], char *buf) { +tokenize(char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH], char *buf) { char *c; if (!buf || buf[0] == '\0') return 0; for (c = buf; *c == ' '; c++) @@ -175,13 +151,13 @@ tokenize(char tokens[10][20], char *buf) { size_t cur_tok = 0; size_t cur_tok_len = 0; while (*c != '\0') { - if (cur_tok >= 10) { + if (cur_tok >= MAX_TOKENS) { return 0; } else if (*c == ' ') { tokens[cur_tok][cur_tok_len] = '\0'; cur_tok++; cur_tok_len = 0; - } else if (cur_tok_len >= 19) { + } else if (cur_tok_len >= MAX_TOKEN_LENGTH - 1) { return 0; } else { tokens[cur_tok][cur_tok_len++] = *c; @@ -194,19 +170,20 @@ tokenize(char tokens[10][20], char *buf) { static void proc_cmds(ltk_window *window, char *buf) { - char tokens[10][20]; + char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH]; int num_tokens; if (!(num_tokens = tokenize(tokens, buf))) return; - if (strcmp(tokens[0], "create") == 0) { - create_widget(window, tokens, num_tokens); - } else if (strcmp(tokens[0], "grid") == 0) { - grid_widget(window, tokens, num_tokens); + if (strcmp(tokens[0], "grid") == 0) { + ltk_grid_cmd(window, tokens, num_tokens); + } else if (strcmp(tokens[0], "button") == 0) { + ltk_button_cmd(window, tokens, num_tokens); + } else if (strcmp(tokens[0], "set-root-widget") == 0) { + ltk_set_root_widget_cmd(window, tokens, num_tokens); } else { (void)fprintf(stderr, "Invalid command.\n"); } } -/* FIXME: destroy remaining widgets in hash on exit */ int ltk_mainloop(ltk_window *window) { XEvent event; @@ -215,7 +192,7 @@ ltk_mainloop(ltk_window *window) { struct timeval tv; fd_set rfds; int fd_in = fileno(stdin); - char buf[200]; /* FIXME: what would be sensible? */ + char buf[MAX_CMD_LENGTH]; int retval; tick.tv_sec = 0; tick.tv_nsec = 10000; @@ -246,7 +223,7 @@ ltk_mainloop(ltk_window *window) { free(last); } while (cur); window->first_event = window->last_event = NULL; - } else if (retval != -1 && retval != 0 && !read_line(fd_in, buf, sizeof(buf))) { + } else if (retval > 0 && !read_line(fd_in, buf, sizeof(buf))) { proc_cmds(window, buf); } /* yes, this should be improved */ @@ -322,9 +299,17 @@ ltk_create_window(const char *theme_path, const char *title, int x, int y, unsig void ltk_destroy_window(ltk_window *window) { + khint_t k; ltk_widget *ptr = window->root_widget; - if (ptr) ptr->destroy(ptr); + if (ptr) ptr->destroy(ptr, 0); XDestroyWindow(window->dpy, window->xwindow); + for (k = kh_begin(window->widget_hash); k != kh_end(window->widget_hash); k++) { + if (kh_exist(window->widget_hash, k)) { + ptr = kh_value(window->widget_hash, k); + ptr->destroy(ptr, 1); + } + } + kh_destroy(widget, window->widget_hash); free(window); } @@ -438,11 +423,13 @@ ltk_window_set_active_widget(ltk_window *window, ltk_widget *widget) { void ltk_fill_widget_defaults(ltk_widget *widget, const char *id, ltk_window *window, - void (*draw) (void *), void (*destroy) (void *), unsigned int needs_redraw) { + void (*draw) (void *), void (*destroy) (void *, int), unsigned int needs_redraw, + ltk_widget_type type) { widget->id = strdup(id); widget->window = window; widget->active_widget = NULL; widget->parent = NULL; + widget->type = type; widget->key_press = NULL; widget->key_release = NULL; @@ -546,13 +533,55 @@ ltk_handle_event(ltk_window *window, XEvent event) { } } +int +ltk_check_widget_id_free(ltk_window *window, const char *id, const char *caller) { + khint_t k; + k = kh_get(widget, window->widget_hash, id); + if (k != kh_end(window->widget_hash)) { + (void)fprintf(stderr, "%s: Widget \"%s\" already exists.\n", caller, id); + return 0; + } + return 1; +} + +ltk_widget * +ltk_get_widget(ltk_window *window, const char *id, ltk_widget_type type, + const char *caller) { + khint_t k; + ltk_widget *widget; + k = kh_get(widget, window->widget_hash, id); + if (k == kh_end(window->widget_hash)) { + (void)fprintf(stderr, "%s: Widget \"%s\" doesn't exist.\n", caller, id); + return NULL; + } + widget = kh_value(window->widget_hash, k); + if (type != LTK_WIDGET && widget->type != type) { + (void)fprintf(stderr, "%s: Widget \"%s\" has wrong type.\n", caller, id); + return NULL; + } + return widget; +} + +void +ltk_set_widget(ltk_window *window, ltk_widget *widget, const char *id) { + int ret; + khint_t k; + /* apparently, khash requires the string to stay accessible */ + char *tmp = strdup(id); + k = kh_put(widget, window->widget_hash, tmp, &ret); + kh_value(window->widget_hash, k) = widget; +} + +void +ltk_remove_widget(ltk_window *window, const char *id) { + khint_t k; + k = kh_get(widget, window->widget_hash, id); + if (k != kh_end(window->widget_hash)) { + kh_del(widget, window->widget_hash, k); + } +} + int main(int argc, char *argv[]) { ltk_window *window = ltk_create_window("theme.ini", "Demo", 0, 0, 500, 500); - ltk_grid *grid = ltk_create_grid(window, "grid", 2, 2); - window->root_widget = grid; - ltk_set_row_weight(grid, 0, 1); - ltk_set_row_weight(grid, 1, 1); - ltk_set_column_weight(grid, 0, 1); - ltk_set_column_weight(grid, 1, 1); ltk_mainloop(window); } diff --git a/ltk.h b/ltk.h @@ -24,6 +24,10 @@ #ifndef _LTK_H_ #define _LTK_H_ +#define MAX_TOKENS 20 +#define MAX_TOKEN_LENGTH 20 +#define MAX_CMD_LENGTH 400 + /* Requires the following includes: <X11/Xlib.h>, <X11/Xutil.h>, "drw.h" */ typedef struct { @@ -47,12 +51,19 @@ typedef enum { LTK_DISABLED } ltk_widget_state; +typedef enum { + LTK_GRID, + LTK_BUTTON, + LTK_WIDGET +} ltk_widget_type; + typedef struct ltk_window ltk_window; typedef struct ltk_widget { ltk_window *window; struct ltk_widget *active_widget; struct ltk_widget *parent; + ltk_widget_type type; char *id; void (*key_press) (void *, XEvent event); @@ -65,7 +76,7 @@ typedef struct ltk_widget { void (*resize) (void *, int, int); void (*draw) (void *); - void (*destroy) (void *); + void (*destroy) (void *, int); ltk_rect rect; unsigned int row; @@ -145,7 +156,8 @@ void ltk_remove_active_widget(ltk_widget *widget); void ltk_set_active_widget(ltk_window *window, ltk_widget *widget); void ltk_fill_widget_defaults(ltk_widget *widget, const char *id, ltk_window * window, - void (*draw) (void *), void (*destroy) (void *), unsigned int needs_redraw); + void (*draw) (void *), void (*destroy) (void *, int), unsigned int needs_redraw, + ltk_widget_type type); void ltk_widget_mouse_press_event(ltk_widget *widget, XEvent event); @@ -155,4 +167,10 @@ void ltk_widget_motion_notify_event(ltk_widget *widget, XEvent event); void ltk_handle_event(ltk_window *window, XEvent event); +int ltk_check_widget_id_free(ltk_window *window, const char *id, + const char *caller); +ltk_widget *ltk_get_widget(ltk_window *window, const char *id, + ltk_widget_type type, const char *caller); +void ltk_set_widget(ltk_window *window, ltk_widget *widget, const char *id); + #endif diff --git a/strtonum.c b/strtonum.c @@ -0,0 +1,66 @@ +/* $OpenBSD: strtonum.c,v 1.8 2015/09/13 08:31:48 guenther Exp $ */ + +/* + * Copyright (c) 2004 Ted Unangst and Todd Miller + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <errno.h> +#include <limits.h> +#include <stdlib.h> + +#define INVALID 1 +#define TOOSMALL 2 +#define TOOLARGE 3 + +long long +strtonum(const char *numstr, long long minval, long long maxval, + const char **errstrp) +{ + long long ll = 0; + int error = 0; + char *ep; + struct errval { + const char *errstr; + int err; + } ev[4] = { + { NULL, 0 }, + { "invalid", EINVAL }, + { "too small", ERANGE }, + { "too large", ERANGE }, + }; + + ev[0].err = errno; + errno = 0; + if (minval > maxval) { + error = INVALID; + } else { + ll = strtoll(numstr, &ep, 10); + if (numstr == ep || *ep != '\0') + error = INVALID; + else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval) + error = TOOSMALL; + else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval) + error = TOOLARGE; + } + if (errstrp != NULL) + *errstrp = ev[error].errstr; + errno = ev[error].err; + if (error) + ll = 0; + + return (ll); +} +DEF_WEAK(strtonum); diff --git a/test.gui b/test.gui @@ -1,4 +1,10 @@ -create button btn1 -grid btn1 0 0 -create button btn2 -grid btn2 1 1 +grid grd1 create 2 2 +grid grd1 set-row-weight 0 1 +grid grd1 set-row-weight 1 1 +grid grd1 set-column-weight 0 1 +grid grd1 set-column-weight 1 1 +set-root-widget grd1 +button btn1 create +grid grd1 add btn1 0 0 1 1 0 +button btn2 create +grid grd1 add btn2 1 0 1 2 3 diff --git a/util.h b/util.h @@ -0,0 +1,5 @@ +#ifndef __OpenBSD__ +long long +strtonum(const char *numstr, long long minval, long long maxval, + const char **errstrp); +#endif