--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "message.h"
+
+UiMessageHandler* uic_simple_msg_handler(int in, int out, msg_received_callback callback) {
+ UiSimpleMessageHandler *handler = malloc(sizeof(UiSimpleMessageHandler));
+ handler->handler.start = uic_simple_msg_handler_start;
+ handler->handler.stop = uic_simple_msg_handler_stop;
+ handler->handler.send = uic_simple_msg_handler_send;
+ handler->handler.callback = callback;
+ handler->in = in;
+ handler->out = out;
+ handler->outbuf = cxBufferCreate(NULL, 4096, NULL, CX_BUFFER_FREE_CONTENTS | CX_BUFFER_AUTO_EXTEND);
+ handler->stop = 0;
+ pthread_mutex_init(&handler->queue_lock, NULL);
+ pthread_mutex_init(&handler->avlbl_lock, NULL);
+ pthread_cond_init(&handler->available, NULL);
+ return (UiMessageHandler*)handler;
+}
+
+int uic_simple_msg_handler_start(UiMessageHandler *handler) {
+ UiSimpleMessageHandler *sh = (UiSimpleMessageHandler*)handler;
+ if(pthread_create(&sh->in_thread, NULL, uic_simple_msg_handler_in_thread, sh)) {
+ return 1;
+ }
+ if(pthread_create(&sh->out_thread, NULL, uic_simple_msg_handler_out_thread, sh)) {
+ return 1;
+ }
+ return 0;
+}
+
+int uic_simple_msg_handler_stop(UiMessageHandler *handler) {
+ UiSimpleMessageHandler *sh = (UiSimpleMessageHandler*)handler;
+ pthread_mutex_lock(&sh->queue_lock);
+ sh->stop = 0;
+ pthread_cond_signal(&sh->available);
+ pthread_mutex_unlock(&sh->queue_lock);
+ close(sh->in);
+ sh->in = -1;
+
+ pthread_join(sh->in_thread, NULL);
+ pthread_join(sh->out_thread, NULL);
+
+ return 0;
+}
+
+int uic_simple_msg_handler_send(UiMessageHandler *handler, cxstring msg) {
+ UiSimpleMessageHandler *sh = (UiSimpleMessageHandler*)handler;
+ pthread_mutex_lock(&sh->queue_lock);
+ cxBufferWrite(msg.ptr, 1, msg.length, sh->outbuf);
+ pthread_cond_signal(&sh->available);
+ pthread_mutex_unlock(&sh->queue_lock);
+ return 0;
+}
+
+#define HEADERBUF_SIZE 64
+
+void* uic_simple_msg_handler_in_thread(void *data) {
+ UiSimpleMessageHandler *handler = data;
+
+ char *msg = NULL;
+ size_t msg_size = 0;
+ size_t msg_pos = 0; // currently received message length
+
+ char headerbuf[HEADERBUF_SIZE];
+ size_t headerpos = 0;
+
+ char buf[2048];
+ ssize_t r;
+ while((r = read(handler->in, buf, 2024)) > 0) {
+ char *buffer = buf;
+ size_t available = r;
+
+ while(available > 0) {
+ if(msg) {
+ // read message
+ size_t need = msg_size - msg_pos;
+ size_t cplen = r > need ? need : available;
+ memcpy(msg+msg_pos, buffer, cplen);
+ buffer += cplen;
+ available -= cplen;
+ msg_pos += cplen;
+ if(msg_pos == msg_size) {
+ // message complete
+ //fprintf(stderr, "send: %.*s\n", (int)msg_size, msg);
+ if(handler->handler.callback) {
+ handler->handler.callback(cx_mutstrn(msg, msg_size));
+ }
+ msg = NULL;
+ msg_size = 0;
+ msg_pos = 0;
+ }
+ } else {
+ size_t header_max = HEADERBUF_SIZE - headerpos - 1;
+ if(header_max > available) {
+ header_max = available;
+ }
+ // search for line break
+ int i;
+ int header_complete = 0;
+ for(i=0;i<header_max;i++) {
+ if(buffer[i] == '\n') {
+ header_complete = 1;
+ break;
+ }
+ }
+ i++;
+ memcpy(headerbuf+headerpos, buffer, i);
+ headerpos += i;
+ buffer += i;
+ available -= i;
+
+ if(header_complete) {
+ headerbuf[headerpos-1] = 0; // terminate buffer
+ char *end;
+ long length = strtol(headerbuf, &end, 10);
+ if(*end == '\0') {
+ //fprintf(stderr, "header: %d\n", (int)length);
+ msg = malloc(length);
+ msg_size = length;
+ headerpos = 0;
+ } else {
+ fprintf(stderr, "Error: invalid message {%s}\n", headerbuf);
+ }
+ } else if(headerpos+1 >= HEADERBUF_SIZE) {
+ fprintf(stderr, "Error: message header too big\n");
+ exit(-1);
+ }
+ }
+ }
+
+
+ }
+ perror("error");
+ fprintf(stderr, "stop simple_msg_handler_in_thread\n");
+
+ return NULL;
+}
+
+void* uic_simple_msg_handler_out_thread(void *data) {
+ UiSimpleMessageHandler *handler = data;
+ CxBuffer *buffer = handler->outbuf;
+
+ pthread_mutex_lock(&handler->queue_lock);
+
+ for(;;) {
+ if(buffer->pos == 0) {
+ pthread_cond_wait(&handler->available, &handler->queue_lock);
+ continue;
+ } else {
+ size_t n = buffer->pos;
+ size_t pos = 0;
+ while(n > 0) {
+ ssize_t w = write(handler->out, buffer->space + pos, n);
+ if(w <= 0) {
+ fprintf(stderr, "Error: output error\n");
+ break;
+ }
+ n -= w;
+ pos += w;
+ }
+ if(n > 0) {
+ break; // error
+ }
+ buffer->pos = 0;
+ }
+ }
+
+ pthread_mutex_unlock(&handler->queue_lock);
+
+ return NULL;
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UIC_MESSAGE_H
+#define UIC_MESSAGE_H
+
+#include <cx/string.h>
+#include <cx/json.h>
+#include <cx/buffer.h>
+
+#include <pthread.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef struct UiMessageHandler UiMessageHandler;
+
+typedef void(*msg_received_callback)(cxmutstr msg);
+
+struct UiMessageHandler {
+ int (*start)(UiMessageHandler *handler);
+ int (*stop)(UiMessageHandler *handler);
+ int (*send)(UiMessageHandler *handler, cxstring msg);
+
+ msg_received_callback callback;
+};
+
+typedef struct UiSimpleMessageHandler {
+ UiMessageHandler handler;
+ int in;
+ int out;
+ pthread_t in_thread;
+ pthread_t out_thread;
+ pthread_mutex_t queue_lock;
+ pthread_mutex_t avlbl_lock;
+ pthread_cond_t available;
+ CxBuffer *outbuf;
+ int stop;
+} UiSimpleMessageHandler;
+
+UiMessageHandler* uic_simple_msg_handler(int in, int out, msg_received_callback callback);
+int uic_simple_msg_handler_start(UiMessageHandler *handler);
+int uic_simple_msg_handler_stop(UiMessageHandler *handler);
+int uic_simple_msg_handler_send(UiMessageHandler *handler, cxstring msg);
+
+void* uic_simple_msg_handler_in_thread(void *data);
+void* uic_simple_msg_handler_out_thread(void *data);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UIC_MESSAGE_H */
+
COMMON_OBJ += args$(OBJ_EXT)
COMMON_OBJ += wrapper$(OBJ_EXT)
COMMON_OBJ += utils$(OBJ_EXT)
+COMMON_OBJ += message$(OBJ_EXT)
TOOLKITOBJS += $(COMMON_OBJ:%=$(COMMON_OBJPRE)uic_%)
TOOLKITSOURCE += $(COMMON_OBJ:%$(OBJ_EXT)=common/%.c)
UiThreadpool* threadpool_new(int min, int max) {
UiThreadpool *pool = malloc(sizeof(UiThreadpool));
- pool->queue = NULL;
- pool->queue_len = 0;
+ pool->queue = ui_queue_create();
pool->num_idle = 0;
pool->min_threads = min;
pool->max_threads = max;
- pthread_mutex_init(&pool->queue_lock, NULL);
- pthread_mutex_init(&pool->avlbl_lock, NULL);
- pthread_cond_init(&pool->available, NULL);
-
return pool;
}
int threadpool_start(UiThreadpool *pool) {
+ pool->nthreads = pool->min_threads;
+ pool->threads = calloc(pool->max_threads, sizeof(pthread_t));
/* create pool threads */
- for(int i=0;i<pool->min_threads;i++) {
- pthread_t t;
- if (pthread_create(&t, NULL, threadpool_func, pool) != 0) {
+ for(int i=0;i<pool->nthreads;i++) {
+ if (pthread_create(&pool->threads[i], NULL, threadpool_func, pool) != 0) {
fprintf(stderr, "uic: threadpool_start: pthread_create failed: %s", strerror(errno));
return 1;
}
return 0;
}
+int threadpool_join(UiThreadpool *pool) {
+ int err = 0;
+ for(int i=0;i<pool->nthreads;i++) {
+ if(pthread_join(pool->threads[i], NULL)) {
+ err = 1;
+ }
+ }
+ return err;
+}
+
void* threadpool_func(void *data) {
UiThreadpool *pool = (UiThreadpool*)data;
}
threadpool_job* threadpool_get_job(UiThreadpool *pool) {
- pthread_mutex_lock(&pool->queue_lock);
-
- threadpool_job *job = NULL;
- pool->num_idle++;
- while(job == NULL) {
- if(pool->queue_len == 0) {
- pthread_cond_wait(&pool->available, &pool->queue_lock);
- continue;
- } else {
- pool_queue_t *q = pool->queue;
- job = q->job;
- pool->queue = q->next;
- pool->queue_len--;
- free(q);
- }
- }
- pool->num_idle--;
-
- pthread_mutex_unlock(&pool->queue_lock);
+ threadpool_job *job = ui_queue_get_wait(pool->queue);
return job;
}
-void threadpool_run(UiThreadpool *pool, job_callback_f func, void *data) {
- // TODO: handle errors
-
+void threadpool_run(UiThreadpool *pool, job_callback_f func, void *data) {
threadpool_job *job = malloc(sizeof(threadpool_job));
job->callback = func;
job->data = data;
-
- pthread_mutex_lock(&pool->queue_lock);
- threadpool_enqueue_job(pool, job);
-
- int create_thread = 0;
- int destroy_thread = 0;
- int diff = pool->queue_len - pool->num_idle;
-
- //if(pool->queue_len == 1) {
- pthread_cond_signal(&pool->available);
- //}
-
- pthread_mutex_unlock(&pool->queue_lock);
-}
-
-void threadpool_enqueue_job(UiThreadpool *pool, threadpool_job *job) {
- pool_queue_t *q = malloc(sizeof(pool_queue_t));
- q->job = job;
- q->next = NULL;
-
- if(pool->queue == NULL) {
- pool->queue = q;
- } else {
- pool_queue_t *last_elem = pool->queue;
- while(last_elem->next != NULL) {
- last_elem = last_elem->next;
- }
- last_elem->next = q;
- }
- pool->queue_len++;
+ ui_queue_put(pool->queue, job);
}
-
-
-
UiThreadpool* ui_threadpool_create(int nthreads) {
UiThreadpool *pool = threadpool_new(nthreads, nthreads);
threadpool_start(pool); // TODO: check return value
threadpool_run(pool, ui_threadpool_job_func, job);
}
+/* --------------------------------- Queue --------------------------------- */
+
+UiQueue* ui_queue_create(void) {
+ UiQueue *queue = calloc(1, sizeof(UiQueue));
+ pthread_mutex_init(&queue->lock, NULL);
+ pthread_mutex_init(&queue->avlbl_lock, NULL);
+ pthread_cond_init(&queue->available, NULL);
+ return queue;
+}
+
+void ui_queue_free(UiQueue *queue) {
+ // TODO
+}
+
+void ui_queue_put(UiQueue *queue, void *data) {
+ // create queue element
+ UiQueueElm *elm = malloc(sizeof(UiQueueElm));
+ elm->data = data;
+ elm->next = NULL;
+
+ pthread_mutex_lock(&queue->lock);
+
+ // put queue element at the end of the linked list
+ if(queue->elements) {
+ UiQueueElm *end = queue->elements;
+ while(end->next) {
+ end = end->next;
+ }
+ end->next = elm;
+ } else {
+ queue->elements = elm;
+ }
+ queue->length++;
+
+ // signal new available data
+ pthread_cond_signal(&queue->available);
+
+ pthread_mutex_unlock(&queue->lock);
+}
+
+void* ui_queue_get_wait(UiQueue *queue) {
+ pthread_mutex_lock(&queue->lock);
+
+ void *data = NULL;
+ while(data == NULL) {
+ if(queue->length == 0) {
+ pthread_cond_wait(&queue->available, &queue->lock);
+ continue;
+ } else {
+ UiQueueElm *q = queue->elements;
+ data = q->data;
+ queue->elements = q->next;
+ queue->length--;
+ free(q);
+ }
+ }
+
+ pthread_mutex_unlock(&queue->lock);
+ return data;
+}
#endif
extern "C" {
#endif
+typedef struct UiQueueElm UiQueueElm;
+typedef struct UiQueue UiQueue;
typedef struct UiJob {
UiObject *obj;
void *finish_data;
} UiJob;
+struct UiQueueElm {
+ void *data;
+ UiQueueElm *next;
+};
+
+struct UiQueue {
+ UiQueueElm *elements;
+ size_t length;
+ pthread_mutex_t lock;
+ pthread_mutex_t avlbl_lock;
+ pthread_cond_t available;
+};
typedef struct _threadpool_job threadpool_job;
typedef void*(*job_callback_f)(void *data);
typedef struct _pool_queue pool_queue_t;
struct UiThreadpool {
- pthread_mutex_t queue_lock;
- pthread_mutex_t avlbl_lock;
- pthread_cond_t available;
- pool_queue_t *queue;
- uint32_t queue_len;
- uint32_t num_idle;
- int min_threads;
- int max_threads;
+ UiQueue *queue;
+ uint32_t num_idle;
+ int min_threads;
+ int max_threads;
+ pthread_t *threads;
+ int nthreads;
};
struct _threadpool_job {
UiThreadpool* threadpool_new(int min, int max);
int threadpool_start(UiThreadpool *pool);
+int threadpool_join(UiThreadpool *pool);
void* threadpool_func(void *data);
threadpool_job* threadpool_get_job(UiThreadpool *pool);
void threadpool_run(UiThreadpool *pool, job_callback_f func, void *data);
-void threadpool_enqueue_job(UiThreadpool *pool, threadpool_job *job);
+
+UiQueue* ui_queue_create(void);
+void ui_queue_free(UiQueue *queue);
+void ui_queue_put(UiQueue *queue, void *data);
+void* ui_queue_get_wait(UiQueue *queue);
#ifdef __cplusplus
}
void ui_listview_update2(UiList *list, int i) {
UiListView *view = list->obj;
+ view->current_row = -1;
if(i < 0) {
ui_update_liststore(view->liststore, list);
} else {
#if GTK_MAJOR_VERSION == 4
static const char *ui_gtk_css =
+".ui-entry-box {\n"
+" background-color: alpha(currentColor, 0.1);"
+" border-radius: 6px;"
+" padding: 7px;"
+"}\n"
"#path-textfield-box {\n"
" background-color: alpha(currentColor, 0.1);"
" border-radius: 6px;"
GtkWidget *content_box = ui_gtk_vbox_new(0);
WINDOW_SET_CONTENT(obj->widget, vbox);
- if(sidebar) {
+ if(sidebar || splitview) {
GtkWidget *paned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
- GtkWidget *sidebar_vbox = ui_gtk_vbox_new(0);
- gtk_paned_add1(GTK_PANED(paned), sidebar_vbox);
- gtk_paned_add2(GTK_PANED(paned), content_box);
+ if(sidebar) {
+ GtkWidget *sidebar_vbox = ui_gtk_vbox_new(0);
+ gtk_paned_add1(GTK_PANED(paned), sidebar_vbox);
+ g_object_set_data(G_OBJECT(obj->widget), "ui_sidebar", sidebar_vbox);
+ gtk_paned_set_position(GTK_PANED(paned), 200);
+ }
+
+ if(splitview) {
+ GtkWidget *content_paned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
+ gtk_paned_add2(GTK_PANED(paned), content_paned);
+
+ GtkWidget *right_content_box = ui_gtk_vbox_new(0);
+ gtk_paned_add1(GTK_PANED(content_paned), content_box);
+ gtk_paned_add2(GTK_PANED(content_paned), right_content_box);
+
+ g_object_set_data(G_OBJECT(obj->widget), "ui_window_splitview", content_paned);
+ g_object_set_data(G_OBJECT(obj->widget), "ui_left_panel", content_box);
+ g_object_set_data(G_OBJECT(obj->widget), "ui_right_panel", right_content_box);
+ } else {
+ gtk_paned_add2(GTK_PANED(paned), content_box);
+ }
+
BOX_ADD_EXPAND(GTK_BOX(vbox), paned);
- g_object_set_data(G_OBJECT(obj->widget), "ui_sidebar", sidebar_vbox);
- gtk_paned_set_position (GTK_PANED(paned), 200);
} else {
BOX_ADD_EXPAND(GTK_BOX(vbox), content_box);
}
#endif
+#elif UI_SERVER
+
+#define UIWIDGET void*
+#define UIWINDOW void*
+#define UIMENU void*
+
#endif
#ifndef TRUE
typedef UIWIDGET(*ui_createwidget_func)(UiObject *obj, UiWidgetArgs *args, void *userdata);
#elif defined(UI_WIN32)
typedef UIWIDGET(*ui_createwidget_func)(UiObject *obj, UiWidgetArgs *args, void *userdata);
+#elif defined(UI_SERVER)
+typedef UIWIDGET(*ui_createwidget_func)(UiObject *obj, UiWidgetArgs *args, void *userdata);
#endif
UIEXPORT UIWIDGET ui_customwidget_create(UiObject *obj, ui_createwidget_func create_widget, void *userdata, UiWidgetArgs *args);