From: Olaf Wintermann Date: Sat, 6 Dec 2025 17:08:14 +0000 (+0100) Subject: update toolkit (gtk3 splitwindow fix) X-Git-Url: https://uap-core.de/gitweb/?a=commitdiff_plain;h=refs%2Fheads%2Fmain;p=note.git update toolkit (gtk3 splitwindow fix) --- diff --git a/ui/common/message.c b/ui/common/message.c new file mode 100644 index 0000000..921b1a6 --- /dev/null +++ b/ui/common/message.c @@ -0,0 +1,202 @@ +/* + * 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 +#include +#include + +#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= 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; +} diff --git a/ui/common/message.h b/ui/common/message.h new file mode 100644 index 0000000..00c83b7 --- /dev/null +++ b/ui/common/message.h @@ -0,0 +1,83 @@ +/* + * 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 +#include +#include + +#include + + +#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 */ + diff --git a/ui/common/objs.mk b/ui/common/objs.mk index 176180c..2d01bda 100644 --- a/ui/common/objs.mk +++ b/ui/common/objs.mk @@ -43,6 +43,7 @@ COMMON_OBJ += condvar$(OBJ_EXT) 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) diff --git a/ui/common/threadpool.c b/ui/common/threadpool.c index 7988eab..a66af62 100644 --- a/ui/common/threadpool.c +++ b/ui/common/threadpool.c @@ -40,24 +40,20 @@ static threadpool_job kill_job; 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;imin_threads;i++) { - pthread_t t; - if (pthread_create(&t, NULL, threadpool_func, pool) != 0) { + for(int i=0;inthreads;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; } @@ -65,6 +61,16 @@ int threadpool_start(UiThreadpool *pool) { return 0; } +int threadpool_join(UiThreadpool *pool) { + int err = 0; + for(int i=0;inthreads;i++) { + if(pthread_join(pool->threads[i], NULL)) { + err = 1; + } + } + return err; +} + void* threadpool_func(void *data) { UiThreadpool *pool = (UiThreadpool*)data; @@ -82,71 +88,19 @@ void* threadpool_func(void *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 @@ -191,6 +145,66 @@ void ui_threadpool_job(UiThreadpool* pool, UiObject* obj, ui_threadfunc tf, void 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 diff --git a/ui/common/threadpool.h b/ui/common/threadpool.h index d167062..4a997f6 100644 --- a/ui/common/threadpool.h +++ b/ui/common/threadpool.h @@ -39,6 +39,8 @@ extern "C" { #endif +typedef struct UiQueueElm UiQueueElm; +typedef struct UiQueue UiQueue; typedef struct UiJob { UiObject *obj; @@ -48,20 +50,30 @@ typedef struct UiJob { 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 { @@ -76,10 +88,15 @@ struct _pool_queue { 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 } diff --git a/ui/gtk/list.c b/ui/gtk/list.c index abb576c..8e2d217 100644 --- a/ui/gtk/list.c +++ b/ui/gtk/list.c @@ -860,6 +860,7 @@ void ui_update_liststore_static(GListStore *liststore, char **elm, size_t nelm) 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 { diff --git a/ui/gtk/toolkit.c b/ui/gtk/toolkit.c index f5a2545..3c5557d 100644 --- a/ui/gtk/toolkit.c +++ b/ui/gtk/toolkit.c @@ -346,6 +346,11 @@ UiObject *ui_get_active_window() { #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;" diff --git a/ui/gtk/window.c b/ui/gtk/window.c index 13c521e..4b103c3 100644 --- a/ui/gtk/window.c +++ b/ui/gtk/window.c @@ -337,14 +337,31 @@ static UiObject* create_window(const char *title, void *window_data, UiBool side 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); } diff --git a/ui/ui/toolkit.h b/ui/ui/toolkit.h index 7b8309a..89751d2 100644 --- a/ui/ui/toolkit.h +++ b/ui/ui/toolkit.h @@ -146,6 +146,12 @@ public: #endif +#elif UI_SERVER + +#define UIWIDGET void* +#define UIWINDOW void* +#define UIMENU void* + #endif #ifndef TRUE diff --git a/ui/ui/widget.h b/ui/ui/widget.h index aea5b8e..40503ba 100644 --- a/ui/ui/widget.h +++ b/ui/ui/widget.h @@ -64,6 +64,8 @@ typedef UIWIDGET (*ui_createwidget_func)(UiObject *obj, UiWidgetArgs *args, void 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);