ltkx

GUI toolkit for X11 (WIP)
git clone git://lumidify.org/ltkx.git
Log | Files | Refs | README | LICENSE

commit a8d438e533db40481c75ddd952a14f14b6f68c94
parent 8a3b7084765b5ce72d0e55adf283e737a0049e03
Author: lumidify <nobody@lumidify.org>
Date:   Thu,  5 Jan 2017 10:24:48 +0100

Turn the basic framework into a single .c and .h file pair

Diffstat:
MMakefile | 2+-
Dcommon.c | 208-------------------------------------------------------------------------------
Dcommon.h | 187-------------------------------------------------------------------------------
Mltk.c | 328+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mltk.h | 246+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Dtheme.c | 64----------------------------------------------------------------
Dtheme.h | 51---------------------------------------------------
Dwindow.c | 153-------------------------------------------------------------------------------
Dwindow.h | 54------------------------------------------------------
9 files changed, 571 insertions(+), 722 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,7 +1,7 @@ LIBS = -lX11 -lm -ldl STD = -std=c89 FLAGS = -g -w -Wall -Werror -Wextra -pedantic -CFILES = ltk.c cJSON.c common.c grid.c window.c theme.c button.c test1.c +CFILES = ltk.c cJSON.c grid.c button.c test1.c all: test1.c gcc $(STD) $(FLAGS) $(LIBS) $(CFILES) -o test diff --git a/common.c b/common.c @@ -1,208 +0,0 @@ -/* - * This file is part of the Lumidify ToolKit (LTK) - * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.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 - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "ltk.h" - -char *ltk_read_file(const char *path) -{ - FILE *f; - long len; - char *file_contents; - f = fopen(path, "rb"); - fseek(f, 0, SEEK_END); - len = ftell(f); - fseek(f, 0, SEEK_SET); - file_contents = malloc(len + 1); - fread(file_contents, 1, len, f); - file_contents[len] = '\0'; - fclose(f); - - return file_contents; -} - -int ltk_collide_rect(LtkRect rect, int x, int y) -{ - return (rect.x <= x && (rect.x + rect.w) >= x && rect.y <= y && (rect.y + rect.h) >= y); -} - -void ltk_remove_active_widget(void *widget) -{ - if (!widget) return; - LtkWidget *parent = widget; - LtkWidget *child; - while (parent->active_widget) - { - child = parent->active_widget; - child->state = LTK_NORMAL; - child->draw(child); - parent->active_widget = NULL; - parent = child; - } -} - -void ltk_change_active_widget_state(void *widget, LtkWidgetState state) -{ - if (!widget) return; - LtkWidget *ptr = widget; - while (ptr = ptr->active_widget) - { - ptr->state = state; - ptr->draw(ptr); - } -} - -void ltk_remove_hover_widget(void *widget) -{ - if (!widget) return; - LtkWidget *parent = widget; - LtkWidget *child; - while (parent->hover_widget) - { - child = parent->hover_widget; - child->state = child->state == LTK_HOVERACTIVE ? LTK_ACTIVE : LTK_NORMAL; - child->draw(child); - parent->hover_widget = NULL; - parent = child; - } -} - -LtkWidget ltk_create_widget(LtkWindow *window, void (*draw)(void *), void (*destroy)(void *), int needs_redraw) -{ - LtkWidget widget; - widget.window = window; - widget.active_widget = NULL; - widget.hover_widget = NULL; - widget.parent = NULL; - - widget.key_press = NULL; - widget.key_release = NULL; - widget.mouse_press = NULL; - widget.mouse_release = NULL; - widget.motion_notify = NULL; - - widget.resize = NULL; - widget.draw = draw; - widget.destroy = destroy; - - widget.needs_redraw = needs_redraw; - widget.state = LTK_NORMAL; - widget.row = 0; - widget.rect.x = 0; - widget.rect.y = 0; - widget.rect.w = 100; - widget.rect.h = 100; - - widget.row = NULL; - widget.column = NULL; - widget.row_span = NULL; - widget.column_span = NULL; - widget.sticky = NULL; - - return widget; -} - -void ltk_mouse_press_event(void *widget, XEvent event) -{ - LtkWidget *ptr = widget; - if (!ptr || ptr->state == LTK_DISABLED) return; - if (event.xbutton.button == 1) - { - LtkWidget *parent = ptr->parent; - if (parent) - { - ltk_remove_active_widget(parent); - parent->active_widget = ptr; - } - ptr->state = LTK_PRESSED; - if (ptr->needs_redraw) ptr->draw(ptr); - } - if (ptr->mouse_press) - { - ptr->mouse_press(ptr, event); - } -} - -void ltk_mouse_release_event(void *widget, XEvent event) -{ - LtkWidget *ptr = widget; - if (!ptr || ptr->state == LTK_DISABLED) return; - if (ptr->state == LTK_PRESSED) - { - ptr->state = LTK_HOVERACTIVE; - if (ptr->needs_redraw) ptr->draw(ptr); - } - if (ptr->mouse_release) - { - ptr->mouse_release(ptr, event); - } -} - -void ltk_motion_notify_event(void *widget, XEvent event) -{ - LtkWidget *ptr = widget; - if (ptr && (ptr->state == LTK_NORMAL || ptr->state == LTK_ACTIVE) && - (event.xmotion.state & Button1Mask) != Button1Mask) - { - ptr->state = ptr->state == LTK_ACTIVE ? LTK_HOVERACTIVE : LTK_HOVER; - LtkWidget *parent = ptr->parent; - if (parent) - { - ltk_remove_hover_widget(parent); - parent->hover_widget = ptr; - } - if (ptr->needs_redraw) ptr->draw(ptr); - } - if (ptr->motion_notify) - { - ptr->motion_notify(ptr, event); - } -} - -void ltk_handle_event(XEvent event) -{ - LtkWindow *window; - LtkWidget *root_widget; - HASH_FIND_INT(ltk_global->window_hash, &event.xany.window, window); - if (!window) return; - root_widget = window->root_widget; - switch (event.type) - { - case KeyPress: - break; - case KeyRelease: - break; - case ButtonPress: - if (root_widget) ltk_mouse_press_event(root_widget, event); - break; - case ButtonRelease: - if (root_widget) ltk_mouse_release_event(root_widget, event); - break; - case MotionNotify: - if (root_widget) ltk_motion_notify_event(root_widget, event); - break; - default: - /* FIXME: users should be able to register other events like closing the window */ - if (window->other_event) - window->other_event(window, event); - } -} diff --git a/common.h b/common.h @@ -1,187 +0,0 @@ -/* - * This file is part of the Lumidify ToolKit (LTK) - * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.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 - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _LTK_COMMON_H_ -#define _LTK_COMMON_H_ - -typedef struct LtkWidget LtkWidget; -typedef struct LtkWindow LtkWindow; - -/* - * Struct to represent a rectangle. - */ -typedef struct -{ - int x; - int y; - int w; - int h; -} LtkRect; - -typedef enum { - LTK_STICKY_LEFT = 1 << 0, - LTK_STICKY_RIGHT = 1 << 1, - LTK_STICKY_TOP = 1 << 2, - LTK_STICKY_BOTTOM = 1 << 3 -} LtkStickyMask; - -/* - * An enumeration of all widget states. - */ -typedef enum -{ - LTK_NORMAL = 0, - LTK_HOVER = 1, - LTK_PRESSED = 2, - LTK_ACTIVE = 3, - LTK_HOVERACTIVE = 4, - LTK_DISABLED = 5 -} LtkWidgetState; - -/* - * A struct to contain all basic widget information. - * First element of every widget so the widget can - * be cast to LtkWidget. - */ -typedef struct LtkWidget -{ - /* The window the widget will be displayed on */ - LtkWindow *window; - /* For container widgets; the widget that is currently active */ - struct LtkWidget *active_widget; - /* For container widgets; the widget that is currently highlighted */ - struct LtkWidget *hover_widget; - /* Parent widget */ - struct LtkWidget *parent; - - /* Called on KeyPress events */ - void (*key_press)(void *, XEvent event); - /* Called on KeyRelease events */ - void (*key_release)(void *, XEvent event); - /* Called on ButtonPress events */ - void (*mouse_press)(void *, XEvent event); - /* Called on ButtonRelease event */ - void (*mouse_release)(void *, XEvent event); - /* Called on MotionNotify events */ - void (*motion_notify)(void *, XEvent event); - - /* Function to update the widget after its LtkRect has been modified */ - void (*resize)(void *); - /* Function to draw the widget */ - void (*draw)(void *); - /* Function to destroy the widget */ - void (*destroy)(void *); - - /* Position and size of the widget */ - LtkRect rect; - /* Row of widget if gridded */ - unsigned int row; - /* Column of widget if gridded */ - unsigned int column; - /* Row span of widget if gridded */ - unsigned int row_span; - /* Column span of widget if gridded */ - unsigned int column_span; - /* Specifies if the widget needs to be redrawn after a state change */ - int needs_redraw : 1; - /* State of the widget */ - LtkWidgetState state : 3; - /* Similar to sticky in tk */ - unsigned short sticky : 4; -} LtkWidget; - - -/* - * Check if a rectangle collides with a point. - * rect: The rectangle. - * x: The x coordinate of the point. - * y: The y coordinate of the point. - */ -int ltk_collide_rect(LtkRect rect, int x, int y); - -/* - * Read a file and return a null-terminated string with the contents. - * path: The path to the file. - */ -char *ltk_read_file(const char *path); - -/* - * Recursively set the state of all active_widgets in 'widget' to 'state'. - */ -void ltk_change_active_widget_state(void *widget, LtkWidgetState state); - -/* - * Recursively set the state of all active_widgets in 'widget' to LTK_NORMAL, - * redraw the widgets, and remove the references to them from their parents. - */ -void ltk_remove_active_widget(void *widget); - -/* - * Recursively set the state of all hover_widgets in 'widget' to LTK_NORMAL or - * LTK_ACTIVE, redraw the widgets, and remove the references to them from their parents. - */ -void ltk_remove_hover_widget(void *widget); - -/* - * Create a widget. - * window: The window the widget is to be shown on. - * draw: The function used to draw the widget. - * destroy: The function used to destroy the widget. - * needs_redraw: Flag to indicate if the widget needs to be - * be redrawn when it is resized. - * Returns: The new LtkWidget. - */ -LtkWidget ltk_create_widget(LtkWindow *window, void (*draw)(void *), void (*destroy)(void *), int needs_redraw); - -/* - * Handles mouse press events for all widgets and calls - * specific widget handlers if set. - * widget: Pointer to the widget the mouse is currrently on. - * event: The event to be handled. - */ -void ltk_mouse_press_event(void *widget, XEvent event); - -/* - * Handles mouse release events for all widgets and calls - * specific widget handlers if set. - * widget: Pointer to the widget the mouse is currrently on. - * event: The event to be handled. - */ -void ltk_mouse_release_event(void *widget, XEvent event); - -/* - * Handles mouse motion events for all widgets and calls - * specific widget handlers if set. - * widget: Pointer to the widget the mouse is currrently on. - * event: The event to be handled. - */ -void ltk_motion_notify_event(void *widget, XEvent event); - -/* - * Handles all events and dispatches them to their - * respective handlers. - * event: The event to be handled. - */ -void ltk_handle_event(XEvent event); - -#endif diff --git a/ltk.c b/ltk.c @@ -91,3 +91,330 @@ void ltk_mainloop(void) */ } } + +LtkWindowTheme *ltk_parse_window_theme(cJSON *window_json) +{ + LtkWindowTheme *window_theme = malloc(sizeof(LtkWindowTheme)); + if (!window_theme) ltk_fatal("No memory for new LtkWindowTheme\n"); + cJSON *border_width = cJSON_GetObjectItem(window_json, "border-width"); + cJSON *fg = cJSON_GetObjectItem(window_json, "foreground"); + cJSON *bg = cJSON_GetObjectItem(window_json, "background"); + window_theme->border_width = border_width ? border_width->valueint : 0; + window_theme->fg = ltk_create_xcolor(fg->valuestring); + window_theme->bg = ltk_create_xcolor(bg->valuestring); + + return window_theme; +} + +void ltk_redraw_window(LtkWindow *window) +{ + LtkWidget *ptr; + if (!window) + { + return; + } + if (!window->root_widget) + { + return; + } + ptr = window->root_widget; + ptr->draw(ptr); +} + +LtkWindow *ltk_create_window(const char *title, int x, int y, unsigned int w, unsigned int h) +{ + LtkWindow *window = malloc(sizeof(LtkWindow)); + if (!window) ltk_fatal("Not enough memory left for window!\n"); + LtkWindowTheme *wtheme = ltk_global->theme->window; /* For convenience */ + Display *display = ltk_global->display; /* For convenience */ + window->xwindow = XCreateSimpleWindow(display, DefaultRootWindow(display), x, y, w, h, wtheme->border_width, wtheme->fg.pixel, wtheme->bg.pixel); + window->gc = XCreateGC(display, window->xwindow, 0, 0); + XSetForeground(display, window->gc, wtheme->fg.pixel); + XSetBackground(display, window->gc, wtheme->bg.pixel); + XSetStandardProperties(display, window->xwindow, title, NULL, None, NULL, 0, NULL); + XSetWMProtocols(display, window->xwindow, &ltk_global->wm_delete_msg, 1); + window->root_widget = NULL; + + window->other_event = &ltk_window_other_event; + + window->rect.w = 0; + window->rect.h = 0; + window->rect.x = 0; + window->rect.y = 0; + + XClearWindow(display, window->xwindow); + XMapRaised(display, window->xwindow); + XSelectInput(display, window->xwindow, ExposureMask|KeyPressMask|KeyReleaseMask|ButtonPressMask|ButtonReleaseMask|StructureNotifyMask|PointerMotionMask); + + HASH_ADD_INT(ltk_global->window_hash, xwindow, window); + + return window; +} + +void ltk_remove_window(LtkWindow *window) +{ + ltk_destroy_window(window); + if (!ltk_global->window_hash) ltk_quit(); +} + +void ltk_destroy_window(LtkWindow *window) +{ + HASH_DEL(ltk_global->window_hash, window); + LtkWidget *ptr = window->root_widget; + if (ptr) ptr->destroy(ptr); + XDestroyWindow(ltk_global->display, window->xwindow); + free(window); +} + +void ltk_window_other_event(void *widget, XEvent event) +{ + LtkWindow *window = widget; + LtkWidget *ptr = window->root_widget; + if (event.type == ConfigureNotify) + { + unsigned int w, h; + w = event.xconfigure.width; + h = event.xconfigure.height; + if (ptr && ptr->resize && (window->rect.w != w || window->rect.h != h)) + { + window->rect.w = w; + window->rect.h = h; + ptr->rect.w = w; + ptr->rect.h = h; + ptr->resize(ptr); + ltk_redraw_window(window); + } + } + if (event.type == Expose && event.xexpose.count == 0) + { + ltk_redraw_window(window); + } + if (event.type == ClientMessage && event.xclient.data.l[0] == ltk_global->wm_delete_msg) + { + ltk_remove_window(window); + } +} + +LtkTheme *ltk_load_theme(const char *path) +{ + char *file_contents = ltk_read_file(path); + + cJSON *json = cJSON_Parse(file_contents); + if (!json) + { + printf("Theme error before: [%s]\n", cJSON_GetErrorPtr()); + return NULL; + } + cJSON *button_json = cJSON_GetObjectItem(json, "button"); + if (!button_json) + { + printf("Theme error before: [%s]\n", cJSON_GetErrorPtr()); + return NULL; + } + cJSON *window_json = cJSON_GetObjectItem(json, "window"); + if (!window_json) + { + printf("Theme error before: [%s]\n", cJSON_GetErrorPtr()); + return NULL; + } + + LtkTheme *theme = malloc(sizeof(LtkTheme)); + theme->button = ltk_parse_button_theme(button_json); + theme->window = ltk_parse_window_theme(window_json); + + free(file_contents); + cJSON_Delete(json); + + return theme; +} + +void ltk_destroy_theme(LtkTheme *theme) +{ + free(theme->button); + free(theme->window); + free(theme); +} + +char *ltk_read_file(const char *path) +{ + FILE *f; + long len; + char *file_contents; + f = fopen(path, "rb"); + fseek(f, 0, SEEK_END); + len = ftell(f); + fseek(f, 0, SEEK_SET); + file_contents = malloc(len + 1); + fread(file_contents, 1, len, f); + file_contents[len] = '\0'; + fclose(f); + + return file_contents; +} + +int ltk_collide_rect(LtkRect rect, int x, int y) +{ + return (rect.x <= x && (rect.x + rect.w) >= x && rect.y <= y && (rect.y + rect.h) >= y); +} + +void ltk_remove_active_widget(void *widget) +{ + if (!widget) return; + LtkWidget *parent = widget; + LtkWidget *child; + while (parent->active_widget) + { + child = parent->active_widget; + child->state = LTK_NORMAL; + child->draw(child); + parent->active_widget = NULL; + parent = child; + } +} + +void ltk_change_active_widget_state(void *widget, LtkWidgetState state) +{ + if (!widget) return; + LtkWidget *ptr = widget; + while (ptr = ptr->active_widget) + { + ptr->state = state; + ptr->draw(ptr); + } +} + +void ltk_remove_hover_widget(void *widget) +{ + if (!widget) return; + LtkWidget *parent = widget; + LtkWidget *child; + while (parent->hover_widget) + { + child = parent->hover_widget; + child->state = child->state == LTK_HOVERACTIVE ? LTK_ACTIVE : LTK_NORMAL; + child->draw(child); + parent->hover_widget = NULL; + parent = child; + } +} + +LtkWidget ltk_create_widget(LtkWindow *window, void (*draw)(void *), void (*destroy)(void *), int needs_redraw) +{ + LtkWidget widget; + widget.window = window; + widget.active_widget = NULL; + widget.hover_widget = NULL; + widget.parent = NULL; + + widget.key_press = NULL; + widget.key_release = NULL; + widget.mouse_press = NULL; + widget.mouse_release = NULL; + widget.motion_notify = NULL; + + widget.resize = NULL; + widget.draw = draw; + widget.destroy = destroy; + + widget.needs_redraw = needs_redraw; + widget.state = LTK_NORMAL; + widget.row = 0; + widget.rect.x = 0; + widget.rect.y = 0; + widget.rect.w = 100; + widget.rect.h = 100; + + widget.row = NULL; + widget.column = NULL; + widget.row_span = NULL; + widget.column_span = NULL; + widget.sticky = NULL; + + return widget; +} + +void ltk_mouse_press_event(void *widget, XEvent event) +{ + LtkWidget *ptr = widget; + if (!ptr || ptr->state == LTK_DISABLED) return; + if (event.xbutton.button == 1) + { + LtkWidget *parent = ptr->parent; + if (parent) + { + ltk_remove_active_widget(parent); + parent->active_widget = ptr; + } + ptr->state = LTK_PRESSED; + if (ptr->needs_redraw) ptr->draw(ptr); + } + if (ptr->mouse_press) + { + ptr->mouse_press(ptr, event); + } +} + +void ltk_mouse_release_event(void *widget, XEvent event) +{ + LtkWidget *ptr = widget; + if (!ptr || ptr->state == LTK_DISABLED) return; + if (ptr->state == LTK_PRESSED) + { + ptr->state = LTK_HOVERACTIVE; + if (ptr->needs_redraw) ptr->draw(ptr); + } + if (ptr->mouse_release) + { + ptr->mouse_release(ptr, event); + } +} + +void ltk_motion_notify_event(void *widget, XEvent event) +{ + LtkWidget *ptr = widget; + if (ptr && (ptr->state == LTK_NORMAL || ptr->state == LTK_ACTIVE) && + (event.xmotion.state & Button1Mask) != Button1Mask) + { + ptr->state = ptr->state == LTK_ACTIVE ? LTK_HOVERACTIVE : LTK_HOVER; + LtkWidget *parent = ptr->parent; + if (parent) + { + ltk_remove_hover_widget(parent); + parent->hover_widget = ptr; + } + if (ptr->needs_redraw) ptr->draw(ptr); + } + if (ptr->motion_notify) + { + ptr->motion_notify(ptr, event); + } +} + +void ltk_handle_event(XEvent event) +{ + LtkWindow *window; + LtkWidget *root_widget; + HASH_FIND_INT(ltk_global->window_hash, &event.xany.window, window); + if (!window) return; + root_widget = window->root_widget; + switch (event.type) + { + case KeyPress: + break; + case KeyRelease: + break; + case ButtonPress: + if (root_widget) ltk_mouse_press_event(root_widget, event); + break; + case ButtonRelease: + if (root_widget) ltk_mouse_release_event(root_widget, event); + break; + case MotionNotify: + if (root_widget) ltk_motion_notify_event(root_widget, event); + break; + default: + /* FIXME: users should be able to register other events like closing the window */ + if (window->other_event) + window->other_event(window, event); + } +}+ \ No newline at end of file diff --git a/ltk.h b/ltk.h @@ -30,11 +30,134 @@ #include <X11/Xutil.h> #include "cJSON.h" #include "uthash.h" -#include "common.h" -#include "window.h" -#include "theme.h" -#include "grid.h" + +/* + * Struct to represent a rectangle. + */ +typedef struct +{ + int x; + int y; + int w; + int h; +} LtkRect; + +typedef enum { + LTK_STICKY_LEFT = 1 << 0, + LTK_STICKY_RIGHT = 1 << 1, + LTK_STICKY_TOP = 1 << 2, + LTK_STICKY_BOTTOM = 1 << 3 +} LtkStickyMask; + +/* + * An enumeration of all widget states. + */ +typedef enum +{ + LTK_NORMAL = 0, + LTK_HOVER = 1, + LTK_PRESSED = 2, + LTK_ACTIVE = 3, + LTK_HOVERACTIVE = 4, + LTK_DISABLED = 5 +} LtkWidgetState; + +typedef struct LtkWindow LtkWindow; + +/* + * A struct to contain all basic widget information. + * First element of every widget so the widget can + * be cast to LtkWidget. + */ +typedef struct LtkWidget +{ + /* The window the widget will be displayed on */ + LtkWindow *window; + /* For container widgets; the widget that is currently active */ + struct LtkWidget *active_widget; + /* For container widgets; the widget that is currently highlighted */ + struct LtkWidget *hover_widget; + /* Parent widget */ + struct LtkWidget *parent; + + /* Called on KeyPress events */ + void (*key_press)(void *, XEvent event); + /* Called on KeyRelease events */ + void (*key_release)(void *, XEvent event); + /* Called on ButtonPress events */ + void (*mouse_press)(void *, XEvent event); + /* Called on ButtonRelease event */ + void (*mouse_release)(void *, XEvent event); + /* Called on MotionNotify events */ + void (*motion_notify)(void *, XEvent event); + + /* Function to update the widget after its LtkRect has been modified */ + void (*resize)(void *); + /* Function to draw the widget */ + void (*draw)(void *); + /* Function to destroy the widget */ + void (*destroy)(void *); + + /* Position and size of the widget */ + LtkRect rect; + /* Row of widget if gridded */ + unsigned int row; + /* Column of widget if gridded */ + unsigned int column; + /* Row span of widget if gridded */ + unsigned int row_span; + /* Column span of widget if gridded */ + unsigned int column_span; + /* Specifies if the widget needs to be redrawn after a state change */ + int needs_redraw : 1; + /* State of the widget */ + LtkWidgetState state : 3; + /* Similar to sticky in tk */ + unsigned short sticky : 4; +} LtkWidget; + +/* + * Struct to represent a window. + */ +typedef struct LtkWindow +{ + Window xwindow; + GC gc; + void *root_widget; + void (*other_event)(void *, XEvent event); + LtkRect rect; + UT_hash_handle hh; +} LtkWindow; + +/* + * Struct to represent the border width, + * foreground color, and background color + * of a window. + */ +typedef struct LtkWindowTheme +{ + int border_width; + XColor fg; + XColor bg; +} LtkWindowTheme; + #include "button.h" +#include "grid.h" + +/* + * Struct to contain all styles needed by LTK. + */ +typedef struct +{ + LtkWindowTheme *window; + LtkButtonTheme *button; +} LtkTheme; + +/* + * Load a theme from a JSON file. + * path: The path to the file. + */ +LtkTheme *ltk_load_theme(const char *path); /* * Struct to contain all global information. @@ -80,4 +203,119 @@ XColor ltk_create_xcolor(const char *hex); */ void ltk_mainloop(void); +/* + * Create a window. + * title: The title of the window. + * x: The x position of the window. + * y: The y position of the window. + * w: The width of the window. + * h: The height of the window. + */ +LtkWindow *ltk_create_window(const char *title, int x, int y, unsigned int w, unsigned int h); + +/* + * Redraw the widgets in a window. + * window: The window to redraw. + */ +void ltk_redraw_window(LtkWindow *window); + +/* + * Destroy a window and quit the application + * if no windows are left. + * window: The window to remove. + */ +void ltk_remove_window(LtkWindow *window); + +/* + * Destroy a window, freeing its memory. + * window: The window to destroy. + */ +void ltk_destroy_window(LtkWindow *window); + +/* + * Handles any event other than mouse or keyboard events. + * widget: Pointer to the window. + * event: The XEvent to be handled. + */ +void ltk_window_other_event(void *widget, XEvent event); + +/* + * Destroy an LtkTheme struct. + * theme: Pointer to the struct. + */ +void ltk_destroy_theme(LtkTheme *theme); + +/* + * Check if a rectangle collides with a point. + * rect: The rectangle. + * x: The x coordinate of the point. + * y: The y coordinate of the point. + */ +int ltk_collide_rect(LtkRect rect, int x, int y); + +/* + * Read a file and return a null-terminated string with the contents. + * path: The path to the file. + */ +char *ltk_read_file(const char *path); + +/* + * Recursively set the state of all active_widgets in 'widget' to 'state'. + */ +void ltk_change_active_widget_state(void *widget, LtkWidgetState state); + +/* + * Recursively set the state of all active_widgets in 'widget' to LTK_NORMAL, + * redraw the widgets, and remove the references to them from their parents. + */ +void ltk_remove_active_widget(void *widget); + +/* + * Recursively set the state of all hover_widgets in 'widget' to LTK_NORMAL or + * LTK_ACTIVE, redraw the widgets, and remove the references to them from their parents. + */ +void ltk_remove_hover_widget(void *widget); + +/* + * Create a widget. + * window: The window the widget is to be shown on. + * draw: The function used to draw the widget. + * destroy: The function used to destroy the widget. + * needs_redraw: Flag to indicate if the widget needs to be + * be redrawn when it is resized. + * Returns: The new LtkWidget. + */ +LtkWidget ltk_create_widget(LtkWindow *window, void (*draw)(void *), void (*destroy)(void *), int needs_redraw); + +/* + * Handles mouse press events for all widgets and calls + * specific widget handlers if set. + * widget: Pointer to the widget the mouse is currrently on. + * event: The event to be handled. + */ +void ltk_mouse_press_event(void *widget, XEvent event); + +/* + * Handles mouse release events for all widgets and calls + * specific widget handlers if set. + * widget: Pointer to the widget the mouse is currrently on. + * event: The event to be handled. + */ +void ltk_mouse_release_event(void *widget, XEvent event); + +/* + * Handles mouse motion events for all widgets and calls + * specific widget handlers if set. + * widget: Pointer to the widget the mouse is currrently on. + * event: The event to be handled. + */ +void ltk_motion_notify_event(void *widget, XEvent event); + +/* + * Handles all events and dispatches them to their + * respective handlers. + * event: The event to be handled. + */ +void ltk_handle_event(XEvent event); + #endif diff --git a/theme.c b/theme.c @@ -1,64 +0,0 @@ -/* - * This file is part of the Lumidify ToolKit (LTK) - * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.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 - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "ltk.h" - -LtkTheme *ltk_load_theme(const char *path) -{ - char *file_contents = ltk_read_file(path); - - cJSON *json = cJSON_Parse(file_contents); - if (!json) - { - printf("Theme error before: [%s]\n", cJSON_GetErrorPtr()); - return NULL; - } - cJSON *button_json = cJSON_GetObjectItem(json, "button"); - if (!button_json) - { - printf("Theme error before: [%s]\n", cJSON_GetErrorPtr()); - return NULL; - } - cJSON *window_json = cJSON_GetObjectItem(json, "window"); - if (!window_json) - { - printf("Theme error before: [%s]\n", cJSON_GetErrorPtr()); - return NULL; - } - - LtkTheme *theme = malloc(sizeof(LtkTheme)); - theme->button = ltk_parse_button_theme(button_json); - theme->window = ltk_parse_window_theme(window_json); - - free(file_contents); - cJSON_Delete(json); - - return theme; -} - -void ltk_destroy_theme(LtkTheme *theme) -{ - free(theme->button); - free(theme->window); - free(theme); -} diff --git a/theme.h b/theme.h @@ -1,51 +0,0 @@ -/* - * This file is part of the Lumidify ToolKit (LTK) - * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.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 - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _LTK_THEME_H_ -#define _LTK_THEME_H_ - -typedef struct LtkWindowTheme LtkWindowTheme; -typedef struct LtkButtonTheme LtkButtonTheme; - -/* - * Struct to contain all styles needed by LTK. - */ -typedef struct -{ - LtkWindowTheme *window; - LtkButtonTheme *button; -} LtkTheme; - -/* - * Load a theme from a JSON file. - * path: The path to the file. - */ -LtkTheme *ltk_load_theme(const char *path); - -/* - * Destroy an LtkTheme struct. - * theme: Pointer to the struct. - */ -void ltk_destroy_theme(LtkTheme *theme); - -#endif diff --git a/window.c b/window.c @@ -1,153 +0,0 @@ -/* - * This file is part of the Lumidify ToolKit (LTK) - * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.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 - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "ltk.h" - -LtkWindowTheme *ltk_parse_window_theme(cJSON *window_json) -{ - LtkWindowTheme *window_theme = malloc(sizeof(LtkWindowTheme)); - if (!window_theme) ltk_fatal("No memory for new LtkWindowTheme\n"); - cJSON *border_width = cJSON_GetObjectItem(window_json, "border-width"); - cJSON *fg = cJSON_GetObjectItem(window_json, "foreground"); - cJSON *bg = cJSON_GetObjectItem(window_json, "background"); - window_theme->border_width = border_width ? border_width->valueint : 0; - window_theme->fg = ltk_create_xcolor(fg->valuestring); - window_theme->bg = ltk_create_xcolor(bg->valuestring); - - return window_theme; -} - -void ltk_redraw_window(LtkWindow *window) -{ - LtkWidget *ptr; - if (!window) - { - return; - } - if (!window->root_widget) - { - return; - } - ptr = window->root_widget; - ptr->draw(ptr); -} - -LtkWindow *ltk_create_window(const char *title, int x, int y, unsigned int w, unsigned int h) -{ - LtkWindow *window = malloc(sizeof(LtkWindow)); - if (!window) ltk_fatal("Not enough memory left for window!\n"); - LtkWindowTheme *wtheme = ltk_global->theme->window; /* For convenience */ - Display *display = ltk_global->display; /* For convenience */ - window->xwindow = XCreateSimpleWindow(display, DefaultRootWindow(display), x, y, w, h, wtheme->border_width, wtheme->fg.pixel, wtheme->bg.pixel); - window->gc = XCreateGC(display, window->xwindow, 0, 0); - XSetForeground(display, window->gc, wtheme->fg.pixel); - XSetBackground(display, window->gc, wtheme->bg.pixel); - XSetStandardProperties(display, window->xwindow, title, NULL, None, NULL, 0, NULL); - XSetWMProtocols(display, window->xwindow, &ltk_global->wm_delete_msg, 1); - window->root_widget = NULL; - - window->other_event = &ltk_window_other_event; - - window->rect.w = 0; - window->rect.h = 0; - window->rect.x = 0; - window->rect.y = 0; - - XClearWindow(display, window->xwindow); - XMapRaised(display, window->xwindow); - XSelectInput(display, window->xwindow, ExposureMask|KeyPressMask|KeyReleaseMask|ButtonPressMask|ButtonReleaseMask|StructureNotifyMask|PointerMotionMask); - - HASH_ADD_INT(ltk_global->window_hash, xwindow, window); - - return window; -} - -void ltk_remove_window(LtkWindow *window) -{ - ltk_destroy_window(window); - if (!ltk_global->window_hash) ltk_quit(); -} - -void ltk_destroy_window(LtkWindow *window) -{ - HASH_DEL(ltk_global->window_hash, window); - LtkWidget *ptr = window->root_widget; - if (ptr) ptr->destroy(ptr); - XDestroyWindow(ltk_global->display, window->xwindow); - free(window); -} - -void ltk_window_other_event(void *widget, XEvent event) -{ - LtkWindow *window = widget; - LtkWidget *ptr = window->root_widget; - if (event.type == ConfigureNotify) - { - unsigned int w, h; - w = event.xconfigure.width; - h = event.xconfigure.height; - if (ptr && ptr->resize && (window->rect.w != w || window->rect.h != h)) - { - window->rect.w = w; - window->rect.h = h; - ptr->rect.w = w; - ptr->rect.h = h; - ptr->resize(ptr); - ltk_redraw_window(window); - } - } - if (event.type == Expose && event.xexpose.count == 0) - { - ltk_redraw_window(window); - } - if (event.type == ClientMessage && event.xclient.data.l[0] == ltk_global->wm_delete_msg) - { - ltk_remove_window(window); - } -} - -/* -void ltk_resize_window(Uint32 id, int w, int h) -{ - LtkWindow *window; - LtkWidget *ptr; - HASH_FIND_INT(ltk_window_hash, &id, window); - if (!window) - { - return; - } - ptr = window->root_widget; - if (!ptr) - { - return; - } - ptr->rect.w = w; - ptr->rect.h = h; - ptr->resize(ptr); -} - -void ltk_window_set_root_widget(LtkWindow *window, void *root_widget) -{ - window->root_widget = root_widget; -} -*/ diff --git a/window.h b/window.h @@ -1,54 +0,0 @@ -/* - * This file is part of the Lumidify ToolKit (LTK) - * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.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 - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _LTK_WINDOW_H_ -#define _LTK_WINDOW_H_ - -#include <X11/Xlib.h> -#include "event.h" - -typedef struct LtkWindow -{ - Window xwindow; - GC gc; - void *root_widget; - void (*other_event)(void *, XEvent event); - LtkRect rect; - UT_hash_handle hh; -} LtkWindow; - -typedef struct LtkWindowTheme -{ - int border_width; - XColor fg; - XColor bg; -} LtkWindowTheme; - -LtkWindow *ltk_create_window(const char *title, int x, int y, unsigned int w, unsigned int h); -void ltk_redraw_window(LtkWindow *window); -void ltk_destroy_window(LtkWindow *window); -void ltk_window_key_event(void *widget, XEvent event); -void ltk_window_mouse_event(void *widget, XEvent event); -void ltk_window_other_event(void *widget, XEvent event); - -#endif