commit dfedb3c646b7a664329ae1453e58d07451f9c200
parent 8427dcdb18de419d9d158dfa8eeedefe5baafc61
Author: lumidify <nobody@lumidify.org>
Date: Wed, 3 Jun 2020 21:44:17 +0200
Rework command system
Diffstat:
M | LICENSE | | | 3 | +++ |
M | Makefile | | | 8 | +++++--- |
M | README.md | | | 8 | ++++++++ |
M | button.c | | | 62 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++------- |
M | button.h | | | 9 | +++++---- |
M | grid.c | | | 312 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------ |
M | grid.h | | | 79 | ++----------------------------------------------------------------------------- |
M | ltk.c | | | 147 | +++++++++++++++++++++++++++++++++++++++++++++++-------------------------------- |
M | ltk.h | | | 22 | ++++++++++++++++++++-- |
A | strtonum.c | | | 66 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | test.gui | | | 14 | ++++++++++---- |
A | util.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, <k_button_draw, <k_button_destroy, 1);
+ ltk_fill_widget_defaults(&button->widget, id, window,
+ <k_button_draw, <k_button_destroy, 1, LTK_BUTTON);
button->widget.mouse_release = <k_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, <k_draw_grid, <k_destroy_grid, 0);
+ ltk_fill_widget_defaults(&grid->widget, id, window, <k_grid_draw,
+ <k_grid_destroy, 0, LTK_GRID);
grid->widget.mouse_press = <k_grid_mouse_press;
grid->widget.mouse_release = <k_grid_mouse_release;
grid->widget.motion_notify = <k_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