This commit is contained in:
2026-03-25 01:27:39 +01:00
parent eb4e15c66b
commit 8fe7e726a4
26 changed files with 1896 additions and 517 deletions

View File

@@ -1 +0,0 @@
QtWebEngineProcess

Binary file not shown.

Binary file not shown.

View File

@@ -60,6 +60,7 @@
(define-runtime-path lib-dir "lib") (define-runtime-path lib-dir "lib")
#|
(define libraries-to-preload (define libraries-to-preload
(cond (cond
([eq? os 'windows] ([eq? os 'windows]
@@ -105,11 +106,12 @@
)) ))
) )
) )
|#
(define ffi-library (define ffi-library
(cond (cond
([eq? os 'windows] 'rktwebview_qt.dll) ([eq? os 'windows] 'rktwebview.dll)
([eq? os 'linux] 'librktwebview_qt.so) ([eq? os 'linux] 'librktwebview.so)
) )
) )
@@ -121,16 +123,26 @@
; c:\qt\6.10.2\msvc2022_64\bin\windeployqt.exe rktwebview_qt_test.exe ; c:\qt\6.10.2\msvc2022_64\bin\windeployqt.exe rktwebview_qt_test.exe
(define quiet-call #t) (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 (set! quiet-call
(when (or (eq? os 'windows) (eq? os 'linux)) (when (or (eq? os 'windows) (eq? os 'linux))
(putenv "QT_PLUGIN_PATH" (putenv "QT_PLUGIN_PATH"
(path->string (build-path os-lib-dir))) (path->string (build-path os-lib-dir)))
(putenv "QTWEBENGINEPROCESS_PATH" (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" (putenv "QTWEBENGINE_RESOURCES_PATH"
(path->string (build-path os-lib-dir "resources"))) (path->string (build-path os-lib-dir "resources")))
(putenv "QTWEBENGINE_LOCALES_PATH" (putenv "QTWEBENGINE_LOCALES_PATH"
(path->string (build-path os-lib-dir "translations" "qtwebengine_locales"))) (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) (when (eq? os 'linux)
(putenv "QT_QPA_PLATFORM" "xcb") (putenv "QT_QPA_PLATFORM" "xcb")
(putenv "LD_LIBRARY_PATH" (putenv "LD_LIBRARY_PATH"
@@ -145,6 +157,7 @@
;;; Preload libraries ;;; Preload libraries
#|
(for-each (λ (lib-symbol) (for-each (λ (lib-symbol)
(let* ((libn (if (list? lib-symbol) (car lib-symbol) 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))) (versions (if (list? lib-symbol) (cons (cadr lib-symbol) '(#f)) (list #f)))
@@ -160,6 +173,7 @@
) )
) )
libraries-to-preload) libraries-to-preload)
|#
;;; Actual FFI integration ;;; Actual FFI integration
@@ -192,9 +206,9 @@
;;; Callbacks from the OS library ;;; Callbacks from the OS library
(define callback-box (box '())) ;(define callback-box (box '()))
(define (applier thunk) ;(define (applier thunk)
(thunk)) ; (thunk))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Types / Functions ;; Types / Functions
@@ -279,14 +293,23 @@
(define-rktwebview rkt_webview_cleanup (define-rktwebview rkt_webview_cleanup
(_fun -> _void)) (_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, ;RKTWEBVIEW_QT_EXPORT rkt_wv_context_t rkt_webview_new_context(const char *boilerplate_js,
; const char *optional_server_cert_pem); ; const char *optional_server_cert_pem);
(define-rktwebview rkt_webview_new_context (define-rktwebview rkt_webview_new_context
(_fun _string/utf-8 _bytes -> _int)) (_fun _string/utf-8 _bytes -> _int))
;RKTWEBVIEW_QT_EXPORT void rkt_webview_process_events(int for_ms); ;RKTWEBVIEW_QT_EXPORT void rkt_webview_process_events(int for_ms);
(define-rktwebview rkt_webview_process_events ;(define-rktwebview rkt_webview_process_events
(_fun _int -> _void)) ; (_fun _int -> _void))
;RKTWEBVIEW_QT_EXPORT void rkt_webview_free_data(rkt_data_t *d); ;RKTWEBVIEW_QT_EXPORT void rkt_webview_free_data(rkt_data_t *d);
(define-rktwebview rkt_webview_free_data (define-rktwebview rkt_webview_free_data
@@ -297,13 +320,9 @@
(_fun -> _rkt_data_t-pointer)) (_fun -> _rkt_data_t-pointer))
; RKTWEBVIEW_QT_EXPORT int rkt_webview_create(rkt_wv_context_t context, ; RKTWEBVIEW_QT_EXPORT int rkt_webview_create(rkt_wv_context_t context,
; rktwebview_t parent, ; rktwebview_t parent)
; event_cb_t js_event_cb);
(define-rktwebview rkt_webview_create (define-rktwebview rkt_webview_create
(_fun _int _int (_fun _int _int -> _int))
(_fun #:keep callback-box #:async-apply applier
_rkt_data_t-pointer -> _void)
-> _int))
;RKTWEBVIEW_QT_EXPORT void rkt_webview_close(int wv); ;RKTWEBVIEW_QT_EXPORT void rkt_webview_close(int wv);
(define-rktwebview rkt_webview_close (define-rktwebview rkt_webview_close
@@ -403,28 +422,72 @@
;; Initialize and start library ;; Initialize and start library
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define process-events 'process) ;(define process-events 'process)
(define (stop-event-processing) ;(define (stop-event-processing)
(set! process-events 'stop) ; (set! process-events 'stop)
(while (eq? process-events 'stop) ; (while (eq? process-events 'stop)
(sleep 0.001))) ; (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) (define (start-event-processing)
(thread (λ () (thread (λ ()
(letrec ((f (λ () (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) (sleep 0.001)
(if (eq? process-events 'process) (f))
(f) ))
(begin (f)))
(displayln "Stopping event processing") )
(set! process-events 'stopped) )
'done)))))
(f)))))
(rkt_webview_init) (define evt-processing-thread (start-event-processing))
(set! quiet-call (start-event-processing))
(define (stop-event-processing)
(kill-thread evt-processing-thread))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Provided features ;; Provided features
@@ -459,18 +522,18 @@
(define (rkt-webview-new-context boilerplate-js server-cert) (define (rkt-webview-new-context boilerplate-js server-cert)
(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) (define (rkt-webview-create context parent evt-callback close-callback)
(let* ((evt-queue (make-queue)) (let* ((evt-queue (make-queue))
(parent-win (if (eq? parent #f) 0 (rkt-wv-win parent))) (parent-win (if (eq? parent #f) 0 (rkt-wv-win parent)))
) )
(let ((wv (rkt_webview_create context parent-win (let ((wv (rkt_webview_create context parent-win)))
(λ (rkt-evt) (hash-set! evt-cb-hash wv (λ (evt) (enqueue! evt-queue evt)))
(let* ((e (union-ref (rkt_data_t-data rkt-evt) 1)) ; (λ (rkt-evt)
(evt (cast (rkt_evt_t-evt e) _pointer _string*/utf-8))) ; (let* ((e (union-ref (rkt_data_t-data rkt-evt) 1))
(rkt_webview_free_data rkt-evt) ; Free event data ASAP ; (evt (cast (rkt_evt_t-evt e) _pointer _string*/utf-8)))
(enqueue! evt-queue evt) ; (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))) (let ((handle (make-rkt-wv wv evt-queue evt-callback #t close-callback)))
(thread (λ () (thread (λ ()
(sleep 1) (sleep 1)
@@ -492,6 +555,7 @@
(define (rkt-webview-close handle) (define (rkt-webview-close handle)
(rkt_webview_close (rkt-wv-win handle)) (rkt_webview_close (rkt-wv-win handle))
(enqueue! (rkt-wv-evt-queue handle) 'quit) (enqueue! (rkt-wv-evt-queue handle) 'quit)
(hash-remove! evt-cb-hash (rkt-wv-win handle))
((rkt-wv-close-callback handle)) ((rkt-wv-close-callback handle))
#t) #t)
@@ -608,8 +672,8 @@
(handle (cdr kv))) (handle (cdr kv)))
(rkt-webview-close handle))) (rkt-webview-close handle)))
open-windows)) open-windows))
(stop-event-processing)
(rkt_webview_cleanup) (rkt_webview_cleanup)
(stop-event-processing)
) )
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View File

@@ -655,6 +655,7 @@
(define (webview-call-js wv js) (define (webview-call-js wv js)
(-> wv-win? string? (or/c string? list? boolean? hash?)) (-> wv-win? string? (or/c string? list? boolean? hash?))
(let ((result (rkt-webview-call-js (wv-win-handle wv) js))) (let ((result (rkt-webview-call-js (wv-win-handle wv) js)))
(displayln result)
(if (webview-call-js-result? result) (if (webview-call-js-result? result)
(if (eq? (car result) 'oke) (if (eq? (car result) 'oke)
(hash-ref (fromJson (cadr result)) 'result #f) (hash-ref (fromJson (cadr result)) 'result #f)

View File

@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.14) cmake_minimum_required(VERSION 3.14)
project(rktwebview_qt LANGUAGES CXX) project(rktwebview LANGUAGES CXX)
set(QT_DEBUG_FIND_PACKAGE ON) set(QT_DEBUG_FIND_PACKAGE ON)
set(CMAKE_AUTOUIC 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 NAMES Qt6 REQUIRED COMPONENTS Widgets WebEngineWidgets)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets WebEngineWidgets) find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets WebEngineWidgets)
add_library(rktwebview_qt SHARED add_library(rktwebview SHARED
rktwebview_qt_global.h rktwebview_global.h
rktwebview_qt.cpp
rktwebview_qt.h
rktwebview.h rktwebview.h
rktwebview.cpp 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 webviewqt.h webviewqt.cpp
rktwebview_internal.h
webviewwindow.h webviewwindow.cpp webviewwindow.h webviewwindow.cpp
rktutils.h rktutils.cpp rktutils.h rktutils.cpp
@@ -27,19 +37,18 @@ add_library(rktwebview_qt SHARED
shm.h shm.cpp shm.h shm.cpp
shmqueue.h shmqueue.cpp shmqueue.h shmqueue.cpp
rkt_protocol.h
rktwebview_types.h
) )
target_link_libraries(rktwebview_qt PRIVATE Qt${QT_VERSION_MAJOR}::Widgets) add_executable(rktwebview_test
target_link_libraries(rktwebview_qt PRIVATE Qt${QT_VERSION_MAJOR}::WebEngineWidgets) rktwebview_test.cpp
target_compile_definitions(rktwebview_qt PRIVATE RKTWEBVIEW_QT_LIBRARY)
add_executable(rktwebview_qt_test
main.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)

View File

@@ -26,6 +26,7 @@
#define COMMAND_FILE_OPEN 20 #define COMMAND_FILE_OPEN 20
#define COMMAND_FILE_SAVE 21 #define COMMAND_FILE_SAVE 21
#define COMMAND_SET_OU_TOKEN 22 #define COMMAND_SET_OU_TOKEN 22
#define COMMAND_NEW_CONTEXT 23
class Command class Command
{ {

View File

@@ -7,14 +7,15 @@ LIB="../private/lib/linux/$ARCH"
mkdir -p $LIB mkdir -p $LIB
rm -f $LIB/*.so* rm -f $LIB/*.so*
cp build/Release/*.so $LIB 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" echo "QT_PATH=$QT_PATH"
QT_PLUGINS="$QT_PATH/plugins" QT_PLUGINS="$QT_PATH/plugins"
PLUGINS="platforms position generic iconengines imageformats qmltooling tls xcbglintegrations" 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}'` EXTRA_LIBS_PLATFORM_PLUGIN_XCB=`ldd $QT_PATH/plugins/platforms/libqxcb.so | grep Qt | awk '{print $3}'`
for pl in $PLUGINS for pl in $PLUGINS

505
rktwebview_qt/json.cpp Normal file
View File

@@ -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<JSON> 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<string,JSON>( other.Internal.Map->begin(),
other.Internal.Map->end() );
break;
case Class::Array:
Internal.List =
new deque<JSON>( 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<std::map<string, JSON> > JSON::ObjectRange() {
if( Type == Class::Object )
return JSONWrapper<std::map<std::string,JSON>>( Internal.Map );
return JSONWrapper<std::map<std::string,JSON>>( nullptr );
}
JSON::JSONWrapper<std::deque<JSON> > JSON::ArrayRange() {
if( Type == Class::Array )
return JSONWrapper<std::deque<JSON>>( Internal.List );
return JSONWrapper<std::deque<JSON>>( nullptr );
}
JSON::JSONConstWrapper<std::map<string, JSON> > JSON::ObjectRange() const {
if( Type == Class::Object )
return JSONConstWrapper<std::map<std::string,JSON>>( Internal.Map );
return JSONConstWrapper<std::map<std::string,JSON>>( nullptr );
}
JSON::JSONConstWrapper<std::deque<JSON> > JSON::ArrayRange() const {
if( Type == Class::Array )
return JSONConstWrapper<std::deque<JSON>>( Internal.List );
return JSONConstWrapper<std::deque<JSON>>( 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<std::string,JSON>(); break;
case Class::Array: Internal.List = new std::deque<JSON>(); 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<string,JSON>( other.Internal.Map->begin(),
other.Internal.Map->end() );
break;
case Class::Array:
Internal.List =
new deque<JSON>( 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<void(const std::string &err)> );
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<void(const std::string &err)> 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<void(const std::string &err)> 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<void(const std::string &err)> 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<void(const std::string &err)> 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<void(const std::string &err)> 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<void(const std::string &err)> 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<void(const std::string &err)> 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<void(const std::string &err)> on_error) {
size_t offset = 0;
return std::move( parse_next( str, offset, on_error ) );
}

217
rktwebview_qt/json.h Normal file
View File

@@ -0,0 +1,217 @@
#ifndef JSON_H
#define JSON_H
#include <cstdint>
#include <cmath>
#include <cctype>
#include <string>
#include <deque>
#include <map>
#include <type_traits>
#include <initializer_list>
#include <ostream>
#include <iostream>
#include <functional>
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<JSON> *List;
std::map<std::string,JSON> *Map;
std::string *String;
double Float;
long Int;
bool Bool;
} Internal;
public:
enum class Class {
Null,
Object,
Array,
String,
Floating,
Integral,
Boolean
};
template <typename Container>
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 <typename Container>
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<JSON> list );
JSON( JSON&& other );
JSON( const JSON &other );
JSON& operator=( const JSON &other );
JSON& operator=( JSON&& other );
// Template T constructors
template <typename T>
JSON( T b, typename std::enable_if<std::is_same<T,bool>::value>::type* = 0 );
template <typename T>
JSON( T i, typename std::enable_if<std::is_integral<T>::value && !std::is_same<T,bool>::value>::type* = 0 );
template <typename T>
JSON( T f, typename std::enable_if<std::is_floating_point<T>::value>::type* = 0 );
template <typename T>
JSON( T s, typename std::enable_if<std::is_convertible<T, std::string>::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<void(const std::string &err)> );
// Appending things.
template <typename T>
void append( T arg ) {
SetType( Class::Array ); Internal.List->emplace_back( arg );
}
template <typename T, typename... U>
void append( T arg, U... args ) {
append( arg ); append( args... );
}
// Assignments (template T).
template <typename T>
typename std::enable_if<std::is_same<T,bool>::value, JSON&>::type operator=( T b ) {
SetType( Class::Boolean ); Internal.Bool = b; return *this;
}
template <typename T>
typename std::enable_if<std::is_integral<T>::value && !std::is_same<T,bool>::value, JSON&>::type operator=( T i ) {
SetType( Class::Integral ); Internal.Int = i; return *this;
}
template <typename T>
typename std::enable_if<std::is_floating_point<T>::value, JSON&>::type operator=( T f ) {
SetType( Class::Floating ); Internal.Float = f; return *this;
}
template <typename T>
typename std::enable_if<std::is_convertible<T, std::string>::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<std::map<std::string,JSON>> ObjectRange();
JSONWrapper<std::deque<JSON>> ArrayRange();
JSONConstWrapper<std::map<std::string,JSON>> ObjectRange() const;
JSONConstWrapper<std::deque<JSON>> 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 <typename... T>
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

View File

@@ -1,94 +1,325 @@
#include "rktwebview.h"
#include <QThread> #include <QThread>
#include <QFile> #include <QFile>
#include <QApplication> #include <QApplication>
static int _argc; #include "rkt_protocol.h"
static char **_argv; #include "shm.h"
#include "shmqueue.h"
#include "command.h"
void eventCb(rkt_data_t *e) #include <QJsonDocument>
#include <QJsonObject>
#include "rktwebview_qt.h"
static void free_data(rkt_data_t *d)
{ {
printf("event: %s\n", e->data.event.event); if (d->kind == version) {
rkt_webview_free_data(e); 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 main(int argc, char *argv[])
{ {
int wv1; const char *me = argv[0];
int wv2;
_argc = argc; if (argc < 6) {
_argv = argv; fprintf(stderr, "%s: wrong number of arguments\n", me);
exit(1);
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) { const char *shm_name = argv[1];
rkt_data_t *r = rkt_webview_call_js(wv1, "{ let a = 7 * 6; console.log('a = ' + a); return a; }"); const char *shm_size_str = argv[2];
printf("rkt_js_result: %d: %s\n", r->data.js_result.result, r->data.js_result.value); const char *cmd_slot_str = argv[3];
rkt_webview_free_data(r); 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);
} }
if (i == 4) { Handler *handler = new Handler();
rkt_data_t *r = rkt_webview_call_js(wv1, "let el = document.getElementById('hi');el.value = '10';"); _handler = handler;
printf("rkt_js_result: %d: %s\n", r->data.js_result.result, r->data.js_result.value); handler->shm = new Shm(shm_name, shm_size, false);
rkt_webview_free_data(r); 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;
} }
if (i == 6) { void Handler::run()
//rkt_data_t *r = rkt_webview_call_js(wv1, "document.body.innerHTML = '<h1>Hi!</h1>'; return document.body.innerHTML;"); {
//printf("rkt_js_result: %d: %s\n", r->data.js_result.result, r->data.js_result.value); bool quit = false;
//rkt_webview_free_data(r); while (!quit) {
} int cmd;
std::string data;
command_queue->dequeue(cmd, data, true);
QJsonObject data_obj = QJsonDocument::fromJson(data.c_str()).object();
if (i == 7) { switch(cmd) {
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); 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);
if (i == 10) { result_queue->enqueue(wv);
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");
} }
break;
if (i > 10) { case CMD_CLOSE_WV: {
char buf[1000]; int wv = data_obj["wv"].toInt();
sprintf(buf, "{ let obj = { e: 'test', i: %d }; window.rkt_send_event(obj); }", i); webview_handler->rktWebViewClose(wv);
rkt_webview_run_js(wv1, buf);
} }
break;
if (i == 15) { case CMD_SET_URL: {
rkt_webview_close(wv2); 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<rkt_messagetype_t>(type)
);
result_queue->enqueue(r);
}
break;
default: {
fprintf(stderr, "Unknown command: %d\n", cmd);
} }
i += 1;
} }
rkt_webview_close(wv1);
rkt_webview_cleanup();
} }
QCoreApplication *app = QApplication::instance();
delete app;
printf("%p\n", QApplication::instance());
} }

View File

@@ -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

View File

@@ -1,31 +1,85 @@
#include "rktwebview_internal.h"
#include <malloc.h> #include <malloc.h>
#include <string.h>
#include <chrono> #include <chrono>
#include <stdint.h> #include <stdint.h>
//#include <unistd.h> #include <string.h>
#include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include "rktwebview_qt.h" #include "rktwebview.h"
#include "rkt_protocol.h"
#include <spawn.h>
#include "shmqueue.h" #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() { uint64_t current_ms() {
using namespace std::chrono; using namespace std::chrono;
return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count(); return duration_cast<milliseconds>(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<char *>(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 // 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() void rkt_webview_cleanup()
{ {
@@ -54,76 +108,190 @@ void rkt_webview_cleanup()
handler = nullptr; 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() void rkt_webview_init()
{ {
if (handler == nullptr) { if (handler == nullptr) {
handler = new Rktwebview_qt(); // Create shared memory and communication queues
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
}
} }
if (handler->app() == nullptr) { bool rkt_webview_valid(rktwebview_t wv)
handler->initApp(); {
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_wv_context_t rkt_webview_new_context(const char *boilerplate_js, const char *optional_server_cert_pem)
{ {
rkt_webview_init(); 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(); 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) void rkt_webview_close(rktwebview_t wv)
{ {
rkt_webview_init(); 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_t>(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) result_t rkt_webview_set_url(rktwebview_t wv, const char *url)
{ {
rkt_webview_init(); CMDRES(CMD_SET_URL, wv, "url", url)
result_t r = handler->rktSetUrl(wv, url);
return r;
} }
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(); CMDRES(CMD_SET_HTML, wv, "html", html)
result_t r = handler->rktSetHtml(wv, url);
return r;
} }
result_t rkt_webview_run_js(rktwebview_t wv, const char *js) result_t rkt_webview_run_js(rktwebview_t wv, const char *js)
{ {
rkt_webview_init(); CMDRES(CMD_RUN_JS, wv, "js", js)
result_t r = handler->rktRunJs(wv, js);
return r;
} }
rkt_data_t *rkt_webview_call_js(rktwebview_t wv, const char *js) rkt_data_t *rkt_webview_call_js(rktwebview_t wv, const char *js)
{ {
rkt_webview_init(); 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_t>(result);
r->data.js_result.value = strdup(json_result.c_str());
return r; return r;
} }
result_t rkt_webview_open_devtools(rktwebview_t wv) result_t rkt_webview_open_devtools(rktwebview_t wv)
{ {
rkt_webview_init(); CMDRES(CMD_OPEN_DEVTOOLS, wv, "nil", "none")
result_t r = handler->rktOpenDevtools(wv);
return r;
} }
/*
void rkt_webview_process_events(int for_ms) void rkt_webview_process_events(int for_ms)
{ {
rkt_webview_init(); rkt_webview_init();
@@ -136,106 +304,94 @@ void rkt_webview_process_events(int for_ms)
handler->doEvents(); handler->doEvents();
} }
} }
*/
result_t rkt_webview_move(rktwebview_t wv, int x, int y) result_t rkt_webview_move(rktwebview_t wv, int x, int y)
{ {
rkt_webview_init(); CMDRES2(CMD_MOVE, wv, "x", x, "y", y)
result_t r = handler->rktMove(wv, x, y);
return r;
} }
result_t rkt_webview_resize(rktwebview_t wv, int width, int height) result_t rkt_webview_resize(rktwebview_t wv, int width, int height)
{ {
rkt_webview_init(); CMDRES2(CMD_RESIZE, wv, "w", width, "h", height)
result_t r = handler->rktResize(wv, width, height);
return r;
} }
bool rkt_webview_valid(rktwebview_t wv)
{
rkt_webview_init();
return handler->rktValid(wv);
}
result_t rkt_webview_hide(rktwebview_t w) result_t rkt_webview_hide(rktwebview_t w)
{ {
rkt_webview_init(); CMDRES0(CMD_HIDE, w)
return handler->rktHideWindow(w);
} }
result_t rkt_webview_show(rktwebview_t w) result_t rkt_webview_show(rktwebview_t w)
{ {
rkt_webview_init(); CMDRES0(CMD_SHOW, w)
return handler->rktShowWindow(w);
} }
result_t rkt_webview_present(rktwebview_t w) result_t rkt_webview_present(rktwebview_t w)
{ {
rkt_webview_init(); CMDRES0(CMD_PRESENT, w)
return handler->rktPresentWindow(w);
} }
result_t rkt_webview_maximize(rktwebview_t w) result_t rkt_webview_maximize(rktwebview_t w)
{ {
rkt_webview_init(); CMDRES0(CMD_MAXIMIZE, w)
return handler->rktMaximizeWindow(w);
} }
result_t rkt_webview_minimize(rktwebview_t w) result_t rkt_webview_minimize(rktwebview_t w)
{ {
rkt_webview_init(); CMDRES0(CMD_MINIMIZE, w)
return handler->rktMinimizeWindow(w);
} }
result_t rkt_webview_show_normal(rktwebview_t w) result_t rkt_webview_show_normal(rktwebview_t w)
{ {
rkt_webview_init(); CMDRES0(CMD_SHOW_NORMAL, w)
return handler->rktShowNormalWindow(w);
} }
window_state_t rkt_webview_window_state(rktwebview_t w) window_state_t rkt_webview_window_state(rktwebview_t w)
{ {
rkt_webview_init(); auto f = [w]() {
return handler->rktWindowState(w); CMDRES0(CMD_WINDOW_STATE, w)
};
int r = f();
return static_cast<window_state_t>(r);
} }
result_t rkt_webview_set_title(rktwebview_t wv, const char *title) result_t rkt_webview_set_title(rktwebview_t wv, const char *title)
{ {
rkt_webview_init(); CMDRES(CMD_SET_TITLE, wv, "title", title)
return handler->rktWindowSetTitle(wv, title);
} }
result_t rkt_webview_choose_dir(rktwebview_t w, const char *title, const char *base_dir) result_t rkt_webview_choose_dir(rktwebview_t w, const char *title, const char *base_dir)
{ {
rkt_webview_init(); CMDRES2(CMD_CHOOSE_DIR, w, "title", title, "base_dir", base_dir)
return handler->rktChooseDir(w, title, base_dir);
} }
result_t rkt_webview_file_open(rktwebview_t w, const char *title, const char *base_dir, const char *permitted_exts) result_t rkt_webview_file_open(rktwebview_t w, const char *title, const char *base_dir, const char *permitted_exts)
{ {
rkt_webview_init(); CMDRES3(CMD_FILE_OPEN, w, "title", title, "base_dir", base_dir, "permitted_exts", permitted_exts)
return handler->rktFileOpen(w, title, base_dir, permitted_exts);
} }
result_t rkt_webview_file_save(rktwebview_t w, const char *title, const char *base_dir, const char *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(); CMDRES3(CMD_FILE_SAVE, w, "title", title, "base_dir", base_dir, "permitted_exts", permitted_exts)
return handler->rktFileSave(w, title, base_dir, permitted_exts);
} }
void rkt_webview_set_ou_token(rktwebview_t wv, const char *token) void rkt_webview_set_ou_token(rktwebview_t wv, const char *token)
{ {
rkt_webview_init(); 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) void rkt_webview_free_data(rkt_data_t *d)
{ {
if (d == nullptr) { return; }
if (d->kind == version) { if (d->kind == version) {
free(d); free(d);
} else if (d->kind == event) { } 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_major = RKT_WEBVIEW_API_MAJOR;
d->data.version.api_minor = RKT_WEBVIEW_API_MINOR; d->data.version.api_minor = RKT_WEBVIEW_API_MINOR;
d->data.version.api_patch = RKT_WEBVIEW_API_PATCH; 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; return d;
} }
result_t rkt_webview_message_box(rktwebview_t w, const char *title, const char *message, const char *submessage, rkt_messagetype_t type) 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(); CMDRES4(CMD_MSG_BOX, w, "title", title, "message", message, "submessage", submessage, "type", static_cast<int>(type))
return handler->rktMessageBox(w, title, message, submessage, 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<rkt_data_t *>(malloc(sizeof(rkt_data_t)));
d->kind = rkt_data_kind_t::event;
size_t ds = data.size();
d->data.event.event = reinterpret_cast<char *>(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;
}
} }

View File

@@ -1,7 +1,8 @@
#ifndef RKTWEBVIEW_H #ifndef RKTWEBVIEW_H
#define 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_MAJOR 0
#define RKT_WEBVIEW_API_MINOR 1 #define RKT_WEBVIEW_API_MINOR 1
@@ -9,122 +10,47 @@
extern "C" { extern "C" {
typedef int rktwebview_t; RKTWEBVIEW_EXPORT void rkt_webview_init();
typedef int rkt_wv_context_t; RKTWEBVIEW_EXPORT void rkt_webview_cleanup();
//RKTWEBVIEW_EXPORT void rkt_webview_process_events(int for_ms);
typedef struct { RKTWEBVIEW_EXPORT void rkt_webview_free_data(rkt_data_t *d);
rktwebview_t wv; RKTWEBVIEW_EXPORT rkt_data_t *rkt_webview_version();
char *event;
} rkt_event_t;
typedef enum { RKTWEBVIEW_EXPORT int rkt_webview_events_waiting();
no_result_yet = -1, RKTWEBVIEW_EXPORT rkt_data_t *rkt_webview_get_event();
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 { RKTWEBVIEW_EXPORT rkt_wv_context_t rkt_webview_new_context(const char *boilerplate_js, const char *optional_server_cert_pem);
result_t result; RKTWEBVIEW_EXPORT int rkt_webview_create(rkt_wv_context_t context, rktwebview_t parent);
char *value;
} rkt_js_result_t;
typedef enum { RKTWEBVIEW_EXPORT void rkt_webview_close(rktwebview_t wv);
invalid = -1, RKTWEBVIEW_EXPORT bool rkt_webview_valid(rktwebview_t wv);
normal = 0, RKTWEBVIEW_EXPORT result_t rkt_webview_set_title(rktwebview_t wv, const char *title);
minimized = 1, RKTWEBVIEW_EXPORT void rkt_webview_set_ou_token(rktwebview_t wv, const char *token);
maximized = 2,
hidden = 3,
normal_active = 16,
maximized_active = 18
} window_state_t;
typedef enum { RKTWEBVIEW_EXPORT result_t rkt_webview_set_url(rktwebview_t wv, const char *url);
info = 1, RKTWEBVIEW_EXPORT result_t rkt_webview_set_html(rktwebview_t wv, const char *html);
error = 2,
warning = 3,
yes_no = 4,
oke_cancel = 5
} rkt_messagetype_t;
typedef struct { RKTWEBVIEW_EXPORT result_t rkt_webview_run_js(rktwebview_t wv, const char *js);
int qt_major; RKTWEBVIEW_EXPORT rkt_data_t *rkt_webview_call_js(rktwebview_t wv, const char *js);
int qt_minor;
int qt_patch;
int api_major;
int api_minor;
int api_patch;
} rkt_version_t;
typedef enum { RKTWEBVIEW_EXPORT result_t rkt_webview_open_devtools(rktwebview_t wv);
version = 1,
event = 2,
js_result = 3
} rkt_data_kind_t;
typedef struct { RKTWEBVIEW_EXPORT result_t rkt_webview_move(rktwebview_t w, int x, int y);
rkt_data_kind_t kind; RKTWEBVIEW_EXPORT result_t rkt_webview_resize(rktwebview_t w, int width, int height);
union { RKTWEBVIEW_EXPORT result_t rkt_webview_hide(rktwebview_t w);
rkt_version_t version; RKTWEBVIEW_EXPORT result_t rkt_webview_show(rktwebview_t w);
rkt_event_t event; RKTWEBVIEW_EXPORT result_t rkt_webview_show_normal(rktwebview_t w);
rkt_js_result_t js_result; RKTWEBVIEW_EXPORT result_t rkt_webview_present(rktwebview_t w);
} data; RKTWEBVIEW_EXPORT result_t rkt_webview_maximize(rktwebview_t w);
} rkt_data_t; 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_EXPORT result_t rkt_webview_message_box(rktwebview_t w, const char *title, const char *message, const char *submessage, rkt_messagetype_t type);
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);
} }

View File

@@ -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

View File

@@ -1,6 +0,0 @@
#ifndef RKTWEBVIEW_INTERNAL_H
#define RKTWEBVIEW_INTERNAL_H
#include "rktwebview.h"
#endif // RKTWEBVIEW_INTERNAL_H

View File

@@ -1,6 +1,6 @@
#include "rktwebview_qt.h" #include "rktwebview_qt.h"
#include "webviewqt.h" #include "webviewqt.h"
#include "rktwebview.h" #include "rktwebview_types.h"
#include "webviewwindow.h" #include "webviewwindow.h"
#include "rktutils.h" #include "rktutils.h"
#include <QApplication> #include <QApplication>
@@ -29,6 +29,92 @@ static inline char *copyString(const char *s)
void Rktwebview_qt::processCommand(Command *cmd) void Rktwebview_qt::processCommand(Command *cmd)
{ {
switch(cmd->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<QSslCertificate> 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<QWebEngineScript> 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<QWebEngineScript> l = c->toList();
_contexts[_context_counter] = p;
cmd->result = _context_counter;
cmd->done = true;
}
break;
case COMMAND_CREATE: { case COMMAND_CREATE: {
rkt_wv_context_t context = cmd->args[0].toInt(); rkt_wv_context_t context = cmd->args[0].toInt();
rktwebview_t parent = cmd->args[1].toInt(); rktwebview_t parent = cmd->args[1].toInt();
@@ -151,6 +237,7 @@ void Rktwebview_qt::processCommand(Command *cmd)
QString js = cmd->args[1].toString(); QString js = cmd->args[1].toString();
if (_views.contains(wv)) { if (_views.contains(wv)) {
WebviewWindow *w = _views[wv]; WebviewWindow *w = _views[wv];
fprintf(stderr, "Running %s\n", js.toUtf8().constData());
w->runJs(js); w->runJs(js);
cmd->result = true; cmd->result = true;
} else { } else {
@@ -367,78 +454,20 @@ int Rktwebview_qt::nextHandle()
return h; 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; Command c(COMMAND_NEW_CONTEXT);
if (optional_server_cert_pem != nullptr) {
QByteArray scp = QByteArray(optional_server_cert_pem);
QList<QSslCertificate> 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" c.args.push_back(boilerplate_js);
"window.rkt_evt_frame_el = null;\n" c.args.push_back(has_pem);
"window.rkt_evt_frame_win = null;\n" c.args.push_back(optional_server_cert_pem);
"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<QWebEngineScript> scripts; postCommand(&c);
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; while(!c.done) { doEvents(); }
s2.setInjectionPoint(QWebEngineScript::InjectionPoint::DocumentReady);
s2.setName("boilerplate");
s2.setSourceCode(boilerplate_js);
s2.setWorldId(QWebEngineScript::MainWorld);
scripts.append(s2);
col->insert(scripts); int id = c.result.toInt();
return id;
QWebEngineScriptCollection *c = p->scripts();
QList<QWebEngineScript> l = c->toList();
_contexts[_context_counter] = p;
return _context_counter;
} }
int Rktwebview_qt::rktWebViewCreate(rkt_wv_context_t context, rktwebview_t parent, event_cb_t js_evt_cb) 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) rkt_data_t *Rktwebview_qt::rktCallJs(rktwebview_t wv, const char *js)
{ {
fprintf(stderr, "calljs: %s\n", js);
Command c(COMMAND_CALL_JS); Command c(COMMAND_CALL_JS);
QString _js(js); QString _js(js);
c.args.push_back(wv); 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->kind = js_result;
r->data.js_result.result = c.js_result_ok ? result_t::oke : result_t::eval_js_failed; 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()); r->data.js_result.value = copyString(c.result.toString().toUtf8());
fprintf(stderr, "js-result: %s\n", r->data.js_result.value);
return r; 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); Command c(COMMAND_RUN_JS);
QString _js(js);
c.args.push_back(wv); c.args.push_back(wv);
c.args.push_back(_js); c.args.push_back(js);
postCommand(&c); postCommand(&c);
while(!c.done) { doEvents(); } while(!c.done) { doEvents(); }
bool r = c.result.toBool(); bool r = c.result.toBool();
@@ -604,6 +636,12 @@ window_state_t Rktwebview_qt::rktWindowState(rktwebview_t w)
return ws; 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) 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)) { if (_views.contains(w)) {
@@ -836,6 +874,7 @@ void Rktwebview_qt::deleteApp()
void Rktwebview_qt::initApp() void Rktwebview_qt::initApp()
{ {
_app = new QApplication(_argc, _argv); _app = new QApplication(_argc, _argv);
_app->setQuitOnLastWindowClosed(false);
// See Qt 6.10 remark at doEvents. // See Qt 6.10 remark at doEvents.
//connect(&_evt_loop_timer, &QTimer::timeout, this, &Rktwebview_qt::stopEventloop); //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 // Because we are using processEvents only (Qt 6.10), we need this dispatcher to
// handle deferred Deletes. // handle deferred Deletes.
const auto *eventDispatcher = QThread::currentThread()->eventDispatcher(); /*const auto *eventDispatcher = QThread::currentThread()->eventDispatcher();
QObject::connect(eventDispatcher, &QAbstractEventDispatcher::aboutToBlock, QObject::connect(eventDispatcher, &QAbstractEventDispatcher::aboutToBlock,
QThread::currentThread(), []{ QThread::currentThread(), []{
if (QThread::currentThread()->loopLevel() == 0) if (QThread::currentThread()->loopLevel() == 0)
QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
} }
); );
*/
} }
void Rktwebview_qt::runJs(rktwebview_t wv, const char *js) void Rktwebview_qt::runJs(rktwebview_t wv, const char *js)
{ {
if (_views.contains(wv)) { if (_views.contains(wv)) {
@@ -888,7 +927,7 @@ void Rktwebview_qt::doEvents()
//QTime ct = QTime::currentTime(); //QTime ct = QTime::currentTime();
//QTime expire = QTime::currentTime().addMSecs(2); //QTime expire = QTime::currentTime().addMSecs(2);
//while(QTime::currentTime() <= expire) { //while(QTime::currentTime() <= expire) {
_app->processEvents(); //_app->processEvents();
//} //}
// Qt 6.10 --> this leads to a core dump // Qt 6.10 --> this leads to a core dump
@@ -901,6 +940,7 @@ void Rktwebview_qt::doEvents()
//_evt_loop.exec(); //_evt_loop.exec();
_app->exec(); _app->exec();
}*/ }*/
QThread::usleep(500);
} }
void Rktwebview_qt::stopEventloop() 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<char *>("Rktwebview_qt");
_context_counter = 0; _context_counter = 0;
_current_handle = 0; _current_handle = 0;
_evt_loop_depth = 0; _evt_loop_depth = 0;
_argc = argc;
_argv = argv;
_app = nullptr; _app = nullptr;
} }

View File

@@ -1,9 +1,8 @@
#ifndef RKTWEBVIEW_QT_H #ifndef RKTWEBVIEW_QT_H
#define RKTWEBVIEW_QT_H #define RKTWEBVIEW_QT_H
#include "rktwebview_qt_global.h"
#include "rktwebview_internal.h"
#include "rktutils.h" #include "rktutils.h"
#include "rktwebview_types.h"
#include <QApplication> #include <QApplication>
#include <QWebEngineView> #include <QWebEngineView>
@@ -21,7 +20,7 @@ class WebviewWindow;
class Command; class Command;
class CommandEvent; class CommandEvent;
class RKTWEBVIEW_QT_EXPORT Rktwebview_qt : public QObject class Rktwebview_qt : public QObject
{ {
Q_OBJECT Q_OBJECT
private: private:
@@ -39,7 +38,7 @@ private:
QTimer _evt_loop_timer; QTimer _evt_loop_timer;
int _argc; int _argc;
char *_argv[1]; char **_argv;
private: private:
void runJs(rktwebview_t wv, const char *js); void runJs(rktwebview_t wv, const char *js);
@@ -71,7 +70,7 @@ public:
int nextHandle(); int nextHandle();
public: 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); int rktWebViewCreate(rkt_wv_context_t context, rktwebview_t parent, event_cb_t js_evt_cb);
void rktWebViewClose(int wv); void rktWebViewClose(int wv);
void rktSetOUToken(rktwebview_t wv, const char *ou_token); void rktSetOUToken(rktwebview_t wv, const char *ou_token);
@@ -79,7 +78,7 @@ public:
result_t rktOpenDevtools(rktwebview_t wv); result_t rktOpenDevtools(rktwebview_t wv);
result_t rktSetUrl(rktwebview_t wv, const char *url); result_t rktSetUrl(rktwebview_t wv, const char *url);
result_t rktSetHtml(rktwebview_t wv, const char *html); 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); rkt_data_t *rktCallJs(rktwebview_t wv, const char *js);
result_t rktMove(rktwebview_t wv, int x, int y); result_t rktMove(rktwebview_t wv, int x, int y);
@@ -92,6 +91,8 @@ public:
result_t rktShowNormalWindow(rktwebview_t w); result_t rktShowNormalWindow(rktwebview_t w);
window_state_t rktWindowState(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 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 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); result_t rktFileSave(rktwebview_t w, const char *title, const char *base_dir, const char *permitted_exts);
@@ -117,7 +118,7 @@ public:
void runCommandThread(); void runCommandThread();
public: public:
Rktwebview_qt(); Rktwebview_qt(int argc, char *argv[]);
~Rktwebview_qt(); ~Rktwebview_qt();
}; };

View File

@@ -1,12 +0,0 @@
#ifndef RKTWEBVIEW_QT_GLOBAL_H
#define RKTWEBVIEW_QT_GLOBAL_H
#include <QtCore/qglobal.h>
#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

View File

@@ -0,0 +1,32 @@
#include "rktwebview.h"
#include <string>
#include <unistd.h>
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();
}

View File

@@ -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

View File

@@ -10,12 +10,13 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h>
#include <semaphore.h> #include <semaphore.h>
typedef struct __shm_item__ { typedef struct __shm_item__ {
struct __shm_item__ *next; ShmPlace next;
struct __shm_item__ *prev; ShmPlace prev;
size_t size; size_t size;
} ShmItem; } ShmItem;
@@ -34,10 +35,9 @@ private:
char *_sem_name; char *_sem_name;
size_t *_used; size_t *_used;
ShmItem **__first;
ShmItem **__free_list;
ShmPlace *_slots; ShmPlace *_slots;
ShmPlace *_first;
ShmPlace *_free_list;
int _in_critical; int _in_critical;
@@ -66,57 +66,82 @@ public:
return _mem_name; return _mem_name;
} }
public:
template <class T> void ref(int place, T** p) {
if (place == SHM_NULL) {
*p = nullptr;
} else {
*p = reinterpret_cast<T *>(reinterpret_cast<char *>(_mem) + place);
}
}
inline void *ref(int place)
{
if (place == SHM_NULL) {
return nullptr;
}
return reinterpret_cast<char *>(_mem) + place;
}
public: public:
int alloc(size_t bytes) { int alloc(size_t bytes) {
enterCritical(); enterCritical();
ShmItem *_free_list = *__free_list; ShmPlace p_i = *_free_list;
ShmItem *_first = *__first; ShmItem *i;
ref(p_i, &i);
ShmItem *i = _free_list;
while (i != nullptr && i->size < bytes) { while (i != nullptr && i->size < bytes) {
i = i->next; p_i = i->next;
ref(p_i, &i);
} }
int place; int place;
if (i != nullptr) { if (i != nullptr) {
if (i->prev != nullptr) { if (i->prev != SHM_NULL) {
i->prev->next = i->next; ShmItem *prev_i;
ref(i->prev, &prev_i);
prev_i->next = i->next;
} }
if (i->next != nullptr) { if (i->next != SHM_NULL) {
i->next->prev = i->prev; ShmItem *next_i;
ref(i->next, &next_i);
next_i->prev = i->prev;
} }
if (i->prev == nullptr) { if (i->prev == SHM_NULL) {
_free_list = i->next; *_free_list = i->next;
} }
i->next = _first; i->next = *_first;
if (_first != nullptr) { if (*_first != SHM_NULL) {
_first->prev = i; ShmItem *first_i;
ref(*_first, &first_i);
first_i->prev = p_i;
} }
i->prev = nullptr; i->prev = SHM_NULL;
_first = i; *_first = p_i;
*__first = _first; place = p_i + sizeof(ShmItem);
*__free_list = _free_list;
place = (reinterpret_cast<char *>(i) + sizeof(ShmItem)) - reinterpret_cast<char *>(_mem);
} else { } else {
i = reinterpret_cast<ShmItem *>(reinterpret_cast<char *>(_mem) + *_used); ShmPlace p_i = *_used;
i = reinterpret_cast<ShmItem *>(reinterpret_cast<char *>(_mem) + p_i);
size_t u = *_used + sizeof(ShmItem) + bytes; size_t u = *_used + sizeof(ShmItem) + bytes;
if (u >= _size) { if (u >= _size) {
place = -1; place = SHM_NULL;
} else { } else {
*_used = u; *_used = u;
i->prev = nullptr; i->prev = SHM_NULL;
i->size = bytes; 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 = p_i;
*__first = _first; place = p_i + sizeof(ShmItem);
place = (reinterpret_cast<char *>(i) + sizeof(ShmItem)) - reinterpret_cast<char *>(_mem);
} }
} }
@@ -124,39 +149,42 @@ public:
return place; return place;
} }
void free(int place) { void free(ShmPlace place) {
if (place == SHM_NULL) {
return;
}
enterCritical(); enterCritical();
ShmItem *i = reinterpret_cast<ShmItem *>(reinterpret_cast<char *>(_mem) + place - sizeof(ShmItem));
ShmItem *_free_list = *__free_list; ShmPlace p_i = place - sizeof(ShmItem);
ShmItem *_first = *__first; ShmItem *i;
ref(p_i, &i);
if (i->prev != nullptr) { if (i->prev != SHM_NULL) {
i->prev->next = i->next; ShmItem *prev_i;
ref(i->prev, &prev_i);
prev_i->next = i->next;
} }
if (i->next != nullptr) { if (i->next != SHM_NULL) {
i->next->prev = i->prev; ShmItem *next_i;
ref(i->next, &next_i);
next_i->prev = i->prev;
} }
if (i->prev == nullptr) { if (i->prev == SHM_NULL) {
_first = i->next; *_first = i->next;
} }
i->next = _free_list; i->next = *_free_list;
if (_free_list != nullptr) { if (*_free_list != SHM_NULL) {
_free_list->prev = i; ShmItem *fl;
ref(*_free_list, &fl);
fl->prev = p_i;
} }
i->prev = nullptr; i->prev = SHM_NULL;
_free_list = i; *_free_list = p_i;
*__first = _first;
*__free_list = _free_list;
leaveCritical(); leaveCritical();
} }
inline void *ref(int place)
{
return reinterpret_cast<char *>(_mem) + place;
}
void enterCritical() { void enterCritical() {
if (_in_critical > 0) { if (_in_critical > 0) {
_in_critical++; _in_critical++;
@@ -188,25 +216,28 @@ public:
size_t slots_size = sizeof(ShmPlace) * SHM_MAX_SLOTS; size_t slots_size = sizeof(ShmPlace) * SHM_MAX_SLOTS;
size += slots_size; size += slots_size;
_sem = sem_open(_sem_name, O_CREAT); 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); _shm_fd = shm_open(name, O_CREAT | O_RDWR, 0600);
ftruncate(_shm_fd, size); 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; _size = size;
_mem = mmap(nullptr, _size, PROT_READ|PROT_WRITE, MAP_SHARED, _shm_fd, 0); _mem = mmap(nullptr, _size, PROT_READ|PROT_WRITE, MAP_SHARED, _shm_fd, 0);
_slots = reinterpret_cast <ShmPlace *>(reinterpret_cast<char *>(_mem) + sizeof(size_t) + sizeof(ShmItem *) + sizeof(ShmItem *)); _slots = reinterpret_cast <ShmPlace *>(reinterpret_cast<char *>(_mem) + sizeof(size_t) + sizeof(ShmPlace) + sizeof(ShmPlace));
_used = reinterpret_cast<size_t *>(_mem); _used = reinterpret_cast<size_t *>(_mem);
__first = reinterpret_cast<ShmItem **>(reinterpret_cast<char *>(_mem) + sizeof(size_t)); _first = reinterpret_cast<ShmPlace *>(reinterpret_cast<char *>(_mem) + sizeof(size_t));
_free_list = reinterpret_cast<ShmPlace *>(reinterpret_cast<char *>(_mem) + sizeof(size_t) + sizeof(ShmPlace));
__free_list = reinterpret_cast<ShmItem **>(reinterpret_cast<char *>(_mem) + sizeof(size_t) + sizeof(ShmItem *));
_owner = owner; _owner = owner;
if (_owner) { if (_owner) {
*__first = nullptr; *_first = SHM_NULL;
*__free_list = nullptr; *_free_list = SHM_NULL;
*_used = sizeof(size_t) + sizeof(ShmItem *) + sizeof(ShmItem *) + slots_size; *_used = sizeof(size_t) + sizeof(ShmItem *) + sizeof(ShmItem *) + slots_size;
sem_post(_sem); sem_post(_sem);
} }
@@ -320,26 +351,38 @@ Shm::~Shm()
delete _shm_api; delete _shm_api;
} }
template<class T> void ref(Shm *shm, int place, T **p)
{
*p = reinterpret_cast<T *>(shm->ref(place));
}
void ShmSem::post() { void ShmSem::post() {
sem_post(reinterpret_cast<sem_t *>(_sem)); sem_post(reinterpret_cast<sem_t *>(_sem));
} }
void ShmSem::wait() { void ShmSem::wait() {
sem_wait(reinterpret_cast<sem_t *>(_sem)); int r = sem_wait(reinterpret_cast<sem_t *>(_sem));
if (r != 0) {
fprintf(stderr, "sem_wait error: %d, %s\n", errno, strerror(errno));
}
} }
bool ShmSem::trywait() { bool ShmSem::trywait() {
return sem_trywait(reinterpret_cast<sem_t *>(_sem)) == 0; int r = sem_trywait(reinterpret_cast<sem_t *>(_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) { ShmSem::ShmSem(const char *n, bool owner) {
_name = strdup(n); _name = strdup(n);
_sem = reinterpret_cast<void *>(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<void *>(s);
_owner = owner; _owner = owner;
} }

View File

@@ -55,6 +55,9 @@ public:
~Shm(); ~Shm();
}; };
template <class T> void ref(Shm *shm, int place, T **p); template <class T> inline void ref(Shm *shm, int place, T **p)
{
*p = reinterpret_cast<T *>(shm->ref(place));
}
#endif // SHM_H #endif // SHM_H

View File

@@ -1,4 +1,5 @@
#include "shmqueue.h" #include "shmqueue.h"
#include <string.h>
ShmQueue::ShmQueue(Shm *shm, ShmSlot slot, bool owner) ShmQueue::ShmQueue(Shm *shm, ShmSlot slot, bool owner)
{ {
@@ -43,15 +44,15 @@ int ShmQueue::depth()
return d; 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; char *s;
ref(_shm, str_place, &s); ref(_shm, str_place, &s);
memcpy(s, b.constData(), b.size()); memcpy(s, json_data.data(), jd_size);
s[b.size()] = '\0'; s[jd_size] = '\0';
int item_place = _shm->alloc(sizeof(ShmQueueItem)); int item_place = _shm->alloc(sizeof(ShmQueueItem));
ShmQueueItem *item; ShmQueueItem *item;
@@ -60,6 +61,11 @@ void ShmQueue::enqueue(int cmd, const QString &json_data)
item->data_place = str_place; item->data_place = str_place;
item->prev = _queue->last; item->prev = _queue->last;
item->next = SHM_NULL; 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(); _shm->lock();
_queue->last = item_place; _queue->last = item_place;
@@ -71,7 +77,13 @@ void ShmQueue::enqueue(int cmd, const QString &json_data)
_queue_sem->post(); _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) { if (wait) {
_queue_sem->wait(); _queue_sem->wait();
@@ -96,7 +108,7 @@ bool ShmQueue::dequeue(int &cmd, QString &json_data, bool wait)
ShmPlace data_place = item->data_place; ShmPlace data_place = item->data_place;
char *data; char *data;
ref(_shm, data_place, &data); ref(_shm, data_place, &data);
json_data = QString::fromUtf8(data, strlen(data)); json_data = data;
_shm->free(data_place); _shm->free(data_place);
cmd = item->cmd; cmd = item->cmd;

View File

@@ -2,10 +2,7 @@
#define SHMQUEUE_H #define SHMQUEUE_H
#include "shm.h" #include "shm.h"
#include <QString> #include <string>
#define COMMAND_SLOT 1
#define EVENT_SLOT 2
typedef struct __ShmQueueItem__ typedef struct __ShmQueueItem__
{ {
@@ -40,8 +37,9 @@ public:
int depth(); int depth();
public: public:
bool dequeue(int &cmd, QString &json_data, bool wait = false); bool dequeue(int &cmd, std::string &json_data, bool wait = false);
void enqueue(int cmd, const QString &json_data); void enqueue(int cmd, const std::string &json_data);
void enqueue(int cmd);
public: public:
ShmQueue(Shm *shm, ShmSlot slot, bool owner); ShmQueue(Shm *shm, ShmSlot slot, bool owner);