diff --git a/private/lib/linux/x86_64/QtWebEngineProcess.exe b/private/lib/linux/x86_64/QtWebEngineProcess.exe deleted file mode 120000 index 81792f9..0000000 --- a/private/lib/linux/x86_64/QtWebEngineProcess.exe +++ /dev/null @@ -1 +0,0 @@ -QtWebEngineProcess \ No newline at end of file diff --git a/private/lib/linux/x86_64/librktwebview.so b/private/lib/linux/x86_64/librktwebview.so new file mode 100755 index 0000000..3ca127e Binary files /dev/null and b/private/lib/linux/x86_64/librktwebview.so differ diff --git a/private/lib/linux/x86_64/librktwebview_qt.so b/private/lib/linux/x86_64/librktwebview_qt.so deleted file mode 100755 index 6aaa0b0..0000000 Binary files a/private/lib/linux/x86_64/librktwebview_qt.so and /dev/null differ diff --git a/private/lib/linux/x86_64/rktwebview_prg b/private/lib/linux/x86_64/rktwebview_prg new file mode 100755 index 0000000..d65bb72 Binary files /dev/null and b/private/lib/linux/x86_64/rktwebview_prg differ diff --git a/private/racket-webview-qt.rkt b/private/racket-webview-qt.rkt index 876d1cf..ad997fb 100644 --- a/private/racket-webview-qt.rkt +++ b/private/racket-webview-qt.rkt @@ -60,6 +60,7 @@ (define-runtime-path lib-dir "lib") +#| (define libraries-to-preload (cond ([eq? os 'windows] @@ -105,11 +106,12 @@ )) ) ) +|# (define ffi-library (cond - ([eq? os 'windows] 'rktwebview_qt.dll) - ([eq? os 'linux] 'librktwebview_qt.so) + ([eq? os 'windows] 'rktwebview.dll) + ([eq? os 'linux] 'librktwebview.so) ) ) @@ -121,16 +123,26 @@ ; c:\qt\6.10.2\msvc2022_64\bin\windeployqt.exe rktwebview_qt_test.exe (define quiet-call #t) +(define rktwebview-prg (if (eq? os 'windows) + "rktwebview_prg.exe" + "rktwebview_prg")) + +(define webengine-process (if (eq? os 'windows) + "QtWebEngineProcess.exe" + "QtWebEngineProcess")) + (set! quiet-call (when (or (eq? os 'windows) (eq? os 'linux)) (putenv "QT_PLUGIN_PATH" (path->string (build-path os-lib-dir))) (putenv "QTWEBENGINEPROCESS_PATH" - (path->string (build-path os-lib-dir "QtWebEngineProcess.exe"))) + (path->string (build-path os-lib-dir webengine-process))) (putenv "QTWEBENGINE_RESOURCES_PATH" (path->string (build-path os-lib-dir "resources"))) (putenv "QTWEBENGINE_LOCALES_PATH" (path->string (build-path os-lib-dir "translations" "qtwebengine_locales"))) + (putenv "RKT_WEBVIEW_PRG" + (path->string (build-path os-lib-dir rktwebview-prg))) (when (eq? os 'linux) (putenv "QT_QPA_PLATFORM" "xcb") (putenv "LD_LIBRARY_PATH" @@ -145,6 +157,7 @@ ;;; Preload libraries +#| (for-each (λ (lib-symbol) (let* ((libn (if (list? lib-symbol) (car lib-symbol) lib-symbol)) (versions (if (list? lib-symbol) (cons (cadr lib-symbol) '(#f)) (list #f))) @@ -160,6 +173,7 @@ ) ) libraries-to-preload) +|# ;;; Actual FFI integration @@ -192,9 +206,9 @@ ;;; Callbacks from the OS library -(define callback-box (box '())) -(define (applier thunk) - (thunk)) +;(define callback-box (box '())) +;(define (applier thunk) +; (thunk)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Types / Functions @@ -279,14 +293,23 @@ (define-rktwebview rkt_webview_cleanup (_fun -> _void)) + +;RKTWEBVIEW_EXPORT int rkt_webview_events_waiting(); +(define-rktwebview rkt_webview_events_waiting + (_fun -> _int)) + +;RKTWEBVIEW_EXPORT rkt_data_t *rkt_webview_get_event(); +(define-rktwebview rkt_webview_get_event + (_fun -> _rkt_data_t-pointer/null)) + ;RKTWEBVIEW_QT_EXPORT rkt_wv_context_t rkt_webview_new_context(const char *boilerplate_js, ; const char *optional_server_cert_pem); (define-rktwebview rkt_webview_new_context (_fun _string/utf-8 _bytes -> _int)) ;RKTWEBVIEW_QT_EXPORT void rkt_webview_process_events(int for_ms); -(define-rktwebview rkt_webview_process_events - (_fun _int -> _void)) +;(define-rktwebview rkt_webview_process_events +; (_fun _int -> _void)) ;RKTWEBVIEW_QT_EXPORT void rkt_webview_free_data(rkt_data_t *d); (define-rktwebview rkt_webview_free_data @@ -297,13 +320,9 @@ (_fun -> _rkt_data_t-pointer)) ; RKTWEBVIEW_QT_EXPORT int rkt_webview_create(rkt_wv_context_t context, -; rktwebview_t parent, -; event_cb_t js_event_cb); +; rktwebview_t parent) (define-rktwebview rkt_webview_create - (_fun _int _int - (_fun #:keep callback-box #:async-apply applier - _rkt_data_t-pointer -> _void) - -> _int)) + (_fun _int _int -> _int)) ;RKTWEBVIEW_QT_EXPORT void rkt_webview_close(int wv); (define-rktwebview rkt_webview_close @@ -403,28 +422,72 @@ ;; Initialize and start library ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(define process-events 'process) +;(define process-events 'process) -(define (stop-event-processing) - (set! process-events 'stop) - (while (eq? process-events 'stop) - (sleep 0.001))) +;(define (stop-event-processing) +; (set! process-events 'stop) +; (while (eq? process-events 'stop) +; (sleep 0.001))) + +;(define (start-event-processing) +; (thread (λ () +; (letrec ((f (λ () +; (rkt_webview_process_events 1) +; (sleep 0.001) +; (if (eq? process-events 'process) +; (f) +; (begin +; (displayln "Stopping event processing") +; (set! process-events 'stopped) +; 'done))))) +; (f))))) + +(rkt_webview_init) + + +;(set! quiet-call (start-event-processing)) +(define evt-cb-hash (make-hash)) (define (start-event-processing) (thread (λ () (letrec ((f (λ () - (rkt_webview_process_events 1) + (let ((waiting (rkt_webview_events_waiting))) + ;(displayln (format "Events waiting: ~a" waiting)) + (while (> waiting 0) + (let* ((rkt-evt (rkt_webview_get_event))) + ;(displayln rkt-evt) + (if (eq? rkt-evt #f) + (displayln (format "Unexpected: event = nullptr")) + (let ((data (rkt_data_t-data rkt-evt))) + ;(displayln data) + (let ((e (union-ref data 1))) +; (displayln e) + (let ((wv (rkt_evt_t-w e))) + ;(displayln wv) + (let ((evt (cast (rkt_evt_t-evt e) _pointer _string*/utf-8))) +; (displayln evt) + (rkt_webview_free_data rkt-evt) + (let ((cb (hash-ref evt-cb-hash wv #f))) + (unless (eq? cb #f) + (cb evt))))) + ) + ) + ) + ) + (set! waiting (- waiting 1)) + ) + ) (sleep 0.001) - (if (eq? process-events 'process) - (f) - (begin - (displayln "Stopping event processing") - (set! process-events 'stopped) - 'done))))) - (f))))) + (f)) + )) + (f))) + ) + ) -(rkt_webview_init) -(set! quiet-call (start-event-processing)) +(define evt-processing-thread (start-event-processing)) + +(define (stop-event-processing) + (kill-thread evt-processing-thread)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Provided features @@ -459,18 +522,18 @@ (define (rkt-webview-new-context boilerplate-js server-cert) (rkt_webview_new_context boilerplate-js server-cert)) - (define (rkt-webview-create context parent evt-callback close-callback) (let* ((evt-queue (make-queue)) (parent-win (if (eq? parent #f) 0 (rkt-wv-win parent))) ) - (let ((wv (rkt_webview_create context parent-win - (λ (rkt-evt) - (let* ((e (union-ref (rkt_data_t-data rkt-evt) 1)) - (evt (cast (rkt_evt_t-evt e) _pointer _string*/utf-8))) - (rkt_webview_free_data rkt-evt) ; Free event data ASAP - (enqueue! evt-queue evt) - ))))) + (let ((wv (rkt_webview_create context parent-win))) + (hash-set! evt-cb-hash wv (λ (evt) (enqueue! evt-queue evt))) +; (λ (rkt-evt) +; (let* ((e (union-ref (rkt_data_t-data rkt-evt) 1)) +; (evt (cast (rkt_evt_t-evt e) _pointer _string*/utf-8))) +; (rkt_webview_free_data rkt-evt) ; Free event data ASAP +; (enqueue! evt-queue evt) +; ))))) (let ((handle (make-rkt-wv wv evt-queue evt-callback #t close-callback))) (thread (λ () (sleep 1) @@ -492,6 +555,7 @@ (define (rkt-webview-close handle) (rkt_webview_close (rkt-wv-win handle)) (enqueue! (rkt-wv-evt-queue handle) 'quit) + (hash-remove! evt-cb-hash (rkt-wv-win handle)) ((rkt-wv-close-callback handle)) #t) @@ -608,8 +672,8 @@ (handle (cdr kv))) (rkt-webview-close handle))) open-windows)) - (stop-event-processing) (rkt_webview_cleanup) + (stop-event-processing) ) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/private/racket-webview.rkt b/private/racket-webview.rkt index 292d8a1..c6111b9 100644 --- a/private/racket-webview.rkt +++ b/private/racket-webview.rkt @@ -655,6 +655,7 @@ (define (webview-call-js wv js) (-> wv-win? string? (or/c string? list? boolean? hash?)) (let ((result (rkt-webview-call-js (wv-win-handle wv) js))) + (displayln result) (if (webview-call-js-result? result) (if (eq? (car result) 'oke) (hash-ref (fromJson (cadr result)) 'result #f) diff --git a/rktwebview_qt/CMakeLists.txt b/rktwebview_qt/CMakeLists.txt index a934dad..26adecf 100644 --- a/rktwebview_qt/CMakeLists.txt +++ b/rktwebview_qt/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.14) -project(rktwebview_qt LANGUAGES CXX) +project(rktwebview LANGUAGES CXX) set(QT_DEBUG_FIND_PACKAGE ON) set(CMAKE_AUTOUIC ON) @@ -12,14 +12,24 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(QT NAMES Qt6 REQUIRED COMPONENTS Widgets WebEngineWidgets) find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets WebEngineWidgets) -add_library(rktwebview_qt SHARED - rktwebview_qt_global.h - rktwebview_qt.cpp - rktwebview_qt.h +add_library(rktwebview SHARED + rktwebview_global.h rktwebview.h rktwebview.cpp + shm.h shm.cpp + shmqueue.h shmqueue.cpp + rkt_protocol.h + rktwebview_types.h + json.cpp json.h +) + +add_executable(rktwebview_prg + main.cpp + + rktwebview_qt.cpp + rktwebview_qt.h + webviewqt.h webviewqt.cpp - rktwebview_internal.h webviewwindow.h webviewwindow.cpp rktutils.h rktutils.cpp @@ -27,19 +37,18 @@ add_library(rktwebview_qt SHARED shm.h shm.cpp shmqueue.h shmqueue.cpp + rkt_protocol.h + rktwebview_types.h ) -target_link_libraries(rktwebview_qt PRIVATE Qt${QT_VERSION_MAJOR}::Widgets) -target_link_libraries(rktwebview_qt PRIVATE Qt${QT_VERSION_MAJOR}::WebEngineWidgets) - -target_compile_definitions(rktwebview_qt PRIVATE RKTWEBVIEW_QT_LIBRARY) - -add_executable(rktwebview_qt_test - main.cpp +add_executable(rktwebview_test + rktwebview_test.cpp ) -target_compile_definitions(rktwebview_qt_test PRIVATE RKTWEBVIEW_QT_LIBRARY) +target_link_libraries(rktwebview_prg PRIVATE Qt${QT_VERSION_MAJOR}::Widgets) +target_link_libraries(rktwebview_prg PRIVATE Qt${QT_VERSION_MAJOR}::WebEngineWidgets) + +target_compile_definitions(rktwebview PRIVATE RKTWEBVIEW_LIBRARY) + +target_link_Libraries(rktwebview_test PRIVATE rktwebview) -target_link_libraries(rktwebview_qt_test PRIVATE Qt${QT_VERSION_MAJOR}::Widgets) -target_link_libraries(rktwebview_qt_test PRIVATE Qt${QT_VERSION_MAJOR}::WebEngineWidgets) -target_link_libraries(rktwebview_qt_test PRIVATE rktwebview_qt) diff --git a/rktwebview_qt/command.h b/rktwebview_qt/command.h index fea5d76..26d3ada 100644 --- a/rktwebview_qt/command.h +++ b/rktwebview_qt/command.h @@ -26,6 +26,7 @@ #define COMMAND_FILE_OPEN 20 #define COMMAND_FILE_SAVE 21 #define COMMAND_SET_OU_TOKEN 22 +#define COMMAND_NEW_CONTEXT 23 class Command { diff --git a/rktwebview_qt/install-linux.sh b/rktwebview_qt/install-linux.sh index 23a77fd..b29deeb 100755 --- a/rktwebview_qt/install-linux.sh +++ b/rktwebview_qt/install-linux.sh @@ -7,14 +7,15 @@ LIB="../private/lib/linux/$ARCH" mkdir -p $LIB rm -f $LIB/*.so* cp build/Release/*.so $LIB +cp build/Release/rktwebview_prg $LIB -QT_PATH=`ldd build/Release/*.so | grep Qt | awk '{print $3}' | head -1 | sed -e 's%[/]lib[/].*%%'` +QT_PATH=`ldd build/Release/rktwebview_prg | grep Qt | awk '{print $3}' | head -1 | sed -e 's%[/]lib[/].*%%'` echo "QT_PATH=$QT_PATH" QT_PLUGINS="$QT_PATH/plugins" PLUGINS="platforms position generic iconengines imageformats qmltooling tls xcbglintegrations" -EXTRA_LIBS_SO=`ldd build/Release/*.so | grep Qt | awk '{ print $3 }'` +EXTRA_LIBS_SO=`ldd build/Release/rktwebview_prg | grep Qt | awk '{ print $3 }'` EXTRA_LIBS_PLATFORM_PLUGIN_XCB=`ldd $QT_PATH/plugins/platforms/libqxcb.so | grep Qt | awk '{print $3}'` for pl in $PLUGINS diff --git a/rktwebview_qt/json.cpp b/rktwebview_qt/json.cpp new file mode 100644 index 0000000..571eaa9 --- /dev/null +++ b/rktwebview_qt/json.cpp @@ -0,0 +1,505 @@ +#include "json.h" + +using std::map; +using std::deque; +using std::string; +using std::enable_if; +using std::initializer_list; +using std::is_same; +using std::is_convertible; +using std::is_integral; +using std::is_floating_point; + +std::string json_escape(const string &str) { + string output; + for( unsigned i = 0; i < str.length(); ++i ) + switch( str[i] ) { + case '\"': output += "\\\""; break; + case '\\': output += "\\\\"; break; + case '\b': output += "\\b"; break; + case '\f': output += "\\f"; break; + case '\n': output += "\\n"; break; + case '\r': output += "\\r"; break; + case '\t': output += "\\t"; break; + default : output += str[i]; break; + } + return std::move( output ); +} + +JSON::JSON() : Internal(), Type( Class::Null ){} + +JSON::JSON(std::initializer_list list) + : JSON() +{ + SetType( Class::Object ); + for( auto i = list.begin(), e = list.end(); i != e; ++i, ++i ) + operator[]( i->toString() ) = *std::next( i ); +} + +JSON::JSON(JSON &&other) + : Internal( other.Internal ) + , Type( other.Type ) +{ other.Type = Class::Null; other.Internal.Map = nullptr; } + +JSON::JSON(const JSON &other) { + switch( other.Type ) { + case Class::Object: + Internal.Map = + new map( other.Internal.Map->begin(), + other.Internal.Map->end() ); + break; + case Class::Array: + Internal.List = + new deque( other.Internal.List->begin(), + other.Internal.List->end() ); + break; + case Class::String: + Internal.String = + new string( *other.Internal.String ); + break; + default: + Internal = other.Internal; + } + Type = other.Type; +} + +JSON JSON::Make(Class type) { + JSON ret; ret.SetType( type ); + return ret; +} + +JSON &JSON::operator[](unsigned int index) { + SetType( Class::Array ); + if( index >= Internal.List->size() ) Internal.List->resize( index + 1 ); + return Internal.List->operator[]( index ); +} + +JSON &JSON::at(const std::string &key) { + return operator[]( key ); +} + +const JSON &JSON::at(const std::string &key) const { + return Internal.Map->at( key ); +} + +JSON &JSON::at(unsigned int index) { + return operator[]( index ); +} + +const JSON &JSON::at(unsigned int index) const { + return Internal.List->at( index ); +} + +int JSON::length() const { + if( Type == Class::Array ) + return Internal.List->size(); + else + return -1; +} + +bool JSON::hasKey(const std::string &key) const { + if( Type == Class::Object ) + return Internal.Map->find( key ) != Internal.Map->end(); + return false; +} + +int JSON::size() const { + if( Type == Class::Object ) + return Internal.Map->size(); + else if( Type == Class::Array ) + return Internal.List->size(); + else + return -1; +} + +JSON::Class JSON::JSONType() const { return Type; } + +bool JSON::IsNull() const { return Type == Class::Null; } + +string JSON::toString() const { bool b; return std::move( toString( b ) ); } + +string JSON::toString(bool &ok) const { + ok = (Type == Class::String); + return ok ? std::move( json_escape( *Internal.String ) ): string(""); +} + +double JSON::toFloat() const { bool b; return toFloat( b ); } + +double JSON::toFloat(bool &ok) const { + ok = (Type == Class::Floating); + return ok ? Internal.Float : 0.0; +} + +long JSON::toInt() const { bool b; return toInt( b ); } + +long JSON::toInt(bool &ok) const { + ok = (Type == Class::Integral); + return ok ? Internal.Int : 0; +} + +bool JSON::toBool() const { bool b; return toBool( b ); } + +bool JSON::toBool(bool &ok) const { + ok = (Type == Class::Boolean); + return ok ? Internal.Bool : false; +} + +JSON::JSONWrapper > JSON::ObjectRange() { + if( Type == Class::Object ) + return JSONWrapper>( Internal.Map ); + return JSONWrapper>( nullptr ); +} + +JSON::JSONWrapper > JSON::ArrayRange() { + if( Type == Class::Array ) + return JSONWrapper>( Internal.List ); + return JSONWrapper>( nullptr ); +} + +JSON::JSONConstWrapper > JSON::ObjectRange() const { + if( Type == Class::Object ) + return JSONConstWrapper>( Internal.Map ); + return JSONConstWrapper>( nullptr ); +} + +JSON::JSONConstWrapper > JSON::ArrayRange() const { + if( Type == Class::Array ) + return JSONConstWrapper>( Internal.List ); + return JSONConstWrapper>( nullptr ); +} + +string JSON::dump(int depth, const std::string &tab) const { + std::string pad = ""; + for( int i = 0; i < depth; ++i, pad += tab ); + + switch( Type ) { + case Class::Null: + return "null"; + case Class::Object: { + std::string s = "{ "; + bool skip = true; + for( auto &p : *Internal.Map ) { + if( !skip ) s += ", "; + s += ( pad + "\"" + p.first + "\" : " + p.second.dump( depth + 1, tab ) ); + skip = false; + } + s += ( " " + pad.erase( 0, 2 ) + "}" ) ; + return s; + } + case Class::Array: { + std::string s = "["; + bool skip = true; + for( auto &p : *Internal.List ) { + if( !skip ) s += ", "; + s += p.dump( depth + 1, tab ); + skip = false; + } + s += "]"; + return s; + } + case Class::String: + return "\"" + json_escape( *Internal.String ) + "\""; + case Class::Floating: + return std::to_string( Internal.Float ); + case Class::Integral: + return std::to_string( Internal.Int ); + case Class::Boolean: + return Internal.Bool ? "true" : "false"; + default: + return ""; + } + return ""; +} + +void JSON::SetType(Class type) { + if( type == Type ) + return; + + ClearInternal(); + + switch( type ) { + case Class::Null: Internal.Map = nullptr; break; + case Class::Object: Internal.Map = new std::map(); break; + case Class::Array: Internal.List = new std::deque(); break; + case Class::String: Internal.String = new std::string(); break; + case Class::Floating: Internal.Float = 0.0; break; + case Class::Integral: Internal.Int = 0; break; + case Class::Boolean: Internal.Bool = false; break; + } + + Type = type; +} + +void JSON::ClearInternal() { + switch( Type ) { + case Class::Object: delete Internal.Map; break; + case Class::Array: delete Internal.List; break; + case Class::String: delete Internal.String; break; + default:; + } +} + +JSON &JSON::operator[](const std::string &key) { + SetType( Class::Object ); return Internal.Map->operator[]( key ); +} + +JSON &JSON::operator=(const JSON &other) { + + if (&other == this) { return *this; } + + ClearInternal(); + switch( other.Type ) { + case Class::Object: + Internal.Map = + new map( other.Internal.Map->begin(), + other.Internal.Map->end() ); + break; + case Class::Array: + Internal.List = + new deque( other.Internal.List->begin(), + other.Internal.List->end() ); + break; + case Class::String: + Internal.String = + new string( *other.Internal.String ); + break; + default: + Internal = other.Internal; + } + Type = other.Type; + return *this; +} + +JSON &JSON::operator=(JSON &&other) { + ClearInternal(); + Internal = other.Internal; + Type = other.Type; + other.Internal.Map = nullptr; + other.Type = Class::Null; + return *this; +} + +JSON Array() { + return std::move( JSON::Make( JSON::Class::Array ) ); +} + +//JSON Object() { return std::move(JSON::Make(JSON::Class::Object)); } + +JSON Object() { + return std::move( JSON::Make( JSON::Class::Object ) ); +} + +std::ostream &operator<<(std::ostream &os, const JSON &json) { + os << json.dump(); + return os; +} + +// Private functions. + +static JSON parse_next( const std::string &, size_t &, std::function ); + + +static void consume_ws( const std::string &str, size_t &offset ) { + while( isspace( str[offset] ) ) ++offset; +} + +static JSON parse_object( const string &str, size_t &offset, std::function on_error ) { + JSON Object = JSON::Make( JSON::Class::Object ); + + ++offset; + consume_ws( str, offset ); + if( str[offset] == '}' ) { + ++offset; return std::move( Object ); + } + + while( true ) { + JSON Key = parse_next( str, offset, on_error ); + consume_ws( str, offset ); + if( str[offset] != ':' ) { + on_error(std::string("Error: Object: Expected colon, found '") + str[offset] + "'"); + break; + } + consume_ws( str, ++offset ); + JSON Value = parse_next( str, offset, on_error ); + Object[Key.toString()] = Value; + + consume_ws( str, offset ); + if( str[offset] == ',' ) { + ++offset; continue; + } + else if( str[offset] == '}' ) { + ++offset; break; + } + else { + on_error(std::string("ERROR: Object: Expected comma, found '") + str[offset] + "'"); + break; + } + } + + return std::move( Object ); +} + +static JSON parse_array( const string &str, size_t &offset, std::function on_error ) { + JSON Array = JSON::Make( JSON::Class::Array ); + unsigned index = 0; + + ++offset; + consume_ws( str, offset ); + if( str[offset] == ']' ) { + ++offset; return std::move( Array ); + } + + while( true ) { + Array[index++] = parse_next( str, offset, on_error ); + consume_ws( str, offset ); + + if( str[offset] == ',' ) { + ++offset; continue; + } + else if( str[offset] == ']' ) { + ++offset; break; + } + else { + on_error(std::string("ERROR: Array: Expected ',' or ']', found '") + str[offset] + "'"); + return std::move( JSON::Make( JSON::Class::Array ) ); + } + } + + return std::move( Array ); +} + +static JSON parse_string( const string &str, size_t &offset, std::function on_error ) { + JSON String; + string val; + for( char c = str[++offset]; c != '\"' ; c = str[++offset] ) { + if( c == '\\' ) { + switch( str[ ++offset ] ) { + case '\"': val += '\"'; break; + case '\\': val += '\\'; break; + case '/' : val += '/' ; break; + case 'b' : val += '\b'; break; + case 'f' : val += '\f'; break; + case 'n' : val += '\n'; break; + case 'r' : val += '\r'; break; + case 't' : val += '\t'; break; + case 'u' : { + val += "\\u" ; + for( unsigned i = 1; i <= 4; ++i ) { + c = str[offset+i]; + if( (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') ) + val += c; + else { + on_error(std::string("ERROR: String: Expected hex character in unicode escape, found '") + c + "'"); + return std::move( JSON::Make( JSON::Class::String ) ); + } + } + offset += 4; + } break; + default : val += '\\'; break; + } + } + else + val += c; + } + ++offset; + String = val; + return std::move( String ); +} + +static JSON parse_number( const string &str, size_t &offset, std::function on_error ) { + JSON Number; + string val, exp_str; + char c; + bool isDouble = false; + long exp = 0; + while( true ) { + c = str[offset++]; + if( (c == '-') || (c >= '0' && c <= '9') ) + val += c; + else if( c == '.' ) { + val += c; + isDouble = true; + } + else + break; + } + if( c == 'E' || c == 'e' ) { + c = str[ offset++ ]; + if( c == '-' ){ ++offset; exp_str += '-';} + while( true ) { + c = str[ offset++ ]; + if( c >= '0' && c <= '9' ) + exp_str += c; + else if( !isspace( c ) && c != ',' && c != ']' && c != '}' ) { + on_error(std::string("ERROR: Number: Expected a number for exponent, found '") + c + "'"); + return std::move( JSON::Make( JSON::Class::Null ) ); + } + else + break; + } + exp = std::stol( exp_str ); + } + else if( !isspace( c ) && c != ',' && c != ']' && c != '}' ) { + on_error(std::string("ERROR: Number: unexpected character '") + c + "'"); + return std::move( JSON::Make( JSON::Class::Null ) ); + } + --offset; + + if( isDouble ) + Number = std::stod( val ) * std::pow( 10, exp ); + else { + if( !exp_str.empty() ) + Number = std::stol( val ) * std::pow( 10, exp ); + else + Number = std::stol( val ); + } + return std::move( Number ); +} + +static JSON parse_bool( const string &str, size_t &offset, std::function on_error ) { + JSON Bool; + if( str.substr( offset, 4 ) == "true" ) + Bool = true; + else if( str.substr( offset, 5 ) == "false" ) + Bool = false; + else { + on_error(std::string("ERROR: Bool: Expected 'true' or 'false', found '") + str.substr( offset, 5 ) + "'"); + return std::move( JSON::Make( JSON::Class::Null ) ); + } + offset += (Bool.toBool() ? 4 : 5); + return std::move( Bool ); +} + +static JSON parse_null( const string &str, size_t &offset, std::function on_error ) { + JSON Null; + if( str.substr( offset, 4 ) != "null" ) { + on_error(std::string("ERROR: Null: Expected 'null', found '") + str.substr( offset, 4 ) + "'" ); + return std::move( JSON::Make( JSON::Class::Null ) ); + } + offset += 4; + return std::move( Null ); +} + +static JSON parse_next( const string &str, size_t &offset, std::function on_error ) { + char value; + consume_ws( str, offset ); + value = str[offset]; + switch( value ) { + case '[' : return std::move( parse_array( str, offset, on_error ) ); + case '{' : return std::move( parse_object( str, offset, on_error ) ); + case '\"': return std::move( parse_string( str, offset, on_error ) ); + case 't' : + case 'f' : return std::move( parse_bool( str, offset, on_error ) ); + case 'n' : return std::move( parse_null( str, offset, on_error ) ); + default : if( ( value <= '9' && value >= '0' ) || value == '-' ) + return std::move( parse_number( str, offset, on_error ) ); + } + on_error(std::string("ERROR: Parse: Unknown starting character '") + value + "'"); + return JSON(); +} + +JSON JSON::Load( const string &str, std::function on_error) { + size_t offset = 0; + return std::move( parse_next( str, offset, on_error ) ); +} + diff --git a/rktwebview_qt/json.h b/rktwebview_qt/json.h new file mode 100644 index 0000000..87f1b57 --- /dev/null +++ b/rktwebview_qt/json.h @@ -0,0 +1,217 @@ +#ifndef JSON_H +#define JSON_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +std::string json_escape( const std::string &str ); + +class JSON +{ + union BackingData { + BackingData( double d ) : Float( d ){} + BackingData( long l ) : Int( l ){} + BackingData( bool b ) : Bool( b ){} + BackingData( std::string s ) : String( new std::string( s ) ){} + BackingData() : Int( 0 ){} + + std::deque *List; + std::map *Map; + std::string *String; + double Float; + long Int; + bool Bool; + } Internal; + +public: + enum class Class { + Null, + Object, + Array, + String, + Floating, + Integral, + Boolean + }; + + template + class JSONWrapper { + Container *object; + + public: + JSONWrapper( Container *val ) : object( val ) {} + JSONWrapper( std::nullptr_t ) : object( nullptr ) {} + + typename Container::iterator begin() { return object ? object->begin() : typename Container::iterator(); } + typename Container::iterator end() { return object ? object->end() : typename Container::iterator(); } + typename Container::const_iterator begin() const { return object ? object->begin() : typename Container::iterator(); } + typename Container::const_iterator end() const { return object ? object->end() : typename Container::iterator(); } + }; + + template + class JSONConstWrapper { + const Container *object; + + public: + JSONConstWrapper( const Container *val ) : object( val ) {} + JSONConstWrapper( std::nullptr_t ) : object( nullptr ) {} + + typename Container::const_iterator begin() const { return object ? object->begin() : typename Container::const_iterator(); } + typename Container::const_iterator end() const { return object ? object->end() : typename Container::const_iterator(); } + }; + + JSON(); + JSON( std::nullptr_t ) : Internal(), Type( Class::Null ){} + JSON( std::initializer_list list ); + JSON( JSON&& other ); + JSON( const JSON &other ); + + JSON& operator=( const JSON &other ); + JSON& operator=( JSON&& other ); + + // Template T constructors + template + JSON( T b, typename std::enable_if::value>::type* = 0 ); + + template + JSON( T i, typename std::enable_if::value && !std::is_same::value>::type* = 0 ); + + template + JSON( T f, typename std::enable_if::value>::type* = 0 ); + + template + JSON( T s, typename std::enable_if::value>::type* = 0 ); + + ~JSON() { + switch( Type ) { + case Class::Array: + delete Internal.List; + break; + case Class::Object: + delete Internal.Map; + break; + case Class::String: + delete Internal.String; + break; + default:; + } + } + + static JSON Make( Class type ); + static JSON Load( const std::string &, std::function ); + + // Appending things. + + template + void append( T arg ) { + SetType( Class::Array ); Internal.List->emplace_back( arg ); + } + + template + void append( T arg, U... args ) { + append( arg ); append( args... ); + } + + // Assignments (template T). + + template + typename std::enable_if::value, JSON&>::type operator=( T b ) { + SetType( Class::Boolean ); Internal.Bool = b; return *this; + } + + template + typename std::enable_if::value && !std::is_same::value, JSON&>::type operator=( T i ) { + SetType( Class::Integral ); Internal.Int = i; return *this; + } + + template + typename std::enable_if::value, JSON&>::type operator=( T f ) { + SetType( Class::Floating ); Internal.Float = f; return *this; + } + + template + typename std::enable_if::value, JSON&>::type operator=( T s ) { + SetType( Class::String ); *Internal.String = std::string( s ); return *this; + } + + + + // Indexing. + JSON& operator[]( const std::string &key ); + JSON& operator[]( unsigned index ); + + JSON &at( const std::string &key ); + const JSON &at( const std::string &key ) const; + JSON &at( unsigned index ); + const JSON &at( unsigned index ) const; + + int length() const; + int size() const; + + bool hasKey( const std::string &key ) const; + + Class JSONType() const; + + /// Functions for getting primitives from the JSON object. + bool IsNull() const; + + std::string toString() const; + std::string toString( bool &ok ) const; + + double toFloat() const; + double toFloat( bool &ok ) const; + + long toInt() const; + long toInt( bool &ok ) const; + + bool toBool() const; + bool toBool( bool &ok ) const; + + JSONWrapper> ObjectRange(); + JSONWrapper> ArrayRange(); + JSONConstWrapper> ObjectRange() const; + JSONConstWrapper> ArrayRange() const; + + std::string dump( int depth = 1, const std::string &tab = std::string(" ")) const; + + friend std::ostream& operator<<( std::ostream&, const JSON & ); + +private: + void SetType( Class type ); + +private: + /* beware: only call if YOU know that Internal is allocated. No checks performed here. + This function should be called in a constructed JSON just before you are going to + overwrite Internal... + */ + void ClearInternal(); + +private: + + Class Type = Class::Null; +}; + +JSON Array(); + +template +JSON Array( T... args ) { + JSON arr = JSON::Make( JSON::Class::Array ); + arr.append( args... ); + return std::move( arr ); +} + +JSON Object(); + +std::ostream& operator<<( std::ostream &os, const JSON &json ); + + +#endif // JSON_H diff --git a/rktwebview_qt/main.cpp b/rktwebview_qt/main.cpp index 1be04a9..1b96cc4 100644 --- a/rktwebview_qt/main.cpp +++ b/rktwebview_qt/main.cpp @@ -1,94 +1,325 @@ -#include "rktwebview.h" - #include #include #include -static int _argc; -static char **_argv; +#include "rkt_protocol.h" +#include "shm.h" +#include "shmqueue.h" +#include "command.h" -void eventCb(rkt_data_t *e) +#include +#include + +#include "rktwebview_qt.h" + +static void free_data(rkt_data_t *d) { - printf("event: %s\n", e->data.event.event); - rkt_webview_free_data(e); + if (d->kind == version) { + free(d); + } else if (d->kind == event) { + free(d->data.event.event); + free(d); + } else if (d->kind == js_result) { + free(d->data.js_result.value); + free(d); + } else { + fprintf(stderr, "UNEXPECTED: data kind %d cannot be freed\n", d->kind); + } +} + +class Handler : public QThread +{ +public: + Shm *shm; + ShmQueue *command_queue; + ShmQueue *result_queue; + ShmQueue *event_queue; + Rktwebview_qt *webview_handler; + + // QThread interface +protected: + void run(); +}; + +static Handler *_handler; + +static void event_cb(rkt_data_t *data) +{ + if (data->kind != rkt_data_kind_t::event) { + return; + } + + int wv = data->data.event.wv; + char *evt = data->data.event.event; + + std::string evt_d(evt); + _handler->event_queue->enqueue(wv, evt_d); + + free_data(data); } int main(int argc, char *argv[]) { - int wv1; - int wv2; + const char *me = argv[0]; - _argc = argc; - _argv = argv; - - int k = 0; - while(k < 2) { - rkt_webview_init(); - - int context = rkt_webview_new_context("console.log('boilerplate!');", nullptr); - - wv1 = rkt_webview_create(context, 0, eventCb); - - rkt_webview_move(wv1, 200, 300); - rkt_webview_resize(wv1, 800, 600); - rkt_webview_set_url(wv1, "https://wikipedia.org"); //"http://127.0.0.1:8083"); - - k += 1; - int i = 0; - while(i < 10) { - printf("Waiting...%d\n", i); - rkt_webview_process_events(1000); - - if (i == 6) { - rkt_webview_open_devtools(wv1); - } - - if (i == 3) { - rkt_data_t *r = rkt_webview_call_js(wv1, "{ let a = 7 * 6; console.log('a = ' + a); return a; }"); - printf("rkt_js_result: %d: %s\n", r->data.js_result.result, r->data.js_result.value); - rkt_webview_free_data(r); - } - - if (i == 4) { - rkt_data_t *r = rkt_webview_call_js(wv1, "let el = document.getElementById('hi');el.value = '10';"); - printf("rkt_js_result: %d: %s\n", r->data.js_result.result, r->data.js_result.value); - rkt_webview_free_data(r); - } - - if (i == 6) { - //rkt_data_t *r = rkt_webview_call_js(wv1, "document.body.innerHTML = '

Hi!

'; return document.body.innerHTML;"); - //printf("rkt_js_result: %d: %s\n", r->data.js_result.result, r->data.js_result.value); - //rkt_webview_free_data(r); - } - - if (i == 7) { - result_t r = rkt_webview_message_box(wv1, "This is a title", "This is my message", "This is my submessage", rkt_messagetype_t::yes_no); - } - - - if (i == 10) { - wv2 = rkt_webview_create(context, wv1, eventCb); - rkt_webview_move(wv2, 400, 200); - rkt_webview_resize(wv2, 800, 600); - rkt_webview_set_url(wv2, "https://127.0.0.1"); - } - - if (i > 10) { - char buf[1000]; - sprintf(buf, "{ let obj = { e: 'test', i: %d }; window.rkt_send_event(obj); }", i); - rkt_webview_run_js(wv1, buf); - } - - if (i == 15) { - rkt_webview_close(wv2); - } - i += 1; - } - - rkt_webview_close(wv1); - rkt_webview_cleanup(); + if (argc < 6) { + fprintf(stderr, "%s: wrong number of arguments\n", me); + exit(1); + } + + const char *shm_name = argv[1]; + const char *shm_size_str = argv[2]; + const char *cmd_slot_str = argv[3]; + const char *res_slot_str = argv[4]; + const char *evt_slot_str = argv[5]; + + size_t shm_size = atoi(shm_size_str); + int cmd_slot = atoi(cmd_slot_str); + int res_slot = atoi(res_slot_str); + int evt_slot = atoi(evt_slot_str); + + fprintf(stderr, "%s %s %s %s %s %s\n", me, shm_name, shm_size_str, cmd_slot_str, res_slot_str, evt_slot_str); + fprintf(stderr, "%s %s %ld %d %d %d\n", me, shm_name, shm_size, cmd_slot, res_slot, evt_slot); + + if (!(shm_size > 0 && cmd_slot > 0 && res_slot > 0 && evt_slot > 0)) { + fprintf(stderr, "%s: Invalid shm size or slots\n", me); + exit(2); + } + + Handler *handler = new Handler(); + _handler = handler; + handler->shm = new Shm(shm_name, shm_size, false); + handler->command_queue = new ShmQueue(handler->shm, cmd_slot, false); + handler->result_queue = new ShmQueue(handler->shm, res_slot, false); + handler->event_queue = new ShmQueue(handler->shm, evt_slot, false); + handler->webview_handler = new Rktwebview_qt(argc, argv); + handler->start(); + + handler->webview_handler->initApp(); + handler->webview_handler->execApp(); + + fprintf(stderr, "waiting for thread to end\n"); + handler->wait(); + + fprintf(stderr, "cleaning up shm\n"); + + delete handler->webview_handler; + delete handler->command_queue; + delete handler->result_queue; + delete handler->event_queue; + delete handler->shm; + delete handler; + + return 0; +} + +void Handler::run() +{ + bool quit = false; + while (!quit) { + int cmd; + std::string data; + command_queue->dequeue(cmd, data, true); + QJsonObject data_obj = QJsonDocument::fromJson(data.c_str()).object(); + + switch(cmd) { + case CMD_QUIT: { + fprintf(stderr, "Got quit message\n"); + webview_handler->rktQuit(); + fprintf(stderr, "Enqueing RESULT_QUIT to result queue\n"); + result_queue->enqueue(RESULT_QUIT); + quit = true; + } + break; + case CMD_HANDLE_IS_VALID: { + int wv = data_obj["wv"].toInt(); + bool oke = webview_handler->rktValid(wv); + result_queue->enqueue(oke); + } + break; + case CMD_CONTEXT_NEW: { + QString boilerplate_js = data_obj["boilerplate_js"].toString(); + bool has_pem_cert = data_obj["has_pem_cert"].toBool(); + QString pem_cert = data_obj["pem_cert"].toString(); + int context = webview_handler->newContext(boilerplate_js, has_pem_cert, pem_cert); + result_queue->enqueue(context); + } + break; + case CMD_CREATE_WV: { + int context = data_obj["context"].toInt(); + int parent = data_obj["parent"].toInt(); + + int wv = webview_handler->rktWebViewCreate(context, parent, event_cb); + result_queue->enqueue(wv); + } + break; + case CMD_CLOSE_WV: { + int wv = data_obj["wv"].toInt(); + webview_handler->rktWebViewClose(wv); + } + break; + case CMD_SET_URL: { + int wv = data_obj["wv"].toInt(); + QString url = data_obj["url"].toString(); + result_t r = webview_handler->rktSetUrl(wv, url.toUtf8().constData()); + result_queue->enqueue(r); + } + break; + case CMD_SET_HTML: { + int wv = data_obj["wv"].toInt(); + QString html = data_obj["html"].toString(); + result_t r = webview_handler->rktSetHtml(wv, html.toUtf8().constData()); + result_queue->enqueue(r); + } + break; + case CMD_RUN_JS: { + int wv = data_obj["wv"].toInt(); + QString js = data_obj["js"].toString(); + result_t r = webview_handler->rktRunJs(wv, js); + result_queue->enqueue(r); + } + break; + case CMD_CALL_JS: { + fprintf(stderr, "Calljs\n"); + int wv = data_obj["wv"].toInt(); + QString js = data_obj["js"].toString(); + rkt_data_t *res = webview_handler->rktCallJs(wv, js.toUtf8().constData()); + result_queue->enqueue(res->data.js_result.result, res->data.js_result.value); + free_data(res); + } + break; + case CMD_OPEN_DEVTOOLS: { + int wv = data_obj["wv"].toInt(); + result_t r = webview_handler->rktOpenDevtools(wv); + result_queue->enqueue(r); + } + break; + case CMD_MOVE: { + int wv = data_obj["wv"].toInt(); + int x = data_obj["x"].toInt(); + int y = data_obj["y"].toInt(); + result_t r = webview_handler->rktMove(wv, x, y); + result_queue->enqueue(r); + } + break; + case CMD_RESIZE: { + int wv = data_obj["wv"].toInt(); + int w = data_obj["w"].toInt(); + int h = data_obj["h"].toInt(); + result_t r = webview_handler->rktResize(wv, w, h); + result_queue->enqueue(r); + } + break; + case CMD_HIDE: { + int wv = data_obj["wv"].toInt(); + result_t r = webview_handler->rktHideWindow(wv); + result_queue->enqueue(r); + } + break; + case CMD_SHOW: { + int wv = data_obj["wv"].toInt(); + result_t r = webview_handler->rktShowWindow(wv); + result_queue->enqueue(r); + } + break; + case CMD_PRESENT: { + int wv = data_obj["wv"].toInt(); + result_t r = webview_handler->rktPresentWindow(wv); + result_queue->enqueue(r); + } + break; + case CMD_MAXIMIZE: { + int wv = data_obj["wv"].toInt(); + result_t r = webview_handler->rktMaximizeWindow(wv); + result_queue->enqueue(r); + } + break; + case CMD_MINIMIZE: { + int wv = data_obj["wv"].toInt(); + result_t r = webview_handler->rktMinimizeWindow(wv); + result_queue->enqueue(r); + } + break; + case CMD_SHOW_NORMAL: { + int wv = data_obj["wv"].toInt(); + result_t r = webview_handler->rktShowNormalWindow(wv); + result_queue->enqueue(r); + } + break; + case CMD_WINDOW_STATE: { + int wv = data_obj["wv"].toInt(); + window_state_t r = webview_handler->rktWindowState(wv); + result_queue->enqueue(r); + } + break; + case CMD_SET_TITLE: { + int wv = data_obj["wv"].toInt(); + QString title = data_obj["title"].toString(); + result_t r = webview_handler->rktWindowSetTitle(wv, title.toUtf8().constData()); + result_queue->enqueue(r); + } + break; + case CMD_CHOOSE_DIR: { + int wv = data_obj["wv"].toInt(); + QString title = data_obj["title"].toString(); + QString base_dir = data_obj["base_dir"].toString(); + result_t r = webview_handler->rktChooseDir(wv, + title.toUtf8().constData(), + base_dir.toUtf8().constData() + ); + result_queue->enqueue(r); + } + break; + case CMD_FILE_OPEN: { + int wv = data_obj["wv"].toInt(); + QString title = data_obj["title"].toString(); + QString base_dir = data_obj["base_dir"].toString(); + QString permitted_exts = data_obj["permitted_exts"].toString(); + result_t r = webview_handler->rktFileOpen(wv, + title.toUtf8().constData(), + base_dir.toUtf8().constData(), + permitted_exts.toUtf8().constData() + ); + result_queue->enqueue(r); + } + break; + case CMD_FILE_SAVE: { + int wv = data_obj["wv"].toInt(); + QString title = data_obj["title"].toString(); + QString base_dir = data_obj["base_dir"].toString(); + QString permitted_exts = data_obj["permitted_exts"].toString(); + result_t r = webview_handler->rktFileSave(wv, + title.toUtf8().constData(), + base_dir.toUtf8().constData(), + permitted_exts.toUtf8().constData() + ); + result_queue->enqueue(r); + } + break; + case CMD_SET_OU_TOKEN: { + int wv = data_obj["wv"].toInt(); + QString token = data_obj["token"].toString(); + webview_handler->rktSetOUToken(wv, token.toUtf8().constData()); + } + break; + case CMD_MSG_BOX: { + int wv = data_obj["wv"].toInt(); + QString title = data_obj["title"].toString(); + QString message = data_obj["message"].toString(); + QString submsg = data_obj["submessage"].toString(); + int type = data_obj["type"].toInt(); + result_t r = webview_handler->rktMessageBox(wv, + title.toUtf8().constData(), + message.toUtf8().constData(), + submsg.toUtf8().constData(), + static_cast(type) + ); + result_queue->enqueue(r); + } + break; + default: { + fprintf(stderr, "Unknown command: %d\n", cmd); + } + } } - QCoreApplication *app = QApplication::instance(); - delete app; - printf("%p\n", QApplication::instance()); } diff --git a/rktwebview_qt/rkt_protocol.h b/rktwebview_qt/rkt_protocol.h new file mode 100644 index 0000000..aacfe2b --- /dev/null +++ b/rktwebview_qt/rkt_protocol.h @@ -0,0 +1,33 @@ +#ifndef RKT_PROTOCOL_H +#define RKT_PROTOCOL_H + +#define CMD_HANDLE_IS_VALID 1 +#define CMD_QUIT 2 +#define CMD_CONTEXT_NEW 3 // arguments: boilerplate_js: string, has_cert: bool, cert_pem: string -> context: int +#define CMD_CREATE_WV 4 // arguments: context: int, parent: int -> wv: int +#define CMD_CLOSE_WV 5 // arguments: wv: int -> void +#define CMD_SET_URL 6 // arguments: wv: int, url: string -> result_t: int +#define CMD_SET_HTML 7 // arguments: wv: int, html: string -> result_t: int +#define CMD_RUN_JS 8 // arguments: wv: int, js: string -> result_t: int +#define CMD_CALL_JS 9 // arguments: wv: int, js: string -> result_t: int +#define CMD_OPEN_DEVTOOLS 10 // arguments: wv: int -> result_t: int +#define CMD_MOVE 11 // arguments: wv: int, x: int, y: int -> result_t: int +#define CMD_RESIZE 12 // arguments: wv: int, w: int, h: int -> result_t: int +#define CMD_HIDE 13 // arguments: wv: int -> result_t: int +#define CMD_SHOW 14 // arguments: wv: int -> result_t: int +#define CMD_PRESENT 15 // arguments: wv: int -> result_t: int +#define CMD_MAXIMIZE 16 // arguments: wv: int -> result_t: int +#define CMD_MINIMIZE 17 // arguments: wv: int -> result_t: int +#define CMD_SHOW_NORMAL 18 // arguments: wv: int -> result_t: int +#define CMD_WINDOW_STATE 19 // arguments: wv: int -> window_state_t: int +#define CMD_SET_TITLE 20 // arguments: wv: int, title: string -> result_t: int +#define CMD_CHOOSE_DIR 21 // arguments: wv: int, title: string, base_dir: string -> result_t: int +#define CMD_FILE_OPEN 22 // arguments: wv: int, title: string, base_dir: string, permitted_exts: string -> result_t: int +#define CMD_FILE_SAVE 23 // arguments: wv: int, title: string, base_dir: string, permitted_exts: string -> result_t: int +#define CMD_SET_OU_TOKEN 24 // arguments: wv: int, token: string -> result_t: int +#define CMD_MSG_BOX 25 // arguments: wv: int, title:string, message: string, submessage: string, type:int -> result_t: int + +#define RESULT_QUIT 36379 + + +#endif // RKT_PROTOCOL_H diff --git a/rktwebview_qt/rktwebview.cpp b/rktwebview_qt/rktwebview.cpp index 3877dcf..3e6d10a 100644 --- a/rktwebview_qt/rktwebview.cpp +++ b/rktwebview_qt/rktwebview.cpp @@ -1,31 +1,85 @@ -#include "rktwebview_internal.h" - #include -#include #include #include -//#include +#include +#include #include -#include "rktwebview_qt.h" +#include "rktwebview.h" +#include "rkt_protocol.h" +#include #include "shmqueue.h" +#include "json.h" + +#define SHM_SIZE (10 * 1024 * 1024) // 10MB +#define COMMAND_SLOT 1 +#define COMMAND_RESULT_SLOT 2 +#define EVENT_SLOT 3 + +//#define DEBUG + +///////////////////////////////////////////////////////////////////// +// Utility functions +///////////////////////////////////////////////////////////////////// + +typedef struct { + std::string name; + size_t shm_size; + Shm *shm; + ShmQueue *command_queue; + ShmQueue *command_result_queue; + ShmQueue *event_queue; + pid_t rkt_webview_prg_pid; + bool rkt_webview_prg_started; +} Handle_t; + +Handle_t *handler = nullptr; + +static bool fileExists(const char *filename) { + return access(filename, X_OK) != -1; +} uint64_t current_ms() { using namespace std::chrono; return duration_cast(system_clock::now().time_since_epoch()).count(); } +bool runRktWebview(Handle_t *handler) +{ + // run rktwebview_prg using the environment variable RKT_WEBVIEW_PRG +#ifdef _WIN32 +#else + char *rkt_webview_prg_path = getenv("RKT_WEBVIEW_PRG"); + if (rkt_webview_prg_path == nullptr) { + fprintf(stderr, "RKT_WEBVIEW_PRG environment variable not set, cannot start webview program\n"); + return false; + } + if (!fileExists(rkt_webview_prg_path)) { + fprintf(stderr, "%s does not exist or is not executable\n", rkt_webview_prg_path); + return false; + } + + char shm_size_str[30]; + char command_slot[10]; + char command_result_slot[10]; + char event_slot[10]; + + sprintf(shm_size_str, "%ld", handler->shm_size); + sprintf(command_slot, "%d", COMMAND_SLOT); + sprintf(command_result_slot, "%d", COMMAND_RESULT_SLOT); + sprintf(event_slot, "%d", EVENT_SLOT); + + char *argv[] = { rkt_webview_prg_path, const_cast(handler->name.c_str()), shm_size_str, command_slot, command_result_slot, event_slot, nullptr }; + + int r = posix_spawn(&handler->rkt_webview_prg_pid, rkt_webview_prg_path, nullptr, nullptr, argv, environ); + + return (r == 0); +#endif +} + ///////////////////////////////////////////////////////////////////// // Main C Interface ///////////////////////////////////////////////////////////////////// -typedef struct { - Shm *shm; - ShmQueue *command_queue; - ShmQueue *event_queue; - Rktwebview_qt *rkt; -} Handle_t; - -Handle_t *handler = nullptr; void rkt_webview_cleanup() { @@ -54,76 +108,190 @@ void rkt_webview_cleanup() handler = nullptr; } */ + fprintf(stderr, "Sending quit message\n"); + handler->command_queue->enqueue(CMD_QUIT); + fprintf(stderr, "Message sent\n"); + bool stopped = false; + while(!stopped) { + int cmd; + std::string s; + fprintf(stderr, "Getting result of quit message\n"); + handler->command_result_queue->dequeue(cmd, s, true); + fprintf(stderr, "got %d\n", cmd); + if (cmd == RESULT_QUIT) { + stopped = true; + } + } + + delete handler->event_queue; + delete handler->command_result_queue; + delete handler->command_queue; + delete handler->shm; + delete handler; + handler = nullptr; } } void rkt_webview_init() { if (handler == nullptr) { - handler = new Rktwebview_qt(); - } + // Create shared memory and communication queues - if (handler->app() == nullptr) { - handler->initApp(); + char buf[1024]; +#ifdef DEBUG + sprintf(buf, "rktwebview-dbg"); +#else + pid_t p = getpid(); + sprintf(buf, "rktwebview-%x", p); +#endif + + handler = new Handle_t; + handler->name = buf; + + handler->shm_size = SHM_SIZE; + handler->shm = new Shm(handler->name.data(), handler->shm_size, true); + handler->command_queue = new ShmQueue(handler->shm, COMMAND_SLOT, true); + handler->command_result_queue = new ShmQueue(handler->shm, COMMAND_RESULT_SLOT, true); + handler->event_queue = new ShmQueue(handler->shm, EVENT_SLOT, true); + + // Start rktwebview_prg application with the right information +#ifndef DEBUG + handler->rkt_webview_prg_started = runRktWebview(handler); +#endif + } +} + +bool rkt_webview_valid(rktwebview_t wv) +{ + rkt_webview_init(); + if (handler != nullptr && handler->rkt_webview_prg_started) { + handler->command_queue->enqueue(CMD_HANDLE_IS_VALID); + int result; + std::string json_result; + handler->command_result_queue->dequeue(result, json_result, true); + return result == 0; + } else { + return false; } } rkt_wv_context_t rkt_webview_new_context(const char *boilerplate_js, const char *optional_server_cert_pem) { rkt_webview_init(); - return handler->newContext(boilerplate_js, optional_server_cert_pem); + + JSON j; + + std::string bpj(boilerplate_js); + bool has_pem_cert = optional_server_cert_pem != nullptr; + std::string osc(has_pem_cert ? optional_server_cert_pem : ""); + + j["boilerplate_js"] = bpj; + j["has_pem_cert"] = has_pem_cert; + j["pem_cert"] = osc; + + handler->command_queue->enqueue(CMD_CONTEXT_NEW, j.dump()); + + int result; + std::string json_result; + handler->command_result_queue->dequeue(result, json_result, true); + return result; } -int rkt_webview_create(rkt_wv_context_t context, rktwebview_t parent, event_cb_t js_event_cb) +int rkt_webview_create(rkt_wv_context_t context, rktwebview_t parent) { rkt_webview_init(); - return handler->rktWebViewCreate(context, parent, js_event_cb); + JSON j; + j["context"] = context; + j["parent"] = parent; + + handler->command_queue->enqueue(CMD_CREATE_WV, j.dump()); + + int result; + std::string json_result; + handler->command_result_queue->dequeue(result, json_result, true); + + return result; } void rkt_webview_close(rktwebview_t wv) { rkt_webview_init(); - handler->rktWebViewClose(wv); + + JSON j; + j["wv"] = wv; + handler->command_queue->enqueue(CMD_CLOSE_WV, j.dump()); } +#define CMDRES4(cmd, wv, key, val, key2, val2, key3, val3, key4, val4) \ + rkt_webview_init(); \ + JSON j; \ + j["wv"] = wv; \ + j[key] = val; \ + j[key2] = val2; \ + j[key3] = val3; \ + j[key4] = val4; \ + handler->command_queue->enqueue(cmd, j.dump()); \ + int result; \ + std::string json_result; \ + handler->command_result_queue->dequeue(result, json_result, true); \ + result_t r = static_cast(result); \ + return r; + +#define CMDRES3(cmd, wv, key, val, key2, val2, key3, val3) \ + CMDRES4(cmd, wv, key, val, key2, val2, key3, val3, "nil3", "none") + +#define CMDRES2(cmd, wv, key, val, key2, val2) \ + CMDRES3(cmd, wv, key, val, key2, val2, "nil2", "none") + +#define CMDRES(cmd, wv, key, val) \ + CMDRES2(cmd, wv, key, val, "nil1", "none") + +#define CMDRES0(cmd, wv) \ + CMDRES(cmd, wv, "nil0", "none") + result_t rkt_webview_set_url(rktwebview_t wv, const char *url) { - rkt_webview_init(); - result_t r = handler->rktSetUrl(wv, url); - return r; + CMDRES(CMD_SET_URL, wv, "url", url) } -result_t rkt_webview_set_html(rktwebview_t wv, const char *url) +result_t rkt_webview_set_html(rktwebview_t wv, const char *html) { - rkt_webview_init(); - result_t r = handler->rktSetHtml(wv, url); - return r; + CMDRES(CMD_SET_HTML, wv, "html", html) } result_t rkt_webview_run_js(rktwebview_t wv, const char *js) { - rkt_webview_init(); - result_t r = handler->rktRunJs(wv, js); - return r; + CMDRES(CMD_RUN_JS, wv, "js", js) } rkt_data_t *rkt_webview_call_js(rktwebview_t wv, const char *js) { rkt_webview_init(); - rkt_data_t *r = handler->rktCallJs(wv, js); + + JSON j; + j["wv"] = wv; + j["js"] = std::string(js); + handler->command_queue->enqueue(CMD_CALL_JS, j.dump()); + + int result; + std::string json_result; + handler->command_result_queue->dequeue(result, json_result, true); + + rkt_data_t *r = new rkt_data_t(); + r->kind = rkt_data_kind_t::js_result; + r->data.js_result.result = static_cast(result); + r->data.js_result.value = strdup(json_result.c_str()); + return r; } - - result_t rkt_webview_open_devtools(rktwebview_t wv) { - rkt_webview_init(); - result_t r = handler->rktOpenDevtools(wv); - return r; + CMDRES(CMD_OPEN_DEVTOOLS, wv, "nil", "none") } +/* void rkt_webview_process_events(int for_ms) { rkt_webview_init(); @@ -136,106 +304,94 @@ void rkt_webview_process_events(int for_ms) handler->doEvents(); } } +*/ result_t rkt_webview_move(rktwebview_t wv, int x, int y) { - rkt_webview_init(); - - result_t r = handler->rktMove(wv, x, y); - return r; + CMDRES2(CMD_MOVE, wv, "x", x, "y", y) } result_t rkt_webview_resize(rktwebview_t wv, int width, int height) { - rkt_webview_init(); - - result_t r = handler->rktResize(wv, width, height); - return r; + CMDRES2(CMD_RESIZE, wv, "w", width, "h", height) } -bool rkt_webview_valid(rktwebview_t wv) -{ - rkt_webview_init(); - return handler->rktValid(wv); -} - result_t rkt_webview_hide(rktwebview_t w) { - rkt_webview_init(); - return handler->rktHideWindow(w); - + CMDRES0(CMD_HIDE, w) } result_t rkt_webview_show(rktwebview_t w) { - rkt_webview_init(); - return handler->rktShowWindow(w); + CMDRES0(CMD_SHOW, w) } result_t rkt_webview_present(rktwebview_t w) { - rkt_webview_init(); - return handler->rktPresentWindow(w); + CMDRES0(CMD_PRESENT, w) } result_t rkt_webview_maximize(rktwebview_t w) { - rkt_webview_init(); - return handler->rktMaximizeWindow(w); + CMDRES0(CMD_MAXIMIZE, w) } result_t rkt_webview_minimize(rktwebview_t w) { - rkt_webview_init(); - return handler->rktMinimizeWindow(w); + CMDRES0(CMD_MINIMIZE, w) } result_t rkt_webview_show_normal(rktwebview_t w) { - rkt_webview_init(); - return handler->rktShowNormalWindow(w); + CMDRES0(CMD_SHOW_NORMAL, w) } window_state_t rkt_webview_window_state(rktwebview_t w) { - rkt_webview_init(); - return handler->rktWindowState(w); + auto f = [w]() { + CMDRES0(CMD_WINDOW_STATE, w) + }; + int r = f(); + return static_cast(r); } result_t rkt_webview_set_title(rktwebview_t wv, const char *title) { - rkt_webview_init(); - return handler->rktWindowSetTitle(wv, title); + CMDRES(CMD_SET_TITLE, wv, "title", title) } result_t rkt_webview_choose_dir(rktwebview_t w, const char *title, const char *base_dir) { - rkt_webview_init(); - return handler->rktChooseDir(w, title, base_dir); + CMDRES2(CMD_CHOOSE_DIR, w, "title", title, "base_dir", base_dir) } result_t rkt_webview_file_open(rktwebview_t w, const char *title, const char *base_dir, const char *permitted_exts) { - rkt_webview_init(); - return handler->rktFileOpen(w, title, base_dir, permitted_exts); + CMDRES3(CMD_FILE_OPEN, w, "title", title, "base_dir", base_dir, "permitted_exts", permitted_exts) } result_t rkt_webview_file_save(rktwebview_t w, const char *title, const char *base_dir, const char *permitted_exts) { - rkt_webview_init(); - return handler->rktFileSave(w, title, base_dir, permitted_exts); + CMDRES3(CMD_FILE_SAVE, w, "title", title, "base_dir", base_dir, "permitted_exts", permitted_exts) } void rkt_webview_set_ou_token(rktwebview_t wv, const char *token) { rkt_webview_init(); - handler->rktSetOUToken(wv, token); + + JSON j; + j["wv"] = wv; + j["token"] = std::string(token); + + handler->command_queue->enqueue(CMD_SET_OU_TOKEN, j.dump()); } void rkt_webview_free_data(rkt_data_t *d) { + if (d == nullptr) { return; } + if (d->kind == version) { free(d); } else if (d->kind == event) { @@ -256,14 +412,37 @@ rkt_data_t *rkt_webview_version() d->data.version.api_major = RKT_WEBVIEW_API_MAJOR; d->data.version.api_minor = RKT_WEBVIEW_API_MINOR; d->data.version.api_patch = RKT_WEBVIEW_API_PATCH; - d->data.version.qt_major = QT_VERSION_MAJOR; - d->data.version.qt_minor = QT_VERSION_MINOR; - d->data.version.qt_patch = QT_VERSION_PATCH; return d; } result_t rkt_webview_message_box(rktwebview_t w, const char *title, const char *message, const char *submessage, rkt_messagetype_t type) { - rkt_webview_init(); - return handler->rktMessageBox(w, title, message, submessage, type); + CMDRES4(CMD_MSG_BOX, w, "title", title, "message", message, "submessage", submessage, "type", static_cast(type)) +} + +int rkt_webview_events_waiting() +{ + return handler->event_queue->depth(); +} + +rkt_data_t *rkt_webview_get_event() +{ + //fprintf(stderr, "rkt_webview_get_event\n"); + int wv; + std::string data; + if (handler->event_queue->dequeue(wv, data, false)) { + //fprintf(stderr, "got event %d %s\n", wv, data.c_str()); + rkt_data_t *d = reinterpret_cast(malloc(sizeof(rkt_data_t))); + d->kind = rkt_data_kind_t::event; + + size_t ds = data.size(); + d->data.event.event = reinterpret_cast(malloc(ds + 1)); + memcpy(d->data.event.event, data.c_str(), ds); + d->data.event.event[ds] = '\0'; + d->data.event.wv = wv; + fprintf(stderr, "event: %d - '%s'\n", d->data.event.wv, d->data.event.event); + return d; + } else { + return nullptr; + } } diff --git a/rktwebview_qt/rktwebview.h b/rktwebview_qt/rktwebview.h index c661221..902a7d2 100644 --- a/rktwebview_qt/rktwebview.h +++ b/rktwebview_qt/rktwebview.h @@ -1,7 +1,8 @@ #ifndef RKTWEBVIEW_H #define RKTWEBVIEW_H -#include "rktwebview_qt_global.h" +#include "rktwebview_global.h" +#include "rktwebview_types.h" #define RKT_WEBVIEW_API_MAJOR 0 #define RKT_WEBVIEW_API_MINOR 1 @@ -9,122 +10,47 @@ extern "C" { -typedef int rktwebview_t; -typedef int rkt_wv_context_t; +RKTWEBVIEW_EXPORT void rkt_webview_init(); +RKTWEBVIEW_EXPORT void rkt_webview_cleanup(); +//RKTWEBVIEW_EXPORT void rkt_webview_process_events(int for_ms); -typedef struct { - rktwebview_t wv; - char *event; -} rkt_event_t; +RKTWEBVIEW_EXPORT void rkt_webview_free_data(rkt_data_t *d); +RKTWEBVIEW_EXPORT rkt_data_t *rkt_webview_version(); -typedef enum { - no_result_yet = -1, - oke = 0, - set_html_failed = 1, - set_navigate_failed = 2, - eval_js_failed = 3, - no_devtools_on_platform = 4, - no_delegate_for_context = 5, - webview_missing_dependency = 6, - webview_canceled = 7, - webview_invalid_state = 8, - webview_invalid_argument = 9, - webview_unspecified = 10, - webview_dispatch_failed = 11, - move_failed = 12, - resize_failed = 13, - choose_dir_failed = 14, - open_file_failed = 15, - save_file_failed = 16, - failed = 17 -} result_t; +RKTWEBVIEW_EXPORT int rkt_webview_events_waiting(); +RKTWEBVIEW_EXPORT rkt_data_t *rkt_webview_get_event(); -typedef struct { - result_t result; - char *value; -} rkt_js_result_t; +RKTWEBVIEW_EXPORT rkt_wv_context_t rkt_webview_new_context(const char *boilerplate_js, const char *optional_server_cert_pem); +RKTWEBVIEW_EXPORT int rkt_webview_create(rkt_wv_context_t context, rktwebview_t parent); -typedef enum { - invalid = -1, - normal = 0, - minimized = 1, - maximized = 2, - hidden = 3, - normal_active = 16, - maximized_active = 18 -} window_state_t; +RKTWEBVIEW_EXPORT void rkt_webview_close(rktwebview_t wv); +RKTWEBVIEW_EXPORT bool rkt_webview_valid(rktwebview_t wv); +RKTWEBVIEW_EXPORT result_t rkt_webview_set_title(rktwebview_t wv, const char *title); +RKTWEBVIEW_EXPORT void rkt_webview_set_ou_token(rktwebview_t wv, const char *token); -typedef enum { - info = 1, - error = 2, - warning = 3, - yes_no = 4, - oke_cancel = 5 -} rkt_messagetype_t; +RKTWEBVIEW_EXPORT result_t rkt_webview_set_url(rktwebview_t wv, const char *url); +RKTWEBVIEW_EXPORT result_t rkt_webview_set_html(rktwebview_t wv, const char *html); -typedef struct { - int qt_major; - int qt_minor; - int qt_patch; - int api_major; - int api_minor; - int api_patch; -} rkt_version_t; +RKTWEBVIEW_EXPORT result_t rkt_webview_run_js(rktwebview_t wv, const char *js); +RKTWEBVIEW_EXPORT rkt_data_t *rkt_webview_call_js(rktwebview_t wv, const char *js); -typedef enum { - version = 1, - event = 2, - js_result = 3 -} rkt_data_kind_t; +RKTWEBVIEW_EXPORT result_t rkt_webview_open_devtools(rktwebview_t wv); -typedef struct { - rkt_data_kind_t kind; - union { - rkt_version_t version; - rkt_event_t event; - rkt_js_result_t js_result; - } data; -} rkt_data_t; +RKTWEBVIEW_EXPORT result_t rkt_webview_move(rktwebview_t w, int x, int y); +RKTWEBVIEW_EXPORT result_t rkt_webview_resize(rktwebview_t w, int width, int height); +RKTWEBVIEW_EXPORT result_t rkt_webview_hide(rktwebview_t w); +RKTWEBVIEW_EXPORT result_t rkt_webview_show(rktwebview_t w); +RKTWEBVIEW_EXPORT result_t rkt_webview_show_normal(rktwebview_t w); +RKTWEBVIEW_EXPORT result_t rkt_webview_present(rktwebview_t w); +RKTWEBVIEW_EXPORT result_t rkt_webview_maximize(rktwebview_t w); +RKTWEBVIEW_EXPORT result_t rkt_webview_minimize(rktwebview_t w); +RKTWEBVIEW_EXPORT window_state_t rkt_webview_window_state(rktwebview_t w); -typedef void (*event_cb_t)(rkt_data_t *); +RKTWEBVIEW_EXPORT result_t rkt_webview_choose_dir(rktwebview_t w, const char *title, const char *base_dir); +RKTWEBVIEW_EXPORT result_t rkt_webview_file_open(rktwebview_t w, const char *title, const char *base_dir, const char *permitted_exts); +RKTWEBVIEW_EXPORT result_t rkt_webview_file_save(rktwebview_t w, const char *title, const char *base_dir, const char *permitted_exts); -RKTWEBVIEW_QT_EXPORT void rkt_webview_init(); -RKTWEBVIEW_QT_EXPORT void rkt_webview_cleanup(); -RKTWEBVIEW_QT_EXPORT void rkt_webview_process_events(int for_ms); -RKTWEBVIEW_QT_EXPORT void rkt_webview_free_data(rkt_data_t *d); -RKTWEBVIEW_QT_EXPORT rkt_data_t *rkt_webview_version(); - -RKTWEBVIEW_QT_EXPORT rkt_wv_context_t rkt_webview_new_context(const char *boilerplate_js, const char *optional_server_cert_pem); - -RKTWEBVIEW_QT_EXPORT int rkt_webview_create(rkt_wv_context_t context, rktwebview_t parent, event_cb_t js_event_cb); -RKTWEBVIEW_QT_EXPORT void rkt_webview_close(rktwebview_t wv); -RKTWEBVIEW_QT_EXPORT bool rkt_webview_valid(rktwebview_t wv); -RKTWEBVIEW_QT_EXPORT result_t rkt_webview_set_title(rktwebview_t wv, const char *title); -RKTWEBVIEW_QT_EXPORT void rkt_webview_set_ou_token(rktwebview_t wv, const char *token); - -RKTWEBVIEW_QT_EXPORT result_t rkt_webview_set_url(rktwebview_t wv, const char *url); -RKTWEBVIEW_QT_EXPORT result_t rkt_webview_set_html(rktwebview_t wv, const char *html); - -RKTWEBVIEW_QT_EXPORT result_t rkt_webview_run_js(rktwebview_t wv, const char *js); -RKTWEBVIEW_QT_EXPORT rkt_data_t *rkt_webview_call_js(rktwebview_t wv, const char *js); - -RKTWEBVIEW_QT_EXPORT result_t rkt_webview_open_devtools(rktwebview_t wv); - -RKTWEBVIEW_QT_EXPORT result_t rkt_webview_move(rktwebview_t w, int x, int y); -RKTWEBVIEW_QT_EXPORT result_t rkt_webview_resize(rktwebview_t w, int width, int height); -RKTWEBVIEW_QT_EXPORT result_t rkt_webview_hide(rktwebview_t w); -RKTWEBVIEW_QT_EXPORT result_t rkt_webview_show(rktwebview_t w); -RKTWEBVIEW_QT_EXPORT result_t rkt_webview_show_normal(rktwebview_t w); -RKTWEBVIEW_QT_EXPORT result_t rkt_webview_present(rktwebview_t w); -RKTWEBVIEW_QT_EXPORT result_t rkt_webview_maximize(rktwebview_t w); -RKTWEBVIEW_QT_EXPORT result_t rkt_webview_minimize(rktwebview_t w); -RKTWEBVIEW_QT_EXPORT window_state_t rkt_webview_window_state(rktwebview_t w); - -RKTWEBVIEW_QT_EXPORT result_t rkt_webview_choose_dir(rktwebview_t w, const char *title, const char *base_dir); -RKTWEBVIEW_QT_EXPORT result_t rkt_webview_file_open(rktwebview_t w, const char *title, const char *base_dir, const char *permitted_exts); -RKTWEBVIEW_QT_EXPORT result_t rkt_webview_file_save(rktwebview_t w, const char *title, const char *base_dir, const char *permitted_exts); - -RKTWEBVIEW_QT_EXPORT result_t rkt_webview_message_box(rktwebview_t w, const char *title, const char *message, const char *submessage, rkt_messagetype_t type); +RKTWEBVIEW_EXPORT result_t rkt_webview_message_box(rktwebview_t w, const char *title, const char *message, const char *submessage, rkt_messagetype_t type); } diff --git a/rktwebview_qt/rktwebview_global.h b/rktwebview_qt/rktwebview_global.h new file mode 100644 index 0000000..962d205 --- /dev/null +++ b/rktwebview_qt/rktwebview_global.h @@ -0,0 +1,18 @@ +#ifndef RKTWEBVIEW_GLOBAL_H +#define RKTWEBVIEW_GLOBAL_H + +#if defined(RKTWEBVIEW_LIBRARY) + #ifdef _WIN32 + #define RKTWEBVIEW_EXPORT __declspec(dllexport) + #else + #define RKTWEBVIEW_EXPORT + #endif +#else + #ifdef _WIN32 + #define RKTWEBVIEW_EXPORT __declspec(dllimport) + #else + #define RKTWEBVIEW_EXPORT + #endif +#endif + +#endif // RKTWEBVIEW_GLOBAL_H diff --git a/rktwebview_qt/rktwebview_internal.h b/rktwebview_qt/rktwebview_internal.h deleted file mode 100644 index 71ff3c2..0000000 --- a/rktwebview_qt/rktwebview_internal.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef RKTWEBVIEW_INTERNAL_H -#define RKTWEBVIEW_INTERNAL_H - -#include "rktwebview.h" - -#endif // RKTWEBVIEW_INTERNAL_H diff --git a/rktwebview_qt/rktwebview_qt.cpp b/rktwebview_qt/rktwebview_qt.cpp index a9d07dd..31d9afc 100644 --- a/rktwebview_qt/rktwebview_qt.cpp +++ b/rktwebview_qt/rktwebview_qt.cpp @@ -1,6 +1,6 @@ #include "rktwebview_qt.h" #include "webviewqt.h" -#include "rktwebview.h" +#include "rktwebview_types.h" #include "webviewwindow.h" #include "rktutils.h" #include @@ -29,6 +29,92 @@ static inline char *copyString(const char *s) void Rktwebview_qt::processCommand(Command *cmd) { switch(cmd->cmd) { + case COMMAND_QUIT: { // Quit application + _app->quit(); + } + break; + case COMMAND_NEW_CONTEXT: { + QString boilerplate_js = cmd->args[0].toString(); + bool has_pem = cmd->args[1].toBool(); + QString optional_server_cert_pem = cmd->args[2].toString(); + + fprintf(stderr, "bjs: %s\n", boilerplate_js.toUtf8().constData()); + fprintf(stderr, "oscp: %s\n", optional_server_cert_pem.toUtf8().constData()); + + QWebEngineProfileBuilder b; + if (has_pem) { + QByteArray scp = optional_server_cert_pem.toUtf8(); + fprintf(stderr, "Installing cert: %s\n", scp.constData()); + QList certs; + QSslCertificate cert(scp); + certs.append(cert); + b.setAdditionalTrustedCertificates(certs); + } + _context_counter += 1; + QString name = QString::asprintf("profile-%d", _context_counter); + + QString code = "if (window.rkt_event_queue === undefined) { window.rkt_event_queue = []; }\n" + "window.rkt_evt_frame_el = null;\n" + "window.rkt_evt_frame_win = null;\n" + "window.rkt_send_event = function(obj) {\n" + " //console.log('Sending event: ' + obj);\n" + " window.rkt_event_queue.push(obj);\n" + " if (window.rkt_evt_frame_el) {\n" + " window.rkt_evt_frame_win.print();\n" + " }\n" + "};\n" + "window.rkt_get_events = function() {\n" + " let q = window.rkt_event_queue;\n" + " window.rkt_event_queue = [];\n" + " let json_q = JSON.stringify(q);\n" + " return json_q;\n" + "};\n" + "// add hidden hover element to body if necessary\n" + "setInterval(function () {\n" + " if (window.rkt_evt_frame_el === null || window.rkt_evt_frame_el === undefined) {\n" + " window.rkt_evt_frame_el = document.createElement('iframe');\n" + " window.rkt_evt_frame_el.style.display = 'none';\n" + " window.rkt_evt_frame_el.setAttribute('id', 'rkt-evt-frame');\n" + " window.rkt_evt_frame_el.setAttribute('name', 'rkt-evt-frame');\n" + " document.body.append(window.rkt_evt_frame_el);\n" + " window.rkt_evt_frame_win = window.rkt_evt_frame_el.contentWindow;\n" + " } else {" + " if (window.rkt_event_queue.length > 0) {\n" + " window.rkt_evt_frame_win.print();\n" + " }\n" + " }\n" + "},\n" + "10);\n" + ""; + + QList scripts; + QWebEngineProfile *p = b.createProfile(name); + QWebEngineScriptCollection *col = p->scripts(); + QWebEngineScript s1; + s1.setInjectionPoint(QWebEngineScript::InjectionPoint::DocumentReady); + s1.setName("eventing"); + s1.setSourceCode(code); + s1.setWorldId(QWebEngineScript::MainWorld); + scripts.append(s1); + + QWebEngineScript s2; + s2.setInjectionPoint(QWebEngineScript::InjectionPoint::DocumentReady); + s2.setName("boilerplate"); + s2.setSourceCode(boilerplate_js); + s2.setWorldId(QWebEngineScript::MainWorld); + scripts.append(s2); + + col->insert(scripts); + + QWebEngineScriptCollection *c = p->scripts(); + QList l = c->toList(); + + _contexts[_context_counter] = p; + + cmd->result = _context_counter; + cmd->done = true; + } + break; case COMMAND_CREATE: { rkt_wv_context_t context = cmd->args[0].toInt(); rktwebview_t parent = cmd->args[1].toInt(); @@ -151,6 +237,7 @@ void Rktwebview_qt::processCommand(Command *cmd) QString js = cmd->args[1].toString(); if (_views.contains(wv)) { WebviewWindow *w = _views[wv]; + fprintf(stderr, "Running %s\n", js.toUtf8().constData()); w->runJs(js); cmd->result = true; } else { @@ -367,78 +454,20 @@ int Rktwebview_qt::nextHandle() return h; } -rkt_wv_context_t Rktwebview_qt::newContext(const char *boilerplate_js, const char *optional_server_cert_pem) +rkt_wv_context_t Rktwebview_qt::newContext(const QString &boilerplate_js, bool has_pem, const QString &optional_server_cert_pem) { - QWebEngineProfileBuilder b; - if (optional_server_cert_pem != nullptr) { - QByteArray scp = QByteArray(optional_server_cert_pem); - QList certs; - QSslCertificate cert(scp); - certs.append(cert); - b.setAdditionalTrustedCertificates(certs); - } - _context_counter += 1; - QString name = QString::asprintf("profile-%d", _context_counter); + Command c(COMMAND_NEW_CONTEXT); - QString code = "if (window.rkt_event_queue === undefined) { window.rkt_event_queue = []; }\n" - "window.rkt_evt_frame_el = null;\n" - "window.rkt_evt_frame_win = null;\n" - "window.rkt_send_event = function(obj) {\n" - " //console.log('Sending event: ' + obj);\n" - " window.rkt_event_queue.push(obj);\n" - " if (window.rkt_evt_frame_el) {\n" - " window.rkt_evt_frame_win.print();\n" - " }\n" - "};\n" - "window.rkt_get_events = function() {\n" - " let q = window.rkt_event_queue;\n" - " window.rkt_event_queue = [];\n" - " let json_q = JSON.stringify(q);\n" - " return json_q;\n" - "};\n" - "// add hidden hover element to body if necessary\n" - "setInterval(function () {\n" - " if (window.rkt_evt_frame_el === null || window.rkt_evt_frame_el === undefined) {\n" - " window.rkt_evt_frame_el = document.createElement('iframe');\n" - " window.rkt_evt_frame_el.style.display = 'none';\n" - " window.rkt_evt_frame_el.setAttribute('id', 'rkt-evt-frame');\n" - " window.rkt_evt_frame_el.setAttribute('name', 'rkt-evt-frame');\n" - " document.body.append(window.rkt_evt_frame_el);\n" - " window.rkt_evt_frame_win = window.rkt_evt_frame_el.contentWindow;\n" - " } else {" - " if (window.rkt_event_queue.length > 0) {\n" - " window.rkt_evt_frame_win.print();\n" - " }\n" - " }\n" - "},\n" - "10);\n" - ""; + c.args.push_back(boilerplate_js); + c.args.push_back(has_pem); + c.args.push_back(optional_server_cert_pem); - QList scripts; - QWebEngineProfile *p = b.createProfile(name); - QWebEngineScriptCollection *col = p->scripts(); - QWebEngineScript s1; - s1.setInjectionPoint(QWebEngineScript::InjectionPoint::DocumentReady); - s1.setName("eventing"); - s1.setSourceCode(code); - s1.setWorldId(QWebEngineScript::MainWorld); - scripts.append(s1); + postCommand(&c); - QWebEngineScript s2; - s2.setInjectionPoint(QWebEngineScript::InjectionPoint::DocumentReady); - s2.setName("boilerplate"); - s2.setSourceCode(boilerplate_js); - s2.setWorldId(QWebEngineScript::MainWorld); - scripts.append(s2); + while(!c.done) { doEvents(); } - col->insert(scripts); - - QWebEngineScriptCollection *c = p->scripts(); - QList l = c->toList(); - - _contexts[_context_counter] = p; - - return _context_counter; + int id = c.result.toInt(); + return id; } int Rktwebview_qt::rktWebViewCreate(rkt_wv_context_t context, rktwebview_t parent, event_cb_t js_evt_cb) @@ -512,6 +541,8 @@ result_t Rktwebview_qt::rktSetHtml(rktwebview_t wv, const char *html) rkt_data_t *Rktwebview_qt::rktCallJs(rktwebview_t wv, const char *js) { + fprintf(stderr, "calljs: %s\n", js); + Command c(COMMAND_CALL_JS); QString _js(js); c.args.push_back(wv); @@ -523,16 +554,17 @@ rkt_data_t *Rktwebview_qt::rktCallJs(rktwebview_t wv, const char *js) r->kind = js_result; r->data.js_result.result = c.js_result_ok ? result_t::oke : result_t::eval_js_failed; r->data.js_result.value = copyString(c.result.toString().toUtf8()); + fprintf(stderr, "js-result: %s\n", r->data.js_result.value); return r; } -result_t Rktwebview_qt::rktRunJs(rktwebview_t wv, const char *js) +result_t Rktwebview_qt::rktRunJs(rktwebview_t wv, const QString &js) { + fprintf(stderr, "rktRunJs: %s\n", js.toUtf8().constData()); Command c(COMMAND_RUN_JS); - QString _js(js); c.args.push_back(wv); - c.args.push_back(_js); + c.args.push_back(js); postCommand(&c); while(!c.done) { doEvents(); } bool r = c.result.toBool(); @@ -604,6 +636,12 @@ window_state_t Rktwebview_qt::rktWindowState(rktwebview_t w) return ws; } +void Rktwebview_qt::rktQuit() +{ + Command c(COMMAND_QUIT); + postCommand(&c); +} + result_t Rktwebview_qt::fileDlg(rktwebview_t w, const char *title, const char *base, const char *filters,QFileDialog::FileMode mode, QFileDialog::AcceptMode am, QString evt_ok, QString evt_cancel) { if (_views.contains(w)) { @@ -836,6 +874,7 @@ void Rktwebview_qt::deleteApp() void Rktwebview_qt::initApp() { _app = new QApplication(_argc, _argv); + _app->setQuitOnLastWindowClosed(false); // See Qt 6.10 remark at doEvents. //connect(&_evt_loop_timer, &QTimer::timeout, this, &Rktwebview_qt::stopEventloop); @@ -843,17 +882,17 @@ void Rktwebview_qt::initApp() // Because we are using processEvents only (Qt 6.10), we need this dispatcher to // handle deferred Deletes. - const auto *eventDispatcher = QThread::currentThread()->eventDispatcher(); + /*const auto *eventDispatcher = QThread::currentThread()->eventDispatcher(); QObject::connect(eventDispatcher, &QAbstractEventDispatcher::aboutToBlock, QThread::currentThread(), []{ if (QThread::currentThread()->loopLevel() == 0) QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); } ); - - + */ } + void Rktwebview_qt::runJs(rktwebview_t wv, const char *js) { if (_views.contains(wv)) { @@ -888,7 +927,7 @@ void Rktwebview_qt::doEvents() //QTime ct = QTime::currentTime(); //QTime expire = QTime::currentTime().addMSecs(2); //while(QTime::currentTime() <= expire) { - _app->processEvents(); + //_app->processEvents(); //} // Qt 6.10 --> this leads to a core dump @@ -901,6 +940,7 @@ void Rktwebview_qt::doEvents() //_evt_loop.exec(); _app->exec(); }*/ + QThread::usleep(500); } void Rktwebview_qt::stopEventloop() @@ -912,15 +952,15 @@ void Rktwebview_qt::stopEventloop() -Rktwebview_qt::Rktwebview_qt() : QObject() +Rktwebview_qt::Rktwebview_qt(int argc, char *argv[]) : QObject() { - _argc = 1; - _argv[0] = const_cast("Rktwebview_qt"); - _context_counter = 0; _current_handle = 0; _evt_loop_depth = 0; + _argc = argc; + _argv = argv; + _app = nullptr; } diff --git a/rktwebview_qt/rktwebview_qt.h b/rktwebview_qt/rktwebview_qt.h index 2901364..67ec898 100644 --- a/rktwebview_qt/rktwebview_qt.h +++ b/rktwebview_qt/rktwebview_qt.h @@ -1,9 +1,8 @@ #ifndef RKTWEBVIEW_QT_H #define RKTWEBVIEW_QT_H -#include "rktwebview_qt_global.h" -#include "rktwebview_internal.h" #include "rktutils.h" +#include "rktwebview_types.h" #include #include @@ -21,7 +20,7 @@ class WebviewWindow; class Command; class CommandEvent; -class RKTWEBVIEW_QT_EXPORT Rktwebview_qt : public QObject +class Rktwebview_qt : public QObject { Q_OBJECT private: @@ -39,7 +38,7 @@ private: QTimer _evt_loop_timer; int _argc; - char *_argv[1]; + char **_argv; private: void runJs(rktwebview_t wv, const char *js); @@ -71,7 +70,7 @@ public: int nextHandle(); public: - rkt_wv_context_t newContext(const char *boilerplate_js, const char *optional_server_cert_pem); + rkt_wv_context_t newContext(const QString &boilerplate_js, bool has_pem, const QString &optional_server_cert_pem); int rktWebViewCreate(rkt_wv_context_t context, rktwebview_t parent, event_cb_t js_evt_cb); void rktWebViewClose(int wv); void rktSetOUToken(rktwebview_t wv, const char *ou_token); @@ -79,7 +78,7 @@ public: result_t rktOpenDevtools(rktwebview_t wv); result_t rktSetUrl(rktwebview_t wv, const char *url); result_t rktSetHtml(rktwebview_t wv, const char *html); - result_t rktRunJs(rktwebview_t wv, const char *js); + result_t rktRunJs(rktwebview_t wv, const QString &js); rkt_data_t *rktCallJs(rktwebview_t wv, const char *js); result_t rktMove(rktwebview_t wv, int x, int y); @@ -92,6 +91,8 @@ public: result_t rktShowNormalWindow(rktwebview_t w); window_state_t rktWindowState(rktwebview_t w); + void rktQuit(); + result_t rktChooseDir(rktwebview_t w, const char *title, const char *base_dir); result_t rktFileOpen(rktwebview_t w, const char *title, const char *base_dir, const char *permitted_exts); result_t rktFileSave(rktwebview_t w, const char *title, const char *base_dir, const char *permitted_exts); @@ -117,7 +118,7 @@ public: void runCommandThread(); public: - Rktwebview_qt(); + Rktwebview_qt(int argc, char *argv[]); ~Rktwebview_qt(); }; diff --git a/rktwebview_qt/rktwebview_qt_global.h b/rktwebview_qt/rktwebview_qt_global.h deleted file mode 100644 index 98ed696..0000000 --- a/rktwebview_qt/rktwebview_qt_global.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef RKTWEBVIEW_QT_GLOBAL_H -#define RKTWEBVIEW_QT_GLOBAL_H - -#include - -#if defined(RKTWEBVIEW_QT_LIBRARY) -#define RKTWEBVIEW_QT_EXPORT Q_DECL_EXPORT -#else -#define RKTWEBVIEW_QT_EXPORT Q_DECL_IMPORT -#endif - -#endif // RKTWEBVIEW_QT_GLOBAL_H diff --git a/rktwebview_qt/rktwebview_test.cpp b/rktwebview_qt/rktwebview_test.cpp new file mode 100644 index 0000000..eca74bb --- /dev/null +++ b/rktwebview_qt/rktwebview_test.cpp @@ -0,0 +1,32 @@ +#include "rktwebview.h" +#include +#include + +int main(int argc, char *argv[]) +{std::string me = argv[0]; + int idx = me.rfind("/"); + + std::string loc = me.substr(0, idx); + std::string prg = loc + "/rktwebview_prg"; + + setenv("RKT_WEBVIEW_PRG", prg.c_str(), true); + setenv("LD_LIBRARY_PATH", loc.c_str(), true); + + int context = rkt_webview_new_context("", nullptr); + + int wv = rkt_webview_create(context, 0); + + rkt_webview_move(wv, 100, 200); + rkt_webview_resize(wv, 800, 600); + rkt_webview_set_url(wv, "https://wikipedia.org"); + + while(rkt_webview_events_waiting() > 0) { + rkt_data_t *d = rkt_webview_get_event(); + rkt_webview_free_data(d); + } + sleep(10); + + rkt_webview_close(wv); + + rkt_webview_cleanup(); +} \ No newline at end of file diff --git a/rktwebview_qt/rktwebview_types.h b/rktwebview_qt/rktwebview_types.h new file mode 100644 index 0000000..51dcaea --- /dev/null +++ b/rktwebview_qt/rktwebview_types.h @@ -0,0 +1,84 @@ +#ifndef RKTWEBVIEW_TYPES_H +#define RKTWEBVIEW_TYPES_H + +extern "C" { + +typedef int rktwebview_t; +typedef int rkt_wv_context_t; + +typedef struct { + rktwebview_t wv; + char *event; +} rkt_event_t; + +typedef enum { + no_result_yet = -1, + oke = 0, + set_html_failed = 1, + set_navigate_failed = 2, + eval_js_failed = 3, + no_devtools_on_platform = 4, + no_delegate_for_context = 5, + webview_missing_dependency = 6, + webview_canceled = 7, + webview_invalid_state = 8, + webview_invalid_argument = 9, + webview_unspecified = 10, + webview_dispatch_failed = 11, + move_failed = 12, + resize_failed = 13, + choose_dir_failed = 14, + open_file_failed = 15, + save_file_failed = 16, + failed = 17 +} result_t; + +typedef struct { + result_t result; + char *value; +} rkt_js_result_t; + +typedef enum { + invalid = -1, + normal = 0, + minimized = 1, + maximized = 2, + hidden = 3, + normal_active = 16, + maximized_active = 18 +} window_state_t; + +typedef enum { + info = 1, + error = 2, + warning = 3, + yes_no = 4, + oke_cancel = 5 +} rkt_messagetype_t; + +typedef struct { + int api_major; + int api_minor; + int api_patch; +} rkt_version_t; + +typedef enum { + version = 1, + event = 2, + js_result = 3 +} rkt_data_kind_t; + +typedef struct { + rkt_data_kind_t kind; + union { + rkt_version_t version; + rkt_event_t event; + rkt_js_result_t js_result; + } data; +} rkt_data_t; + +typedef void (*event_cb_t)(rkt_data_t *); + +} + +#endif // RKTWEBVIEW_TYPES_H diff --git a/rktwebview_qt/shm.cpp b/rktwebview_qt/shm.cpp index 1360fcc..e2b4b59 100644 --- a/rktwebview_qt/shm.cpp +++ b/rktwebview_qt/shm.cpp @@ -10,13 +10,14 @@ #include #include #include +#include #include typedef struct __shm_item__ { - struct __shm_item__ *next; - struct __shm_item__ *prev; - size_t size; + ShmPlace next; + ShmPlace prev; + size_t size; } ShmItem; @@ -34,10 +35,9 @@ private: char *_sem_name; size_t *_used; - ShmItem **__first; - ShmItem **__free_list; - ShmPlace *_slots; + ShmPlace *_first; + ShmPlace *_free_list; int _in_critical; @@ -66,57 +66,82 @@ public: return _mem_name; } +public: + template void ref(int place, T** p) { + if (place == SHM_NULL) { + *p = nullptr; + } else { + *p = reinterpret_cast(reinterpret_cast(_mem) + place); + } + } + + inline void *ref(int place) + { + if (place == SHM_NULL) { + return nullptr; + } + + return reinterpret_cast(_mem) + place; + } + public: int alloc(size_t bytes) { enterCritical(); - ShmItem *_free_list = *__free_list; - ShmItem *_first = *__first; - - ShmItem *i = _free_list; + ShmPlace p_i = *_free_list; + ShmItem *i; + ref(p_i, &i); while (i != nullptr && i->size < bytes) { - i = i->next; + p_i = i->next; + ref(p_i, &i); } int place; if (i != nullptr) { - if (i->prev != nullptr) { - i->prev->next = i->next; + if (i->prev != SHM_NULL) { + ShmItem *prev_i; + ref(i->prev, &prev_i); + prev_i->next = i->next; } - if (i->next != nullptr) { - i->next->prev = i->prev; + if (i->next != SHM_NULL) { + ShmItem *next_i; + ref(i->next, &next_i); + next_i->prev = i->prev; } - if (i->prev == nullptr) { - _free_list = i->next; + if (i->prev == SHM_NULL) { + *_free_list = i->next; } - i->next = _first; - if (_first != nullptr) { - _first->prev = i; + i->next = *_first; + if (*_first != SHM_NULL) { + ShmItem *first_i; + ref(*_first, &first_i); + first_i->prev = p_i; } - i->prev = nullptr; + i->prev = SHM_NULL; - _first = i; - *__first = _first; - *__free_list = _free_list; - - place = (reinterpret_cast(i) + sizeof(ShmItem)) - reinterpret_cast(_mem); + *_first = p_i; + place = p_i + sizeof(ShmItem); } else { - i = reinterpret_cast(reinterpret_cast(_mem) + *_used); + ShmPlace p_i = *_used; + i = reinterpret_cast(reinterpret_cast(_mem) + p_i); size_t u = *_used + sizeof(ShmItem) + bytes; if (u >= _size) { - place = -1; + place = SHM_NULL; } else { *_used = u; - i->prev = nullptr; + i->prev = SHM_NULL; i->size = bytes; - i->next = _first; + i->next = *_first; + if (*_first != SHM_NULL) { + ShmItem *first_i; + ref(*_first, &first_i); + first_i->prev = p_i; + } - _first = i; - *__first = _first; - - place = (reinterpret_cast(i) + sizeof(ShmItem)) - reinterpret_cast(_mem); + *_first = p_i; + place = p_i + sizeof(ShmItem); } } @@ -124,39 +149,42 @@ public: return place; } - void free(int place) { + void free(ShmPlace place) { + if (place == SHM_NULL) { + return; + } + enterCritical(); - ShmItem *i = reinterpret_cast(reinterpret_cast(_mem) + place - sizeof(ShmItem)); - ShmItem *_free_list = *__free_list; - ShmItem *_first = *__first; + ShmPlace p_i = place - sizeof(ShmItem); + ShmItem *i; + ref(p_i, &i); - if (i->prev != nullptr) { - i->prev->next = i->next; + if (i->prev != SHM_NULL) { + ShmItem *prev_i; + ref(i->prev, &prev_i); + prev_i->next = i->next; } - if (i->next != nullptr) { - i->next->prev = i->prev; + if (i->next != SHM_NULL) { + ShmItem *next_i; + ref(i->next, &next_i); + next_i->prev = i->prev; } - if (i->prev == nullptr) { - _first = i->next; + if (i->prev == SHM_NULL) { + *_first = i->next; } - i->next = _free_list; - if (_free_list != nullptr) { - _free_list->prev = i; + i->next = *_free_list; + if (*_free_list != SHM_NULL) { + ShmItem *fl; + ref(*_free_list, &fl); + fl->prev = p_i; } - i->prev = nullptr; - _free_list = i; + i->prev = SHM_NULL; + *_free_list = p_i; - *__first = _first; - *__free_list = _free_list; leaveCritical(); } - inline void *ref(int place) - { - return reinterpret_cast(_mem) + place; - } - void enterCritical() { if (_in_critical > 0) { _in_critical++; @@ -188,25 +216,28 @@ public: size_t slots_size = sizeof(ShmPlace) * SHM_MAX_SLOTS; size += slots_size; - _sem = sem_open(_sem_name, O_CREAT); - _shm_fd = shm_open(name, O_CREAT | O_RDWR, 0600); - ftruncate(_shm_fd, size); + if (owner) { + _sem = sem_open(_sem_name, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR, 0); + _shm_fd = shm_open(name, O_CREAT | O_RDWR, 0600); + ftruncate(_shm_fd, size); + } else { + _sem = sem_open(_sem_name, O_RDWR, S_IRUSR | S_IWUSR, 0); + _shm_fd = shm_open(name, O_RDWR, 0600); + } _size = size; _mem = mmap(nullptr, _size, PROT_READ|PROT_WRITE, MAP_SHARED, _shm_fd, 0); - _slots = reinterpret_cast (reinterpret_cast(_mem) + sizeof(size_t) + sizeof(ShmItem *) + sizeof(ShmItem *)); - + _slots = reinterpret_cast (reinterpret_cast(_mem) + sizeof(size_t) + sizeof(ShmPlace) + sizeof(ShmPlace)); _used = reinterpret_cast(_mem); - __first = reinterpret_cast(reinterpret_cast(_mem) + sizeof(size_t)); - - __free_list = reinterpret_cast(reinterpret_cast(_mem) + sizeof(size_t) + sizeof(ShmItem *)); + _first = reinterpret_cast(reinterpret_cast(_mem) + sizeof(size_t)); + _free_list = reinterpret_cast(reinterpret_cast(_mem) + sizeof(size_t) + sizeof(ShmPlace)); _owner = owner; if (_owner) { - *__first = nullptr; - *__free_list = nullptr; + *_first = SHM_NULL; + *_free_list = SHM_NULL; *_used = sizeof(size_t) + sizeof(ShmItem *) + sizeof(ShmItem *) + slots_size; sem_post(_sem); } @@ -320,26 +351,38 @@ Shm::~Shm() delete _shm_api; } -template void ref(Shm *shm, int place, T **p) -{ - *p = reinterpret_cast(shm->ref(place)); -} + void ShmSem::post() { sem_post(reinterpret_cast(_sem)); } void ShmSem::wait() { - sem_wait(reinterpret_cast(_sem)); + int r = sem_wait(reinterpret_cast(_sem)); + if (r != 0) { + fprintf(stderr, "sem_wait error: %d, %s\n", errno, strerror(errno)); + } } bool ShmSem::trywait() { - return sem_trywait(reinterpret_cast(_sem)) == 0; + int r = sem_trywait(reinterpret_cast(_sem)); + if (r != 0 && r != EAGAIN) { + fprintf(stderr, "sem_wait error: %d, %s\n", errno, strerror(errno)); + } + + return (r == 0); } ShmSem::ShmSem(const char *n, bool owner) { _name = strdup(n); - _sem = reinterpret_cast(sem_open(n, O_CREAT)); + + sem_t *s; + if (owner) { + s = sem_open(n, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR, 0); + } else { + s = sem_open(n, O_RDWR, S_IRUSR | S_IWUSR, 0); + } + _sem = reinterpret_cast(s); _owner = owner; } diff --git a/rktwebview_qt/shm.h b/rktwebview_qt/shm.h index 05793f9..1b567df 100644 --- a/rktwebview_qt/shm.h +++ b/rktwebview_qt/shm.h @@ -55,6 +55,9 @@ public: ~Shm(); }; -template void ref(Shm *shm, int place, T **p); +template inline void ref(Shm *shm, int place, T **p) +{ + *p = reinterpret_cast(shm->ref(place)); +} #endif // SHM_H diff --git a/rktwebview_qt/shmqueue.cpp b/rktwebview_qt/shmqueue.cpp index ece7412..e0e255c 100644 --- a/rktwebview_qt/shmqueue.cpp +++ b/rktwebview_qt/shmqueue.cpp @@ -1,4 +1,5 @@ #include "shmqueue.h" +#include ShmQueue::ShmQueue(Shm *shm, ShmSlot slot, bool owner) { @@ -43,15 +44,15 @@ int ShmQueue::depth() return d; } -void ShmQueue::enqueue(int cmd, const QString &json_data) +void ShmQueue::enqueue(int cmd, const std::string &json_data) { - QByteArray b(json_data.toUtf8()); + size_t jd_size = json_data.size(); + int str_place = _shm->alloc(jd_size + 1); - int str_place = _shm->alloc(b.size() + 1); char *s; ref(_shm, str_place, &s); - memcpy(s, b.constData(), b.size()); - s[b.size()] = '\0'; + memcpy(s, json_data.data(), jd_size); + s[jd_size] = '\0'; int item_place = _shm->alloc(sizeof(ShmQueueItem)); ShmQueueItem *item; @@ -60,6 +61,11 @@ void ShmQueue::enqueue(int cmd, const QString &json_data) item->data_place = str_place; item->prev = _queue->last; item->next = SHM_NULL; + if (_queue->last != SHM_NULL) { + ShmQueueItem *last_i; + ref(_shm, _queue->last, &last_i); + last_i->next = item_place; + } _shm->lock(); _queue->last = item_place; @@ -71,7 +77,13 @@ void ShmQueue::enqueue(int cmd, const QString &json_data) _queue_sem->post(); } -bool ShmQueue::dequeue(int &cmd, QString &json_data, bool wait) +void ShmQueue::enqueue(int cmd) +{ + std::string s; + enqueue(cmd, s); +} + +bool ShmQueue::dequeue(int &cmd, std::string &json_data, bool wait) { if (wait) { _queue_sem->wait(); @@ -96,7 +108,7 @@ bool ShmQueue::dequeue(int &cmd, QString &json_data, bool wait) ShmPlace data_place = item->data_place; char *data; ref(_shm, data_place, &data); - json_data = QString::fromUtf8(data, strlen(data)); + json_data = data; _shm->free(data_place); cmd = item->cmd; diff --git a/rktwebview_qt/shmqueue.h b/rktwebview_qt/shmqueue.h index 21f59f1..61176fe 100644 --- a/rktwebview_qt/shmqueue.h +++ b/rktwebview_qt/shmqueue.h @@ -2,10 +2,7 @@ #define SHMQUEUE_H #include "shm.h" -#include - -#define COMMAND_SLOT 1 -#define EVENT_SLOT 2 +#include typedef struct __ShmQueueItem__ { @@ -40,8 +37,9 @@ public: int depth(); public: - bool dequeue(int &cmd, QString &json_data, bool wait = false); - void enqueue(int cmd, const QString &json_data); + bool dequeue(int &cmd, std::string &json_data, bool wait = false); + void enqueue(int cmd, const std::string &json_data); + void enqueue(int cmd); public: ShmQueue(Shm *shm, ShmSlot slot, bool owner);