diff --git a/.gitignore b/.gitignore index e257658..28db11b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,34 +1,75 @@ -# ---> C++ -# Prerequisites -*.d +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- -# Compiled Object files -*.slo -*.lo +*~ +*.autosave +*.a +*.core +*.moc *.o *.obj - -# Precompiled Headers -*.gch -*.pch - -# Compiled Dynamic libraries +*.orig +*.rej *.so -*.dylib -*.dll - -# Fortran module files -*.mod -*.smod - -# Compiled Static libraries -*.lai -*.la -*.a -*.lib - -# Executables -*.exe -*.out +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl *.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash +# qtcreator generated files +*.pro.user* +CMakeLists.txt.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + +build diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..e1d4bb3 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,57 @@ +cmake_minimum_required(VERSION 3.14) + +project(rktwebview LANGUAGES CXX) + +set(QT_DEBUG_FIND_PACKAGE ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(QT NAMES Qt6 REQUIRED COMPONENTS Widgets WebEngineWidgets) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets WebEngineWidgets) + +add_library(rktwebview SHARED + rktwebview_global.h + rktwebview.h + rktwebview.cpp + shm.h shm.cpp + shmqueue.h shmqueue.cpp + rkt_protocol.h + rktwebview_types.h + json.cpp json.h + utils.h + utils.cpp +) + +add_executable(rktwebview_prg + main.cpp + + rktwebview_qt.cpp + rktwebview_qt.h + + webviewqt.h webviewqt.cpp + webviewwindow.h webviewwindow.cpp + + rktutils.h rktutils.cpp + command.h command.cpp + shm.h shm.cpp + shmqueue.h shmqueue.cpp + + rkt_protocol.h + rktwebview_types.h + utils.cpp utils.h +) + +add_executable(rktwebview_test + rktwebview_test.cpp +) + +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) + diff --git a/command.cpp b/command.cpp new file mode 100644 index 0000000..970fe0e --- /dev/null +++ b/command.cpp @@ -0,0 +1,20 @@ +#include "command.h" + + +Command::Command(int _cmd) +{ + cmd = _cmd; + done = false; + js_result_ok = true; +} + +Command *CommandEvent::cmd() +{ + return _cmd; +} + +CommandEvent::CommandEvent(Command *c) + : QEvent(COMMAND_EVENT) +{ + _cmd = c; +} diff --git a/command.h b/command.h new file mode 100644 index 0000000..e29fa64 --- /dev/null +++ b/command.h @@ -0,0 +1,60 @@ +#ifndef COMMAND_H +#define COMMAND_H + +#include +#include + +#define COMMAND_QUIT 1 +#define COMMAND_CLOSE 2 +#define COMMAND_CREATE 3 +#define COMMAND_SET_URL 4 +#define COMMAND_SET_HTML 5 +#define COMMAND_RUN_JS 6 +#define COMMAND_DEV_TOOLS 7 +#define COMMAND_MOVE 8 +#define COMMAND_RESIZE 9 +#define COMMAND_CALL_JS 10 +#define COMMAND_HIDE_WIN 11 +#define COMMAND_SHOW_WIN 12 +#define COMMAND_MAX_WIN 13 +#define COMMAND_MIN_WIN 14 +#define COMMAND_PRESENT_WIN 15 +#define COMMAND_SHOW_NORMAL_WIN 16 +#define COMMAND_WINDOW_STATUS 17 +#define COMMAND_SET_TITLE 18 +#define COMMAND_CHOOSE_DIR 19 +#define COMMAND_FILE_OPEN 20 +#define COMMAND_FILE_SAVE 21 +#define COMMAND_SET_OU_TOKEN 22 +#define COMMAND_NEW_CONTEXT 23 +#define COMMAND_MESSAGE 24 + +class Command +{ +public: + int cmd; + QVector args; + QVariant result; + bool done; + bool js_result_ok; +public: + Command(int _cmd); +}; + + +class CommandEvent : public QEvent +{ +private: + Command *_cmd; + +public: + Command *cmd(); + +public: + CommandEvent(Command *c); +}; + + +const QEvent::Type COMMAND_EVENT = static_cast(QEvent::User + 1); + +#endif // COMMAND_H diff --git a/install-linux.sh b/install-linux.sh new file mode 100644 index 0000000..52cbfa6 --- /dev/null +++ b/install-linux.sh @@ -0,0 +1,66 @@ +#!/bin/bash +# +ARCH=`uname -m` + +LIB="lib/linux/$ARCH" + +mkdir -p $LIB +rm -f $LIB/*.so* +cp build/Release/*.so $LIB +cp build/Release/rktwebview_prg $LIB + +QT_PATH=`ldd build/Release/rktwebview_prg | grep Qt | awk '{print $3}' | head -1 | sed -e 's%[/]lib[/].*%%'` +echo "QT_PATH=$QT_PATH" + +QT_PLUGINS="$QT_PATH/plugins" +PLUGINS="platforms position generic iconengines imageformats qmltooling tls xcbglintegrations" + +EXTRA_LIBS_SO=`ldd build/Release/rktwebview_prg | grep Qt | awk '{ print $3 }'` +EXTRA_LIBS_PLATFORM_PLUGIN_XCB=`ldd $QT_PATH/plugins/platforms/libqxcb.so | grep Qt | awk '{print $3}'` + +for pl in $PLUGINS +do + echo "Assembling libs for $pl" + LIBS=`ls $QT_PLUGINS/$pl | grep so` + for ll in $LIBS + do + l="$QT_PLUGINS/$pl/$ll" + ELS=`ldd $l | grep Qt | grep -v no\ version\ information | awk '{print $3}'` + EXTRA_LIBS_SO="$EXTRA_LIBS_SO $ELS" + done +done + +#echo $EXTRA_LIBS_SO | less + +EXTRA_LIBS=`echo $EXTRA_LIBS_SO $EXTRA_LIBS_PLATFORM_PLUGIN_XCB | sort | uniq` + +for l in $EXTRA_LIBS; do + version_so=`basename $l` + if [ ! -r "$LIB/$version_so" ]; then + echo "Copying $l..." + cp $l $LIB + so=`echo $version_so | sed -e 's/[.]so.*$//'` + lib_so=`echo -n $so; echo ".so"` + (cd $LIB; ln -s $version_so $lib_so) + fi +done + + +for p in $PLUGINS +do + echo "Plugin $p..." + (cd $QT_PLUGINS; tar cf - $p) | (cd $LIB; tar xf -) +done + +RESOURCES="resources translations" +QT_RESOURCES="$QT_PATH" + +for r in $RESOURCES +do + echo "Resource $r..." + (cd $QT_RESOURCES; tar cf - $r) | (cd $LIB; tar xf - ) +done + +cp $QT_PATH/libexec/QtWebEngineProcess $LIB + + diff --git a/json.cpp b/json.cpp new file mode 100644 index 0000000..571eaa9 --- /dev/null +++ b/json.cpp @@ -0,0 +1,505 @@ +#include "json.h" + +using std::map; +using std::deque; +using std::string; +using std::enable_if; +using std::initializer_list; +using std::is_same; +using std::is_convertible; +using std::is_integral; +using std::is_floating_point; + +std::string json_escape(const string &str) { + string output; + for( unsigned i = 0; i < str.length(); ++i ) + switch( str[i] ) { + case '\"': output += "\\\""; break; + case '\\': output += "\\\\"; break; + case '\b': output += "\\b"; break; + case '\f': output += "\\f"; break; + case '\n': output += "\\n"; break; + case '\r': output += "\\r"; break; + case '\t': output += "\\t"; break; + default : output += str[i]; break; + } + return std::move( output ); +} + +JSON::JSON() : Internal(), Type( Class::Null ){} + +JSON::JSON(std::initializer_list list) + : JSON() +{ + SetType( Class::Object ); + for( auto i = list.begin(), e = list.end(); i != e; ++i, ++i ) + operator[]( i->toString() ) = *std::next( i ); +} + +JSON::JSON(JSON &&other) + : Internal( other.Internal ) + , Type( other.Type ) +{ other.Type = Class::Null; other.Internal.Map = nullptr; } + +JSON::JSON(const JSON &other) { + switch( other.Type ) { + case Class::Object: + Internal.Map = + new map( other.Internal.Map->begin(), + other.Internal.Map->end() ); + break; + case Class::Array: + Internal.List = + new deque( other.Internal.List->begin(), + other.Internal.List->end() ); + break; + case Class::String: + Internal.String = + new string( *other.Internal.String ); + break; + default: + Internal = other.Internal; + } + Type = other.Type; +} + +JSON JSON::Make(Class type) { + JSON ret; ret.SetType( type ); + return ret; +} + +JSON &JSON::operator[](unsigned int index) { + SetType( Class::Array ); + if( index >= Internal.List->size() ) Internal.List->resize( index + 1 ); + return Internal.List->operator[]( index ); +} + +JSON &JSON::at(const std::string &key) { + return operator[]( key ); +} + +const JSON &JSON::at(const std::string &key) const { + return Internal.Map->at( key ); +} + +JSON &JSON::at(unsigned int index) { + return operator[]( index ); +} + +const JSON &JSON::at(unsigned int index) const { + return Internal.List->at( index ); +} + +int JSON::length() const { + if( Type == Class::Array ) + return Internal.List->size(); + else + return -1; +} + +bool JSON::hasKey(const std::string &key) const { + if( Type == Class::Object ) + return Internal.Map->find( key ) != Internal.Map->end(); + return false; +} + +int JSON::size() const { + if( Type == Class::Object ) + return Internal.Map->size(); + else if( Type == Class::Array ) + return Internal.List->size(); + else + return -1; +} + +JSON::Class JSON::JSONType() const { return Type; } + +bool JSON::IsNull() const { return Type == Class::Null; } + +string JSON::toString() const { bool b; return std::move( toString( b ) ); } + +string JSON::toString(bool &ok) const { + ok = (Type == Class::String); + return ok ? std::move( json_escape( *Internal.String ) ): string(""); +} + +double JSON::toFloat() const { bool b; return toFloat( b ); } + +double JSON::toFloat(bool &ok) const { + ok = (Type == Class::Floating); + return ok ? Internal.Float : 0.0; +} + +long JSON::toInt() const { bool b; return toInt( b ); } + +long JSON::toInt(bool &ok) const { + ok = (Type == Class::Integral); + return ok ? Internal.Int : 0; +} + +bool JSON::toBool() const { bool b; return toBool( b ); } + +bool JSON::toBool(bool &ok) const { + ok = (Type == Class::Boolean); + return ok ? Internal.Bool : false; +} + +JSON::JSONWrapper > JSON::ObjectRange() { + if( Type == Class::Object ) + return JSONWrapper>( Internal.Map ); + return JSONWrapper>( nullptr ); +} + +JSON::JSONWrapper > JSON::ArrayRange() { + if( Type == Class::Array ) + return JSONWrapper>( Internal.List ); + return JSONWrapper>( nullptr ); +} + +JSON::JSONConstWrapper > JSON::ObjectRange() const { + if( Type == Class::Object ) + return JSONConstWrapper>( Internal.Map ); + return JSONConstWrapper>( nullptr ); +} + +JSON::JSONConstWrapper > JSON::ArrayRange() const { + if( Type == Class::Array ) + return JSONConstWrapper>( Internal.List ); + return JSONConstWrapper>( nullptr ); +} + +string JSON::dump(int depth, const std::string &tab) const { + std::string pad = ""; + for( int i = 0; i < depth; ++i, pad += tab ); + + switch( Type ) { + case Class::Null: + return "null"; + case Class::Object: { + std::string s = "{ "; + bool skip = true; + for( auto &p : *Internal.Map ) { + if( !skip ) s += ", "; + s += ( pad + "\"" + p.first + "\" : " + p.second.dump( depth + 1, tab ) ); + skip = false; + } + s += ( " " + pad.erase( 0, 2 ) + "}" ) ; + return s; + } + case Class::Array: { + std::string s = "["; + bool skip = true; + for( auto &p : *Internal.List ) { + if( !skip ) s += ", "; + s += p.dump( depth + 1, tab ); + skip = false; + } + s += "]"; + return s; + } + case Class::String: + return "\"" + json_escape( *Internal.String ) + "\""; + case Class::Floating: + return std::to_string( Internal.Float ); + case Class::Integral: + return std::to_string( Internal.Int ); + case Class::Boolean: + return Internal.Bool ? "true" : "false"; + default: + return ""; + } + return ""; +} + +void JSON::SetType(Class type) { + if( type == Type ) + return; + + ClearInternal(); + + switch( type ) { + case Class::Null: Internal.Map = nullptr; break; + case Class::Object: Internal.Map = new std::map(); break; + case Class::Array: Internal.List = new std::deque(); break; + case Class::String: Internal.String = new std::string(); break; + case Class::Floating: Internal.Float = 0.0; break; + case Class::Integral: Internal.Int = 0; break; + case Class::Boolean: Internal.Bool = false; break; + } + + Type = type; +} + +void JSON::ClearInternal() { + switch( Type ) { + case Class::Object: delete Internal.Map; break; + case Class::Array: delete Internal.List; break; + case Class::String: delete Internal.String; break; + default:; + } +} + +JSON &JSON::operator[](const std::string &key) { + SetType( Class::Object ); return Internal.Map->operator[]( key ); +} + +JSON &JSON::operator=(const JSON &other) { + + if (&other == this) { return *this; } + + ClearInternal(); + switch( other.Type ) { + case Class::Object: + Internal.Map = + new map( other.Internal.Map->begin(), + other.Internal.Map->end() ); + break; + case Class::Array: + Internal.List = + new deque( other.Internal.List->begin(), + other.Internal.List->end() ); + break; + case Class::String: + Internal.String = + new string( *other.Internal.String ); + break; + default: + Internal = other.Internal; + } + Type = other.Type; + return *this; +} + +JSON &JSON::operator=(JSON &&other) { + ClearInternal(); + Internal = other.Internal; + Type = other.Type; + other.Internal.Map = nullptr; + other.Type = Class::Null; + return *this; +} + +JSON Array() { + return std::move( JSON::Make( JSON::Class::Array ) ); +} + +//JSON Object() { return std::move(JSON::Make(JSON::Class::Object)); } + +JSON Object() { + return std::move( JSON::Make( JSON::Class::Object ) ); +} + +std::ostream &operator<<(std::ostream &os, const JSON &json) { + os << json.dump(); + return os; +} + +// Private functions. + +static JSON parse_next( const std::string &, size_t &, std::function ); + + +static void consume_ws( const std::string &str, size_t &offset ) { + while( isspace( str[offset] ) ) ++offset; +} + +static JSON parse_object( const string &str, size_t &offset, std::function on_error ) { + JSON Object = JSON::Make( JSON::Class::Object ); + + ++offset; + consume_ws( str, offset ); + if( str[offset] == '}' ) { + ++offset; return std::move( Object ); + } + + while( true ) { + JSON Key = parse_next( str, offset, on_error ); + consume_ws( str, offset ); + if( str[offset] != ':' ) { + on_error(std::string("Error: Object: Expected colon, found '") + str[offset] + "'"); + break; + } + consume_ws( str, ++offset ); + JSON Value = parse_next( str, offset, on_error ); + Object[Key.toString()] = Value; + + consume_ws( str, offset ); + if( str[offset] == ',' ) { + ++offset; continue; + } + else if( str[offset] == '}' ) { + ++offset; break; + } + else { + on_error(std::string("ERROR: Object: Expected comma, found '") + str[offset] + "'"); + break; + } + } + + return std::move( Object ); +} + +static JSON parse_array( const string &str, size_t &offset, std::function on_error ) { + JSON Array = JSON::Make( JSON::Class::Array ); + unsigned index = 0; + + ++offset; + consume_ws( str, offset ); + if( str[offset] == ']' ) { + ++offset; return std::move( Array ); + } + + while( true ) { + Array[index++] = parse_next( str, offset, on_error ); + consume_ws( str, offset ); + + if( str[offset] == ',' ) { + ++offset; continue; + } + else if( str[offset] == ']' ) { + ++offset; break; + } + else { + on_error(std::string("ERROR: Array: Expected ',' or ']', found '") + str[offset] + "'"); + return std::move( JSON::Make( JSON::Class::Array ) ); + } + } + + return std::move( Array ); +} + +static JSON parse_string( const string &str, size_t &offset, std::function on_error ) { + JSON String; + string val; + for( char c = str[++offset]; c != '\"' ; c = str[++offset] ) { + if( c == '\\' ) { + switch( str[ ++offset ] ) { + case '\"': val += '\"'; break; + case '\\': val += '\\'; break; + case '/' : val += '/' ; break; + case 'b' : val += '\b'; break; + case 'f' : val += '\f'; break; + case 'n' : val += '\n'; break; + case 'r' : val += '\r'; break; + case 't' : val += '\t'; break; + case 'u' : { + val += "\\u" ; + for( unsigned i = 1; i <= 4; ++i ) { + c = str[offset+i]; + if( (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') ) + val += c; + else { + on_error(std::string("ERROR: String: Expected hex character in unicode escape, found '") + c + "'"); + return std::move( JSON::Make( JSON::Class::String ) ); + } + } + offset += 4; + } break; + default : val += '\\'; break; + } + } + else + val += c; + } + ++offset; + String = val; + return std::move( String ); +} + +static JSON parse_number( const string &str, size_t &offset, std::function on_error ) { + JSON Number; + string val, exp_str; + char c; + bool isDouble = false; + long exp = 0; + while( true ) { + c = str[offset++]; + if( (c == '-') || (c >= '0' && c <= '9') ) + val += c; + else if( c == '.' ) { + val += c; + isDouble = true; + } + else + break; + } + if( c == 'E' || c == 'e' ) { + c = str[ offset++ ]; + if( c == '-' ){ ++offset; exp_str += '-';} + while( true ) { + c = str[ offset++ ]; + if( c >= '0' && c <= '9' ) + exp_str += c; + else if( !isspace( c ) && c != ',' && c != ']' && c != '}' ) { + on_error(std::string("ERROR: Number: Expected a number for exponent, found '") + c + "'"); + return std::move( JSON::Make( JSON::Class::Null ) ); + } + else + break; + } + exp = std::stol( exp_str ); + } + else if( !isspace( c ) && c != ',' && c != ']' && c != '}' ) { + on_error(std::string("ERROR: Number: unexpected character '") + c + "'"); + return std::move( JSON::Make( JSON::Class::Null ) ); + } + --offset; + + if( isDouble ) + Number = std::stod( val ) * std::pow( 10, exp ); + else { + if( !exp_str.empty() ) + Number = std::stol( val ) * std::pow( 10, exp ); + else + Number = std::stol( val ); + } + return std::move( Number ); +} + +static JSON parse_bool( const string &str, size_t &offset, std::function on_error ) { + JSON Bool; + if( str.substr( offset, 4 ) == "true" ) + Bool = true; + else if( str.substr( offset, 5 ) == "false" ) + Bool = false; + else { + on_error(std::string("ERROR: Bool: Expected 'true' or 'false', found '") + str.substr( offset, 5 ) + "'"); + return std::move( JSON::Make( JSON::Class::Null ) ); + } + offset += (Bool.toBool() ? 4 : 5); + return std::move( Bool ); +} + +static JSON parse_null( const string &str, size_t &offset, std::function on_error ) { + JSON Null; + if( str.substr( offset, 4 ) != "null" ) { + on_error(std::string("ERROR: Null: Expected 'null', found '") + str.substr( offset, 4 ) + "'" ); + return std::move( JSON::Make( JSON::Class::Null ) ); + } + offset += 4; + return std::move( Null ); +} + +static JSON parse_next( const string &str, size_t &offset, std::function on_error ) { + char value; + consume_ws( str, offset ); + value = str[offset]; + switch( value ) { + case '[' : return std::move( parse_array( str, offset, on_error ) ); + case '{' : return std::move( parse_object( str, offset, on_error ) ); + case '\"': return std::move( parse_string( str, offset, on_error ) ); + case 't' : + case 'f' : return std::move( parse_bool( str, offset, on_error ) ); + case 'n' : return std::move( parse_null( str, offset, on_error ) ); + default : if( ( value <= '9' && value >= '0' ) || value == '-' ) + return std::move( parse_number( str, offset, on_error ) ); + } + on_error(std::string("ERROR: Parse: Unknown starting character '") + value + "'"); + return JSON(); +} + +JSON JSON::Load( const string &str, std::function on_error) { + size_t offset = 0; + return std::move( parse_next( str, offset, on_error ) ); +} + diff --git a/json.h b/json.h new file mode 100644 index 0000000..87f1b57 --- /dev/null +++ b/json.h @@ -0,0 +1,217 @@ +#ifndef JSON_H +#define JSON_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +std::string json_escape( const std::string &str ); + +class JSON +{ + union BackingData { + BackingData( double d ) : Float( d ){} + BackingData( long l ) : Int( l ){} + BackingData( bool b ) : Bool( b ){} + BackingData( std::string s ) : String( new std::string( s ) ){} + BackingData() : Int( 0 ){} + + std::deque *List; + std::map *Map; + std::string *String; + double Float; + long Int; + bool Bool; + } Internal; + +public: + enum class Class { + Null, + Object, + Array, + String, + Floating, + Integral, + Boolean + }; + + template + class JSONWrapper { + Container *object; + + public: + JSONWrapper( Container *val ) : object( val ) {} + JSONWrapper( std::nullptr_t ) : object( nullptr ) {} + + typename Container::iterator begin() { return object ? object->begin() : typename Container::iterator(); } + typename Container::iterator end() { return object ? object->end() : typename Container::iterator(); } + typename Container::const_iterator begin() const { return object ? object->begin() : typename Container::iterator(); } + typename Container::const_iterator end() const { return object ? object->end() : typename Container::iterator(); } + }; + + template + class JSONConstWrapper { + const Container *object; + + public: + JSONConstWrapper( const Container *val ) : object( val ) {} + JSONConstWrapper( std::nullptr_t ) : object( nullptr ) {} + + typename Container::const_iterator begin() const { return object ? object->begin() : typename Container::const_iterator(); } + typename Container::const_iterator end() const { return object ? object->end() : typename Container::const_iterator(); } + }; + + JSON(); + JSON( std::nullptr_t ) : Internal(), Type( Class::Null ){} + JSON( std::initializer_list list ); + JSON( JSON&& other ); + JSON( const JSON &other ); + + JSON& operator=( const JSON &other ); + JSON& operator=( JSON&& other ); + + // Template T constructors + template + JSON( T b, typename std::enable_if::value>::type* = 0 ); + + template + JSON( T i, typename std::enable_if::value && !std::is_same::value>::type* = 0 ); + + template + JSON( T f, typename std::enable_if::value>::type* = 0 ); + + template + JSON( T s, typename std::enable_if::value>::type* = 0 ); + + ~JSON() { + switch( Type ) { + case Class::Array: + delete Internal.List; + break; + case Class::Object: + delete Internal.Map; + break; + case Class::String: + delete Internal.String; + break; + default:; + } + } + + static JSON Make( Class type ); + static JSON Load( const std::string &, std::function ); + + // Appending things. + + template + void append( T arg ) { + SetType( Class::Array ); Internal.List->emplace_back( arg ); + } + + template + void append( T arg, U... args ) { + append( arg ); append( args... ); + } + + // Assignments (template T). + + template + typename std::enable_if::value, JSON&>::type operator=( T b ) { + SetType( Class::Boolean ); Internal.Bool = b; return *this; + } + + template + typename std::enable_if::value && !std::is_same::value, JSON&>::type operator=( T i ) { + SetType( Class::Integral ); Internal.Int = i; return *this; + } + + template + typename std::enable_if::value, JSON&>::type operator=( T f ) { + SetType( Class::Floating ); Internal.Float = f; return *this; + } + + template + typename std::enable_if::value, JSON&>::type operator=( T s ) { + SetType( Class::String ); *Internal.String = std::string( s ); return *this; + } + + + + // Indexing. + JSON& operator[]( const std::string &key ); + JSON& operator[]( unsigned index ); + + JSON &at( const std::string &key ); + const JSON &at( const std::string &key ) const; + JSON &at( unsigned index ); + const JSON &at( unsigned index ) const; + + int length() const; + int size() const; + + bool hasKey( const std::string &key ) const; + + Class JSONType() const; + + /// Functions for getting primitives from the JSON object. + bool IsNull() const; + + std::string toString() const; + std::string toString( bool &ok ) const; + + double toFloat() const; + double toFloat( bool &ok ) const; + + long toInt() const; + long toInt( bool &ok ) const; + + bool toBool() const; + bool toBool( bool &ok ) const; + + JSONWrapper> ObjectRange(); + JSONWrapper> ArrayRange(); + JSONConstWrapper> ObjectRange() const; + JSONConstWrapper> ArrayRange() const; + + std::string dump( int depth = 1, const std::string &tab = std::string(" ")) const; + + friend std::ostream& operator<<( std::ostream&, const JSON & ); + +private: + void SetType( Class type ); + +private: + /* beware: only call if YOU know that Internal is allocated. No checks performed here. + This function should be called in a constructed JSON just before you are going to + overwrite Internal... + */ + void ClearInternal(); + +private: + + Class Type = Class::Null; +}; + +JSON Array(); + +template +JSON Array( T... args ) { + JSON arr = JSON::Make( JSON::Class::Array ); + arr.append( args... ); + return std::move( arr ); +} + +JSON Object(); + +std::ostream& operator<<( std::ostream &os, const JSON &json ); + + +#endif // JSON_H diff --git a/lib/linux/x86_64/QtWebEngineProcess b/lib/linux/x86_64/QtWebEngineProcess new file mode 100644 index 0000000..82ec426 Binary files /dev/null and b/lib/linux/x86_64/QtWebEngineProcess differ diff --git a/lib/linux/x86_64/resources/icudtl.dat b/lib/linux/x86_64/resources/icudtl.dat new file mode 100644 index 0000000..7da895f Binary files /dev/null and b/lib/linux/x86_64/resources/icudtl.dat differ diff --git a/lib/linux/x86_64/resources/qtwebengine_devtools_resources.pak b/lib/linux/x86_64/resources/qtwebengine_devtools_resources.pak new file mode 100644 index 0000000..9d7c261 Binary files /dev/null and b/lib/linux/x86_64/resources/qtwebengine_devtools_resources.pak differ diff --git a/lib/linux/x86_64/resources/qtwebengine_resources.pak b/lib/linux/x86_64/resources/qtwebengine_resources.pak new file mode 100644 index 0000000..476b8f5 Binary files /dev/null and b/lib/linux/x86_64/resources/qtwebengine_resources.pak differ diff --git a/lib/linux/x86_64/resources/qtwebengine_resources_100p.pak b/lib/linux/x86_64/resources/qtwebengine_resources_100p.pak new file mode 100644 index 0000000..793dc75 Binary files /dev/null and b/lib/linux/x86_64/resources/qtwebengine_resources_100p.pak differ diff --git a/lib/linux/x86_64/resources/qtwebengine_resources_200p.pak b/lib/linux/x86_64/resources/qtwebengine_resources_200p.pak new file mode 100644 index 0000000..5a81e58 Binary files /dev/null and b/lib/linux/x86_64/resources/qtwebengine_resources_200p.pak differ diff --git a/lib/linux/x86_64/resources/v8_context_snapshot.bin b/lib/linux/x86_64/resources/v8_context_snapshot.bin new file mode 100644 index 0000000..f6b0d99 Binary files /dev/null and b/lib/linux/x86_64/resources/v8_context_snapshot.bin differ diff --git a/lib/linux/x86_64/rktwebview_prg b/lib/linux/x86_64/rktwebview_prg new file mode 100644 index 0000000..53c74f5 Binary files /dev/null and b/lib/linux/x86_64/rktwebview_prg differ diff --git a/lib/linux/x86_64/translations/catalogs.json b/lib/linux/x86_64/translations/catalogs.json new file mode 100644 index 0000000..368048f --- /dev/null +++ b/lib/linux/x86_64/translations/catalogs.json @@ -0,0 +1,48 @@ +[ + { + "name": "qtbase", + "repositories": ["qtbase", "qtactiveqt", "qtimageformats"] + }, + { + "name": "qtdeclarative", + "repositories": ["qtdeclarative"] + }, + { + "name": "qtmultimedia", + "repositories": ["qtmultimedia"] + }, + { + "name": "qtconnectivity", + "repositories": ["qtconnectivity"] + }, + { + "name": "qtlocation", + "repositories": ["qtlocation"] + }, + { + "name": "qtwebsockets", + "repositories": ["qtwebsockets"] + }, + { + "name": "qtserialport", + "repositories": ["qtserialport"] + }, + { + "name": "qtwebengine", + "repositories": ["qtwebengine"] + }, + { + "name": "designer", + "modules": ["Designer"] + }, + { + "name": "linguist" + }, + { + "name": "assistant" + }, + { + "name": "qt_help", + "modules": ["Help"] + } +] diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/am.pak b/lib/linux/x86_64/translations/qtwebengine_locales/am.pak new file mode 100644 index 0000000..19e01b8 Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/am.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/ar.pak b/lib/linux/x86_64/translations/qtwebengine_locales/ar.pak new file mode 100644 index 0000000..9f394d8 Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/ar.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/bg.pak b/lib/linux/x86_64/translations/qtwebengine_locales/bg.pak new file mode 100644 index 0000000..6dbc59b Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/bg.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/bn.pak b/lib/linux/x86_64/translations/qtwebengine_locales/bn.pak new file mode 100644 index 0000000..4bfd2bc Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/bn.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/ca.pak b/lib/linux/x86_64/translations/qtwebengine_locales/ca.pak new file mode 100644 index 0000000..90190c6 Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/ca.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/cs.pak b/lib/linux/x86_64/translations/qtwebengine_locales/cs.pak new file mode 100644 index 0000000..309e4a6 Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/cs.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/da.pak b/lib/linux/x86_64/translations/qtwebengine_locales/da.pak new file mode 100644 index 0000000..aa46c17 Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/da.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/de.pak b/lib/linux/x86_64/translations/qtwebengine_locales/de.pak new file mode 100644 index 0000000..731b54c Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/de.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/el.pak b/lib/linux/x86_64/translations/qtwebengine_locales/el.pak new file mode 100644 index 0000000..e6a13aa Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/el.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/en-GB.pak b/lib/linux/x86_64/translations/qtwebengine_locales/en-GB.pak new file mode 100644 index 0000000..623fed1 Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/en-GB.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/en-US.pak b/lib/linux/x86_64/translations/qtwebengine_locales/en-US.pak new file mode 100644 index 0000000..5286198 Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/en-US.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/es-419.pak b/lib/linux/x86_64/translations/qtwebengine_locales/es-419.pak new file mode 100644 index 0000000..6ade8af Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/es-419.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/es.pak b/lib/linux/x86_64/translations/qtwebengine_locales/es.pak new file mode 100644 index 0000000..ac36996 Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/es.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/et.pak b/lib/linux/x86_64/translations/qtwebengine_locales/et.pak new file mode 100644 index 0000000..5ea8273 Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/et.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/fa.pak b/lib/linux/x86_64/translations/qtwebengine_locales/fa.pak new file mode 100644 index 0000000..db746a0 Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/fa.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/fi.pak b/lib/linux/x86_64/translations/qtwebengine_locales/fi.pak new file mode 100644 index 0000000..73bf37b Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/fi.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/fil.pak b/lib/linux/x86_64/translations/qtwebengine_locales/fil.pak new file mode 100644 index 0000000..3ec2209 Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/fil.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/fr.pak b/lib/linux/x86_64/translations/qtwebengine_locales/fr.pak new file mode 100644 index 0000000..679fd99 Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/fr.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/gu.pak b/lib/linux/x86_64/translations/qtwebengine_locales/gu.pak new file mode 100644 index 0000000..564f08c Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/gu.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/he.pak b/lib/linux/x86_64/translations/qtwebengine_locales/he.pak new file mode 100644 index 0000000..7f46ecb Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/he.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/hi.pak b/lib/linux/x86_64/translations/qtwebengine_locales/hi.pak new file mode 100644 index 0000000..bb90d4d Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/hi.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/hr.pak b/lib/linux/x86_64/translations/qtwebengine_locales/hr.pak new file mode 100644 index 0000000..8504b61 Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/hr.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/hu.pak b/lib/linux/x86_64/translations/qtwebengine_locales/hu.pak new file mode 100644 index 0000000..40249bf Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/hu.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/id.pak b/lib/linux/x86_64/translations/qtwebengine_locales/id.pak new file mode 100644 index 0000000..625e2ef Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/id.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/it.pak b/lib/linux/x86_64/translations/qtwebengine_locales/it.pak new file mode 100644 index 0000000..4c99e7a Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/it.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/ja.pak b/lib/linux/x86_64/translations/qtwebengine_locales/ja.pak new file mode 100644 index 0000000..68004ec Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/ja.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/kn.pak b/lib/linux/x86_64/translations/qtwebengine_locales/kn.pak new file mode 100644 index 0000000..70ad523 Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/kn.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/ko.pak b/lib/linux/x86_64/translations/qtwebengine_locales/ko.pak new file mode 100644 index 0000000..c6c0729 Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/ko.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/lt.pak b/lib/linux/x86_64/translations/qtwebengine_locales/lt.pak new file mode 100644 index 0000000..6ed9f37 Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/lt.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/lv.pak b/lib/linux/x86_64/translations/qtwebengine_locales/lv.pak new file mode 100644 index 0000000..8e96ee9 Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/lv.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/ml.pak b/lib/linux/x86_64/translations/qtwebengine_locales/ml.pak new file mode 100644 index 0000000..22f30b4 Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/ml.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/mr.pak b/lib/linux/x86_64/translations/qtwebengine_locales/mr.pak new file mode 100644 index 0000000..58f56ac Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/mr.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/ms.pak b/lib/linux/x86_64/translations/qtwebengine_locales/ms.pak new file mode 100644 index 0000000..57356ce Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/ms.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/nb.pak b/lib/linux/x86_64/translations/qtwebengine_locales/nb.pak new file mode 100644 index 0000000..d0d22de Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/nb.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/nl.pak b/lib/linux/x86_64/translations/qtwebengine_locales/nl.pak new file mode 100644 index 0000000..5fff5d7 Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/nl.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/pl.pak b/lib/linux/x86_64/translations/qtwebengine_locales/pl.pak new file mode 100644 index 0000000..66042f6 Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/pl.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/pt-BR.pak b/lib/linux/x86_64/translations/qtwebengine_locales/pt-BR.pak new file mode 100644 index 0000000..4246eba Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/pt-BR.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/pt-PT.pak b/lib/linux/x86_64/translations/qtwebengine_locales/pt-PT.pak new file mode 100644 index 0000000..d7d28db Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/pt-PT.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/ro.pak b/lib/linux/x86_64/translations/qtwebengine_locales/ro.pak new file mode 100644 index 0000000..1a9963c Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/ro.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/ru.pak b/lib/linux/x86_64/translations/qtwebengine_locales/ru.pak new file mode 100644 index 0000000..83d92f4 Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/ru.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/sk.pak b/lib/linux/x86_64/translations/qtwebengine_locales/sk.pak new file mode 100644 index 0000000..d8bd569 Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/sk.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/sl.pak b/lib/linux/x86_64/translations/qtwebengine_locales/sl.pak new file mode 100644 index 0000000..a22f47c Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/sl.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/sr.pak b/lib/linux/x86_64/translations/qtwebengine_locales/sr.pak new file mode 100644 index 0000000..4e01fc5 Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/sr.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/sv.pak b/lib/linux/x86_64/translations/qtwebengine_locales/sv.pak new file mode 100644 index 0000000..d868332 Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/sv.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/sw.pak b/lib/linux/x86_64/translations/qtwebengine_locales/sw.pak new file mode 100644 index 0000000..06f6b92 Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/sw.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/ta.pak b/lib/linux/x86_64/translations/qtwebengine_locales/ta.pak new file mode 100644 index 0000000..9e6abc2 Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/ta.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/te.pak b/lib/linux/x86_64/translations/qtwebengine_locales/te.pak new file mode 100644 index 0000000..4c25bb0 Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/te.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/th.pak b/lib/linux/x86_64/translations/qtwebengine_locales/th.pak new file mode 100644 index 0000000..affacf6 Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/th.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/tr.pak b/lib/linux/x86_64/translations/qtwebengine_locales/tr.pak new file mode 100644 index 0000000..e0776c0 Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/tr.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/uk.pak b/lib/linux/x86_64/translations/qtwebengine_locales/uk.pak new file mode 100644 index 0000000..e41fb96 Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/uk.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/vi.pak b/lib/linux/x86_64/translations/qtwebengine_locales/vi.pak new file mode 100644 index 0000000..49c7aa4 Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/vi.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/zh-CN.pak b/lib/linux/x86_64/translations/qtwebengine_locales/zh-CN.pak new file mode 100644 index 0000000..173c458 Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/zh-CN.pak differ diff --git a/lib/linux/x86_64/translations/qtwebengine_locales/zh-TW.pak b/lib/linux/x86_64/translations/qtwebengine_locales/zh-TW.pak new file mode 100644 index 0000000..43b87cf Binary files /dev/null and b/lib/linux/x86_64/translations/qtwebengine_locales/zh-TW.pak differ diff --git a/lib/windows/x86_64/resources/icudtl.dat b/lib/windows/x86_64/resources/icudtl.dat new file mode 100644 index 0000000..7da895f Binary files /dev/null and b/lib/windows/x86_64/resources/icudtl.dat differ diff --git a/lib/windows/x86_64/resources/qtwebengine_devtools_resources.pak b/lib/windows/x86_64/resources/qtwebengine_devtools_resources.pak new file mode 100644 index 0000000..7808256 Binary files /dev/null and b/lib/windows/x86_64/resources/qtwebengine_devtools_resources.pak differ diff --git a/lib/windows/x86_64/resources/qtwebengine_resources.pak b/lib/windows/x86_64/resources/qtwebengine_resources.pak new file mode 100644 index 0000000..20da265 Binary files /dev/null and b/lib/windows/x86_64/resources/qtwebengine_resources.pak differ diff --git a/lib/windows/x86_64/resources/qtwebengine_resources_100p.pak b/lib/windows/x86_64/resources/qtwebengine_resources_100p.pak new file mode 100644 index 0000000..6613c77 Binary files /dev/null and b/lib/windows/x86_64/resources/qtwebengine_resources_100p.pak differ diff --git a/lib/windows/x86_64/resources/qtwebengine_resources_200p.pak b/lib/windows/x86_64/resources/qtwebengine_resources_200p.pak new file mode 100644 index 0000000..d50f7a3 Binary files /dev/null and b/lib/windows/x86_64/resources/qtwebengine_resources_200p.pak differ diff --git a/lib/windows/x86_64/resources/v8_context_snapshot.bin b/lib/windows/x86_64/resources/v8_context_snapshot.bin new file mode 100644 index 0000000..f262a09 Binary files /dev/null and b/lib/windows/x86_64/resources/v8_context_snapshot.bin differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/am.pak b/lib/windows/x86_64/translations/qtwebengine_locales/am.pak new file mode 100644 index 0000000..ca33fbc Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/am.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/ar.pak b/lib/windows/x86_64/translations/qtwebengine_locales/ar.pak new file mode 100644 index 0000000..71f3233 Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/ar.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/bg.pak b/lib/windows/x86_64/translations/qtwebengine_locales/bg.pak new file mode 100644 index 0000000..9d45a4c Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/bg.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/bn.pak b/lib/windows/x86_64/translations/qtwebengine_locales/bn.pak new file mode 100644 index 0000000..b735290 Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/bn.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/ca.pak b/lib/windows/x86_64/translations/qtwebengine_locales/ca.pak new file mode 100644 index 0000000..43f83c3 Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/ca.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/cs.pak b/lib/windows/x86_64/translations/qtwebengine_locales/cs.pak new file mode 100644 index 0000000..cf0fa40 Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/cs.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/da.pak b/lib/windows/x86_64/translations/qtwebengine_locales/da.pak new file mode 100644 index 0000000..f320504 Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/da.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/de.pak b/lib/windows/x86_64/translations/qtwebengine_locales/de.pak new file mode 100644 index 0000000..22e8cbc Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/de.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/el.pak b/lib/windows/x86_64/translations/qtwebengine_locales/el.pak new file mode 100644 index 0000000..7b9a672 Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/el.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/en-GB.pak b/lib/windows/x86_64/translations/qtwebengine_locales/en-GB.pak new file mode 100644 index 0000000..ded4437 Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/en-GB.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/en-US.pak b/lib/windows/x86_64/translations/qtwebengine_locales/en-US.pak new file mode 100644 index 0000000..49868d1 Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/en-US.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/es-419.pak b/lib/windows/x86_64/translations/qtwebengine_locales/es-419.pak new file mode 100644 index 0000000..4608277 Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/es-419.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/es.pak b/lib/windows/x86_64/translations/qtwebengine_locales/es.pak new file mode 100644 index 0000000..564ef3c Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/es.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/et.pak b/lib/windows/x86_64/translations/qtwebengine_locales/et.pak new file mode 100644 index 0000000..6725c88 Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/et.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/fa.pak b/lib/windows/x86_64/translations/qtwebengine_locales/fa.pak new file mode 100644 index 0000000..8d3c3fd Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/fa.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/fi.pak b/lib/windows/x86_64/translations/qtwebengine_locales/fi.pak new file mode 100644 index 0000000..b752d9d Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/fi.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/fil.pak b/lib/windows/x86_64/translations/qtwebengine_locales/fil.pak new file mode 100644 index 0000000..c730f9b Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/fil.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/fr.pak b/lib/windows/x86_64/translations/qtwebengine_locales/fr.pak new file mode 100644 index 0000000..9e0881d Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/fr.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/gu.pak b/lib/windows/x86_64/translations/qtwebengine_locales/gu.pak new file mode 100644 index 0000000..2691c2d Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/gu.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/he.pak b/lib/windows/x86_64/translations/qtwebengine_locales/he.pak new file mode 100644 index 0000000..3bc7761 Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/he.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/hi.pak b/lib/windows/x86_64/translations/qtwebengine_locales/hi.pak new file mode 100644 index 0000000..f93f38e Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/hi.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/hr.pak b/lib/windows/x86_64/translations/qtwebengine_locales/hr.pak new file mode 100644 index 0000000..a172195 Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/hr.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/hu.pak b/lib/windows/x86_64/translations/qtwebengine_locales/hu.pak new file mode 100644 index 0000000..3bcb49d Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/hu.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/id.pak b/lib/windows/x86_64/translations/qtwebengine_locales/id.pak new file mode 100644 index 0000000..418ae22 Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/id.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/it.pak b/lib/windows/x86_64/translations/qtwebengine_locales/it.pak new file mode 100644 index 0000000..a266dab Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/it.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/ja.pak b/lib/windows/x86_64/translations/qtwebengine_locales/ja.pak new file mode 100644 index 0000000..c40fbd3 Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/ja.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/kn.pak b/lib/windows/x86_64/translations/qtwebengine_locales/kn.pak new file mode 100644 index 0000000..6d2b1e5 Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/kn.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/ko.pak b/lib/windows/x86_64/translations/qtwebengine_locales/ko.pak new file mode 100644 index 0000000..be098b5 Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/ko.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/lt.pak b/lib/windows/x86_64/translations/qtwebengine_locales/lt.pak new file mode 100644 index 0000000..75a3730 Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/lt.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/lv.pak b/lib/windows/x86_64/translations/qtwebengine_locales/lv.pak new file mode 100644 index 0000000..d6c616e Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/lv.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/ml.pak b/lib/windows/x86_64/translations/qtwebengine_locales/ml.pak new file mode 100644 index 0000000..86170c5 Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/ml.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/mr.pak b/lib/windows/x86_64/translations/qtwebengine_locales/mr.pak new file mode 100644 index 0000000..eb598a6 Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/mr.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/ms.pak b/lib/windows/x86_64/translations/qtwebengine_locales/ms.pak new file mode 100644 index 0000000..d3f24fe Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/ms.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/nb.pak b/lib/windows/x86_64/translations/qtwebengine_locales/nb.pak new file mode 100644 index 0000000..9a897da Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/nb.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/nl.pak b/lib/windows/x86_64/translations/qtwebengine_locales/nl.pak new file mode 100644 index 0000000..3620cb5 Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/nl.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/pl.pak b/lib/windows/x86_64/translations/qtwebengine_locales/pl.pak new file mode 100644 index 0000000..c311d8f Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/pl.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/pt-BR.pak b/lib/windows/x86_64/translations/qtwebengine_locales/pt-BR.pak new file mode 100644 index 0000000..31fbeed Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/pt-BR.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/pt-PT.pak b/lib/windows/x86_64/translations/qtwebengine_locales/pt-PT.pak new file mode 100644 index 0000000..da23775 Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/pt-PT.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/ro.pak b/lib/windows/x86_64/translations/qtwebengine_locales/ro.pak new file mode 100644 index 0000000..fae835a Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/ro.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/ru.pak b/lib/windows/x86_64/translations/qtwebengine_locales/ru.pak new file mode 100644 index 0000000..7816480 Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/ru.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/sk.pak b/lib/windows/x86_64/translations/qtwebengine_locales/sk.pak new file mode 100644 index 0000000..c45dca2 Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/sk.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/sl.pak b/lib/windows/x86_64/translations/qtwebengine_locales/sl.pak new file mode 100644 index 0000000..0b92e61 Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/sl.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/sr.pak b/lib/windows/x86_64/translations/qtwebengine_locales/sr.pak new file mode 100644 index 0000000..6ee3ff6 Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/sr.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/sv.pak b/lib/windows/x86_64/translations/qtwebengine_locales/sv.pak new file mode 100644 index 0000000..f1a1bb3 Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/sv.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/sw.pak b/lib/windows/x86_64/translations/qtwebengine_locales/sw.pak new file mode 100644 index 0000000..39f5317 Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/sw.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/ta.pak b/lib/windows/x86_64/translations/qtwebengine_locales/ta.pak new file mode 100644 index 0000000..bdc3491 Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/ta.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/te.pak b/lib/windows/x86_64/translations/qtwebengine_locales/te.pak new file mode 100644 index 0000000..58cbf0f Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/te.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/th.pak b/lib/windows/x86_64/translations/qtwebengine_locales/th.pak new file mode 100644 index 0000000..fdeb130 Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/th.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/tr.pak b/lib/windows/x86_64/translations/qtwebengine_locales/tr.pak new file mode 100644 index 0000000..5e42c75 Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/tr.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/uk.pak b/lib/windows/x86_64/translations/qtwebengine_locales/uk.pak new file mode 100644 index 0000000..372a742 Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/uk.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/vi.pak b/lib/windows/x86_64/translations/qtwebengine_locales/vi.pak new file mode 100644 index 0000000..fefc1f5 Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/vi.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/zh-CN.pak b/lib/windows/x86_64/translations/qtwebengine_locales/zh-CN.pak new file mode 100644 index 0000000..b45f7fd Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/zh-CN.pak differ diff --git a/lib/windows/x86_64/translations/qtwebengine_locales/zh-TW.pak b/lib/windows/x86_64/translations/qtwebengine_locales/zh-TW.pak new file mode 100644 index 0000000..8fd6778 Binary files /dev/null and b/lib/windows/x86_64/translations/qtwebengine_locales/zh-TW.pak differ diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..fa81e6a --- /dev/null +++ b/main.cpp @@ -0,0 +1,337 @@ +#include +#include +#include + +#include "rkt_protocol.h" +#include "shm.h" +#include "shmqueue.h" +#include "command.h" +#include "utils.h" + +#include +#include + +#include "rktwebview_qt.h" + +static void free_data(rkt_data_t *d) +{ + do_free_data(d); +} + +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[]) +{ + const char *me = argv[0]; + + { + time_t my_time = time(NULL); + INFO1("Starting at %s", ctime(&my_time)); + } + + if (argc < 6) { + ERROR1("%s: wrong number of arguments\n", me); + exit(1); + } + + const char *shm_name = argv[1]; + const char *shm_size_str = argv[2]; + const char *cmd_slot_str = argv[3]; + const char *res_slot_str = argv[4]; + const char *evt_slot_str = argv[5]; + + size_t shm_size = atoi(shm_size_str); + int cmd_slot = atoi(cmd_slot_str); + int res_slot = atoi(res_slot_str); + int evt_slot = atoi(evt_slot_str); + + MKLOGSTMT(LOG_INFO, fprintf(stderr, "%s %s %s %s %s %s\n", me, shm_name, shm_size_str, cmd_slot_str, res_slot_str, evt_slot_str)); + MKLOGSTMT(LOG_INFO, 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)) { + ERROR1("%s: Invalid shm size or slots\n", me); + exit(2); + } + + Handler *handler = new Handler(); + _handler = handler; + handler->shm = new Shm(shm_name, shm_size, false); + handler->command_queue = new ShmQueue(handler->shm, cmd_slot, false); + handler->result_queue = new ShmQueue(handler->shm, res_slot, false); + handler->event_queue = new ShmQueue(handler->shm, evt_slot, false); + handler->webview_handler = new Rktwebview_qt(argc, argv); + handler->start(); + + handler->webview_handler->initApp(); + handler->webview_handler->execApp(); + + INFO0("waiting for thread to end\n"); + handler->wait(); + + INFO0("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; + + { + time_t my_time = time(NULL); + INFO1("Exiting at %s", ctime(&my_time)); + } + + return 0; +} + +void Handler::run() +{ + bool quit = false; + while (!quit) { + int cmd; + std::string data; + command_queue->dequeue(cmd, data, true); + QJsonObject data_obj = QJsonDocument::fromJson(data.c_str()).object(); + + switch(cmd) { + case CMD_QUIT: { + INFO0("Got quit message\n"); + webview_handler->rktQuit(); + INFO0("Enqueing RESULT_QUIT to result queue\n"); + result_queue->enqueue(RESULT_QUIT); + quit = true; + } + break; + case CMD_INFO: { + int open_windows = webview_handler->openWindows(); + result_queue->enqueue(open_windows); + } + 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_SET_LOGLEVEL: { + int ll = data_obj["wv"].toInt(); + setLogLevel(ll); + bool oke = true; + result_queue->enqueue(oke); + } + break; + case CMD_CONTEXT_NEW: { + QString boilerplate_js = data_obj["boilerplate_js"].toString(); + bool has_pem_cert = data_obj["has_pem_cert"].toBool(); + QString pem_cert = data_obj["pem_cert"].toString(); + int context = webview_handler->newContext(boilerplate_js, has_pem_cert, pem_cert); + result_queue->enqueue(context); + } + break; + case CMD_CREATE_WV: { + int context = data_obj["context"].toInt(); + int parent = data_obj["parent"].toInt(); + + int wv = webview_handler->rktWebViewCreate(context, parent, event_cb); + result_queue->enqueue(wv); + } + break; + case CMD_CLOSE_WV: { + int wv = data_obj["wv"].toInt(); + webview_handler->rktWebViewClose(wv); + } + break; + case CMD_SET_URL: { + int wv = data_obj["wv"].toInt(); + QString url = data_obj["url"].toString(); + result_t r = webview_handler->rktSetUrl(wv, url.toUtf8().constData()); + result_queue->enqueue(r); + } + break; + case CMD_SET_HTML: { + int wv = data_obj["wv"].toInt(); + QString html = data_obj["html"].toString(); + result_t r = webview_handler->rktSetHtml(wv, html.toUtf8().constData()); + result_queue->enqueue(r); + } + break; + case CMD_RUN_JS: { + int wv = data_obj["wv"].toInt(); + QString js = data_obj["js"].toString(); + result_t r = webview_handler->rktRunJs(wv, js); + result_queue->enqueue(r); + } + break; + case CMD_CALL_JS: { + int wv = data_obj["wv"].toInt(); + QString js = data_obj["js"].toString(); + rkt_data_t *res = webview_handler->rktCallJs(wv, js.toUtf8().constData()); + result_queue->enqueue(res->data.js_result.result, res->data.js_result.value); + free_data(res); + } + break; + case CMD_OPEN_DEVTOOLS: { + int wv = data_obj["wv"].toInt(); + result_t r = webview_handler->rktOpenDevtools(wv); + result_queue->enqueue(r); + } + break; + case CMD_MOVE: { + int wv = data_obj["wv"].toInt(); + int x = data_obj["x"].toInt(); + int y = data_obj["y"].toInt(); + result_t r = webview_handler->rktMove(wv, x, y); + result_queue->enqueue(r); + } + break; + case CMD_RESIZE: { + int wv = data_obj["wv"].toInt(); + int w = data_obj["w"].toInt(); + int h = data_obj["h"].toInt(); + result_t r = webview_handler->rktResize(wv, w, h); + result_queue->enqueue(r); + } + break; + case CMD_HIDE: { + int wv = data_obj["wv"].toInt(); + result_t r = webview_handler->rktHideWindow(wv); + result_queue->enqueue(r); + } + break; + case CMD_SHOW: { + int wv = data_obj["wv"].toInt(); + result_t r = webview_handler->rktShowWindow(wv); + result_queue->enqueue(r); + } + break; + case CMD_PRESENT: { + int wv = data_obj["wv"].toInt(); + result_t r = webview_handler->rktPresentWindow(wv); + result_queue->enqueue(r); + } + break; + case CMD_MAXIMIZE: { + int wv = data_obj["wv"].toInt(); + result_t r = webview_handler->rktMaximizeWindow(wv); + result_queue->enqueue(r); + } + break; + case CMD_MINIMIZE: { + int wv = data_obj["wv"].toInt(); + result_t r = webview_handler->rktMinimizeWindow(wv); + result_queue->enqueue(r); + } + break; + case CMD_SHOW_NORMAL: { + int wv = data_obj["wv"].toInt(); + result_t r = webview_handler->rktShowNormalWindow(wv); + result_queue->enqueue(r); + } + break; + case CMD_WINDOW_STATE: { + int wv = data_obj["wv"].toInt(); + window_state_t r = webview_handler->rktWindowState(wv); + result_queue->enqueue(r); + } + break; + case CMD_SET_TITLE: { + int wv = data_obj["wv"].toInt(); + QString title = data_obj["title"].toString(); + result_t r = webview_handler->rktWindowSetTitle(wv, title.toUtf8().constData()); + result_queue->enqueue(r); + } + break; + case CMD_CHOOSE_DIR: { + int wv = data_obj["wv"].toInt(); + QString title = data_obj["title"].toString(); + QString base_dir = data_obj["base_dir"].toString(); + result_t r = webview_handler->rktChooseDir(wv, + title.toUtf8().constData(), + base_dir.toUtf8().constData() + ); + result_queue->enqueue(r); + } + break; + case CMD_FILE_OPEN: { + int wv = data_obj["wv"].toInt(); + QString title = data_obj["title"].toString(); + QString base_dir = data_obj["base_dir"].toString(); + QString permitted_exts = data_obj["permitted_exts"].toString(); + result_t r = webview_handler->rktFileOpen(wv, + title.toUtf8().constData(), + base_dir.toUtf8().constData(), + permitted_exts.toUtf8().constData() + ); + result_queue->enqueue(r); + } + break; + case CMD_FILE_SAVE: { + int wv = data_obj["wv"].toInt(); + QString title = data_obj["title"].toString(); + QString base_dir = data_obj["base_dir"].toString(); + QString permitted_exts = data_obj["permitted_exts"].toString(); + result_t r = webview_handler->rktFileSave(wv, + title.toUtf8().constData(), + base_dir.toUtf8().constData(), + permitted_exts.toUtf8().constData() + ); + result_queue->enqueue(r); + } + break; + case CMD_SET_OU_TOKEN: { + int wv = data_obj["wv"].toInt(); + QString token = data_obj["token"].toString(); + webview_handler->rktSetOUToken(wv, token.toUtf8().constData()); + } + break; + case CMD_MSG_BOX: { + int wv = data_obj["wv"].toInt(); + QString title = data_obj["title"].toString(); + QString message = data_obj["message"].toString(); + QString submsg = data_obj["submessage"].toString(); + int type = data_obj["type"].toInt(); + result_t r = webview_handler->rktMessageBox(wv, + title.toUtf8().constData(), + message.toUtf8().constData(), + submsg.toUtf8().constData(), + static_cast(type) + ); + result_queue->enqueue(r); + } + break; + default: { + ERROR1("Unknown command: %d\n", cmd); + } + } + } +} diff --git a/rkt_protocol.h b/rkt_protocol.h new file mode 100644 index 0000000..b1c3082 --- /dev/null +++ b/rkt_protocol.h @@ -0,0 +1,35 @@ +#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 CMD_SET_LOGLEVEL 26 // arguments: ll: int +#define CMD_INFO 27 // arguments: none + +#define RESULT_QUIT 36379 + + +#endif // RKT_PROTOCOL_H diff --git a/rktutils.cpp b/rktutils.cpp new file mode 100644 index 0000000..7bf54e1 --- /dev/null +++ b/rktutils.cpp @@ -0,0 +1,30 @@ +#include "rktutils.h" + +#include +#include +#include + +QString mkEventJson(const EventContainer &kv) +{ + QJsonObject obj; + + QList keys = kv.keys(); + int i, N; + for(i = 0, N = keys.length(); i < N; i++) { + const QString &key = keys[i]; + const QVariant &v = kv[key]; + obj[key] = v.toJsonValue(); + } + + QJsonDocument doc(obj); + return QString::fromUtf8(doc.toJson(QJsonDocument::JsonFormat::Compact)); +} + +QHash mkEvent() +{ + QHash h; + return h; +} + +int EventContainer::evt_count = 0; +qint64 EventContainer::ms_start = -1; diff --git a/rktutils.h b/rktutils.h new file mode 100644 index 0000000..b1d06fb --- /dev/null +++ b/rktutils.h @@ -0,0 +1,33 @@ +#ifndef __RKT_UTILS_H__ +#define __RKT_UTILS_H__ + +#include +#include +#include +#include + +class EventContainer : public QHash +{ +private: + static int evt_count; + static qint64 ms_start; +public: + EventContainer(const QString &evt) { + if (ms_start < 0) { + ms_start = QDateTime::currentMSecsSinceEpoch(); + } + this->insert("event", evt); + this->insert("evt-id", ++evt_count); + + qint64 ms = QDateTime::currentMSecsSinceEpoch(); + this->insert("timestamp", ms); + this->insert("elaped", static_cast(ms - ms_start) / 1000.0); + } +}; + +QString mkEventJson(const EventContainer &kv); + + + + +#endif diff --git a/rktwebview.cpp b/rktwebview.cpp new file mode 100644 index 0000000..983593e --- /dev/null +++ b/rktwebview.cpp @@ -0,0 +1,593 @@ +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#else +#include +#include +#include +#include +#include +#include +#endif + +#include +#include "rktwebview.h" +#include "rkt_protocol.h" +#include "shmqueue.h" +#include "json.h" +#include "utils.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; +#ifdef _WIN32 + HANDLE rkt_webview_prg_pid; +#else + pid_t rkt_webview_prg_pid; +#endif + + bool rkt_webview_prg_started; + bool valid; + int function_calls; + int events; + std::string log_file; +} Handle_t; + +Handle_t *handler = nullptr; + +static bool fileExists(const char *filename) { +#ifdef _WIN32 + DWORD dwAttrib = GetFileAttributes(filename); + return (dwAttrib != INVALID_FILE_ATTRIBUTES && + !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); +#else + return access(filename, X_OK) != -1; +#endif +} + +uint64_t current_ms() { + using namespace std::chrono; + return duration_cast(system_clock::now().time_since_epoch()).count(); +} + +bool runRktWebview(Handle_t *handler) +{ + char *rkt_webview_prg_path = getenv("RKT_WEBVIEW_PRG"); + if (rkt_webview_prg_path == nullptr) { + ERROR0("RKT_WEBVIEW_PRG environment variable not set, cannot start webview program\n"); + return false; + } + if (!fileExists(rkt_webview_prg_path)) { + ERROR1("%s does not exist or is not executable\n", rkt_webview_prg_path); + return false; + } + + std::string path = basedir(rkt_webview_prg_path); + + char shm_size_str[30]; + char command_slot[10]; + char command_result_slot[10]; + char event_slot[10]; + + sprintf(shm_size_str, "%d", static_cast(handler->shm_size)); + sprintf(command_slot, "%d", COMMAND_SLOT); + sprintf(command_result_slot, "%d", COMMAND_RESULT_SLOT); + sprintf(event_slot, "%d", EVENT_SLOT); + + // run rktwebview_prg using the environment variable RKT_WEBVIEW_PRG +#ifdef _WIN32 + + const char *td = getenv("TEMP"); + if (td == nullptr) { td = "C:\\"; } + std::string tmpdir = td; + std::string logfile = tmpdir + "\\" + "rktwebview.log"; //handler->name + ".log"; + handler->log_file = logfile; + + SECURITY_ATTRIBUTES sa; + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = TRUE; + + HANDLE h = CreateFile(logfile.c_str(), + FILE_WRITE_DATA, //FILE_APPEND_DATA, + FILE_SHARE_WRITE | FILE_SHARE_READ, + &sa, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + + STARTUPINFO si; + PROCESS_INFORMATION pi; + ZeroMemory( &si, sizeof(si) ); + ZeroMemory( &pi, sizeof(pi) ); + + si.cb = sizeof(si); + si.dwFlags |= STARTF_USESTDHANDLES; + si.hStdInput= NULL; + si.hStdOutput = h; + si.hStdError = h; + + DWORD flags = CREATE_NO_WINDOW | NORMAL_PRIORITY_CLASS; + + std::string cmdargs = std::string("") + handler->name + " " + shm_size_str + " " + command_slot + " " + command_result_slot + " " + event_slot; + std::string exe = std::string(rkt_webview_prg_path); + std::string dir = basedir(exe); + + std::string cmdline = exe + " " + cmdargs; + + char *cmdline_str = const_cast(cmdline.c_str()); + + bool r = CreateProcessA(NULL, cmdline_str, NULL, NULL, TRUE, flags, NULL, path.c_str(), &si, &pi); + if (!r) { + ERROR2("Cannot create process '%s' (error = %ld)\n", cmdline_str, GetLastError()); + } + return r; +#else + char *argv[] = { rkt_webview_prg_path, const_cast(handler->name.c_str()), shm_size_str, command_slot, command_result_slot, event_slot, nullptr }; + + struct passwd *pw = getpwuid(getuid()); + const char *homedir = pw->pw_dir; + + std::string log_file = std::string(homedir) + "/.racket-webview.log"; + handler->log_file = log_file; + + posix_spawn_file_actions_t action; + posix_spawn_file_actions_init(&action); + unlink(log_file.c_str()); + posix_spawn_file_actions_addopen(&action, STDOUT_FILENO, log_file.c_str(), O_CREAT|O_WRONLY, 0600); + posix_spawn_file_actions_addopen(&action, STDERR_FILENO, log_file.c_str(), O_WRONLY, 0600); + + int r = posix_spawn(&handler->rkt_webview_prg_pid, rkt_webview_prg_path, &action, nullptr, argv, environ); + + posix_spawn_file_actions_destroy(&action); + + return (r == 0); +#endif +} + +///////////////////////////////////////////////////////////////////// +// Main C Interface +///////////////////////////////////////////////////////////////////// + + +void rkt_webview_cleanup() +{ + if (handler != nullptr) { + // Does nothing. + // See QTBUG-145033 + // QtWebEngine cannot be started as part of QApplication more than once in an application run. + // So we would need to cleanup at exit of racket/drracket. + // Cleaning up when exiting the current custodian is not enough. + + if (handler->valid) { + INFO0("Sending quit message\n"); + handler->command_queue->enqueue(CMD_QUIT); + INFO0("Message sent\n"); + bool stopped = false; + while(!stopped) { + int cmd; + std::string s; + INFO0("Getting result of quit message\n"); + handler->command_result_queue->dequeue(cmd, s, true); + INFO1("got %d\n", cmd); + if (cmd == RESULT_QUIT) { + stopped = true; + } + } + } + + delete handler->event_queue; + delete handler->command_result_queue; + delete handler->command_queue; + delete handler->shm; + delete handler; + handler = nullptr; + } +} + +void rkt_webview_init() +{ + if (handler == nullptr) { + // Create shared memory and communication queues + + char buf[1024]; +#ifdef DEBUG + sprintf(buf, "rktwebview-dbg"); +#else +#ifdef _WIN32 + DWORD p = GetCurrentProcessId(); + sprintf(buf, "rktwebview-%ld", p); +#else + pid_t p = getpid(); + sprintf(buf, "rktwebview-%x", p); +#endif +#endif + + handler = new Handle_t; + handler->valid = true; + handler->name = buf; + handler->function_calls = 0; + handler->events = 0; + + handler->shm_size = SHM_SIZE; + handler->shm = new Shm(handler->name.data(), handler->shm_size, true); + if (!handler->shm->isValid()) { handler->valid = false; } + 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); + if (!handler->rkt_webview_prg_started) { handler->valid = false; } +#endif + } else { + handler->function_calls++; + } +} + +bool rkt_webview_valid(rktwebview_t wv) +{ + rkt_webview_init(); + if (handler != nullptr && handler->valid) { + 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; + } +} + +static inline bool validHandle() +{ + return (handler != nullptr) && handler->valid; +} + +#define FAIL_HANDLE if (!validHandle()) { return result_t::invalid_handle; } +#define FAIL_CONTEXT if (!validHandle()) { return 0; } +#define FAIL_WV if (!validHandle()) { return 0; } +#define NOOP_HANDLE if (!validHandle()) { return; } +#define FAIL_CALL_JS if (!validHandle()) { return nullptr; } +#define FAIL_INFO FAIL_CALL_JS + +rkt_wv_context_t rkt_webview_new_context(const char *boilerplate_js, const char *optional_server_cert_pem) +{ + rkt_webview_init(); + FAIL_CONTEXT + + 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) +{ + rkt_webview_init(); + FAIL_WV + + JSON j; + j["context"] = context; + j["parent"] = parent; + + handler->command_queue->enqueue(CMD_CREATE_WV, j.dump()); + + int result; + std::string json_result; + handler->command_result_queue->dequeue(result, json_result, true); + + return result; +} + +void rkt_webview_close(rktwebview_t wv) +{ + rkt_webview_init(); + NOOP_HANDLE + + 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(); \ + FAIL_HANDLE \ + JSON j; \ + j["wv"] = wv; \ + j[key] = val; \ + j[key2] = val2; \ + j[key3] = val3; \ + j[key4] = val4; \ + handler->command_queue->enqueue(cmd, j.dump()); \ + int result; \ + std::string json_result; \ + handler->command_result_queue->dequeue(result, json_result, true); \ + result_t r = static_cast(result); \ + return r; + +#define CMDRES3(cmd, wv, key, val, key2, val2, key3, val3) \ + CMDRES4(cmd, wv, key, val, key2, val2, key3, val3, "nil3", "none") + +#define CMDRES2(cmd, wv, key, val, key2, val2) \ + CMDRES3(cmd, wv, key, val, key2, val2, "nil2", "none") + +#define CMDRES(cmd, wv, key, val) \ + CMDRES2(cmd, wv, key, val, "nil1", "none") + +#define CMDRES0(cmd, wv) \ + CMDRES(cmd, wv, "nil0", "none") + +result_t rkt_webview_set_url(rktwebview_t wv, const char *url) +{ + CMDRES(CMD_SET_URL, wv, "url", url) +} + +result_t rkt_webview_set_html(rktwebview_t wv, const char *html) +{ + CMDRES(CMD_SET_HTML, wv, "html", html) +} + + +result_t rkt_webview_run_js(rktwebview_t wv, const char *js) +{ + CMDRES(CMD_RUN_JS, wv, "js", js) +} + +rkt_data_t *rkt_webview_call_js(rktwebview_t wv, const char *js) +{ + rkt_webview_init(); + FAIL_CALL_JS + + JSON j; + j["wv"] = wv; + j["js"] = std::string(js); + handler->command_queue->enqueue(CMD_CALL_JS, j.dump()); + + int result; + std::string json_result; + handler->command_result_queue->dequeue(result, json_result, true); + + rkt_data_t *r = new rkt_data_t(); + r->kind = rkt_data_kind_t::js_result; + r->data.js_result.result = static_cast(result); + r->data.js_result.value = strdup(json_result.c_str()); + + return r; +} + +result_t rkt_webview_open_devtools(rktwebview_t wv) +{ + CMDRES(CMD_OPEN_DEVTOOLS, wv, "nil", "none") +} + +/* +void rkt_webview_process_events(int for_ms) +{ + rkt_webview_init(); + + uint64_t start_ms = current_ms(); + uint64_t end_ms = start_ms + for_ms; + + while (current_ms() < end_ms) { + QThread::usleep(500); // sleep 0.5 ms + handler->doEvents(); + } +} +*/ + +result_t rkt_webview_move(rktwebview_t wv, int x, int y) +{ + CMDRES2(CMD_MOVE, wv, "x", x, "y", y) +} + + +result_t rkt_webview_resize(rktwebview_t wv, int width, int height) +{ + CMDRES2(CMD_RESIZE, wv, "w", width, "h", height) +} + + +result_t rkt_webview_hide(rktwebview_t w) +{ + CMDRES0(CMD_HIDE, w) +} + +result_t rkt_webview_show(rktwebview_t w) +{ + CMDRES0(CMD_SHOW, w) +} + +result_t rkt_webview_present(rktwebview_t w) +{ + CMDRES0(CMD_PRESENT, w) +} + +result_t rkt_webview_maximize(rktwebview_t w) +{ + CMDRES0(CMD_MAXIMIZE, w) +} + +result_t rkt_webview_minimize(rktwebview_t w) +{ + CMDRES0(CMD_MINIMIZE, w) +} + +result_t rkt_webview_show_normal(rktwebview_t w) +{ + CMDRES0(CMD_SHOW_NORMAL, w) +} + +void rkt_webview_set_loglevel(rkt_webview_loglevel_t l) +{ + auto f = [l]() { + CMDRES0(CMD_SET_LOGLEVEL, static_cast(l)); + }; + f(); +} + + +window_state_t rkt_webview_window_state(rktwebview_t w) +{ + auto f = [w]() { + CMDRES0(CMD_WINDOW_STATE, w) + }; + int r = f(); + return static_cast(r); +} + +result_t rkt_webview_set_title(rktwebview_t wv, const char *title) +{ + CMDRES(CMD_SET_TITLE, wv, "title", title) +} + +result_t rkt_webview_choose_dir(rktwebview_t w, const char *title, const char *base_dir) +{ + CMDRES2(CMD_CHOOSE_DIR, w, "title", title, "base_dir", base_dir) +} + +result_t rkt_webview_file_open(rktwebview_t w, const char *title, const char *base_dir, const char *permitted_exts) +{ + CMDRES3(CMD_FILE_OPEN, w, "title", title, "base_dir", base_dir, "permitted_exts", permitted_exts) +} + +result_t rkt_webview_file_save(rktwebview_t w, const char *title, const char *base_dir, const char *permitted_exts) +{ + CMDRES3(CMD_FILE_SAVE, w, "title", title, "base_dir", base_dir, "permitted_exts", permitted_exts) +} + +void rkt_webview_set_ou_token(rktwebview_t wv, const char *token) +{ + rkt_webview_init(); + NOOP_HANDLE + + 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) +{ + do_free_data(d); +} + +rkt_data_t *rkt_webview_version() +{ + rkt_data_t *d = static_cast(malloc(sizeof(rkt_data_t))); + d->kind = version; + d->data.version.api_major = RKT_WEBVIEW_API_MAJOR; + d->data.version.api_minor = RKT_WEBVIEW_API_MINOR; + d->data.version.api_patch = RKT_WEBVIEW_API_PATCH; + return d; +} + +result_t rkt_webview_message_box(rktwebview_t w, const char *title, const char *message, const char *submessage, rkt_messagetype_t type) +{ + CMDRES4(CMD_MSG_BOX, w, "title", title, "message", message, "submessage", submessage, "type", static_cast(type)) +} + +int rkt_webview_events_waiting() +{ + return handler->event_queue->depth(); +} + +rkt_data_t *rkt_webview_get_event() +{ + //fprintf(stderr, "rkt_webview_get_event\n"); + int wv; + std::string data; + if (handler->event_queue->dequeue(wv, data, false)) { + //fprintf(stderr, "got event %d %s\n", wv, data.c_str()); + rkt_data_t *d = reinterpret_cast(malloc(sizeof(rkt_data_t))); + d->kind = rkt_data_kind_t::event; + + handler->events += 1; + size_t ds = data.size(); + d->data.event.event = reinterpret_cast(malloc(ds + 1)); + memcpy(d->data.event.event, data.c_str(), ds); + d->data.event.event[ds] = '\0'; + d->data.event.wv = wv; + //fprintf(stderr, "event: %d - '%s'\n", d->data.event.wv, d->data.event.event);fflush(stderr); + return d; + } else { + return nullptr; + } +} + +void rkt_webview_env(const char *env_cmds[]) +{ + int i; + for(i = 0; env_cmds[i] != nullptr && strcmp(env_cmds[i], "") != 0; i++) { +#ifdef WIN32 + _putenv(env_cmds[i]); +#else + putenv(const_cast(env_cmds[i])); +#endif + } +} + + +rkt_data_t *rkt_webview_info() +{ + rkt_webview_init(); + FAIL_INFO + + rkt_data_t *d = new rkt_data_t(); + d->kind = metrics; + + handler->shm->info(d->data.metrics.shm_usage, + d->data.metrics.shm_free_depth, + d->data.metrics.shm_free_size, + d->data.metrics.shm_item_depth, + d->data.metrics.shm_item_size, + d->data.metrics.shm_item_usage_factor); + + JSON j; + handler->command_queue->enqueue(CMD_INFO, j.dump()); + + int open_windows_result; + std::string none; + handler->command_result_queue->dequeue(open_windows_result, none, true); + + d->data.metrics.open_windows = open_windows_result; + d->data.metrics.function_calls = handler->function_calls; + d->data.metrics.log_file = strdup(handler->log_file.c_str()); + d->data.metrics.events = handler->events + handler->event_queue->depth(); + + return d; +} diff --git a/rktwebview.h b/rktwebview.h new file mode 100644 index 0000000..0fd9da7 --- /dev/null +++ b/rktwebview.h @@ -0,0 +1,59 @@ +#ifndef RKTWEBVIEW_H +#define RKTWEBVIEW_H + +#include "rktwebview_global.h" +#include "rktwebview_types.h" + +#define RKT_WEBVIEW_API_MAJOR 0 +#define RKT_WEBVIEW_API_MINOR 1 +#define RKT_WEBVIEW_API_PATCH 1 + +extern "C" { + +RKTWEBVIEW_EXPORT void rkt_webview_env(const char *env_cmds[]); +RKTWEBVIEW_EXPORT void rkt_webview_init(); +RKTWEBVIEW_EXPORT void rkt_webview_cleanup(); +RKTWEBVIEW_EXPORT void rkt_webview_set_loglevel(rkt_webview_loglevel_t l); +RKTWEBVIEW_EXPORT rkt_data_t *rkt_webview_info(); + +RKTWEBVIEW_EXPORT void rkt_webview_free_data(rkt_data_t *d); +RKTWEBVIEW_EXPORT rkt_data_t *rkt_webview_version(); + +RKTWEBVIEW_EXPORT int rkt_webview_events_waiting(); +RKTWEBVIEW_EXPORT rkt_data_t *rkt_webview_get_event(); + +RKTWEBVIEW_EXPORT rkt_wv_context_t rkt_webview_new_context(const char *boilerplate_js, const char *optional_server_cert_pem); +RKTWEBVIEW_EXPORT int rkt_webview_create(rkt_wv_context_t context, rktwebview_t parent); + +RKTWEBVIEW_EXPORT void rkt_webview_close(rktwebview_t wv); +RKTWEBVIEW_EXPORT bool rkt_webview_valid(rktwebview_t wv); +RKTWEBVIEW_EXPORT result_t rkt_webview_set_title(rktwebview_t wv, const char *title); +RKTWEBVIEW_EXPORT void rkt_webview_set_ou_token(rktwebview_t wv, const char *token); + +RKTWEBVIEW_EXPORT result_t rkt_webview_set_url(rktwebview_t wv, const char *url); +RKTWEBVIEW_EXPORT result_t rkt_webview_set_html(rktwebview_t wv, const char *html); + +RKTWEBVIEW_EXPORT result_t rkt_webview_run_js(rktwebview_t wv, const char *js); +RKTWEBVIEW_EXPORT rkt_data_t *rkt_webview_call_js(rktwebview_t wv, const char *js); + +RKTWEBVIEW_EXPORT result_t rkt_webview_open_devtools(rktwebview_t wv); + +RKTWEBVIEW_EXPORT result_t rkt_webview_move(rktwebview_t w, int x, int y); +RKTWEBVIEW_EXPORT result_t rkt_webview_resize(rktwebview_t w, int width, int height); +RKTWEBVIEW_EXPORT result_t rkt_webview_hide(rktwebview_t w); +RKTWEBVIEW_EXPORT result_t rkt_webview_show(rktwebview_t w); +RKTWEBVIEW_EXPORT result_t rkt_webview_show_normal(rktwebview_t w); +RKTWEBVIEW_EXPORT result_t rkt_webview_present(rktwebview_t w); +RKTWEBVIEW_EXPORT result_t rkt_webview_maximize(rktwebview_t w); +RKTWEBVIEW_EXPORT result_t rkt_webview_minimize(rktwebview_t w); +RKTWEBVIEW_EXPORT window_state_t rkt_webview_window_state(rktwebview_t w); + +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_EXPORT result_t rkt_webview_message_box(rktwebview_t w, const char *title, const char *message, const char *submessage, rkt_messagetype_t type); + +} + +#endif // RKTWEBVIEW_H diff --git a/rktwebview_global.h b/rktwebview_global.h new file mode 100644 index 0000000..962d205 --- /dev/null +++ b/rktwebview_global.h @@ -0,0 +1,18 @@ +#ifndef RKTWEBVIEW_GLOBAL_H +#define RKTWEBVIEW_GLOBAL_H + +#if defined(RKTWEBVIEW_LIBRARY) + #ifdef _WIN32 + #define RKTWEBVIEW_EXPORT __declspec(dllexport) + #else + #define RKTWEBVIEW_EXPORT + #endif +#else + #ifdef _WIN32 + #define RKTWEBVIEW_EXPORT __declspec(dllimport) + #else + #define RKTWEBVIEW_EXPORT + #endif +#endif + +#endif // RKTWEBVIEW_GLOBAL_H diff --git a/rktwebview_qt.cpp b/rktwebview_qt.cpp new file mode 100644 index 0000000..211cafc --- /dev/null +++ b/rktwebview_qt.cpp @@ -0,0 +1,1054 @@ +#include "rktwebview_qt.h" +#include "webviewqt.h" +#include "rktwebview_types.h" +#include "webviewwindow.h" +#include "rktutils.h" +#include "utils.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "command.h" +#include +#include +#include + +static inline char *copyString(const char *s) +{ + int l = strlen(s); + char *cpy = static_cast(malloc(l + 1)); + memcpy(cpy, s, l + 1); + return cpy; +} + +void Rktwebview_qt::processCommand(Command *cmd) +{ + switch(cmd->cmd) { + case COMMAND_QUIT: { // Quit application + _app->quit(); + cmd->done = true; + } + 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(); + + DEBUG1("bjs: %s\n", boilerplate_js.toUtf8().constData()); + DEBUG1("oscp: %s\n", optional_server_cert_pem.toUtf8().constData()); + + QWebEngineProfileBuilder b; + if (has_pem) { + QByteArray scp = optional_server_cert_pem.toUtf8(); + DEBUG1("Installing cert: %s\n", scp.constData()); + QList certs; + QSslCertificate cert(scp); + certs.append(cert); + b.setAdditionalTrustedCertificates(certs); + } + _context_counter += 1; + QString name = QString::asprintf("profile-%d", _context_counter); + + QString code = "if (window.rkt_event_queue === undefined) { window.rkt_event_queue = []; }\n" + "window.rkt_evt_frame_el = null;\n" + "window.rkt_evt_frame_win = null;\n" + "window.rkt_send_event = function(obj) {\n" + " obj.timestamp = Date.now();\n" + " //console.log('Sending event: ' + obj);\n" + " window.rkt_event_queue.push(obj);\n" + " if (window.rkt_evt_frame_el) {\n" + " window.rkt_evt_frame_win.print();\n" + " }\n" + "};\n" + "window.rkt_get_events = function() {\n" + " let q = window.rkt_event_queue;\n" + " window.rkt_event_queue = [];\n" + " let json_q = JSON.stringify(q);\n" + " return json_q;\n" + "};\n" + "// add hidden hover element to body if necessary\n" + "setInterval(function () {\n" + " if (window.rkt_evt_frame_el === null || window.rkt_evt_frame_el === undefined) {\n" + " window.rkt_evt_frame_el = document.createElement('iframe');\n" + " window.rkt_evt_frame_el.style.display = 'none';\n" + " window.rkt_evt_frame_el.setAttribute('id', 'rkt-evt-frame');\n" + " window.rkt_evt_frame_el.setAttribute('name', 'rkt-evt-frame');\n" + " document.body.append(window.rkt_evt_frame_el);\n" + " window.rkt_evt_frame_win = window.rkt_evt_frame_el.contentWindow;\n" + " } else {" + " if (window.rkt_event_queue.length > 0) {\n" + " window.rkt_evt_frame_win.print();\n" + " }\n" + " }\n" + "},\n" + "10);\n" + ""; + + QList scripts; + QWebEngineProfile *p = b.createProfile(name); + QWebEngineScriptCollection *col = p->scripts(); + QWebEngineScript s1; + s1.setInjectionPoint(QWebEngineScript::InjectionPoint::DocumentReady); + s1.setName("eventing"); + s1.setSourceCode(code); + s1.setWorldId(QWebEngineScript::MainWorld); + scripts.append(s1); + + QWebEngineScript s2; + s2.setInjectionPoint(QWebEngineScript::InjectionPoint::DocumentReady); + s2.setName("boilerplate"); + s2.setSourceCode(boilerplate_js); + s2.setWorldId(QWebEngineScript::MainWorld); + scripts.append(s2); + + col->insert(scripts); + + QWebEngineScriptCollection *c = p->scripts(); + QList l = c->toList(); + + _contexts[_context_counter] = p; + + cmd->result = _context_counter; + cmd->done = true; + } + break; + case COMMAND_CREATE: { + rkt_wv_context_t context = cmd->args[0].toInt(); + rktwebview_t parent = cmd->args[1].toInt(); + + void *f = cmd->args[2].value(); + event_cb_t js_event_cb = reinterpret_cast (f); + + WebviewWindow *p; + if (_views.contains(parent)) { + p = _views[parent]; + } else { + p = nullptr; + } + + if (!_contexts.contains(context)) { + cmd->result = -1; + cmd->done = true; + } else { + QWebEngineProfile *profile = _contexts[context]; + WebviewWindow *w = new WebviewWindow(profile, p); + WebViewQt *view = new WebViewQt(nextHandle(), w); + w->addView(view, this); + + int id = view->id(); + + _views[id] = w; + _view_js_callbacks[id] = js_event_cb; + + w->show(); + while(!w->windowCreated()) { + _app->processEvents(); + } + + cmd->result = id; + cmd->done = true; + } + } + break; + case COMMAND_CLOSE: { + int wv = cmd->args[0].toInt(); + if (_views.contains(wv)) { + WebviewWindow *w= _views[wv]; + _views.remove(wv); + w->closeView(); + cmd->result = true; + while(w->isVisible()) { + _app->processEvents(); + } + _view_js_callbacks.remove(wv); + delete w; + } else { + cmd->result = false; + } + cmd->done = true; + } + break; + case COMMAND_SET_OU_TOKEN: { + int wv = cmd->args[0].toInt(); + QString ou_token = cmd->args[1].toString(); + if (_views.contains(wv)) { + WebviewWindow *w= _views[wv]; + w->setOUToken(ou_token); + cmd->result = true; + } else { + cmd->result = false; + } + cmd->done = true; + } + break; + case COMMAND_SET_URL: { + int wv = cmd->args[0].toInt(); + QString url = cmd->args[1].toString(); + if (_views.contains(wv)) { + WebviewWindow *w = _views[wv]; + WebViewQt *v = w->view(); + QUrl u(url); + v->setUrl(u); + cmd->result = true; + } else { + cmd->result = false; + } + cmd->done = true; + } + break; + case COMMAND_SET_HTML: { + int wv = cmd->args[0].toInt(); + QString html = cmd->args[1].toString(); + if (_views.contains(wv)) { + WebviewWindow *w = _views[wv]; + WebViewQt *v = w->view(); + v->setHtml(html); + cmd->result = true; + } else { + cmd->result = false; + } + cmd->done = true; + } + break; + case COMMAND_SET_TITLE: { + int wv = cmd->args[0].toInt(); + QString title = cmd->args[1].toString(); + if (_views.contains(wv)) { + WebviewWindow *w = _views[wv]; + w->setWindowTitle(title); + cmd->result = true; + } else { + cmd->result = false; + } + cmd->done = true; + } + break; + case COMMAND_RUN_JS: { + int wv = cmd->args[0].toInt(); + QString js = cmd->args[1].toString(); + if (_views.contains(wv)) { + WebviewWindow *w = _views[wv]; + DEBUG1("Running %s\n", js.toUtf8().constData()); + w->runJs(js); + cmd->result = true; + } else { + cmd->result = false; + } + cmd->done = true; + } + break; + case COMMAND_CALL_JS: { + int wv = cmd->args[0].toInt(); + QString js = cmd->args[1].toString(); + if (_views.contains(wv)) { + WebviewWindow *w = _views[wv]; + w->callJs(js, cmd); + } else { + cmd->result = false; + cmd->js_result_ok = false; + cmd->done = true; + } + } + break; + case COMMAND_DEV_TOOLS: { + int wv = cmd->args[0].toInt(); + if (_views.contains(wv)) { + WebviewWindow *w = _views[wv]; + w->openDevTools(); + cmd->result = true; + } else { + cmd->result = false; + } + cmd->done = true; + } + break; + case COMMAND_SHOW_WIN: + case COMMAND_HIDE_WIN: + case COMMAND_PRESENT_WIN: + case COMMAND_MAX_WIN: + case COMMAND_MIN_WIN: + case COMMAND_SHOW_NORMAL_WIN: + { + int wv = cmd->args[0].toInt(); + if (_views.contains(wv)) { + WebviewWindow *w = _views[wv]; + int c = cmd->cmd; + if (c == COMMAND_SHOW_WIN) w->show(); + else if (c == COMMAND_HIDE_WIN) w->hide(); + else if (c == COMMAND_MAX_WIN) w->showMaximized(); + else if (c == COMMAND_MIN_WIN) { + w->showMinimized(); + } + else if (c == COMMAND_SHOW_NORMAL_WIN) w->showNormal(); + else if (c == COMMAND_PRESENT_WIN) { + w->show(); + w->raise(); + w->activateWindow(); + } + cmd->result = true; + } else { + cmd->result = false; + } + cmd->done = true; + } + break; + case COMMAND_WINDOW_STATUS: + { + int wv = cmd->args[0].toInt(); + window_state_t ws = window_state_t::invalid; + if (_views.contains(wv)) { + WebviewWindow *w = _views[wv]; + if (w->isHidden()) { + ws = window_state_t::hidden; + } else if (w->isMinimized()) { + ws = window_state_t::minimized; + } else if (w->isMaximized()) { + if (w->isActiveWindow()) { + ws = window_state_t::maximized_active; + } else { + ws = window_state_t::maximized; + } + } else if (w->isVisible()) { + if (w->isActiveWindow()) { + ws = window_state_t::normal_active; + } else { + ws = window_state_t::normal; + } + } + } + cmd->result = static_cast(ws); + cmd->done = true; + } + break; + case COMMAND_MOVE: { + int wv = cmd->args[0].toInt(); + int x = cmd->args[1].toInt(); + int y = cmd->args[2].toInt(); + if (_views.contains(wv)) { + WebviewWindow *w = _views[wv]; + int move_count = w->moveCount(); + w->move(x, y); + while (w->moveCount() == move_count) { + _app->processEvents(); + } + cmd->result = true; + } else { + cmd->result = false; + } + cmd->done = true; + } + break; + case COMMAND_RESIZE: { + int wv = cmd->args[0].toInt(); + int width = cmd->args[1].toInt(); + int height = cmd->args[2].toInt(); + if (_views.contains(wv)) { + WebviewWindow *w = _views[wv]; + int resize_count = w->resizeCount(); + w->resize(width, height); + while (w->resizeCount() == resize_count) { + _app->processEvents(); + } + cmd->result = true; + } else { + cmd->result = false; + } + cmd->done = true; + } + break; + case COMMAND_CHOOSE_DIR: { + int wv = cmd->args[0].toInt(); + QString title = cmd->args[1].toString(); + QString base_dir = cmd->args[2].toString(); + if (_views.contains(wv)) { + result_t r = fileDlg(wv, title, base_dir, "", + QFileDialog::Directory, QFileDialog::AcceptOpen, + "choose-dir-ok", "choose-dir-cancel" + ); + cmd->js_result_ok = true; + cmd->result = (r == result_t::oke); + } else { + cmd->js_result_ok = false; + cmd->result = false; + } + cmd->done = true; + } + break; + case COMMAND_FILE_OPEN: { + int wv = cmd->args[0].toInt(); + QString title = cmd->args[1].toString(); + QString base_dir = cmd->args[2].toString(); + QString exts = cmd->args[3].toString(); + if (_views.contains(wv)) { + result_t r = fileDlg(wv, title, base_dir, exts, + QFileDialog::ExistingFile, QFileDialog::AcceptOpen, + "file-open-ok", "file-open-cancel" + ); + cmd->js_result_ok = true; + cmd->result = (r == result_t::oke); + } else { + cmd->js_result_ok = true; + cmd->result = false; + } + cmd->done = true; + } + break; + case COMMAND_FILE_SAVE: { + int wv = cmd->args[0].toInt(); + QString title = cmd->args[1].toString(); + QString base_dir = cmd->args[2].toString(); + QString exts = cmd->args[3].toString(); + if (_views.contains(wv)) { + result_t r = fileDlg(wv, title, base_dir, exts, + QFileDialog::AnyFile, QFileDialog::AcceptSave, + "file-open-ok", "file-open-cancel" + ); + cmd->js_result_ok = true; + cmd->result = (r == result_t::oke); + } else { + cmd->js_result_ok = true; + cmd->result = false; + } + cmd->done = true; + } + break; + case COMMAND_MESSAGE: { + int wv = cmd->args[0].toInt(); + QString title = cmd->args[1].toString(); + QString msg = cmd->args[2].toString(); + QString submsg = cmd->args[3].toString(); + rkt_messagetype_t type = static_cast(cmd->args[4].toInt()); + + if (_views.contains(wv)) { + msgBox(wv, title, msg, submsg, type); + cmd->js_result_ok = true; + cmd->result = true; + } else { + cmd->js_result_ok = true; + cmd->result = false; + } + cmd->done = true; + } + break; + default: { + cmd->result = false; + cmd->done = true; + } + break; + } +} + +void Rktwebview_qt::removeView(int id) +{ + if (_views.contains(id)) { + //WebviewWindow *win = _views[id]; + _views.remove(id); + _view_js_callbacks.remove(id); + } +} + +int Rktwebview_qt::nextHandle() +{ + int h = ++_current_handle; + return h; +} + +rkt_wv_context_t Rktwebview_qt::newContext(const QString &boilerplate_js, bool has_pem, const QString &optional_server_cert_pem) +{ + Command c(COMMAND_NEW_CONTEXT); + + c.args.push_back(boilerplate_js); + c.args.push_back(has_pem); + c.args.push_back(optional_server_cert_pem); + + postCommand(&c); + + while(!c.done) { doEvents(); } + + int id = c.result.toInt(); + return id; +} + +int Rktwebview_qt::rktWebViewCreate(rkt_wv_context_t context, rktwebview_t parent, event_cb_t js_evt_cb) +{ + Command c(COMMAND_CREATE); + c.args.push_back(context); + c.args.push_back(parent); + void *function = reinterpret_cast(js_evt_cb); + QVariant f(QVariant::fromValue(function)); + c.args.push_back(f); + + postCommand(&c); + + while(!c.done) { doEvents(); } + + int id = c.result.toInt(); + return id; +} + +void Rktwebview_qt::rktWebViewClose(int wv) +{ + Command c(COMMAND_CLOSE); + c.args.push_back(wv); + + postCommand(&c); + + while(!c.done) { doEvents(); } +} + +void Rktwebview_qt::rktSetOUToken(rktwebview_t wv, const char *ou_token) +{ + Command c(COMMAND_SET_OU_TOKEN); + c.args.push_back(wv); + QString ou_tok(ou_token); + c.args.push_back(ou_tok); + + postCommand(&c); + + while(!c.done) { doEvents(); } +} + +result_t Rktwebview_qt::rktSetUrl(rktwebview_t wv, const char *url) +{ + Command c(COMMAND_SET_URL); + QString _url(url); + c.args.push_back(wv); + c.args.push_back(_url); + postCommand(&c); + + while(!c.done) { doEvents(); } + + bool r = c.result.toBool(); + + return r ? result_t::oke : result_t::set_navigate_failed; +} + +result_t Rktwebview_qt::rktSetHtml(rktwebview_t wv, const char *html) +{ + Command c(COMMAND_SET_HTML); + QString _html(html); + c.args.push_back(wv); + c.args.push_back(_html); + postCommand(&c); + + while(!c.done) { doEvents(); } + + bool r = c.result.toBool(); + + return r ? result_t::oke : result_t::set_html_failed; +} + +rkt_data_t *Rktwebview_qt::rktCallJs(rktwebview_t wv, const char *js) +{ + DEBUG1("calljs: %s\n", js); + + Command c(COMMAND_CALL_JS); + QString _js(js); + c.args.push_back(wv); + c.args.push_back(_js); + postCommand(&c); + while(!c.done) { doEvents(); } + + rkt_data_t *r = static_cast(malloc(sizeof(rkt_data_t))); + r->kind = js_result; + r->data.js_result.result = c.js_result_ok ? result_t::oke : result_t::eval_js_failed; + r->data.js_result.value = copyString(c.result.toString().toUtf8()); + DEBUG1("js-result: %s\n", r->data.js_result.value); + + return r; +} + +result_t Rktwebview_qt::rktRunJs(rktwebview_t wv, const QString &js) +{ + DEBUG1("rktRunJs: %s\n", js.toUtf8().constData()); + Command c(COMMAND_RUN_JS); + c.args.push_back(wv); + c.args.push_back(js); + postCommand(&c); + while(!c.done) { doEvents(); } + bool r = c.result.toBool(); + return r ? result_t::oke : result_t::eval_js_failed; +} + +result_t Rktwebview_qt::rktMove(rktwebview_t wv, int x, int y) +{ + Command c(COMMAND_MOVE); + c.args.push_back(wv); + c.args.push_back(x); + c.args.push_back(y); + postCommand(&c); + while(!c.done) { doEvents(); } + bool r = c.result.toBool(); + return r ? result_t::oke : result_t::move_failed; +} + +result_t Rktwebview_qt::rktResize(rktwebview_t wv, int w, int h) +{ + Command c(COMMAND_RESIZE); + c.args.push_back(wv); + c.args.push_back(w); + c.args.push_back(h); + postCommand(&c); + while(!c.done) { doEvents(); } + bool r = c.result.toBool(); + return r ? result_t::oke : result_t::resize_failed; +} + +result_t Rktwebview_qt::rktHideWindow(rktwebview_t w) +{ + return doWindow(w, COMMAND_HIDE_WIN, result_t::failed); +} + +result_t Rktwebview_qt::rktShowWindow(rktwebview_t w) +{ + return doWindow(w, COMMAND_SHOW_WIN, result_t::failed); +} + +result_t Rktwebview_qt::rktPresentWindow(rktwebview_t w) +{ + return doWindow(w, COMMAND_PRESENT_WIN, result_t::failed); +} + +result_t Rktwebview_qt::rktMaximizeWindow(rktwebview_t w) +{ + return doWindow(w, COMMAND_MAX_WIN, result_t::failed); +} + +result_t Rktwebview_qt::rktMinimizeWindow(rktwebview_t w) +{ + return doWindow(w, COMMAND_MIN_WIN, result_t::failed); +} + +result_t Rktwebview_qt::rktShowNormalWindow(rktwebview_t w) +{ + return doWindow(w, COMMAND_SHOW_NORMAL_WIN, result_t::failed); +} + +window_state_t Rktwebview_qt::rktWindowState(rktwebview_t w) +{ + Command c(COMMAND_WINDOW_STATUS); + c.args.push_back(w); + postCommand(&c); + while(!c.done) { doEvents(); } + int r = c.result.toInt(); + window_state_t ws = static_cast(r); + return ws; +} + +int Rktwebview_qt::openWindows() +{ + return _views.size(); +} + +void Rktwebview_qt::rktQuit() +{ + Command c(COMMAND_QUIT); + postCommand(&c); + + while(!c.done) { doEvents(); } +} + +result_t Rktwebview_qt::fileDlg(rktwebview_t w, + const QString &title, + const QString &base, + const QString &filters, + QFileDialog::FileMode mode, + QFileDialog::AcceptMode am, + QString evt_ok, + QString evt_cancel) +{ + if (_views.contains(w)) { + WebviewWindow *win = _views[w]; + + QFileDialog *dlg = new QFileDialog(win, title, base); + dlg->setFileMode(mode); + dlg->setAcceptMode(am); + + QString pe = filters; + QStringList p = pe.split(";;"); + + dlg->setNameFilters(p); + dlg->setProperty("rktwebview", w); + dlg->setProperty("evt-ok", evt_ok); + dlg->setProperty("evt-cancel", evt_cancel); + + connect(dlg, &QFileDialog::accepted, this, [this]() { + QFileDialog *dlg = static_cast(sender()); + + rktwebview_t w = dlg->property("rktwebview").toInt(); + QString evt_ok = dlg->property("evt-ok").toString(); + + QStringList l = dlg->selectedFiles(); + QString file; + if (l.size() > 0) { + file = l[0]; + } + + EventContainer e(evt_ok); + e["choosen"] = file; + e["filter"] = dlg->selectedNameFilter(); + e["dir"] = dlg->directory().path(); + + this->triggerEvent(w, e); + }); + + connect(dlg, &QDialog::rejected, this, [this]() { + QFileDialog *dlg = static_cast(sender()); + + rktwebview_t w = dlg->property("rktwebview").toInt(); + QString evt_cancel = dlg->property("evt-cancel").toString(); + + EventContainer e(evt_cancel); + this->triggerEvent(w, e); + }); + dlg->show(); + return result_t::oke; + } else { + return result_t::failed; + } +} + +result_t Rktwebview_qt::rktChooseDir(rktwebview_t w, const char *title, const char *base_dir) +{ + Command c(COMMAND_CHOOSE_DIR); + c.args.push_back(w); + QString s_title(title); + c.args.push_back(s_title); + QString s_base_dir(base_dir); + c.args.push_back(s_base_dir); + + postCommand(&c); + while(!c.done) { doEvents(); } + + bool r = c.result.toBool(); + return r ? result_t::oke : result_t::resize_failed; +} + +result_t Rktwebview_qt::rktFileOpen(rktwebview_t w, const char *title, const char *base_dir, const char *permitted_exts) +{ + Command c(COMMAND_FILE_OPEN); + c.args.push_back(w); + QString s_title(title); + c.args.push_back(s_title); + QString s_base_dir(base_dir); + c.args.push_back(s_base_dir); + QString s_pe(permitted_exts); + c.args.push_back(s_pe); + + postCommand(&c); + while(!c.done) { doEvents(); } + + bool r = c.result.toBool(); + return r ? result_t::oke : result_t::resize_failed; +} + +result_t Rktwebview_qt::rktFileSave(rktwebview_t w, const char *title, const char *base_dir, const char *permitted_exts) +{ + Command c(COMMAND_FILE_SAVE); + c.args.push_back(w); + QString s_title(title); + c.args.push_back(s_title); + QString s_base_dir(base_dir); + c.args.push_back(s_base_dir); + QString s_pe(permitted_exts); + c.args.push_back(s_pe); + + postCommand(&c); + while(!c.done) { doEvents(); } + + bool r = c.result.toBool(); + return r ? result_t::oke : result_t::resize_failed; +} + +result_t Rktwebview_qt::rktWindowSetTitle(rktwebview_t wv, const char *title) +{ + Command c(COMMAND_SET_TITLE); + c.args.push_back(wv); + c.args.push_back(title); + postCommand(&c); + while(!c.done) { doEvents(); } + bool r = c.result.toBool(); + return r ? result_t::oke : result_t::failed; +} + +void Rktwebview_qt::msgBox(rktwebview_t w, const QString &title, const QString &message, const QString &submessage, rkt_messagetype_t type) +{ + + WebviewWindow *win = _views[w]; + QMessageBox *msg = new QMessageBox(win); + + auto prepButton = [msg, w, this](QMessageBox::StandardButton b, QString event) { + msg->addButton(b); + QAbstractButton *btn = msg->button(b); + QVariant v = QVariant::fromValue(msg); + btn->setProperty("messagebox", v); + btn->setProperty("event", event); + btn->setProperty("rktwebview", w); + connect(btn, &QAbstractButton::clicked, this, [this]() { + QAbstractButton *btn = static_cast(sender()); + QMessageBox *mb = btn->property("messagebox").value(); + rktwebview_t w = btn->property("rktwebview").toInt(); + + EventContainer e(btn->property("event").toString()); + this->triggerEvent(w, e); + + mb->close(); + }); + }; + + msg->setWindowTitle(title); + + QString m = QString("

") + message + "

"; + m += QString::asprintf("

%s

", submessage.toUtf8().constData()); + msg->setInformativeText(m); + + QMessageBox::Icon icn; + + if (type == rkt_messagetype_t::error) { + icn = QMessageBox::Icon::Critical; + prepButton(QMessageBox::StandardButton::Ok, "msgbox-ok"); + } else if (type == rkt_messagetype_t::warning) { + icn = QMessageBox::Icon::Warning; + prepButton(QMessageBox::StandardButton::Ok, "msgbox-ok"); + } else if (type == rkt_messagetype_t::yes_no || type == rkt_messagetype_t::oke_cancel) { + icn = QMessageBox::Icon::Question; + if (type == rkt_messagetype_t::yes_no) { + prepButton(QMessageBox::StandardButton::No, "msgbox-no"); + prepButton(QMessageBox::StandardButton::Yes, "msgbox-yes"); + } else { + prepButton(QMessageBox::StandardButton::Cancel, "msgbox-cancel"); + prepButton(QMessageBox::StandardButton::Ok, "msgbox-ok"); + } + } else { // informatio + icn = QMessageBox::Icon::Information; + prepButton(QMessageBox::StandardButton::Ok, "msgbox-ok"); + } + msg->setIcon(icn); + msg->show(); +} + +result_t Rktwebview_qt::rktMessageBox(rktwebview_t w, const char *title, const char *message, const char *submessage, rkt_messagetype_t type) +{ + Command c(COMMAND_MESSAGE); + c.args.push_back(w); + QString s_title(title); + c.args.push_back(s_title); + QString s_msg(message); + c.args.push_back(s_msg); + QString s_submsg(submessage); + c.args.push_back(s_submsg); + c.args.push_back(static_cast(type)); + + postCommand(&c); + + while(!c.done) { doEvents(); } + + bool r = c.result.toBool(); + return r ? result_t::oke : result_t::failed; +} + +result_t Rktwebview_qt::doWindow(rktwebview_t w, int cmd, result_t on_error) +{ + Command c(cmd); + c.args.push_back(w); + postCommand(&c); + while(!c.done) { doEvents(); } + bool r = c.result.toBool(); + return r ? result_t::oke : on_error; +} + +bool Rktwebview_qt::rktValid(rktwebview_t wv) +{ + return _views.contains(wv); +} + +result_t Rktwebview_qt::rktOpenDevtools(rktwebview_t wv) +{ + Command c(COMMAND_DEV_TOOLS); + c.args.push_back(wv); + postCommand(&c); + while(!c.done) { doEvents(); } + bool r = c.result.toBool(); + return r ? result_t::oke : result_t::eval_js_failed; +} + +void Rktwebview_qt::onPageLoad(rktwebview_t w) +{ +} + +void Rktwebview_qt::pageLoaded(rktwebview_t w, bool ok) +{ + if (!ok) { + // Inject code of the profile to this page + WebviewWindow *win = _views[w]; + + if (win->navigationEventSent()) { + return; + } + + QWebEngineProfile *p = win->profile(); + QWebEngineScriptCollection *col = p->scripts(); + QList l = col->toList(); + int i; + for(i = 0; i < l.size(); i++) { + runJs(w, l[i].sourceCode().toUtf8().constData()); + } + } + + + // trigger page loaded. + EventContainer e("page-loaded"); + e["oke"] = ok; + triggerEvent(w, e); +} + +void Rktwebview_qt::triggerEvent(rktwebview_t wv, const EventContainer &e) +{ + triggerEvent(wv, mkEventJson(e)); +} + +void Rktwebview_qt::triggerEvent(rktwebview_t wv, const QString &msg) +{ + if (_view_js_callbacks.contains(wv)) { + event_cb_t js_event_cb = _view_js_callbacks[wv]; + char *evt = copyString(msg.toUtf8().constData()); + rkt_data_t *d = static_cast(malloc(sizeof(rkt_data_t))); + d->kind = rkt_data_kind_t::event; + d->data.event.wv = wv; + d->data.event.event = evt; + js_event_cb(d); + } +} + +void Rktwebview_qt::execApp() +{ + _app->exec(); +} + +QApplication *Rktwebview_qt::app() +{ + return _app; +} + +void Rktwebview_qt::deleteApp() +{ + delete _app; + _app = nullptr; +} + +void Rktwebview_qt::initApp() +{ + _app = new QApplication(_argc, _argv); + _app->setQuitOnLastWindowClosed(false); + + // See Qt 6.10 remark at doEvents. + //connect(&_evt_loop_timer, &QTimer::timeout, this, &Rktwebview_qt::stopEventloop); + + // Because we are using processEvents only (Qt 6.10), we need this dispatcher to + // handle deferred Deletes. + + /*const auto *eventDispatcher = QThread::currentThread()->eventDispatcher(); + QObject::connect(eventDispatcher, &QAbstractEventDispatcher::aboutToBlock, + QThread::currentThread(), []{ + if (QThread::currentThread()->loopLevel() == 0) + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); + } + ); + */ +} + + +void Rktwebview_qt::runJs(rktwebview_t wv, const char *js) +{ + if (_views.contains(wv)) { + QString _js(js); + WebviewWindow *win = _views[wv]; + win->runJs(_js); + } +} + + +void Rktwebview_qt::postCommand(Command *cmd) +{ + CommandEvent *e = new CommandEvent(cmd); + QApplication::postEvent(this, e); +} + +void Rktwebview_qt::handleCommandEvent(CommandEvent *e) +{ + Command *c = e->cmd(); + processCommand(c); +} + +void Rktwebview_qt::customEvent(QEvent *event) +{ + if (event->type() == COMMAND_EVENT) { + handleCommandEvent(static_cast(event)); + } +} + +void Rktwebview_qt::doEvents() +{ + //QTime ct = QTime::currentTime(); + //QTime expire = QTime::currentTime().addMSecs(2); + //while(QTime::currentTime() <= expire) { + //_app->processEvents(); + //} + + // Qt 6.10 --> this leads to a core dump + // together with the stopEventloop stuff. + // Qt 6.4 seem stable. + /*if (_evt_loop_depth == 0) { + _evt_loop_depth += 1; + _evt_loop_timer.setSingleShot(true); + _evt_loop_timer.start(2); + //_evt_loop.exec(); + _app->exec(); + }*/ + QThread::usleep(500); +} + +void Rktwebview_qt::stopEventloop() +{ + //_evt_loop.exit(0); + //_app->exit(0); + //_evt_loop_depth -= 1; +} + + + +Rktwebview_qt::Rktwebview_qt(int argc, char *argv[]) : QObject() +{ + _context_counter = 0; + _current_handle = 0; + _evt_loop_depth = 0; + + _argc = argc; + _argv = argv; + + _app = nullptr; +} + +Rktwebview_qt::~Rktwebview_qt() +{ + QList win_keys = _views.keys(); + int i, N; + for(i = 0, N = win_keys.size(); i < N; i++) { + WebviewWindow *w = _views[win_keys[i]]; + delete w; + } + + QList c_keys = _contexts.keys(); + for(i = 0, N = c_keys.size(); i < N; i++) { + QWebEngineProfile *p = _contexts[c_keys[i]]; + delete p; + } + + delete _app; +} diff --git a/rktwebview_qt.h b/rktwebview_qt.h new file mode 100644 index 0000000..16e5599 --- /dev/null +++ b/rktwebview_qt.h @@ -0,0 +1,138 @@ +#ifndef RKTWEBVIEW_QT_H +#define RKTWEBVIEW_QT_H + +#include "rktutils.h" +#include "rktwebview_types.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +class WebViewQt; +class WebviewWindow; +class Command; +class CommandEvent; + +class Rktwebview_qt : public QObject +{ + Q_OBJECT +private: + QApplication *_app; + rktwebview_t _current_handle; + + int _context_counter; + QHash _contexts; + QHash _views; + QHash _view_js_callbacks; + + QTimer _process_events; + + int _evt_loop_depth; + QTimer _evt_loop_timer; + + int _argc; + char **_argv; + +private: + void runJs(rktwebview_t wv, const char *js); + result_t doWindow(rktwebview_t w, int cmd, result_t on_error); + +private slots: + void stopEventloop(); + +private: + void postCommand(Command *cmd); + void handleCommandEvent(CommandEvent *e); + void processCommand(Command *cmd); + + result_t fileDlg(rktwebview_t w, + const QString &title, const QString &base, + const QString &filters, + QFileDialog::FileMode mode, QFileDialog::AcceptMode am, + QString evt_ok, QString evt_cancel); + + void msgBox(rktwebview_t w, + const QString &title, const QString &message, const QString &submessage, + rkt_messagetype_t type); + +public: + void execApp(); + QApplication *app(); + void deleteApp(); + void initApp(); + +protected: + void customEvent(QEvent *event); + +public: + void removeView(int id); + +public: + int nextHandle(); + +public: + rkt_wv_context_t newContext(const QString &boilerplate_js, bool has_pem, const QString &optional_server_cert_pem); + int rktWebViewCreate(rkt_wv_context_t context, rktwebview_t parent, event_cb_t js_evt_cb); + void rktWebViewClose(int wv); + void rktSetOUToken(rktwebview_t wv, const char *ou_token); + + result_t rktOpenDevtools(rktwebview_t wv); + result_t rktSetUrl(rktwebview_t wv, const char *url); + result_t rktSetHtml(rktwebview_t wv, const char *html); + result_t rktRunJs(rktwebview_t wv, const QString &js); + rkt_data_t *rktCallJs(rktwebview_t wv, const char *js); + + result_t rktMove(rktwebview_t wv, int x, int y); + result_t rktResize(rktwebview_t wv, int w, int h); + result_t rktHideWindow(rktwebview_t w); + result_t rktShowWindow(rktwebview_t w); + result_t rktPresentWindow(rktwebview_t w); + result_t rktMaximizeWindow(rktwebview_t w); + result_t rktMinimizeWindow(rktwebview_t w); + result_t rktShowNormalWindow(rktwebview_t w); + window_state_t rktWindowState(rktwebview_t w); + + void rktQuit(); + + result_t rktChooseDir(rktwebview_t w, const char *title, const char *base_dir); + result_t rktFileOpen(rktwebview_t w, const char *title, const char *base_dir, const char *permitted_exts); + result_t rktFileSave(rktwebview_t w, const char *title, const char *base_dir, const char *permitted_exts); + + result_t rktWindowSetTitle(rktwebview_t wv, const char *title); + result_t rktMessageBox(rktwebview_t w, const char *title, const char *message, const char *submessage, rkt_messagetype_t type); + + bool rktValid(rktwebview_t wv); + +public: + // Events for the backend + void pageLoaded(rktwebview_t w, bool ok); + void onPageLoad(rktwebview_t w); + +public: + int openWindows(); + +public: + void triggerEvent(rktwebview_t wv, const QString &msg); + void triggerEvent(rktwebview_t wv, const EventContainer &e); + +public: + void doEvents(); + +public: + void runCommandThread(); + +public: + Rktwebview_qt(int argc, char *argv[]); + ~Rktwebview_qt(); +}; + + + +#endif // RKTWEBVIEW_QT_H diff --git a/rktwebview_test.cpp b/rktwebview_test.cpp new file mode 100644 index 0000000..681f84d --- /dev/null +++ b/rktwebview_test.cpp @@ -0,0 +1,65 @@ +#include "rktwebview.h" +#include + +#ifdef _WIN32 +#include +#else +#include +#endif +#include "utils.h" + +int main(int argc, char *argv[]) +{ + std::string me = argv[0]; + + std::string loc = basedir(me); +#ifdef _WIN32 + std::string prg = loc + "\\rktwebview_prg.exe"; + SetDllDirectoryA("C:\\Qt\\6.10.2\\msvc2022_64\\bin"); +#else + std::string prg = loc + "/rktwebview_prg"; +#endif + +#ifdef _WIN32 + { + std::string e = std::string("RKT_WEBVIEW_PRG=") + prg; + _putenv(e.c_str()); + } +#else + setenv("RKT_WEBVIEW_PRG", prg.c_str(), true); + setenv("LD_LIBRARY_PATH", loc.c_str(), true); +#endif + + int context = rkt_webview_new_context("", nullptr); + + int wv = rkt_webview_create(context, 0); + + rkt_data_t *d = rkt_webview_info(); + rkt_webview_free_data(d); + + rkt_webview_move(wv, 100, 200); + rkt_webview_resize(wv, 800, 600); + rkt_webview_set_url(wv, "https://wikipedia.org"); + + d = rkt_webview_info(); + rkt_webview_free_data(d); + + while(rkt_webview_events_waiting() > 0) { + rkt_data_t *d = rkt_webview_get_event(); + rkt_webview_free_data(d); + } +#ifdef _WIN32 + Sleep(10); +#else + sleep(10); +#endif + d = rkt_webview_info(); + rkt_webview_free_data(d); + + rkt_webview_close(wv); + + d = rkt_webview_info(); + rkt_webview_free_data(d); + + rkt_webview_cleanup(); +} diff --git a/rktwebview_types.h b/rktwebview_types.h new file mode 100644 index 0000000..fcac80f --- /dev/null +++ b/rktwebview_types.h @@ -0,0 +1,108 @@ +#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 { + log_error = 1, + log_warning = 2, + log_info = 3, + log_debug = 4 +} rkt_webview_loglevel_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, + invalid_handle = 18 +} 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 struct { + int shm_usage; + int shm_free_depth; + int shm_free_size; + int shm_item_depth; + int shm_item_size; + double shm_item_usage_factor; + int open_windows; + int function_calls; + int events; + char *log_file; +} rkt_metrics_t; + +typedef enum { + version = 1, + event = 2, + js_result = 3, + metrics = 4 +} 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; + rkt_metrics_t metrics; + } data; +} rkt_data_t; + +typedef void (*event_cb_t)(rkt_data_t *); + +} + +#endif // RKTWEBVIEW_TYPES_H diff --git a/shm.cpp b/shm.cpp new file mode 100644 index 0000000..d4a6d5d --- /dev/null +++ b/shm.cpp @@ -0,0 +1,652 @@ +#include "shm.h" +#include "utils.h" +#include +#include + +#ifdef __linux +#define _strdup strdup +#endif + +typedef struct __shm_item__ { + + ShmPlace next; + ShmPlace prev; + int size; + int alloc_size; +} ShmItem; + + +class ShmApiBase +{ +protected: + char *_mem; + + int *_used; + ShmPlace *_slots; + ShmPlace *_first; + ShmPlace *_free_list; + + char *_mem_name; + char *_sem_name; + + int _in_critical; + size_t _size; + bool _owner; + +public: + virtual void enterCritical() = 0; + virtual void leaveCritical() = 0; + +public: + int slot(int s) { + return _slots[s]; + } + + void setSlot(int s, ShmPlace place) { + _slots[s] = place; + } + +public: + const char *name() { + return _mem_name; + } + +public: + template void ref(int place, T** p) { + if (place == SHM_NULL) { + *p = nullptr; + } else { + *p = reinterpret_cast(_mem + place); + } + } + + inline void *ref(int place) + { + if (place == SHM_NULL) { + return nullptr; + } + + return _mem + place; + } + +public: + void info(int &usage, int &free_depth, int &free_size, int &item_depth, int &item_size, double &usage_factor) { + enterCritical(); + + usage = *_used; + + auto count = [this](ShmPlace *l, int &depth, int &size, double &factor) { + depth = 0; + size = 0; + int alloc_size = 0; + ShmPlace p = *l; + while (p != SHM_NULL) { + //p -= sizeof(ShmItem); + ShmItem *i; + ref(p, &i); + size += i->size; + alloc_size += i->alloc_size; + depth += 1; + p = i->next; + } + if (size > 0) { + factor = (static_cast(alloc_size) / static_cast(size)); + } else { + factor = 1.0; + } + }; + + double x; + count(_free_list, free_depth, free_size, x); + count(_first, item_depth, item_size, usage_factor); + + leaveCritical(); + } + +public: + int alloc(size_t bytes) { + enterCritical(); + + ShmPlace p_i = *_free_list; + ShmItem *i; + ref(p_i, &i); + + while (i != nullptr && i->size < bytes) { + p_i = i->next; + ref(p_i, &i); + } + + int place; + if (i != nullptr) { + if (i->prev != SHM_NULL) { + ShmItem *prev_i; + ref(i->prev, &prev_i); + prev_i->next = i->next; + } + if (i->next != SHM_NULL) { + ShmItem *next_i; + ref(i->next, &next_i); + next_i->prev = i->prev; + } + if (i->prev == SHM_NULL) { + *_free_list = i->next; + } + i->next = *_first; + if (*_first != SHM_NULL) { + ShmItem *first_i; + ref(*_first, &first_i); + first_i->prev = p_i; + } + i->prev = SHM_NULL; + i->alloc_size = bytes; + + *_first = p_i; + place = p_i + sizeof(ShmItem); + } else { + ShmPlace p_i = *_used; + i = reinterpret_cast(reinterpret_cast(_mem) + p_i); + int u = static_cast(*_used + sizeof(ShmItem) + bytes); + + if (u >= _size) { + place = SHM_NULL; + } else { + *_used = u; + i->prev = SHM_NULL; + i->size = bytes; + i->alloc_size = bytes; + i->next = *_first; + if (*_first != SHM_NULL) { + ShmItem *first_i; + ref(*_first, &first_i); + first_i->prev = p_i; + } + + *_first = p_i; + place = p_i + sizeof(ShmItem); + } + } + + leaveCritical(); + return place; + } + + void free(ShmPlace place) { + if (place == SHM_NULL) { + return; + } + + enterCritical(); + + ShmPlace p_i = place - sizeof(ShmItem); + ShmItem *i; + ref(p_i, &i); + + if (i->prev != SHM_NULL) { + ShmItem *prev_i; + ref(i->prev, &prev_i); + prev_i->next = i->next; + } + if (i->next != SHM_NULL) { + ShmItem *next_i; + ref(i->next, &next_i); + next_i->prev = i->prev; + } + if (i->prev == SHM_NULL) { + *_first = i->next; + } + i->next = *_free_list; + if (*_free_list != SHM_NULL) { + ShmItem *fl; + ref(*_free_list, &fl); + fl->prev = p_i; + } + i->prev = SHM_NULL; + *_free_list = p_i; + + leaveCritical(); + } + +public: + ShmSem *makeSem(const char *name, bool owner) { + return new ShmSem(name, owner); + } + +public: + ShmApiBase(const char *name, size_t size, bool owner) { + char *buf = reinterpret_cast(malloc(strlen(name) + 50)); + sprintf(buf, "sem_%s", name); + _sem_name = _strdup(buf); + _mem_name = _strdup(name); + ::free(buf); + + _owner = owner; + + _size = size; + + _used = nullptr; + _slots = nullptr; + _first = nullptr; + _free_list = nullptr; + _mem = nullptr; + + _in_critical = 0; + } + + virtual ~ShmApiBase() + { + ::free(_sem_name); + ::free(_mem_name); + } +}; + +#ifdef __linux + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +class ShmApi : public ShmApiBase +{ +private: + int _shm_fd; + sem_t *_sem; + +private: + void cleanup() { + // We don't need to do anything here. + + } + +public: + void enterCritical() { + if (_in_critical > 0) { + _in_critical++; + } else { + sem_wait(_sem); + _in_critical = 1; + } + } + + void leaveCritical() { + if (_in_critical > 1) { + _in_critical--; + } else { + _in_critical = 0; + sem_post(_sem); + } + } + +public: + bool isValid() { + return true; + } + +public: + ShmApi(const char *name, size_t size, bool owner) : ShmApiBase(name, size, owner) + { + size_t slots_size = sizeof(ShmPlace) * SHM_MAX_SLOTS; + size += slots_size; + + if (_owner) { + _sem = sem_open(_sem_name, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR, 0); + _shm_fd = shm_open(name, O_CREAT | O_RDWR, 0600); + ftruncate(_shm_fd, size); + } else { + _sem = sem_open(_sem_name, O_RDWR, S_IRUSR | S_IWUSR, 0); + _shm_fd = shm_open(name, O_RDWR, 0600); + } + _mem = reinterpret_cast(mmap(nullptr, _size, PROT_READ|PROT_WRITE, MAP_SHARED, _shm_fd, 0)); + + _slots = reinterpret_cast (_mem + sizeof(int) + sizeof(ShmPlace) + sizeof(ShmPlace)); + _used = reinterpret_cast(_mem); + + _first = reinterpret_cast(_mem + sizeof(int)); + _free_list = reinterpret_cast(_mem + sizeof(int) + sizeof(ShmPlace)); + + if (_owner) { + *_first = SHM_NULL; + *_free_list = SHM_NULL; + *_used = sizeof(int) + sizeof(ShmItem *) + sizeof(ShmItem *) + slots_size; + sem_post(_sem); + } + + _in_critical = 0; + } + + ~ShmApi() { + cleanup(); + munmap(_mem, _size); + close(_shm_fd); + sem_close(_sem); + if (_owner) { + shm_unlink(_mem_name); + sem_unlink(_sem_name); + } + } +}; + +class ShmSemApi { + private: + sem_t *_sem; + char *_name; + bool _owner; + + public: + void post() { + sem_post(_sem); + } + + void wait() { + int r = sem_wait(_sem); + if (r != 0) { + ERROR2("sem_wait error: %d, %s\n", errno, strerror(errno)); + } + } + + bool trywait() { + int r = sem_trywait(reinterpret_cast(_sem)); + if (r != 0 && r != EAGAIN) { + ERROR2("sem_wait error: %d, %s\n", errno, strerror(errno)); + } + + return (r == 0); + } + + public: + ShmSemApi(const char *n, bool owner) { + _name = strdup(n); + + 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 = s; + _owner = owner; + } + + virtual ~ShmSemApi() { + sem_close(_sem); + if (_owner) { + sem_unlink(_name); + } + free(_name); + } +}; +#endif + +#ifdef _WIN32 +#include + +class ShmApi : public ShmApiBase +{ +private: + HANDLE _shm_handle; + HANDLE _sem_handle; + bool _valid; + +public: + ShmApi(const char *name, size_t size, bool owner) : ShmApiBase(name, size, owner) + { + size_t slots_size = sizeof(ShmPlace) * SHM_MAX_SLOTS; + size += slots_size; + + _valid = true; + if (_owner) { + _shm_handle = CreateFileMappingA(INVALID_HANDLE_VALUE, + NULL, + PAGE_READWRITE, + 0, + static_cast(size), // size must be smaller < 2GB + name); + + if (_shm_handle == NULL) { + ERROR2("Cannot create shared memory with name %s and size %lld\n", name, size); + _valid = false; + } + } else { + _shm_handle = OpenFileMappingA(FILE_MAP_ALL_ACCESS, + FALSE, + name); + if (_shm_handle == NULL) { + ERROR1("Cannot open shared memory with name %s\n", name); + _valid = false; + } + } + + if (_valid) { + _mem = static_cast(MapViewOfFile(_shm_handle, + FILE_MAP_ALL_ACCESS, + 0, + 0, + size)); + if (_mem == NULL) { + ERROR1("Cannot map shared memory, errorcode = %ld\n", GetLastError()); + _valid = false; + CloseHandle(_shm_handle); + } + } + + if (_valid) { + if (_owner) { + _sem_handle = CreateSemaphoreA(NULL, + 1, + 10000, + _sem_name + ); + } else { + _sem_handle = OpenSemaphoreA(SYNCHRONIZE|SEMAPHORE_MODIFY_STATE, FALSE, _sem_name); + } + if (_sem_handle == NULL) { + ERROR2("Cannot create or open semaphore with name '%s' (%ld)\n", _sem_name, GetLastError()); + UnmapViewOfFile(_mem); + CloseHandle(_shm_handle); + _valid = false; + } + } + + if (_valid) { + _slots = reinterpret_cast (_mem + sizeof(int) + sizeof(ShmPlace) + sizeof(ShmPlace)); + _used = reinterpret_cast(_mem); + + _first = reinterpret_cast(_mem + sizeof(int)); + _free_list = reinterpret_cast(_mem + sizeof(int) + sizeof(ShmPlace)); + + if (_owner) { + *_first = SHM_NULL; + *_free_list = SHM_NULL; + *_used = sizeof(int) + sizeof(ShmItem *) + sizeof(ShmItem *) + slots_size; + } + } + } + + ~ShmApi() + { + if (_valid) { + UnmapViewOfFile(_mem); + CloseHandle(_shm_handle); + CloseHandle(_sem_handle); + } + } + + bool isValid() { + return _valid; + } + + void enterCritical() + { + if (_in_critical > 0) { + _in_critical++; + } else { + WaitForSingleObject(_sem_handle, INFINITE); + _in_critical = 1; + } + } + + void leaveCritical() + { + if (_in_critical > 1) { + _in_critical--; + } else { + _in_critical = 0; + ReleaseSemaphore(_sem_handle, 1, NULL); + } + } + + ShmSem *sem(const char *name, bool owner) + { + return nullptr; + } +}; + + +class ShmSemApi { +private: + HANDLE _sem; + char *_name; + bool _owner; + +public: + void post() { + ReleaseSemaphore(_sem, 1, NULL); + } + + void wait() { + DWORD r = WaitForSingleObject(_sem, INFINITE); + if (r != WAIT_OBJECT_0) { + ERROR1("sem_wait error: %ld\n", r); + } + } + + bool trywait() { + DWORD r = WaitForSingleObject(_sem , 0); + if (r != WAIT_OBJECT_0 && r != WAIT_TIMEOUT) { + ERROR1("sem_wait error: %ld\n", r); + } + return r == WAIT_OBJECT_0; + } + +public: + ShmSemApi(const char *n, bool owner) { + _name = _strdup(n); + + if (owner) { + _sem = CreateSemaphoreA(NULL, + 0, + 10000, + _name); + } else { + _sem = OpenSemaphoreA(SYNCHRONIZE|SEMAPHORE_MODIFY_STATE, FALSE, _name); + } + + if (_sem == NULL) { + ERROR1("Cannot create or open semaphore with name '%s'\n", _name); + } + _owner = owner; + } + + virtual ~ShmSemApi() { + if (_sem != NULL) { + CloseHandle(_sem); + } + } +}; + +#endif + + + +ShmPlace Shm::alloc(size_t mem) +{ + return _shm_api->alloc(mem); +} + +void Shm::free(ShmPlace place) +{ + _shm_api->free(place); +} + +void Shm::unlock() +{ + _shm_api->leaveCritical(); +} + +ShmPlace Shm::slot(ShmSlot s) +{ + return _shm_api->slot(s); +} + +void Shm::setSlot(ShmSlot s, ShmPlace pl) +{ + _shm_api->setSlot(s, pl); +} + +ShmSem *Shm::sem(const char *name, bool owner) +{ + return _shm_api->makeSem(name, owner); +} + +bool Shm::isValid() +{ + return _shm_api->isValid(); +} + +const char *Shm::name() +{ + return _shm_api->name(); +} + +void *Shm::ref(int place) +{ + return _shm_api->ref(place); +} + +void Shm::info(int &usage, int &free_depth, int &free_size, int &item_depth, int &item_size, double &usage_factor) +{ + _shm_api->info(usage, free_depth, free_size, item_depth, item_size, usage_factor); +} + +void Shm::lock() +{ + _shm_api->enterCritical(); +} + +Shm::Shm(const char *name, size_t bytes, bool owner) +{ + _shm_api = new ShmApi(name, bytes, owner); +} + +Shm::~Shm() +{ + delete _shm_api; +} + + +void ShmSem::post() { + _api->post(); +} + +void ShmSem::wait() { + _api->wait(); +} + +bool ShmSem::trywait() { + return _api->trywait(); +} + +ShmSem::ShmSem(const char *n, bool owner) { + _api = new ShmSemApi(n, owner); +} + +ShmSem::~ShmSem() { + delete _api; +} diff --git a/shm.h b/shm.h new file mode 100644 index 0000000..32b9fc9 --- /dev/null +++ b/shm.h @@ -0,0 +1,68 @@ +#ifndef SHM_H +#define SHM_H + +#include + +class ShmApi; +class ShmSemApi; +typedef int ShmPlace; +typedef int ShmSlot; + +#define SHM_MAX_SLOTS 100 +#define SHM_NULL -1 + +class ShmSem { +private: + ShmSemApi *_api; + +public: + void post(); + void wait(); + bool trywait(); + +public: + ShmSem(const char *n, bool owner); + ~ShmSem(); +}; + +class Shm +{ +private: + ShmApi *_shm_api; + +public: + ShmPlace alloc(size_t mem); + void free(ShmPlace place); + void lock(); + void unlock(); + +public: + ShmPlace slot(ShmSlot s); + void setSlot(ShmSlot s, ShmPlace pl); + +public: + ShmSem *sem(const char *name, bool owner); + +public: + bool isValid(); + +public: + const char *name(); + +public: + void *ref(int place); + +public: + void info(int &usage, int &free_depth, int &free_size, int &item_depth, int &item_size, double &usage_factor); + +public: + Shm(const char *name, size_t bytes, bool owner); + ~Shm(); +}; + +template inline void ref(Shm *shm, int place, T **p) +{ + *p = reinterpret_cast(shm->ref(place)); +} + +#endif // SHM_H diff --git a/shmqueue.cpp b/shmqueue.cpp new file mode 100644 index 0000000..e0e255c --- /dev/null +++ b/shmqueue.cpp @@ -0,0 +1,118 @@ +#include "shmqueue.h" +#include + +ShmQueue::ShmQueue(Shm *shm, ShmSlot slot, bool owner) +{ + _shm = shm; + _owner = owner; + + if (owner) { + ShmPlace queue_place = shm->alloc(sizeof(ShmQueueStart)); + shm->setSlot(slot, queue_place); + ref(shm, queue_place, &_queue); + _queue->first = SHM_NULL; + _queue->last = SHM_NULL; + _queue->count = 0; + } else { + ShmPlace queue_start = shm->slot(slot); + ref(shm, queue_start, &_queue); + } + + char buf[1024]; + sprintf(buf, "sem_%s_%d", shm->name(), slot); + _queue_sem = shm->sem(buf, owner); +} + +ShmQueue::~ShmQueue() +{ + if (_owner) { + cleanup(); + } +} + +void ShmQueue::cleanup() +{ + // does currently nothing + delete _queue_sem; +} + +int ShmQueue::depth() +{ + _shm->lock(); + int d = _queue->count; + _shm->unlock(); + return d; +} + +void ShmQueue::enqueue(int cmd, const std::string &json_data) +{ + size_t jd_size = json_data.size(); + int str_place = _shm->alloc(jd_size + 1); + + char *s; + ref(_shm, str_place, &s); + memcpy(s, json_data.data(), jd_size); + s[jd_size] = '\0'; + + int item_place = _shm->alloc(sizeof(ShmQueueItem)); + ShmQueueItem *item; + ref(_shm, item_place, &item); + item->cmd = cmd; + item->data_place = str_place; + item->prev = _queue->last; + item->next = SHM_NULL; + if (_queue->last != SHM_NULL) { + ShmQueueItem *last_i; + ref(_shm, _queue->last, &last_i); + last_i->next = item_place; + } + + _shm->lock(); + _queue->last = item_place; + if (_queue->first == SHM_NULL) { + _queue->first = _queue->last; + } + _queue->count += 1; + _shm->unlock(); + _queue_sem->post(); +} + +void ShmQueue::enqueue(int cmd) +{ + std::string s; + enqueue(cmd, s); +} + +bool ShmQueue::dequeue(int &cmd, std::string &json_data, bool wait) +{ + if (wait) { + _queue_sem->wait(); + } else { + if (!_queue_sem->trywait()) { + return false; + } + } + _shm->lock(); + + ShmPlace item_place = _queue->first; + ShmQueueItem *item; + ref(_shm, item_place, &item); + _queue->first = item->next; + if (_queue->first == SHM_NULL) { + _queue->last = SHM_NULL; + } + _queue->count -= 1; + + _shm->unlock(); + + ShmPlace data_place = item->data_place; + char *data; + ref(_shm, data_place, &data); + json_data = data; + + _shm->free(data_place); + cmd = item->cmd; + + _shm->free(item_place); + return true; +} diff --git a/shmqueue.h b/shmqueue.h new file mode 100644 index 0000000..61176fe --- /dev/null +++ b/shmqueue.h @@ -0,0 +1,49 @@ +#ifndef SHMQUEUE_H +#define SHMQUEUE_H + +#include "shm.h" +#include + +typedef struct __ShmQueueItem__ +{ + int cmd; + ShmPlace data_place; + ShmPlace next; + ShmPlace prev; +} ShmQueueItem; + +typedef struct __ShmQueue_ +{ + ShmPlace first; + ShmPlace last; + int count; +} ShmQueueStart; + + +class ShmQueue +{ +private: + Shm *_shm; + +private: + ShmQueueStart *_queue; + ShmSem *_queue_sem; + bool _owner; + +private: + void cleanup(); + +public: + int depth(); + +public: + bool dequeue(int &cmd, std::string &json_data, bool wait = false); + void enqueue(int cmd, const std::string &json_data); + void enqueue(int cmd); + +public: + ShmQueue(Shm *shm, ShmSlot slot, bool owner); + ~ShmQueue(); +}; + +#endif // SHMQUEUE_H diff --git a/utils.cpp b/utils.cpp new file mode 100644 index 0000000..33b70e6 --- /dev/null +++ b/utils.cpp @@ -0,0 +1,51 @@ + +#include "utils.h" +#include + +typedef struct { + std::chrono::time_point start; +} timer; + + + +static int _log_level = LOG_INFO; +static timer *_timer = nullptr; + + + +int logLevel() +{ + return _log_level; +} + +void setLogLevel(int l) +{ + _log_level = l; +} + +const char *logIndicator(int l) +{ + switch(l) { + case LOG_ERROR: return "ERROR "; + case LOG_INFO: return "INFO "; + case LOG_DEBUG: return "DEBUG "; + case LOG_WARNING: return "WARNING"; + } + return "UNKNOWN"; +} + +void logElapsed() +{ + if (_timer == nullptr) { + _timer = new timer; + _timer->start = std::chrono::system_clock::now(); + } + + auto c = std::chrono::system_clock::now(); + auto duration = c - _timer->start; + auto milliseconds + = std::chrono::duration_cast( + duration) + .count(); + fprintf(stderr, "%8.3lf: ", milliseconds/ 1000.0); +} diff --git a/utils.h b/utils.h new file mode 100644 index 0000000..d7ccabc --- /dev/null +++ b/utils.h @@ -0,0 +1,80 @@ +#ifndef UTILS_H +#define UTILS_H + +#include +#include "rktwebview_types.h" + +inline std::string basedir(const std::string &path) +{ + int idx1 = static_cast(path.rfind("/")); + int idx2 = static_cast(path.rfind("\\")); + std::string r; + if (idx1 == std::string::npos && idx2 == std::string::npos) { + r = ""; + } else { + int idx; + if (idx1 == std::string::npos) { + idx = idx2; + } else if (idx2 == std::string::npos) { + idx = idx1; + } else if (idx1 < idx2) { + idx = idx2; + } else { + idx = idx1; + } + r = path.substr(0, idx); + } + return r; +} + +int logLevel(); +void setLogLevel(int l); +void logElapsed(); +const char *logIndicator(int l); + +#define LOG_ERROR 1 +#define LOG_WARNING 2 +#define LOG_INFO 3 +#define LOG_DEBUG 4 + +#define MKLOGSTMT(level, code) if (logLevel() >= level) { fprintf(stderr, "%s: ", logIndicator(level));logElapsed();code;fflush(stderr); } +#define MKL0(level, msg) MKLOGSTMT(level, fprintf(stderr, msg)) +#define MKL1(level, msg, a) MKLOGSTMT(level, fprintf(stderr, msg, a)) +#define MKL2(level, msg, a, b) MKLOGSTMT(level, fprintf(stderr, msg, a, b)) + +#define ERROR0(msg) MKL0(LOG_ERROR, msg) +#define WARN0(msg) MKL0(LOG_WARNING, msg) +#define INFO0(msg) MKL0(LOG_INFO, msg) +#define DEBUG0(msg) MKL0(LOG_DEBUG, msg) + +#define ERROR1(msg, a) MKL1(LOG_ERROR, msg, a) +#define WARN1(msg, a) MKL1(LOG_WARNING, msg, a) +#define INFO1(msg, a) MKL1(LOG_INFO, msg, a) +#define DEBUG1(msg, a) MKL1(LOG_DEBUG, msg, a) + +#define ERROR2(msg, a, b) MKL2(LOG_ERROR, msg, a, b) +#define WARN2(msg, a, b) MKL2(LOG_WARNING, msg, a, b) +#define INFO2(msg, a, b) MKL2(LOG_INFO, msg, a, b) +#define DEBUG2(msg, a, b) MKL2(LOG_DEBUG, msg, a, b) + +inline void do_free_data(rkt_data_t *d) +{ + if (d == nullptr) { return; } + + if (d->kind == version) { + free(d); + } else if (d->kind == metrics) { + free(d->data.metrics.log_file); + 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 { + ERROR1("UNEXPECTED: data kind %d cannot be freed\n", d->kind); + } +} + +#endif // UTILS_H diff --git a/webviewqt.cpp b/webviewqt.cpp new file mode 100644 index 0000000..d9bace7 --- /dev/null +++ b/webviewqt.cpp @@ -0,0 +1,24 @@ +#include "webviewqt.h" +#include "webviewwindow.h" +#include +#include + +WebViewQt::WebViewQt(int id, WebviewWindow *window) + : QWebEngineView(window->profile(), window) +{ + _id = id; + _window = window; +} + + +int WebViewQt::id() const +{ + return _id; +} + +WebviewWindow *WebViewQt::wvWin() +{ + return _window; +} + + diff --git a/webviewqt.h b/webviewqt.h new file mode 100644 index 0000000..6589585 --- /dev/null +++ b/webviewqt.h @@ -0,0 +1,28 @@ +#ifndef WEBVIEWQT_H +#define WEBVIEWQT_H + +#include +#include + +class WebviewWindow; + +class WebViewQt : public QWebEngineView +{ + Q_OBJECT +private: + int _id; + WebviewWindow *_window; + +public: + int id() const; + WebviewWindow *wvWin(); + +signals: + void pageLoaded(WebViewQt *, bool ok); + + +public: + WebViewQt(int id, WebviewWindow *window); +}; + +#endif // WEBVIEWQT_H diff --git a/webviewwindow.cpp b/webviewwindow.cpp new file mode 100644 index 0000000..8370487 --- /dev/null +++ b/webviewwindow.cpp @@ -0,0 +1,371 @@ +#include "webviewwindow.h" +#include "utils.h" + +#include "webviewqt.h" +#include "rktwebview_qt.h" +#include "rktutils.h" +#include "command.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* +static void displ(QSslCertificate & cert, QSslCertificate::SubjectInfo &a) { + QStringList issuerInfo = cert.issuerInfo(a); + int i, N; + printf("%d\n", a); + for(i = 0, N = issuerInfo.size(); i < N; i++) { + printf("%d[%d]: %s\n", a, i, issuerInfo[i].toUtf8().constData()); + } +}; + +static void displ1(QSslCertificate &cert, QList l) { + int i, N; + for(i = 0, N = l.size(); i < N; i++) { + displ(cert, l[i]); + } +}; +*/ + +WebviewWindow::WebviewWindow(QWebEngineProfile *profile, WebviewWindow *parent) + : QMainWindow{parent} +{ + static int profile_nr = 0; + + _view = nullptr; + _must_close = false; + + _devtools = nullptr; + _parent = parent; + + _window_created = false; + _moved = 0; + _resized = 0; + + _profile = profile; + _navigation_event_sent = false; + + if (parent != nullptr) { + setWindowModality(Qt::WindowModality::WindowModal); + setWindowFlag(Qt::WindowType::Dialog, true); + } + + connect(&_resize_timer, &QTimer::timeout, this, &WebviewWindow::triggerResize); + connect(&_move_timer, &QTimer::timeout, this, &WebviewWindow::triggerMove); +} + +void WebviewWindow::navigationRequested(QWebEngineNavigationRequest &req) +{ + QWebEngineNavigationRequest::NavigationType t = req.navigationType(); + if (t == QWebEngineNavigationRequest::NavigationType::TypedNavigation || + t == QWebEngineNavigationRequest::RedirectNavigation) { + _navigation_event_sent = false; + req.accept(); + } else { + EventContainer e("navigation-request"); + + QString u = req.url().toString(); + + e["url"] = u; + + QString type; + switch (req.navigationType()) { + case QWebEngineNavigationRequest::NavigationType::LinkClickedNavigation: type = "link-clicked"; + break; + case QWebEngineNavigationRequest::NavigationType::TypedNavigation: type = "typed"; + break; + case QWebEngineNavigationRequest::NavigationType::FormSubmittedNavigation: type = "form-submit"; + break; + case QWebEngineNavigationRequest::NavigationType::BackForwardNavigation: type = "back-or-forward"; + break; + case QWebEngineNavigationRequest::NavigationType::ReloadNavigation: type = "reload"; + break; + case QWebEngineNavigationRequest::NavigationType::RedirectNavigation: type = "redirect"; + break; + default: type = "other"; + break; + } + + e["type"] = type; + _container->triggerEvent(_view->id(), e); + + _navigation_event_sent = true; + + req.reject(); + } +} + +void WebviewWindow::handleCertificate(const QWebEngineCertificateError &certificateError) +{ + /* + QList certs = _view->page()->profile()->additionalTrustedCertificates(); + + auto dodisp = [](QList certs) + { + int i; + for(i = 0; i < certs.size(); i++) { + QSslCertificate cert = certs[i]; + QList attrs; + attrs.append(QSslCertificate::Organization); + attrs.append(QSslCertificate::OrganizationalUnitName); + attrs.append(QSslCertificate::CountryName); + attrs.append(QSslCertificate::CommonName); + displ1(cert, attrs); + } + }; + dodisp(certs); + dodisp(certificateError.certificateChain()); +*/ + + ERROR1("Certificate Error: %s\n", certificateError.description().toUtf8().constData()); + QList chain = certificateError.certificateChain(); + int i; + for(i = 0; i < chain.size(); i++) { + const QSslCertificate &cert = chain[i]; + if (cert.isSelfSigned()) { + QStringList l = cert.issuerInfo(QSslCertificate::OrganizationalUnitName); + if (!l.empty()) { + QString ou = l[0]; + if (ou != "" && ou == _ou_token) { + QWebEngineCertificateError &e = const_cast(certificateError); + e.acceptCertificate(); + } + } + } + } +} + +void WebviewWindow::processJsEvents() +{ + QWebEnginePage *p = _view->page(); + p->runJavaScript("if (window.rkt_get_events) { window.rkt_get_events(); } else { JSON.stringify([]); }", + [this](const QVariant &v) { + QString s = v.toString(); + QJsonParseError err; + QJsonDocument doc(QJsonDocument::fromJson(s.toUtf8(), &err)); + if (err.error == QJsonParseError::NoError) { + QJsonArray a = doc.array(); + int i, N; + for(i = 0, N = a.size(); i < N; i++) { + QJsonObject js_evt = a[i].toObject(); + + EventContainer e("js-evt"); + e["js-evt"] = js_evt; + + this->_container->triggerEvent(_view->id(), e); + } + } else { + + } + } + ); +} + +void WebviewWindow::closeEvent(QCloseEvent *evt) +{ + if (_must_close) { + EventContainer e("closed"); + _container->triggerEvent(_view->id(), e); + _container->removeView(_view->id()); + _view->deleteLater(); + this->deleteLater(); + if (_devtools != nullptr) { + _devtools->close(); + _devtools->deleteLater(); + } + } else { + evt->ignore(); + EventContainer e("can-close?"); + _container->triggerEvent(_view->id(), e); + } +} + +void WebviewWindow::closeView() +{ + _must_close = true; + close(); +} + +bool WebviewWindow::windowCreated() +{ + return _window_created; +} + +int WebviewWindow::moveCount() +{ + return _moved; +} + +int WebviewWindow::resizeCount() +{ + return _resized; +} + +void WebviewWindow::setOUToken(const QString &token) +{ + _ou_token = token; +} + +bool WebviewWindow::navigationEventSent() +{ + return _navigation_event_sent; +} + +void WebviewWindow::addView(WebViewQt *v, Rktwebview_qt *c) +{ + _container = c; + _view = v; + this->setCentralWidget(v); + + QWebEnginePage *page = _view->page(); + + connect(page, &QWebEnginePage::loadFinished, this, [this](bool ok) { + _container->pageLoaded(_view->id(), ok); + }); + connect(page, &QWebEnginePage::loadStarted, this, [this]() { + _container->onPageLoad(_view->id()); + }); + connect(page, &QWebEnginePage::navigationRequested, this, &WebviewWindow::navigationRequested); + + connect(page, &QWebEnginePage::certificateError, this, &WebviewWindow::handleCertificate); + + connect(page, &QWebEnginePage::windowCloseRequested, this, &WebviewWindow::closeView); + + connect(page, &QWebEnginePage::linkHovered, this, &WebviewWindow::linkHovered); + + connect(page, &QWebEnginePage::printRequestedByFrame, this, &WebviewWindow::evtRequested); +} + +WebViewQt *WebviewWindow::view() +{ + return _view; +} + +void WebviewWindow::runJs(const QString &js) +{ + QWebEnginePage *p = _view->page(); + p->runJavaScript(js); +} + +void WebviewWindow::callJs(const QString &js, Command *c) +{ + QWebEnginePage *p = _view->page(); + QString _js = QString("{\n") + + " let f = function() {\n" + + " " + js + "\n" + + " };\n" + + " try {\n" + + " let obj = { oke: true, result: f(), exn: false };\n" + + " obj;\n" + + " } catch(e) {\n" + + " let obj = { oke: false, result: false, exn: e.message };\n" + + " obj;\n" + + " }\n" + + "}"; + p->runJavaScript(_js, [c](const QVariant &v) { + c->result = v; + QJsonObject obj = v.toJsonObject(); + bool ok = obj["oke"].toBool(); + c->js_result_ok = ok; + c->result = QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::JsonFormat::Compact)); + c->done = true; + }); +} + +void WebviewWindow::openDevTools() +{ + _devtools = new QMainWindow(this); + QWebEngineView *devtools_view = new QWebEngineView(_devtools); + _devtools->setCentralWidget(devtools_view); + _devtools->resize(800, 600); + _devtools->show(); + _view->page()->setDevToolsPage(devtools_view->page()); + connect(_devtools, &WebviewWindow::destroyed, this, [this](QObject *) { + _devtools = nullptr; + }); +} + +void WebviewWindow::linkHovered(const QUrl &u) +{ + QString s = u.toString(); + EventContainer hover_event("link-hovered"); + hover_event["url"] = s; + _container->triggerEvent(_view->id(), hover_event); +} + +void WebviewWindow::evtRequested(QWebEngineFrame frame) +{ + if (frame.name() == "rkt-evt-frame") { + processJsEvents(); + } +} + + +void WebviewWindow::moveEvent(QMoveEvent *event) +{ + _x = event->pos().x(); + _y = event->pos().y(); + _move_timer.setSingleShot(true); + _move_timer.start(500); + _moved += 1; + //triggerMove(); +} + +void WebviewWindow::triggerMove() +{ + EventContainer xy("move"); + xy["x"] = _x; + xy["y"] = _y; + _container->triggerEvent(_view->id(), xy); +} + +QWebEngineProfile *WebviewWindow::profile() +{ + return _profile; +} + +void WebviewWindow::resizeEvent(QResizeEvent *event) +{ + _w = event->size().width(); + _h = event->size().height(); + _resize_timer.setSingleShot(true); + _resize_timer.start(500); + _resized += 1; + //triggerResize(); +} + +void WebviewWindow::triggerResize() +{ + EventContainer s("resize"); + s["w"] = _w; + s["h"] = _h; + _container->triggerEvent(_view->id(), s); +} + +void WebviewWindow::showEvent(QShowEvent *event) +{ + _window_created = true; + + EventContainer show("show"); + _container->triggerEvent(_view->id(), show); +} + +void WebviewWindow::hideEvent(QHideEvent *event) +{ + if (!_must_close) { + EventContainer hide("hide"); + _container->triggerEvent(_view->id(), hide); + } +} + + diff --git a/webviewwindow.h b/webviewwindow.h new file mode 100644 index 0000000..9416542 --- /dev/null +++ b/webviewwindow.h @@ -0,0 +1,94 @@ +#ifndef WEBVIEWWINDOW_H +#define WEBVIEWWINDOW_H + +#include +#include +#include +#include +#include +#include + +class WebViewQt; +class Rktwebview_qt; +class Command; + +class WebviewWindow : public QMainWindow +{ + Q_OBJECT +private: + Rktwebview_qt *_container; + WebViewQt *_view; + QMainWindow *_devtools; + WebviewWindow *_parent; + + QTimer _resize_timer; + QTimer _move_timer; + + int _x; + int _y; + int _w; + int _h; + bool _must_close; + + bool _window_created; + int _moved; + int _resized; + + QString _ou_token; + + QWebEngineProfile *_profile; + + bool _navigation_event_sent; + +private slots: + void handleCertificate(const QWebEngineCertificateError &certificateError); + void navigationRequested(QWebEngineNavigationRequest &req); + +public slots: + void processJsEvents(); + void closeView(); + +protected: + void closeEvent(QCloseEvent *evt); + +public: + bool windowCreated(); + int moveCount(); + int resizeCount(); + + void setOUToken(const QString &token); + + bool navigationEventSent(); + +public: + void addView(WebViewQt *v, Rktwebview_qt *c); + WebViewQt *view(); + +public: + void runJs(const QString &js); + void callJs(const QString &js, Command *c); + void openDevTools(); + +public: + explicit WebviewWindow(QWebEngineProfile *profile, WebviewWindow *parent); + +private slots: + void triggerResize(); + void triggerMove(); + void linkHovered(const QUrl &u); + void evtRequested(QWebEngineFrame frame); + +public: + QWebEngineProfile *profile(); + +signals: + + // QWidget interface +protected: + void moveEvent(QMoveEvent *event); + void resizeEvent(QResizeEvent *event); + void showEvent(QShowEvent *event); + void hideEvent(QHideEvent *event); +}; + +#endif // WEBVIEWWINDOW_H