Sun, 30 Nov 2025 21:12:00 +0100
move message handler to ui common
--- a/client/Makefile Sun Nov 30 18:17:49 2025 +0100 +++ b/client/Makefile Sun Nov 30 21:12:00 2025 +0100 @@ -32,7 +32,6 @@ CFLAGS += -I../ui/ -I../ucx APP_BIN_OBJ = ../build/client/main$(OBJ_EXT) -APP_BIN_OBJ += ../build/client/message$(OBJ_EXT) APP_BIN_OBJ += ../build/client/uiclient$(OBJ_EXT) APP_BIN_OBJ += ../build/client/args$(OBJ_EXT)
--- a/client/main.c Sun Nov 30 18:17:49 2025 +0100 +++ b/client/main.c Sun Nov 30 21:12:00 2025 +0100 @@ -86,7 +86,7 @@ return 1; } } - MessageHandler *h = simple_msg_handler(in, out, client_msg_received); + UiMessageHandler *h = uic_simple_msg_handler(in, out, client_msg_received); client_init(h); h->start(h);
--- a/client/main.h Sun Nov 30 18:17:49 2025 +0100 +++ b/client/main.h Sun Nov 30 21:12:00 2025 +0100 @@ -31,7 +31,7 @@ #include <ui/ui.h> -#include "message.h" +#include "../ui/common/message.h" #ifdef __cplusplus extern "C" {
--- a/client/message.c Sun Nov 30 18:17:49 2025 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,174 +0,0 @@ -/* - * 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" - -MessageHandler* simple_msg_handler(int in, int out, msg_received_callback callback) { - SimpleMessageHandler *handler = malloc(sizeof(SimpleMessageHandler)); - handler->handler.start = simple_msg_handler_start; - handler->handler.stop = simple_msg_handler_stop; - handler->handler.send = 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 (MessageHandler*)handler; -} - -int simple_msg_handler_start(MessageHandler *handler) { - SimpleMessageHandler *sh = (SimpleMessageHandler*)handler; - if(pthread_create(&sh->in_thread, NULL, simple_msg_handler_in_thread, sh)) { - return 1; - } - if(pthread_create(&sh->out_thread, NULL, simple_msg_handler_out_thread, sh)) { - return 1; - } - return 0; -} - -int simple_msg_handler_stop(MessageHandler *handler) { - SimpleMessageHandler *sh = (SimpleMessageHandler*)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 simple_msg_handler_send(MessageHandler *handler, cxstring msg) { - SimpleMessageHandler *sh = (SimpleMessageHandler*)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* simple_msg_handler_in_thread(void *data) { - SimpleMessageHandler *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* simple_msg_handler_out_thread(void *data) { - SimpleMessageHandler *handler = data; - - return NULL; -}
--- a/client/message.h Sun Nov 30 18:17:49 2025 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,81 +0,0 @@ -/* - * 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 CLIENT_MESSAGE_H -#define CLIENT_MESSAGE_H - -#include <cx/string.h> -#include <cx/json.h> -#include <cx/buffer.h> - -#include <pthread.h> - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct MessageHandler MessageHandler; - -typedef void(*msg_received_callback)(cxmutstr msg); - -struct MessageHandler { - int (*start)(MessageHandler *handler); - int (*stop)(MessageHandler *handler); - int (*send)(MessageHandler *handler, cxstring msg); - - msg_received_callback callback; -}; - -typedef struct SimpleMessageHandler { - MessageHandler 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; -} SimpleMessageHandler; - -MessageHandler* simple_msg_handler(int in, int out, msg_received_callback callback); -int simple_msg_handler_start(MessageHandler *handler); -int simple_msg_handler_stop(MessageHandler *handler); -int simple_msg_handler_send(MessageHandler *handler, cxstring msg); - -void* simple_msg_handler_in_thread(void *data); -void* simple_msg_handler_out_thread(void *data); - - -#ifdef __cplusplus -} -#endif - -#endif /* CLIENT_MESSAGE_H */ -
--- a/client/uiclient.c Sun Nov 30 18:17:49 2025 +0100 +++ b/client/uiclient.c Sun Nov 30 21:12:00 2025 +0100 @@ -33,12 +33,12 @@ #include "../ui/common/args.h" -static MessageHandler *io; +static UiMessageHandler *io; static CxMap *msg_types; static CxMap *objects; -void client_init(MessageHandler *handler) { +void client_init(UiMessageHandler *handler) { io = handler; msg_types = cxHashMapCreateSimple(CX_STORE_POINTERS);
--- a/client/uiclient.h Sun Nov 30 18:17:49 2025 +0100 +++ b/client/uiclient.h Sun Nov 30 21:12:00 2025 +0100 @@ -34,7 +34,7 @@ #include <cx/json.h> #include <cx/map.h> -#include "message.h" +#include "../ui/common/message.h" #ifdef __cplusplus extern "C" { @@ -46,7 +46,7 @@ typedef int (*json_msg_handler)(UiObject *parent, const CxJsonValue *value); -void client_init(MessageHandler *handler); +void client_init(UiMessageHandler *handler); void client_msg_received(cxmutstr msg);
--- a/configure Sun Nov 30 18:17:49 2025 +0100 +++ b/configure Sun Nov 30 21:12:00 2025 +0100 @@ -85,39 +85,21 @@ printhelp() { echo "Usage: $0 [OPTIONS]..." - cat << __EOF__ -Installation directories: - --prefix=PREFIX path prefix for architecture-independent files - [$prefix] - --exec-prefix=EPREFIX path prefix for architecture-dependent files - [PREFIX] - - --bindir=DIR user executables [EPREFIX/bin] - --sbindir=DIR system admin executables [EPREFIX/sbin] - --libexecdir=DIR program executables [EPREFIX/libexec] - --sysconfdir=DIR system configuration files [PREFIX/etc] - --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] - --localstatedir=DIR modifiable single-machine data [PREFIX/var] - --runstatedir=DIR run-time variable data [LOCALSTATEDIR/run] - --libdir=DIR object code libraries [EPREFIX/lib] - --includedir=DIR C header files [PREFIX/include] - --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] - --datadir=DIR read-only architecture-independent data [DATAROOTDIR] - --infodir=DIR info documentation [DATAROOTDIR/info] - --mandir=DIR man documentation [DATAROOTDIR/man] - --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + echo 'Configuration:' + cat << '__EOF__' Build Types: --debug add extra compile flags for debug builds --release add extra compile flags for release builds Options: - --toolkit=(libadwaita|gtk4|gtk3|gtk2|gtk2legacy|qt5|qt4|cocoa|motif) + --toolkit=(libadwaita|gtk4|gtk3|gtk2|gtk2legacy|qt5|qt4|cocoa|motif|server) Optional Features: --enable-client __EOF__ + abort_configure } # create temporary directory @@ -133,26 +115,7 @@ touch "$TEMP_DIR/options" touch "$TEMP_DIR/features" -# define standard variables -# also define standard prefix (this is where we will search for config.site) -prefix=/usr -exec_prefix= -bindir= -sbindir= -libdir= -libexecdir= -datarootdir= -datadir= -sysconfdir= -sharedstatedir= -localstatedir= -runstatedir= -includedir= -infodir= -localedir= -mandir= - -# custom variables +# config variables # features @@ -163,32 +126,30 @@ for ARG in "$@" do case "$ARG" in - "--prefix="*) prefix=${ARG#--prefix=} ;; - "--exec-prefix="*) exec_prefix=${ARG#--exec-prefix=} ;; - "--bindir="*) bindir=${ARG#----bindir=} ;; - "--sbindir="*) sbindir=${ARG#--sbindir=} ;; - "--libdir="*) libdir=${ARG#--libdir=} ;; - "--libexecdir="*) libexecdir=${ARG#--libexecdir=} ;; - "--datarootdir="*) datarootdir=${ARG#--datarootdir=} ;; - "--datadir="*) datadir=${ARG#--datadir=} ;; - "--sysconfdir="*) sysconfdir=${ARG#--sysconfdir=} ;; - "--sharedstatedir="*) sharedstatedir=${ARG#--sharedstatedir=} ;; - "--localstatedir="*) localstatedir=${ARG#--localstatedir=} ;; - "--includedir="*) includedir=${ARG#--includedir=} ;; - "--infodir="*) infodir=${ARG#--infodir=} ;; - "--mandir"*) mandir=${ARG#--mandir} ;; - "--localedir"*) localedir=${ARG#--localedir} ;; - "--help"*) printhelp; abort_configure ;; - "--debug") BUILD_TYPE="debug" ;; - "--release") BUILD_TYPE="release" ;; + "--help"*) printhelp ;; + "--debug") BUILD_TYPE="debug" ;; + "--release") BUILD_TYPE="release" ;; "--toolkit="*) OPT_TOOLKIT=${ARG#--toolkit=} ;; - "--toolkit") echo "option '$ARG' needs a value:"; echo " $ARG=(libadwaita|gtk4|gtk3|gtk2|gtk2legacy|qt5|qt4|cocoa|motif)"; abort_configure ;; + "--toolkit") echo "option '$ARG' needs a value:"; echo " $ARG=(libadwaita|gtk4|gtk3|gtk2|gtk2legacy|qt5|qt4|cocoa|motif|server)"; abort_configure ;; "--enable-client") FEATURE_CLIENT=on ;; "--disable-client") unset FEATURE_CLIENT ;; "-"*) echo "unknown option: $ARG"; abort_configure ;; esac done +# toolchain detection utilities +. make/toolchain.sh + +# check languages +lang_c= +lang_cpp= +if detect_cpp_compiler ; then + lang_cpp=1 +fi +if detect_c_compiler ; then + lang_c=1 +fi + # set defaults for dir variables @@ -199,67 +160,76 @@ : ${libexecdir:='${exec_prefix}/libexec'} : ${datarootdir:='${prefix}/share'} : ${datadir:='${datarootdir}'} -: ${sysconfdir:='${prefix}/etc'} : ${sharedstatedir:='${prefix}/com'} -: ${localstatedir:='${prefix}/var'} -: ${runstatedir:='${localstatedir}/run'} +if [ -z "$sysconfdir" ]; then + if [ "$prefix" = '/usr' ]; then + sysconfdir='/etc' + else + sysconfdir='${prefix}/etc' + fi +fi +if [ -z "$localstatedir" ]; then + if [ "$prefix" = '/usr' ]; then + localstatedir='/var' + else + localstatedir='${prefix}/var' + fi +fi +if [ -z "$runstatedir" ]; then + if [ "$prefix" = '/usr' ]; then + runstatedir='/var/run' + else + runstatedir='${prefix}/var' + fi +fi : ${includedir:='${prefix}/include'} : ${infodir:='${datarootdir}/info'} : ${mandir:='${datarootdir}/man'} : ${localedir:='${datarootdir}/locale'} -# remember the above values and compare them later -orig_bindir="$bindir" -orig_sbindir="$sbindir" -orig_libdir="$libdir" -orig_libexecdir="$libexecdir" -orig_datarootdir="$datarootdir" -orig_datadir="$datadir" -orig_sysconfdir="$sysconfdir" -orig_sharedstatedir="$sharedstatedir" -orig_localstatedir="$localstatedir" -orig_runstatedir="$runstatedir" -orig_includedir="$includedir" -orig_infodir="$infodir" -orig_mandir="$mandir" -orig_localedir="$localedir" # check if a config.site exists and load it +CONFIG_SITE_OK=0 if [ -n "$CONFIG_SITE" ]; then # CONFIG_SITE may contain space separated file names for cs in $CONFIG_SITE; do printf "loading defaults from $cs... " - . "$cs" - echo ok + if [ -f "$cs" ]; then + . "$cs" + echo ok + CONFIG_SITE_OK=1 + break + else + echo "not found" + fi done elif [ -f "$prefix/share/config.site" ]; then printf "loading site defaults... " . "$prefix/share/config.site" echo ok + CONFIG_SITE_OK=1 elif [ -f "$prefix/etc/config.site" ]; then printf "loading site defaults... " . "$prefix/etc/config.site" echo ok -else + CONFIG_SITE_OK=1 +fi + +if [ $CONFIG_SITE_OK -eq 0 ]; then # try to detect the correct libdir on our own, except it was changed by the user - if test "$libdir" = '${exec_prefix}/lib'; then - if [ "$OS" = "SunOS" ]; then - test -d "${exec_prefix}/lib/amd64" && libdir='${exec_prefix}/lib/amd64' - else - # check if the standard libdir even exists - if test -d "${exec_prefix}/lib" ; then - : + if [ "$libdir" = '${exec_prefix}/lib' ] ; then + if [ "$TOOLCHAIN_WSIZE" = "64" ] ; then + if [ "$OS" = "SunOS" ]; then + [ -d "${exec_prefix}/lib/64" ] && libdir='${exec_prefix}/lib/64' else - # if it does not, maybe a lib32 exists - test -d "${exec_prefix}/lib32" && libdir='${exec_prefix}/lib32' + [ -d "${exec_prefix}/lib64" ] && libdir='${exec_prefix}/lib64' fi - # now check if there is a special 64bit libdir that we should use - for i in x86_64 ppc64 s390x aarch64 aarch64_be arm64 ; do - if [ $ARCH = $i ]; then - test -d "${exec_prefix}/lib64" && libdir='${exec_prefix}/lib64' - break - fi - done + elif [ "$TOOLCHAIN_WSIZE" = "32" ] ; then + if [ "$OS" = "SunOS" ]; then + [ -d "${exec_prefix}/lib/32" ] && libdir='${exec_prefix}/lib/32' + else + [ -d "${exec_prefix}/lib32" ] && libdir='${exec_prefix}/lib32' + fi fi fi fi @@ -267,40 +237,14 @@ # generate vars.mk cat > "$TEMP_DIR/vars.mk" << __EOF__ -prefix=$prefix -exec_prefix=$exec_prefix -bindir=$bindir -sbindir=$sbindir -libdir=$libdir -libexecdir=$libexecdir -datarootdir=$datarootdir -datadir=$datadir -sysconfdir=$sysconfdir -sharedstatedir=$sharedstatedir -localstatedir=$localstatedir -runstatedir=$runstatedir -includedir=$includedir -infodir=$infodir -mandir=$mandir -localedir=$localedir __EOF__ -# toolchain detection utilities -. make/toolchain.sh # # DEPENDENCIES # -# check languages -lang_c= -lang_cpp= -if detect_cpp_compiler ; then - lang_cpp=1 -fi -if detect_c_compiler ; then - lang_c=1 -fi + # create buffer for make variables required by dependencies echo > "$TEMP_DIR/make.mk" @@ -326,6 +270,22 @@ fi } +dependency_error_server() +{ + print_check_msg "$dep_checked_server" "checking for server... " + # dependency server + while true + do + TEMP_CFLAGS="$TEMP_CFLAGS -DUI_SERVER -DUI_WEBVIEW" + print_check_msg "$dep_checked_server" "yes\n" + dep_checked_server=1 + return 1 + done + + print_check_msg "$dep_checked_server" "no\n" + dep_checked_server=1 + return 0 +} dependency_error_gtk2legacy() { print_check_msg "$dep_checked_gtk2legacy" "checking for gtk2legacy... " @@ -364,7 +324,7 @@ if [ -z "$PKG_CONFIG" ]; then break fi - if which qmake-qt5 > /dev/null ; then + if which qmake-qt5 > /dev/null 2>&1 ; then : else break @@ -399,7 +359,7 @@ if [ -z "$PKG_CONFIG" ]; then break fi - if pkg-config --atleast-version=2.20 gtk+-2.0 > /dev/null ; then + if pkg-config --atleast-version=2.20 gtk+-2.0 > /dev/null 2>&1 ; then : else break @@ -944,6 +904,20 @@ __EOF__ return 0 } +checkopt_toolkit_server() +{ + VERR=0 + if dependency_error_server ; then + VERR=1 + fi + if [ $VERR -ne 0 ]; then + return 1 + fi + cat >> "$TEMP_DIR/make.mk" << __EOF__ +TOOLKIT = server +__EOF__ + return 0 +} # # TARGETS @@ -1090,10 +1064,18 @@ ERROR=1 DEPENDENCIES_FAILED="option 'toolkit' $DEPENDENCIES_FAILED" fi + elif [ "$OPT_TOOLKIT" = "server" ]; then + echo " toolkit: $OPT_TOOLKIT" >> $TEMP_DIR/options + if checkopt_toolkit_server ; then + : + else + ERROR=1 + DEPENDENCIES_FAILED="option 'toolkit' $DEPENDENCIES_FAILED" + fi else echo echo "Invalid option value - usage:" - echo " --toolkit=(libadwaita|gtk4|gtk3|gtk2|gtk2legacy|qt5|qt4|cocoa|motif)" + echo " --toolkit=(libadwaita|gtk4|gtk3|gtk2|gtk2legacy|qt5|qt4|cocoa|motif|server)" abort_configure fi fi @@ -1153,7 +1135,7 @@ echo "configure finished" echo -echo "Toolchain" +echo "Toolchain:" echo " name: $TOOLCHAIN_NAME" if [ -n "$TOOLCHAIN_CC" ]; then echo " cc: $TOOLCHAIN_CC" @@ -1168,60 +1150,17 @@ echo " default C std: $TOOLCHAIN_CSTD" fi echo -echo "Build Config:" -echo " prefix: $prefix" -echo " exec_prefix: $exec_prefix" -if [ "$orig_bindir" != "$bindir" ]; then - echo " bindir: $bindir" -fi -if [ "$orig_sbindir" != "$sbindir" ]; then - echo " sbindir: $sbindir" -fi -if [ "$orig_libdir" != "$libdir" ]; then - echo " libdir: $libdir" -fi -if [ "$orig_libexecdir" != "$libexecdir" ]; then - echo " libexecdir: $libexecdir" -fi -if [ "$orig_datarootdir" != "$datarootdir" ]; then - echo " datarootdir: $datarootdir" -fi -if [ "$orig_datadir" != "$datadir" ]; then - echo " datadir: $datadir" -fi -if [ "$orig_sysconfdir" != "$sysconfdir" ]; then - echo " sysconfdir: $sysconfdir" -fi -if [ "$orig_sharedstatedir" != "$sharedstatedir" ]; then - echo " sharedstatedir: $sharedstatedir" -fi -if [ "$orig_localstatedir" != "$localstatedir" ]; then - echo " localstatedir: $localstatedir" -fi -if [ "$orig_runstatedir" != "$runstatedir" ]; then - echo " runstatedir: $runstatedir" -fi -if [ "$orig_includedir" != "$includedir" ]; then - echo " includedir: $includedir" -fi -if [ "$orig_infodir" != "$infodir" ]; then - echo " infodir: $infodir" -fi -if [ "$orig_mandir" != "$mandir" ]; then - echo " mandir: $mandir" -fi -if [ "$orig_localedir" != "$localedir" ]; then - echo " localedir: $localedir" -fi +echo "Config:" echo echo "Options:" cat "$TEMP_DIR/options" echo echo "Features:" +printf ' %-16s' 'client:' if [ -n "$FEATURE_CLIENT" ]; then -echo " client: on" + echo 'on' else -echo " client: off" + echo 'off' fi echo
--- a/make/configure.vm Sun Nov 30 18:17:49 2025 +0100 +++ b/make/configure.vm Sun Nov 30 21:12:00 2025 +0100 @@ -86,27 +86,30 @@ printhelp() { echo "Usage: $0 [OPTIONS]..." - cat << __EOF__ -Installation directories: - --prefix=PREFIX path prefix for architecture-independent files - [${D}prefix] - --exec-prefix=EPREFIX path prefix for architecture-dependent files - [PREFIX] - - --bindir=DIR user executables [EPREFIX/bin] - --sbindir=DIR system admin executables [EPREFIX/sbin] - --libexecdir=DIR program executables [EPREFIX/libexec] - --sysconfdir=DIR system configuration files [PREFIX/etc] - --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] - --localstatedir=DIR modifiable single-machine data [PREFIX/var] - --runstatedir=DIR run-time variable data [LOCALSTATEDIR/run] - --libdir=DIR object code libraries [EPREFIX/lib] - --includedir=DIR C header files [PREFIX/include] - --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] - --datadir=DIR read-only architecture-independent data [DATAROOTDIR] - --infodir=DIR info documentation [DATAROOTDIR/info] - --mandir=DIR man documentation [DATAROOTDIR/man] - --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + echo 'Configuration:' +#foreach( $cfg in $config ) +if true \ +#if( $cfg.platform ) + && isplatform "${cfg.platform}" \ +#end +#foreach( $np in $cfg.notList ) + && notisplatform "${np}" \ +#end + ; then + : + #foreach( $var in $cfg.vars ) + #if( $var.overridable ) + if test -z "${D}${var.varName}__described__"; then + ${var.varName}__described__=1 + cat << '__EOF__' +${var.helpText} +__EOF__ + fi + #end + #end +fi +#end + cat << '__EOF__' Build Types: --debug add extra compile flags for debug builds @@ -115,7 +118,7 @@ Options: #foreach( $opt in $options ) - --${opt.argument}=${opt.valuesString} +${opt.helpText} #end #end #if( $features.size() > 0 ) @@ -127,6 +130,7 @@ #end __EOF__ + abort_configure } # create temporary directory @@ -142,26 +146,7 @@ touch "$TEMP_DIR/options" touch "$TEMP_DIR/features" -# define standard variables -# also define standard prefix (this is where we will search for config.site) -prefix=/usr -exec_prefix= -bindir= -sbindir= -libdir= -libexecdir= -datarootdir= -datadir= -sysconfdir= -sharedstatedir= -localstatedir= -runstatedir= -includedir= -infodir= -localedir= -mandir= - -# custom variables +# config variables #foreach( $cfg in $config ) if true \ #if( $cfg.platform ) @@ -171,12 +156,16 @@ && notisplatform "${np}" \ #end ; then + : #foreach( $var in $cfg.vars ) - #if( $var.exec ) - ${var.varName}=`${var.value}` - #else - ${var.varName}="${var.value}" - #end + if test -z "${D}${var.varName}__initialized__"; then + ${var.varName}__initialized__=1 + #if( $var.exec ) + ${var.varName}=`${var.value}` + #else + ${var.varName}='${var.value}' + #end + fi #end fi #end @@ -195,27 +184,17 @@ for ARG in "$@" do case "$ARG" in - "--prefix="*) prefix=${D}{ARG#--prefix=} ;; - "--exec-prefix="*) exec_prefix=${D}{ARG#--exec-prefix=} ;; - "--bindir="*) bindir=${D}{ARG#----bindir=} ;; - "--sbindir="*) sbindir=${D}{ARG#--sbindir=} ;; - "--libdir="*) libdir=${D}{ARG#--libdir=} ;; - "--libexecdir="*) libexecdir=${D}{ARG#--libexecdir=} ;; - "--datarootdir="*) datarootdir=${D}{ARG#--datarootdir=} ;; - "--datadir="*) datadir=${D}{ARG#--datadir=} ;; - "--sysconfdir="*) sysconfdir=${D}{ARG#--sysconfdir=} ;; - "--sharedstatedir="*) sharedstatedir=${D}{ARG#--sharedstatedir=} ;; - "--localstatedir="*) localstatedir=${D}{ARG#--localstatedir=} ;; - "--includedir="*) includedir=${D}{ARG#--includedir=} ;; - "--infodir="*) infodir=${D}{ARG#--infodir=} ;; - "--mandir"*) mandir=${D}{ARG#--mandir} ;; - "--localedir"*) localedir=${D}{ARG#--localedir} ;; - "--help"*) printhelp; abort_configure ;; - "--debug") BUILD_TYPE="debug" ;; - "--release") BUILD_TYPE="release" ;; + #foreach( $var in $vars ) + #if ($var.overridable) + "--${var.arg}="*) ${var.varName}=${D}{ARG#--${var.arg}=} ;; + #end + #end + "--help"*) printhelp ;; + "--debug") BUILD_TYPE="debug" ;; + "--release") BUILD_TYPE="release" ;; #foreach( $opt in $options ) - "--${opt.argument}="*) ${opt.varName}=${D}{ARG#--${opt.argument}=} ;; - "--${opt.argument}") echo "option '$ARG' needs a value:"; echo " $ARG=${opt.valuesString}"; abort_configure ;; + "--${opt.arg}="*) ${opt.varName}=${D}{ARG#--${opt.arg}=} ;; + "--${opt.arg}") echo "option '$ARG' needs a value:"; echo " $ARG=${opt.valuesString}"; abort_configure ;; #end #foreach( $feature in $features ) "--enable-${feature.arg}") ${feature.varName}=on ;; @@ -225,6 +204,18 @@ esac done +# toolchain detection utilities +. make/toolchain.sh + +# check languages +lang_c= +lang_cpp= +#foreach( $lang in $languages ) +if detect_${lang}_compiler ; then + lang_${lang}=1 +fi +#end + ## Begin unparsed content. ** #[[ @@ -236,67 +227,76 @@ : ${libexecdir:='${exec_prefix}/libexec'} : ${datarootdir:='${prefix}/share'} : ${datadir:='${datarootdir}'} -: ${sysconfdir:='${prefix}/etc'} : ${sharedstatedir:='${prefix}/com'} -: ${localstatedir:='${prefix}/var'} -: ${runstatedir:='${localstatedir}/run'} +if [ -z "$sysconfdir" ]; then + if [ "$prefix" = '/usr' ]; then + sysconfdir='/etc' + else + sysconfdir='${prefix}/etc' + fi +fi +if [ -z "$localstatedir" ]; then + if [ "$prefix" = '/usr' ]; then + localstatedir='/var' + else + localstatedir='${prefix}/var' + fi +fi +if [ -z "$runstatedir" ]; then + if [ "$prefix" = '/usr' ]; then + runstatedir='/var/run' + else + runstatedir='${prefix}/var' + fi +fi : ${includedir:='${prefix}/include'} : ${infodir:='${datarootdir}/info'} : ${mandir:='${datarootdir}/man'} : ${localedir:='${datarootdir}/locale'} -# remember the above values and compare them later -orig_bindir="$bindir" -orig_sbindir="$sbindir" -orig_libdir="$libdir" -orig_libexecdir="$libexecdir" -orig_datarootdir="$datarootdir" -orig_datadir="$datadir" -orig_sysconfdir="$sysconfdir" -orig_sharedstatedir="$sharedstatedir" -orig_localstatedir="$localstatedir" -orig_runstatedir="$runstatedir" -orig_includedir="$includedir" -orig_infodir="$infodir" -orig_mandir="$mandir" -orig_localedir="$localedir" # check if a config.site exists and load it +CONFIG_SITE_OK=0 if [ -n "$CONFIG_SITE" ]; then # CONFIG_SITE may contain space separated file names for cs in $CONFIG_SITE; do printf "loading defaults from $cs... " - . "$cs" - echo ok + if [ -f "$cs" ]; then + . "$cs" + echo ok + CONFIG_SITE_OK=1 + break + else + echo "not found" + fi done elif [ -f "$prefix/share/config.site" ]; then printf "loading site defaults... " . "$prefix/share/config.site" echo ok + CONFIG_SITE_OK=1 elif [ -f "$prefix/etc/config.site" ]; then printf "loading site defaults... " . "$prefix/etc/config.site" echo ok -else + CONFIG_SITE_OK=1 +fi + +if [ $CONFIG_SITE_OK -eq 0 ]; then # try to detect the correct libdir on our own, except it was changed by the user - if test "$libdir" = '${exec_prefix}/lib'; then - if [ "$OS" = "SunOS" ]; then - test -d "${exec_prefix}/lib/amd64" && libdir='${exec_prefix}/lib/amd64' - else - # check if the standard libdir even exists - if test -d "${exec_prefix}/lib" ; then - : + if [ "$libdir" = '${exec_prefix}/lib' ] ; then + if [ "$TOOLCHAIN_WSIZE" = "64" ] ; then + if [ "$OS" = "SunOS" ]; then + [ -d "${exec_prefix}/lib/64" ] && libdir='${exec_prefix}/lib/64' else - # if it does not, maybe a lib32 exists - test -d "${exec_prefix}/lib32" && libdir='${exec_prefix}/lib32' + [ -d "${exec_prefix}/lib64" ] && libdir='${exec_prefix}/lib64' fi - # now check if there is a special 64bit libdir that we should use - for i in x86_64 ppc64 s390x aarch64 aarch64_be arm64 ; do - if [ $ARCH = $i ]; then - test -d "${exec_prefix}/lib64" && libdir='${exec_prefix}/lib64' - break - fi - done + elif [ "$TOOLCHAIN_WSIZE" = "32" ] ; then + if [ "$OS" = "SunOS" ]; then + [ -d "${exec_prefix}/lib/32" ] && libdir='${exec_prefix}/lib/32' + else + [ -d "${exec_prefix}/lib32" ] && libdir='${exec_prefix}/lib32' + fi fi fi fi @@ -305,42 +305,17 @@ # generate vars.mk cat > "$TEMP_DIR/vars.mk" << __EOF__ -prefix=$prefix -exec_prefix=$exec_prefix -bindir=$bindir -sbindir=$sbindir -libdir=$libdir -libexecdir=$libexecdir -datarootdir=$datarootdir -datadir=$datadir -sysconfdir=$sysconfdir -sharedstatedir=$sharedstatedir -localstatedir=$localstatedir -runstatedir=$runstatedir -includedir=$includedir -infodir=$infodir -mandir=$mandir -localedir=$localedir #foreach( $var in $vars ) ${var.varName}=${D}${var.varName} #end __EOF__ -# toolchain detection utilities -. make/toolchain.sh # # DEPENDENCIES # -# check languages -lang_c= -lang_cpp= -#foreach( $lang in $languages ) -if detect_${lang}_compiler ; then - lang_${lang}=1 -fi -#end + # create buffer for make variables required by dependencies echo > "$TEMP_DIR/make.mk" @@ -400,7 +375,7 @@ fi #end #foreach( $test in $sub.tests ) - if $test > /dev/null ; then + if $test > /dev/null 2>&1 ; then : else break @@ -502,7 +477,6 @@ #foreach( $flags in $dependency.flags ) #if( $flags.exec ) - $flags.value > /dev/null if tmp_flags=`$flags.value` ; then TEMP_$flags.varName="$TEMP_$flags.varName $tmp_flags" else @@ -652,9 +626,9 @@ #end #foreach( $opt in $target.options ) -# Option: --${opt.argument} +# Option: --${opt.arg} if [ -z "${D}${opt.varName}" ]; then - echo "auto-detecting option '${opt.argument}'" + echo "auto-detecting option '${opt.arg}'" SAVED_ERROR="$ERROR" SAVED_DEPENDENCIES_FAILED="$DEPENDENCIES_FAILED" ERROR=1 @@ -665,7 +639,7 @@ if isplatform "$optdef.platform"; then #end if $optdef.func ; then - echo " ${opt.argument}: ${optdef.valueName}" >> "$TEMP_DIR/options" + echo " ${opt.arg}: ${optdef.valueName}" >> "$TEMP_DIR/options" ERROR=0 break fi @@ -677,28 +651,28 @@ done if [ $ERROR -ne 0 ]; then SAVED_ERROR=1 - SAVED_DEPENDENCIES_FAILED="option '${opt.argument}' $SAVED_DEPENDENCIES_FAILED" + SAVED_DEPENDENCIES_FAILED="option '${opt.arg}' $SAVED_DEPENDENCIES_FAILED" fi ERROR="$SAVED_ERROR" DEPENDENCIES_FAILED="$SAVED_DEPENDENCIES_FAILED" else - echo "checking option ${opt.argument} = ${D}${opt.varName}" + echo "checking option ${opt.arg} = ${D}${opt.varName}" if false; then false #foreach( $optval in $opt.values ) elif [ "${D}${opt.varName}" = "${optval.value}" ]; then - echo " ${opt.argument}: ${D}${opt.varName}" >> $TEMP_DIR/options + echo " ${opt.arg}: ${D}${opt.varName}" >> $TEMP_DIR/options if $optval.func ; then : else ERROR=1 - DEPENDENCIES_FAILED="option '${opt.argument}' $DEPENDENCIES_FAILED" + DEPENDENCIES_FAILED="option '${opt.arg}' $DEPENDENCIES_FAILED" fi #end else echo echo "Invalid option value - usage:" - echo " --${opt.argument}=${opt.valuesString}" + echo " --${opt.arg}=${opt.valuesString}" abort_configure fi fi @@ -726,7 +700,7 @@ echo "configure finished" echo -echo "Toolchain" +echo "Toolchain:" echo " name: $TOOLCHAIN_NAME" if [ -n "$TOOLCHAIN_CC" ]; then echo " cc: $TOOLCHAIN_CC" @@ -741,51 +715,13 @@ echo " default C std: $TOOLCHAIN_CSTD" fi echo -echo "Build Config:" -echo " prefix: $prefix" -echo " exec_prefix: $exec_prefix" -if [ "$orig_bindir" != "$bindir" ]; then - echo " bindir: $bindir" -fi -if [ "$orig_sbindir" != "$sbindir" ]; then - echo " sbindir: $sbindir" -fi -if [ "$orig_libdir" != "$libdir" ]; then - echo " libdir: $libdir" -fi -if [ "$orig_libexecdir" != "$libexecdir" ]; then - echo " libexecdir: $libexecdir" -fi -if [ "$orig_datarootdir" != "$datarootdir" ]; then - echo " datarootdir: $datarootdir" -fi -if [ "$orig_datadir" != "$datadir" ]; then - echo " datadir: $datadir" -fi -if [ "$orig_sysconfdir" != "$sysconfdir" ]; then - echo " sysconfdir: $sysconfdir" -fi -if [ "$orig_sharedstatedir" != "$sharedstatedir" ]; then - echo " sharedstatedir: $sharedstatedir" -fi -if [ "$orig_localstatedir" != "$localstatedir" ]; then - echo " localstatedir: $localstatedir" -fi -if [ "$orig_runstatedir" != "$runstatedir" ]; then - echo " runstatedir: $runstatedir" -fi -if [ "$orig_includedir" != "$includedir" ]; then - echo " includedir: $includedir" -fi -if [ "$orig_infodir" != "$infodir" ]; then - echo " infodir: $infodir" -fi -if [ "$orig_mandir" != "$mandir" ]; then - echo " mandir: $mandir" -fi -if [ "$orig_localedir" != "$localedir" ]; then - echo " localedir: $localedir" -fi +echo "Config:" +#foreach( $var in $vars ) +#if ($var.overridable) + printf ' %-16s' '${var.arg}:' + echo "${D}${var.varName}" +#end +#end #if ( $options.size() > 0 ) echo echo "Options:" @@ -795,10 +731,11 @@ echo echo "Features:" #foreach( $feature in $features ) +printf ' %-16s' '$feature.name:' if [ -n "${D}${feature.varName}" ]; then -echo " $feature.name: on" + echo 'on' else -echo " $feature.name: off" + echo 'off' fi #end #end
--- a/make/project.xml Sun Nov 30 18:17:49 2025 +0100 +++ b/make/project.xml Sun Nov 30 21:12:00 2025 +0100 @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<project version="0.3" xmlns="http://unixwork.de/uwproj"> +<project version="0.4" xmlns="http://unixwork.de/uwproj"> <dependency> <lang>c</lang> <make>LD = \$(CC)</make> @@ -96,6 +96,10 @@ <!-- webview unsupported --> </dependency> + <dependency name="server"> + <cflags>-DUI_SERVER -DUI_WEBVIEW</cflags> + </dependency> + <dependency platform="macos"> <make>OBJ_EXT = .o</make> <make>LIB_EXT = .a</make> @@ -165,6 +169,10 @@ <dependencies>motif</dependencies> <make>TOOLKIT = motif</make> </value> + <value str="server"> + <dependencies>server</dependencies> + <make>TOOLKIT = server</make> + </value> <default value="winui" platform="windows" /> <default value="cocoa" platform="macos" /> <default value="gtk4" />
--- a/make/toolchain.sh Sun Nov 30 18:17:49 2025 +0100 +++ b/make/toolchain.sh Sun Nov 30 21:12:00 2025 +0100 @@ -24,8 +24,9 @@ check_c_compiler() { - command -v "$1" >/dev/null 2>&1 - if [ $? -ne 0 ] ; then + if command -v "$1" >/dev/null 2>&1 ; then + : + else return 1 fi cat > "$TEMP_DIR/test.c" << __EOF__ @@ -56,8 +57,9 @@ check_cpp_compiler() { - command -v "$1" >/dev/null 2>&1 - if [ $? -ne 0 ] ; then + if command -v "$1" >/dev/null 2>&1 ; then + : + else return 1 fi cat > "$TEMP_DIR/test.cpp" << __EOF__
--- a/make/uwproj.xsd Sun Nov 30 18:17:49 2025 +0100 +++ b/make/uwproj.xsd Sun Nov 30 21:12:00 2025 +0100 @@ -3,7 +3,7 @@ xmlns="http://unixwork.de/uwproj" targetNamespace="http://unixwork.de/uwproj" elementFormDefault="qualified" - version="0.3" + version="0.4" > <xs:element name="project" type="ProjectType"/> @@ -29,23 +29,61 @@ <xs:documentation> <p> The configuration section. - Consists of an arbitrary number of <code>var</code> elements. + Consists of an arbitrary number of <code>var</code> elements and pre-defined elements for + standard installation directories. If you want to use standard installation directories, you + must list the wanted variables here. </p> <p> The optional <code>platform</code> attribute may specify a <em>single</em> platform identifier and the optional <code>not</code> attribute may specify a comma-separated list of platform identifiers. The configure script shall skip this config declaration if the detected platform is not matching the filter specification of these attributes. + When multiple config sections have a matching filter, and declare the same variables, the settings + of the first matching config section will be used for the affected variables. </p> </xs:documentation> </xs:annotation> <xs:sequence> + <xs:element name="prefix" type="StandardConfigVarType" minOccurs="0"/> + <xs:element name="exec-prefix" type="StandardConfigVarType" minOccurs="0"/> + <xs:element name="bindir" type="StandardConfigVarType" minOccurs="0"/> + <xs:element name="sbindir" type="StandardConfigVarType" minOccurs="0"/> + <xs:element name="libdir" type="StandardConfigVarType" minOccurs="0"/> + <xs:element name="libexecdir" type="StandardConfigVarType" minOccurs="0"/> + <xs:element name="datarootdir" type="StandardConfigVarType" minOccurs="0"/> + <xs:element name="datadir" type="StandardConfigVarType" minOccurs="0"/> + <xs:element name="sysconfdir" type="StandardConfigVarType" minOccurs="0"/> + <xs:element name="sharedstatedir" type="StandardConfigVarType" minOccurs="0"/> + <xs:element name="localstatedir" type="StandardConfigVarType" minOccurs="0"/> + <xs:element name="runstatedir" type="StandardConfigVarType" minOccurs="0"/> + <xs:element name="includedir" type="StandardConfigVarType" minOccurs="0"/> + <xs:element name="infodir" type="StandardConfigVarType" minOccurs="0"/> + <xs:element name="localedir" type="StandardConfigVarType" minOccurs="0"/> + <xs:element name="mandir" type="StandardConfigVarType" minOccurs="0"/> <xs:element name="var" type="ConfigVarType" minOccurs="0" maxOccurs="unbounded"/> </xs:sequence> <xs:attribute name="platform" type="xs:string"/> <xs:attribute name="not" type="xs:string"/> </xs:complexType> + <xs:complexType name="StandardConfigVarType"> + <xs:annotation> + <xs:documentation> + The definition of a standard configuration variable. + <p> + You may customize the value and the help text, + but the variable name and the option name are pre-defined. + </p> + </xs:documentation> + </xs:annotation> + <xs:simpleContent> + <xs:extension base="xs:string"> + <xs:attribute name="option-help" type="xs:string"/> + <xs:attribute name="exec" type="xs:boolean" default="false"/> + </xs:extension> + </xs:simpleContent> + </xs:complexType> + <xs:complexType name="ConfigVarType"> <xs:annotation> <xs:documentation> @@ -55,14 +93,23 @@ written to the resulting config file (in contrast to make variables, which are only written to the config file). The <code>name</code> attribute is mandatory, the value is defined by the text body of the element. - The optional Boolean <code>exec</code> attribute (false by default) controls, whether the entire - definition is automatically executed under command substitution. + The optional Boolean <code>exec</code> attribute (false by default) controls, whether value denotes + a command which shall be executed at configuration time to produce the value. + With <code>option</code> and <code>option-help</code> you can control how the variable can be + overridden on the command line. When you don't specify either of those attributes, no command + line option will be generated. When you provide a <code>option-help</code>, but do not specify the + <code>option</code> name, a name is generated. + You can use the string <code>%default</code> in your help text when you want to show the default + value in the text. When <code>exec</code> is used, the default will not be resolved in the help + text and instead the command is shown (to avoid breaking the formatting). </p> </xs:documentation> </xs:annotation> <xs:simpleContent> <xs:extension base="xs:string"> <xs:attribute name="name" type="xs:string" use="required"/> + <xs:attribute name="option" type="xs:string"/> + <xs:attribute name="option-help" type="xs:string"/> <xs:attribute name="exec" type="xs:boolean" default="false"/> </xs:extension> </xs:simpleContent> @@ -228,7 +275,9 @@ <xs:documentation> Declares a configuration option. The option argument name is specified with the <code>arg</code> attribute. - Then, the children of this element specify possible <code>values</code> by defining the conditions + Optionally, a description for the help text of the resulting configure script can be specified by + a <code>desc</code> element. + Then, the next children of this element specify possible <code>values</code> by defining the conditions (in terms of dependencies) and effects (in terms of defines and make variables) of each value. Finally, a set of <code>default</code>s is specified which supposed to automagically select the most appropriate value for a specific platform under the available dependencies (in case the option is not @@ -236,6 +285,7 @@ </xs:documentation> </xs:annotation> <xs:sequence> + <xs:element name="desc" type="xs:string" minOccurs="0"/> <xs:element name="value" type="OptionValueType" minOccurs="0" maxOccurs="unbounded"/> <xs:element name="default" type="OptionDefaultType" minOccurs="0" maxOccurs="unbounded"/> </xs:sequence>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/common/message.c Sun Nov 30 21:12:00 2025 +0100 @@ -0,0 +1,174 @@ +/* + * 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; + + return NULL; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/common/message.h Sun Nov 30 21:12:00 2025 +0100 @@ -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 <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 */ +
--- a/ui/common/objs.mk Sun Nov 30 18:17:49 2025 +0100 +++ b/ui/common/objs.mk Sun Nov 30 21:12:00 2025 +0100 @@ -43,6 +43,7 @@ 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)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/server/Makefile Sun Nov 30 21:12:00 2025 +0100 @@ -0,0 +1,36 @@ +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. +# +# Copyright 2012 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. +# + +$(SERVER_OBJPRE)%.o: server/%.c + $(CC) -o $@ -c -I../ucx $(CFLAGS) $(SHLIB_CFLAGS) $(TK_CFLAGS) $< + +$(UI_LIB): $(OBJ) + $(AR) $(ARFLAGS) $(UI_LIB) $(OBJ) + +$(UI_SHLIB): $(OBJ) + $(CC) -o $(UI_SHLIB) $(LDFLAGS) $(SHLIB_LDFLAGS) $(TK_LDFLAGS) $(OBJ) -L../build/lib -lucx
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/server/objs.mk Sun Nov 30 21:12:00 2025 +0100 @@ -0,0 +1,35 @@ +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. +# +# Copyright 2012 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. +# + +SERVER_SRC_DIR = ui/server/ +SERVER_OBJPRE = $(OBJ_DIR)$(SERVER_SRC_DIR) + +SERVEROBJ = toolkit.o + +TOOLKITOBJS += $(SERVEROBJ:%=$(SERVER_OBJPRE)%) +TOOLKITSOURCE += $(SERVEROBJ:%.o=SERVER/%.c)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/server/toolkit.c Sun Nov 30 21:12:00 2025 +0100 @@ -0,0 +1,36 @@ +/* + * 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 <pthread.h> + +#include "toolkit.h" + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/server/toolkit.h Sun Nov 30 21:12:00 2025 +0100 @@ -0,0 +1,48 @@ +/* + * 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 TOOLKIT_H +#define TOOLKIT_H + +#include <inttypes.h> +#include "../ui/toolkit.h" +#include "../common/context.h" +#include "../common/object.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + +#ifdef __cplusplus +} +#endif + +#endif /* TOOLKIT_H */ +
--- a/ui/ui/toolkit.h Sun Nov 30 18:17:49 2025 +0100 +++ b/ui/ui/toolkit.h Sun Nov 30 21:12:00 2025 +0100 @@ -146,6 +146,12 @@ #endif +#elif UI_SERVER + +#define UIWIDGET void* +#define UIWINDOW void* +#define UIMENU void* + #endif #ifndef TRUE
--- a/ui/ui/widget.h Sun Nov 30 18:17:49 2025 +0100 +++ b/ui/ui/widget.h Sun Nov 30 21:12:00 2025 +0100 @@ -64,6 +64,8 @@ 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);