diff --git a/.appveyor.yml b/.appveyor.yml
new file mode 100644
index 0000000..971e9fe
--- /dev/null
+++ b/.appveyor.yml
@@ -0,0 +1,55 @@
+version: 0.6.0.{build}
+
+branches:
+  only:
+    - master
+    - travis
+
+image:
+  - Visual Studio 2019
+
+environment:
+  matrix:
+  # Python versions:
+  # https://www.appveyor.com/docs/windows-images-software/#python
+
+  - TARGET: Python34
+    ARCH: 32
+
+  - TARGET: Python34-x64
+    ARCH: 64
+
+  - TARGET: Python35-x64
+    ARCH: 64
+
+  - TARGET: Python36-x64
+    ARCH: 64
+
+  - TARGET: Python37-x64
+    ARCH: 64
+
+  - TARGET: Python38-x64
+    ARCH: 64
+
+  - TARGET: Python39-x64
+    ARCH: 64
+
+  - TARGET: Python310-x64
+    ARCH: 64
+
+install:
+  - cmd: echo Testing sciter%ARCH% with %TARGET%.
+  - cmd: echo Current directory is %APPVEYOR_BUILD_FOLDER%
+  - cmd: set PATH=C:\%TARGET%;C:\projects\deps;%PATH%;
+  - python --version
+
+  - mkdir ..\deps
+  - curl -so "..\deps\sciter.dll" "https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.win/x%ARCH%/sciter.dll"
+
+build_script:
+  - cmd: cd
+  - python setup.py install
+
+test_script:
+  - cmd: cd
+  - python tests\test_value.py
diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml
new file mode 100644
index 0000000..06464c2
--- /dev/null
+++ b/.github/workflows/pylint.yml
@@ -0,0 +1,33 @@
+name: Lint
+
+on: [push]
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+
+    strategy:
+      matrix:
+        python-version: ["3.5", "3.6", "3.7", "3.8", "3.9", "3.10"]
+
+    steps:
+    - uses: actions/checkout@v2
+
+    - name: Setup Python ${{ matrix.python-version }}
+      uses: actions/setup-python@v2
+      with:
+        python-version: ${{ matrix.python-version }}
+
+    - name: Install dependencies
+      run: |
+        python -m pip install --upgrade pip
+        pip install flake8 
+        if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
+        
+    - name: Lint with flake8
+      run: |
+        echo stop the build if there are Python syntax errors or undefined names
+        flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
+        
+        echo exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
+        flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
diff --git a/.github/workflows/pysciter-js.yml b/.github/workflows/pysciter-js.yml
new file mode 100644
index 0000000..9c78a27
--- /dev/null
+++ b/.github/workflows/pysciter-js.yml
@@ -0,0 +1,71 @@
+# This workflow will install Python dependencies, run tests and lint
+# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
+name: Sciter.JS
+
+on:
+  push:
+    branches:
+    - master
+    - travis
+
+  pull_request:
+    branches:
+    - master
+
+jobs:
+  test:
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [macos-latest, ubuntu-latest, windows-latest]
+        python-version: ["3.8", "3.9", "3.10"]
+
+    steps:
+    - uses: actions/checkout@v2
+
+    - name: Setup Python ${{ matrix.python-version }}
+      uses: actions/setup-python@v2
+      with:
+        python-version: ${{ matrix.python-version }}
+
+    - name: Windows deps
+      if: runner.os == 'Windows'
+      # Windows: download sciter library
+      run: curl -sSLo "%SCITER_DEPS%/sciter.dll" "https://raw.githubusercontent.com/c-smile/sciter-js-sdk/main/bin/windows/x64/sciter.dll"
+      shell: cmd
+      env:
+        SCITER_DEPS: ${{ runner.workspace }}
+
+    - name: Linux deps
+      if: runner.os == 'Linux'
+      # Linux: download sciter library && install libgtk-3-dev
+      run: |
+        curl -so "$SCITER_DEPS/libsciter-gtk.so" "https://raw.githubusercontent.com/c-smile/sciter-js-sdk/main/bin/linux/x64/libsciter-gtk.so"
+        sudo apt-get update -y && sudo apt-get install libgtk-3-dev libgtk-3-0 -y
+      env:
+        SCITER_DEPS: ${{ runner.workspace }}
+
+    - name: macOS deps
+      if: runner.os == 'macOS'
+      # OSX: download sciter library
+      run: |
+        curl -so "$SCITER_DEPS/libsciter.dylib" "https://raw.githubusercontent.com/c-smile/sciter-js-sdk/main/bin/macosx/libsciter.dylib"
+      env:
+        SCITER_DEPS: ${{ runner.workspace }}
+
+    - name: Install dependencies
+      shell: bash
+      run: |
+        python -m pip install --upgrade pip
+        pip install pytest
+        if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
+
+    - name: Test
+      shell: bash
+      env:
+        SCITER_DEPS: ${{ runner.workspace }}
+      run: |
+        export PATH="$PATH:$SCITER_DEPS"
+        pip install .
+        python tests/test_value.py
diff --git a/.github/workflows/pysciter-tis.yml b/.github/workflows/pysciter-tis.yml
new file mode 100644
index 0000000..c153070
--- /dev/null
+++ b/.github/workflows/pysciter-tis.yml
@@ -0,0 +1,71 @@
+# This workflow will install Python dependencies, run tests and lint
+# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
+name: Sciter.TIS
+
+on:
+  push:
+    branches:
+    - master
+    - travis
+
+  pull_request:
+    branches:
+    - master
+
+jobs:
+  test:
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [macos-latest, ubuntu-latest, windows-latest]
+        python-version: ["3.8", "3.9", "3.10"]
+
+    steps:
+    - uses: actions/checkout@v2
+
+    - name: Setup Python ${{ matrix.python-version }}
+      uses: actions/setup-python@v2
+      with:
+        python-version: ${{ matrix.python-version }}
+
+    - name: Windows deps
+      if: runner.os == 'Windows'
+      # Windows: download sciter library
+      run: curl -sSLo "%SCITER_DEPS%/sciter.dll" "https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.win/x64/sciter.dll"
+      shell: cmd
+      env:
+        SCITER_DEPS: ${{ runner.workspace }}
+
+    - name: Linux deps
+      if: runner.os == 'Linux'
+      # Linux: download sciter library && install libgtk-3-dev
+      run: |
+        curl -so "$SCITER_DEPS/libsciter-gtk.so" "https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so"
+        sudo apt-get update -y && sudo apt-get install libgtk-3-dev libgtk-3-0 -y
+      env:
+        SCITER_DEPS: ${{ runner.workspace }}
+
+    - name: macOS deps
+      if: runner.os == 'macOS'
+      # OSX: download sciter library
+      run: |
+        curl -so "$SCITER_DEPS/libsciter.dylib" "https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.osx/libsciter.dylib"
+      env:
+        SCITER_DEPS: ${{ runner.workspace }}
+
+    - name: Install dependencies
+      shell: bash
+      run: |
+        python -m pip install --upgrade pip
+        pip install pytest
+        if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
+
+    - name: Test
+      shell: bash
+      env:
+        SCITER_DEPS: ${{ runner.workspace }}
+      run: |
+        export PATH="$PATH:$SCITER_DEPS"
+        pip install .
+        python tests/test_value.py
diff --git a/.gitignore b/.gitignore
index f72317c..a404037 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,5 +2,9 @@ env*/*
 **/__pycache__/*
 *.pyc
 *.egg-info/*
+/dist/*
+/build/*
 PySciter.pyproj
 PySciter.sln
+/.vs/
+/.vscode/
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..5e813b1
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,80 @@
+# Based on the "trust" template v0.1.2
+# https://github.com/japaric/trust/tree/v0.1.2
+
+# Ubuntu versions:
+# https://docs.travis-ci.com/user/reference/linux/
+dist: xenial
+sudo: false
+language: python
+
+notifications:
+  email: change
+
+matrix:
+  include:
+    # Python versions:
+    # https://docs.travis-ci.com/user/languages/python/#python-versions
+
+    - os: linux
+      python: "3.4"
+    - os: linux
+      python: "3.7"
+    - os: linux
+      python: "3.8"
+    - os: linux
+      python: "3.9"
+    - os: linux
+      python: "3.10"
+
+    - os: osx
+      # python comes with `osx_image`, see
+      # https://blog.travis-ci.com/2019-08-07-extensive-python-testing-on-travis-ci
+      language: shell
+      osx_image: xcode10.2
+
+    - os: osx
+      language: shell
+      osx_image: xcode12.2
+
+branches:
+  only:
+    - master
+    - travis
+
+addons:
+  apt:
+    sources:
+    - ubuntu-toolchain-r-test
+
+    packages:
+    - libgtk-3-dev
+    - libgtk-3-0
+    - libstdc++-6-pic
+
+
+before_install:
+  - set -e
+  - python3 --version
+
+install:
+  - export SDK_PATH=https://raw.githubusercontent.com/c-smile/sciter-sdk/master
+  - if [ "$TRAVIS_OS_NAME" = "linux" ]; then curl -so "$TRAVIS_BUILD_DIR/libsciter-gtk.so" $SDK_PATH/bin.lnx/x64/libsciter-gtk.so; fi
+  - if [ "$TRAVIS_OS_NAME" = "osx"   ]; then curl -so "$TRAVIS_BUILD_DIR/libsciter.dylib" $SDK_PATH/bin.osx/libsciter.dylib; fi
+
+  - pip3 install -U pip
+  - pip3 install -U pytest
+
+before_script:
+  - if [ "$TRAVIS_OS_NAME" = "osx" ]; then export DYLD_LIBRARY_PATH="$DYLD_LIBRARY_PATH:$TRAVIS_BUILD_DIR"; fi
+  - if [ "$TRAVIS_OS_NAME" = "osx" ]; then cp "$TRAVIS_BUILD_DIR/libsciter.dylib" "$TRAVIS_BUILD_DIR/liblibsciter.dylib"; fi
+
+  - if [ "$TRAVIS_OS_NAME" = "linux" ]; then export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$TRAVIS_BUILD_DIR"; fi
+
+  - export PATH="$PATH:$TRAVIS_BUILD_DIR"
+  - export LIBRARY_PATH="$LIBRARY_PATH:$TRAVIS_BUILD_DIR"
+
+script:
+  - python3 setup.py develop
+  - python3 tests/test_value.py
+
+after_script: set +e
diff --git a/README.md b/README.md
index eec91c8..ff0b814 100644
--- a/README.md
+++ b/README.md
@@ -1,29 +1,76 @@
-# Sciter bindings for Python
+# Python bindings for Sciter
 
-Sciter is an embeddable [multiplatform](http://sciter.com/sciter/crossplatform/) HTML/CSS/script engine designed to render modern desktop application UI. It's a **compact**, single dll/dylib/so file (4-8 mb), engine without any additional dependencies. 
+[![AppVeyor status](https://ci.appveyor.com/api/projects/status/rphv883klffw9em9/branch/master?svg=true)](https://ci.appveyor.com/project/pravic/pysciter)
+[![Travis Status](https://www.travis-ci.com/sciter-sdk/pysciter.svg?branch=master)](https://www.travis-ci.com/sciter-sdk/pysciter)
+[![PyPI](https://img.shields.io/pypi/v/pysciter.svg)](https://pypi.python.org/pypi/PySciter)
+[![License](https://img.shields.io/pypi/l/pysciter.svg)](https://pypi.python.org/pypi/PySciter)
+[![Join the forums at https://sciter.com/forums](https://img.shields.io/badge/forum-sciter.com-orange.svg)](https://sciter.com/forums)
 
-Sciter supports all standard elements defined in HTML5 specification [with some additions](http://sciter.com/developers/for-web-programmers/). CSS extended to better support Desktop UI development, e.g. flow and flex units, vertical and horizontal alignment, OS theming. 
+Check [this page](https://sciter.com/developers/sciter-sdk-bindings/) for other language bindings (Delphi / D / Go / .NET / Python / Rust).
 
-[Sciter SDK](http://sciter.com/download/) comes with demo "browser" with builtin DOM inspector, script debugger and documentation browser:
+----
 
-![Sciter tools](http://sciter.com/images/sciter-tools.png)
 
-Check <http://sciter.com> website and its [documentation resources](http://sciter.com/developers/) for engine principles, architecture and more.
+## Introduction
 
+Sciter is an embeddable [multiplatform](https://sciter.com/sciter/crossplatform/) HTML/CSS/script engine with GPU accelerated rendering designed to render modern desktop application UI. It's a compact, single dll/dylib/so file (4-8 mb) engine without any additional dependencies.
 
-## Getting started:
 
-_Right now, before PySciter will not be published on PYPI_
+## Screenshots
+
+Check the [screenshot gallery](https://github.com/oskca/sciter#sciter-desktop-ui-examples) of the desktop UI examples.
+
+
+## Description
+
+Physically Sciter is a mono library which contains:
+
+* [HTML and CSS](https://sciter.com/developers/for-web-programmers/) rendering engine based on the H-SMILE core used in [HTMLayout](https://terrainformatica.com/a-homepage-section/htmlayout/),
+* JavaScript in [Sciter.JS](https://sciter.com/sciter-js-is-the-mainstream-primary-version-of-sciter/),
+* JavaScript alike [Scripting engine](https://sciter.com/developers/sciter-docs/) – core of [TIScript](https://sciter.com/developers/for-web-programmers/tiscript-vs-javascript/) which by itself is based on [c-smile](https://c-smile.sourceforge.net/) engine,
+* Persistent [Database](https://sciter.com/docs/content/script/Storage.htm) (a.k.a. [JSON DB](https://terrainformatica.com/2006/10/what-the-hell-is-that-json-db/)) based on excellent DB products of [Konstantin Knizhnik](http://garret.ru/databases.html).
+* [Graphics](https://sciter.com/docs/content/sciter/Graphics.htm) module that uses native graphics primitives provided by supported platforms: Direct2D on Windows 7 and above, GDI+ on Windows XP, CoreGraphics on MacOS, Cairo on Linux/GTK. Yet there is an option to use built-in [Skia/OpenGL](https://skia.org/) backend on each platform.
+* Network communication module, it relies on platform HTTP client primitives and/or [Libcurl](https://curl.haxx.se/).
+
+
+Internally it contains the following modules:
+
+* **CSS** – CSS parser and the collection of parsed CSS rules, etc.
+* **HTML DOM** – HTML parser and DOM tree implementation.
+* **layout managers** – collection of various layout managers – text layout, default block layout, flex layouts. Support of positioned floating elements is also here. This module does the layout calculations heavy lifting. This module is also responsible for the rendering of layouts.
+* **input behaviors** – a collection of built-in behaviors – code behind "active" DOM elements: `<input>`, `<select>`, `<textarea>`, etc.
+* **script module** – source-to-bytecode compiler and virtual machine (VM) with compacting garbage collector (GC). This module also contains runtime implementation of standard classes and objects: Array, Object, Function and others.
+* **script DOM** – runtime classes that expose DOM and DOM view (a.k.a. window) to the script.
+* **graphics abstraction layer** – abstract graphics implementation that isolates the modules mentioned above from the particular platform details:
+    * Direct2D/DirectWrite graphics backend (Windows);
+    * GDI+ graphics backend (Windows);
+    * CoreGraphics backend (Mac OS X);
+    * Cairo backend (GTK on all Linux platforms);
+    * Skia/OpenGL backend (all platforms)
+* **core primitives** – set of common primitives: string, arrays, hash maps and so on.
+
+
+Sciter supports all standard elements defined in HTML5 specification [with some additions](https://sciter.com/developers/for-web-programmers/). CSS is extended to better support the Desktop UI development, e.g. flow and flex units, vertical and horizontal alignment, OS theming.
+
+[Sciter SDK](https://sciter.com/download/) comes with a demo "browser" with a builtin DOM inspector, script debugger and documentation viewer:
+
+![Sciter tools](https://sciter.com/wp-content/uploads/2015/10/dom-tree-in-inspector-640x438.png)
 
-1. Download [Sciter SDK](http://sciter.com/download/) and extract it somewhere.
-2. Add target platform binaries to PATH (`bin`, `bin.osx` or `bin.gtk`) and install Sciter shared library to your LIBRARY_PATH.
-3. Install pysciter: `python3 setup.py install`.
-4. Run minimal pysciter sample: `python3 examples/minimal.py`. Also you can run script from zip archive directly: `python3 ./archive.zip` :)
+Check <https://sciter.com> website and its [documentation resources](https://sciter.com/developers/) for engine principles, architecture and more.
+
+
+## Getting started:
+
+1. Download the [Sciter.TIS or Sciter.JS SDK](https://sciter.com/download/) and extract it somewhere.
+2. Add the corresponding target platform binaries to PATH (`bin.win/x64`, `bin.osx` or `bin.lnx/x64`) and install Sciter shared library to your [LIBRARY_PATH](https://github.com/sciter-sdk/go-sciter#getting-started).
+3. Install pysciter: `python3 setup.py install` or `pip install pysciter`.
+4. Run the minimal pysciter sample: `python3 examples/minimal.py`. Also you can run script from zip archive directly: `python3 ./archive.zip` :)
 
 
 ## Brief look:
 
 Minimal sciter app is extremely small:
+
 ```python
 import sciter
 
@@ -34,14 +81,14 @@ if __name__ == '__main__':
     frame.run_app()
 ```
 
-It looks similar like this:
+It looks similar to this:
 
-![Minimal pysciter sample](http://i.imgur.com/ojcM5JJ.png)
+![Minimal pysciter sample](https://i.imgur.com/ojcM5JJ.png)
 
 
 ### Interoperability
 
-In respect of [tiscript](http://www.codeproject.com/Articles/33662/TIScript-language-a-gentle-extension-of-JavaScript) functions calling:
+In respect of [tiscript](https://www.codeproject.com/Articles/33662/TIScript-language-a-gentle-extension-of-JavaScript) or JavaScript functions calling:
 ```python
 answer = self.call_function('script_function', "hello, python!", "and", ["other", 3, "arguments"])
 ```
@@ -54,44 +101,60 @@ def GetNativeApi(): # called from sciter.EventHandler.on_script_call
 
   def on_sub(a, b):
       raise Exception("sub(%d,%d) raised exception" % (a, b))
-      
+
   api = { 'add': on_add,  # plain function
           'sub': on_sub,  # raise exception at script
           'mul': lambda a,b: a * b }   # lambdas supported too
   return api
 ```
 
-So, we can access our api now:
+So, we can access our api now from TIScript:
 ```js
 // `view` represents window where script is runnung.
 // `stdout` stream is a standard output stream (shell or debugger console, for example)
 
 var api = view.GetNativeApi();
 
-// returned `api` object looks like {add: function(a,b) { return a + b; }}; 
-stdout.println("2 + 3 = " + api.add(2, 3)); 
+// returned `api` object looks like {add: function(a,b) { return a + b; }};
+stdout.println("2 + 3 = " + api.add(2, 3));
 ```
 
-_Check [pysciter/examples](https://github.com/pravic/pysciter/tree/master/examples) folder for more complex usage_.
-
-
-## What supported right now:
-
-* [x] [sciter::window](https://github.com/c-smile/sciter-sdk/blob/master/include/sciter-x-window.hpp) which brings together window creation, host and event handlers
-* [x] [sciter::host](https://github.com/c-smile/sciter-sdk/blob/master/include/sciter-x-host-callback.h) extensible implementation with transparent script calls from python code
-* [x] [sciter::event_handler](https://github.com/c-smile/sciter-sdk/blob/master/include/sciter-x-behavior.h) with basic event handling (attached, document_complete, on_script_call), additional handlers will come
-* [x] [sciter::dom](https://github.com/c-smile/sciter-sdk/blob/master/include/sciter-x-dom.hpp) for HTML DOM access and manipulation methods
-* [x] [sciter::value](https://github.com/c-smile/sciter-sdk/blob/master/include/value.hpp) pythonic wrapper with sciter::script_error and sciter::native_function support
-* [ ] [sciter::graphics](https://github.com/c-smile/sciter-sdk/blob/master/include/sciter-x-graphics.hpp) - platform independent graphics native interface (can be used in native behaviors)
-* [ ] [sciter::request](https://github.com/c-smile/sciter-sdk/blob/master/include/sciter-x-request.hpp) - resource request object, used for custom resource downloading and handling
-* [ ] [sciter::video](https://github.com/c-smile/sciter-sdk/blob/master/include/sciter-x-video-api.h) - custom video rendering
-
+or from JavaScript:
+```js
+// `Window.this` represents the window where this script is running.
+const api = Window.this.GetNativeApi();
+console.log("2 + 3", api.add(2, 3));
+````
+
+_Check [pysciter/examples](https://github.com/sciter-sdk/pysciter/tree/master/examples) folder for more complex usage_.
+
+
+## What is supported right now:
+
+* [x] [sciter::window](https://github.com/c-smile/sciter-sdk/blob/master/include/sciter-x-window.hpp) which brings together window creation, host and event handlers.
+* [x] [sciter::host](https://github.com/c-smile/sciter-sdk/blob/master/include/sciter-x-host-callback.h) extensible implementation with transparent script calls from python code.
+* [x] [sciter::event_handler](https://github.com/c-smile/sciter-sdk/blob/master/include/sciter-x-behavior.h) with basic event handling (attached, document_complete, on_script_call), additional handlers will come.
+* [x] [sciter::dom](https://github.com/c-smile/sciter-sdk/blob/master/include/sciter-x-dom.hpp) for HTML DOM access and manipulation methods.
+* [x] [sciter::value](https://github.com/c-smile/sciter-sdk/blob/master/include/value.hpp) pythonic wrapper with `sciter::script_error` and `sciter::native_function` support.
+* [ ] [sciter::behavior_factory](https://github.com/c-smile/sciter-sdk/blob/master/include/sciter-x-behavior.h) - global factory for native behaviors.
+* [ ] [sciter::graphics](https://github.com/c-smile/sciter-sdk/blob/master/include/sciter-x-graphics.hpp) - platform independent graphics native interface (can be used in native behaviors).
+* [ ] [sciter::request](https://github.com/c-smile/sciter-sdk/blob/master/include/sciter-x-request.hpp) - resource request object, used for custom resource downloading and handling.
+* [ ] [sciter::video](https://github.com/c-smile/sciter-sdk/blob/master/include/sciter-x-video-api.h) - custom video rendering.
+* [ ] [sciter::archive](https://github.com/c-smile/sciter-sdk/blob/master/include/sciter-x-host-callback.h) - Sciter's compressed archive produced by `sdk/bin/packfolder`.
+* [ ] [sciter::msg](https://github.com/c-smile/sciter-sdk/blob/master/include/sciter-x-msg) - backend-independent input event processing.
+* [ ] [sciter::om](https://github.com/c-smile/sciter-sdk/blob/master/include/sciter-om.h) - Sciter Object Model, extending Sciter by native code.
+* [ ] [NSE](https://sciter.com/include-library-name-native-extensions/) - native Sciter extensions.
 
 ### Platforms:
 
 * [x] Windows
 * [x] OSX
 * [x] Linux
+* [x] Raspberry Pi
+
+Python 3.x.
+
 
-Python 3.x (2.7 in near future).
+## License
 
+Bindings library licensed under [MIT license](https://opensource.org/licenses/MIT). Sciter Engine has the [own license terms](https://sciter.com/prices/) and [end used license agreement](https://github.com/c-smile/sciter-sdk/blob/master/license.htm) for SDK usage.
diff --git a/examples/download.py b/examples/download.py
index f4df5f8..2edc39b 100644
--- a/examples/download.py
+++ b/examples/download.py
@@ -24,7 +24,7 @@ def on_data_load(self, nm):
         pass
 
     def on_data_loaded(self, nm):
-        # called on every downloaded resource 
+        # called on every downloaded resource
         print("data loaded, uri:", nm.uri, nm.dataSize, "bytes")
         pass
 
@@ -46,7 +46,7 @@ def load(self, url):
 
         # install event handler to content frame to print data_arrived events
         self.handler = ContentEventHandler(element=content)
-        
+
         # make http request to download url and place result as inner of #content
         print("load content")
         content.request_html(url)
@@ -58,11 +58,10 @@ def load(self, url):
 
     print("Sciter version:", sciter.version(as_str=True))
 
-    if len(sys.argv) < 2:
-        sys.exit("at least one Sciter compatible page url is needed")
-    print(sys.argv[1])
+    url = "http://httpbin.org/html" if len(sys.argv) < 2 else sys.argv[1]
+    print(url)
 
     frame = Frame()
-    frame.load(sys.argv[1])
+    frame.load(url)
     frame.expand()
     frame.run_app(False)
diff --git a/examples/handlers.htm b/examples/handlers.htm
index 0c34e7e..6f40026 100644
--- a/examples/handlers.htm
+++ b/examples/handlers.htm
@@ -1,4 +1,4 @@
-<html resizeable>
+<html window-icon="icon.png" resizeable>
   <head>
     <title>Sciter Testing Page</title>
     <style type="text/css">
@@ -65,7 +65,7 @@ <H1>Sciter Testing Page</H1>
     </form>
   </div>
   <div id="output">
-    
+
   </div>
 </body>
 </html>
diff --git a/examples/handlers.py b/examples/handlers.py
index 95013cf..7df718a 100644
--- a/examples/handlers.py
+++ b/examples/handlers.py
@@ -3,19 +3,6 @@
 import sciter
 
 
-def scriptmethod(name=None):
-    """Style decorator for method, called from script."""
-    def decorator(func):
-        attr = True if name is None else name
-        func._from_sciter = attr
-        return func
-    if isinstance(name, str):
-        return decorator
-    func = name
-    name = None
-    return decorator(func)
-
-
 class RootEventHandler(sciter.EventHandler):
     def __init__(self, el, frame):
         super().__init__(element=el)
@@ -27,25 +14,25 @@ def on_event(self, source, target, code, phase, reason):
         #print("-> event:", code, phase, he)
         pass
 
-    @scriptmethod('mcall')
+    @sciter.script("mcall")
     def method_call(self, *args):
         #
         # `root.mcall()` (see handlers.htm) calls behavior method of the root dom element (native equivalent is `Element.call_method()`),
         #  so we need to attach a "behavior" to that element to catch and handle such calls.
-        # Also it can be handled at script by several ways: 
+        # Also it can be handled at script by several ways:
         # * `behavior` - Element subclassing with full control
         # * `aspect` - provides partial handling by attaching a single function to the dom element
         # *  manually attaching function to Element via code like `root.mcall = function(args..) {};`
-        # 
+        #
         print("->mcall args:", "\t".join(map(str, args)))
         # explicit null for example, in other cases you can return any python object like None or True
-        return sciter.Value.null() 
+        return sciter.Value.null()
 
     pass
 
 
 class Frame(sciter.Window):
-        
+
     def __init__(self):
         super().__init__(ismain=True, uni_theme=False, debug=False)
         self.set_dispatch_options(enable=True, require_attribute=False)
@@ -79,7 +66,7 @@ def fn(*args):
         rv['str'] = "a string"
         rv['f'] = fn
         return rv
-    
+
     @sciter.script
     def sumall(self, *args):
         sum = 0
@@ -108,7 +95,7 @@ def on_event(self, source, target, code, phase, reason):
         # TODO: following statement looks ugly.
         # Guess it wasn't a nice idea to split event mask to separate code and phase values
         # Or we may pack all event arguments to single object (dict) to eliminate such parameters bloat
-        # 
+        #
         if code == sciter.event.BEHAVIOR_EVENTS.BUTTON_CLICK and phase == sciter.event.PHASE_MASK.SINKING and he.test('#native'):
             print("native button clicked!")
             return True
diff --git a/examples/icon.png b/examples/icon.png
new file mode 100644
index 0000000..bc950bc
Binary files /dev/null and b/examples/icon.png differ
diff --git a/examples/minimal.htm b/examples/minimal.htm
index 632daf6..b2e5d8f 100644
--- a/examples/minimal.htm
+++ b/examples/minimal.htm
@@ -1,52 +1,136 @@
-<html>
+<html window-icon="icon.png">
   <head>
     <title>Minimalistic Sciter demo</title>
     <style>
-    
+
       html {
         background: radial-gradient(75% 75%, circle farthest-side, white, orange, rgb(0,0,204));
         color:#fff;
       }
-      
+
       html:rtl {
         mapping: left-to-right(background);
       }
-    
+
+      #files {
+        margin: 10dip;
+        padding: 4dip;
+      }
+
+      a { color: white; }
+      code { font-weight: bold; }
+
     </style>
     <script type="text/tiscript">
 
       view.caption = $(head > title).value;
-    
+
+      $(#kind).text = ".TIS";
       $(#machine).text = Sciter.machineName();
-      
+      $(#version).text = String.printf("%d.%d.%d.%d",
+        (Sciter.VERSION >> 16) & 0xffff, Sciter.VERSION & 0xffff,
+        (Sciter.REVISION >> 16) & 0xffff, Sciter.REVISION & 0xffff);
+
+      try {
+        // since 4.2.5.0
+        $(#revision).text = Sciter.BUILD.toString();
+      } catch(e) {
+        $(#revision).text = "N/A";
+      }
+
+      function append(item) {
+        $(#files).$append(<li>{item}</li>);
+      }
+
       var counter = 0;
-      
-      $(button#append).on("click", function(){
-        $(body).$append(<h1#test>{++counter }</h1>);
+      $(button#append).on("click", function() {
+        append(++counter);
       });
-      
-      $(button#open).on("click", function(){
-      
+
+      $(button#open).on("click", function() {
+
         var fn = view.selectFile(#open,
           "HTML Files (*.htm,*.html)|*.HTM;*.HTML|All Files (*.*)|*.*" , "html" );
 
         stdout.println("selected file: " + fn);
-        $(body).$append(<h1#test>{fn}</h1>);
+        if (fn) {
+          append(fn);
+        }
+      });
+
+      // Some tricks with hyperlinks:
+      self.on("click", "a[href]", function(evt, el) {
+        Sciter.launch(el.attributes["href"]);
+        return true;
+      });
+
+      for (var a in $$(a)) {
+        a.attributes["title"] = a.attributes["href"];
+      }
+
+    </script>
+    <script type="module">
+      import * as env from "@env";
+      import * as sciter from "@sciter";
+
+      Window.this.caption = document.$("head > title").value;
+
+      document.$("#kind").innerText = ".JS";
+      document.$("#machine").innerText = env.machineName();
+      document.$("#version").innerText = sciter.VERSION;
+      document.$("#revision").innerText = sciter.REVISION;
+
+      function append(item) {
+        document.$("#files").append(<li>{ item }</li>);
+      }
+
+      var counter = 0;
+      document.$("button#append").on("click", function() {
+        append(++counter);
       });
 
+      document.$("button#open").on("click", function() {
+
+        var fn = Window.this.selectFile({
+          mode: "open",
+          filter: "HTML Files (*.htm,*.html)|*.HTM;*.HTML|All Files (*.*)|*.*",
+          extension: "html"
+        });
+
+        console.log("selected file: " + fn);
+        if (fn) {
+          append(fn);
+        }
+      });
+
+      // Some tricks with hyperlinks:
+      document.on("click", "a[href]", function(evt, el) {
+        env.launch(el.attributes["href"]);
+        return true;
+      });
+
+      for (const a of document.$$("a")) {
+        a.attributes["title"] = a.attributes["href"];
+      }
+
     </script>
   </head>
 <body>
 
   <h1>Minimal Sciter Application</h1>
-  <p>Running on <em #machine /> machine</p>
-  
-  <button #append>Append</button>
-  <button #open>Open</button>
-  <select>
-    <option>First</option>
-    <option>Second</option>
-    <option>Third</option>
-  </select>
+  <p>Sciter<span id="kind" /> version <span id="version">x.x.x</span> rev <span id="revision">N</span>.</p>
+  <p>Running on <em id="machine" /> machine.</p>
+
+  <button id="append" title="Test a script call">Append</button>
+  <button id="open" title="Open a file selection dialog">Open a file</button>
+
+  <ul id="files">
+  </ul>
+
+  <section class=footer>
+    <p>You can inspect this window in the <a href="https://sciter.com/developers/development-tools/">Inspector tool</a>
+    from the <a href="https://sciter.com/download/">Sciter SDK</a>.</p>
+    <p>Run the Inspector first and then press <code>CTRL+SHIFT+I</code> in this window.</p>
+  </section>
 </body>
 </html>
diff --git a/examples/minimal.py b/examples/minimal.py
index 135d7de..0108760 100644
--- a/examples/minimal.py
+++ b/examples/minimal.py
@@ -3,6 +3,9 @@
 import sciter
 
 if __name__ == '__main__':
+    sciter.runtime_features(file_io=True, allow_sysinfo=True)
+
     frame = sciter.Window(ismain=True, uni_theme=True)
+    frame.minimal_menu()
     frame.load_file("examples/minimal.htm")
     frame.run_app()
diff --git a/examples/plain-win.py b/examples/plain-win.py
index e9183f6..0c67a95 100644
--- a/examples/plain-win.py
+++ b/examples/plain-win.py
@@ -1,8 +1,9 @@
 """Sciter sample for Win32 API."""
 
 # sciter import
+import sciter
 from sciter import sapi
-from sciter.scdef import *
+from sciter.capi.scdef import *
 
 # ctypes import
 from ctypes import *
@@ -27,7 +28,7 @@
 
 IDC_ARROW = 31514
 
-WNDPROCTYPE = WINFUNCTYPE(c_int, HWND, c_uint, WPARAM, LPARAM)
+WNDPROCTYPE = WINFUNCTYPE(LRESULT, HWND, c_uint, WPARAM, LPARAM)
 
 
 class WNDCLASSEX(Structure):
@@ -84,17 +85,23 @@ def on_wnd_message(hWnd, Msg, wParam, lParam):
     try:
         return windll.user32.DefWindowProcW(hWnd, Msg, wParam, lParam)
     except:
-        # etype, evalue, estack = sys.exc_info()
+        import traceback
+        etype, evalue, estack = sys.exc_info()
         print("WndProc exception: %X, 0x%04X, 0x%X, 0x%X" % (hWnd, Msg, wParam, lParam))
-        # traceback.print_exception(etype, evalue, estack)
+        traceback.print_exception(etype, evalue, estack)
     return 0
 
 
 def main():
     clsname = sapi.SciterClassName()
+    sciter.runtime_features(allow_sysinfo=True)
+
     title = u"Win32 Sciter"
     clsname = u"PySciter"
 
+    windll.user32.DefWindowProcW.argtypes = [HWND, c_uint, WPARAM, LPARAM]
+    windll.user32.DefWindowProcW.restype = LRESULT
+
     WndProc = WNDPROCTYPE(on_wnd_message)
     wndClass = WNDCLASSEX()
     wndClass.cbSize = sizeof(WNDCLASSEX)
diff --git a/examples/portable.zip b/examples/portable.zip
new file mode 100644
index 0000000..ea04398
Binary files /dev/null and b/examples/portable.zip differ
diff --git a/examples/pysciter.htm b/examples/pysciter.htm
index 091d19d..7587a8f 100644
--- a/examples/pysciter.htm
+++ b/examples/pysciter.htm
@@ -1,4 +1,4 @@
-<html>
+<html window-icon="icon.png">
   <head>
     <title>PySciter sample</title>
     <style>
@@ -7,31 +7,45 @@
         background: radial-gradient(75% 75%, circle farthest-side, white, orange, rgb(0,0,204));
         color:#fff;
       }
-      
+
       html:rtl {
         mapping: left-to-right(background);
       }
 
     </style>
     <script type="text/tiscript">
-    
+
       view.caption = $(head > title).value;
 
+      $(#kind).text = ".TIS";
       $(#machine).text = Sciter.machineName();
-      
+      $(#version).text = String.printf("%d.%d.%d.%d",
+        (Sciter.VERSION >> 16) & 0xffff, Sciter.VERSION & 0xffff,
+        (Sciter.REVISION >> 16) & 0xffff, Sciter.REVISION & 0xffff);
+
+      try {
+        // since 4.2.5.0
+        $(#revision).text = Sciter.BUILD.toString();
+      } catch(e) {
+        $(#revision).text = "N/A";
+      }
+
       var counter = 0;
-      
-      $(button#append).on("click", function(){
-        $(body).$append(<h1#test>{++counter }</h1>);
+
+      $(button#append).on("click", function() {
+        $(body).$append(<h1#test>{ ++counter }</h1>);
       });
-      
-      $(button#open).on("click", function(){
-      
+
+      $(button#open).on("click", function() {
+
         var fn = view.selectFile(#open,
           "HTML Files (*.htm,*.html)|*.HTM;*.HTML|All Files (*.*)|*.*" , "html" );
 
         stdout.println("selected file: " + fn);
-        $(body).$append(<h1#test>{fn}</h1>);
+
+        if (fn) {
+          $(body).$append(<h1#test>{fn}</h1>);
+        }
       });
 
       $(button#ti2py).on("click", function() {
@@ -43,6 +57,28 @@
         var answer = view.ScriptCallTest();
       })
 
+      /*
+      async function async_call(name, args..) {
+        return promise(function (resolve, reject) {
+          return view ??? #name(args, resolve, reject)
+        });
+      }
+      */
+
+      self.on("click", "button#async", async function() {
+        stdout.printf("calling AsyncTest\n");
+        var prom = promise(function (resolve, reject) {
+          stdout.printf("view.AsyncTest(1,2)\n");
+          return view.AsyncTest([1,2], resolve, reject);
+        });
+        try {
+          var r = await prom;
+          stdout.printf("async result %v\n", r);
+        } catch(e) {
+          stdout.printf("async exception %v\n", e);
+        }
+      })
+
       function hello(who) {
       	$(body).$append(<h1#test>python -&gt; script: {who}</h1>);
       	return "its working!";
@@ -62,18 +98,64 @@
         stdout.println(String.printf("2 * 3 = %d", view.api.mul(2, 3)));
         stdout.println(String.printf("2 - 3 = %d", view.api.sub(2, 3)));
       });
-    
+
+    </script>
+    <script type="module">
+      import * as env from "@env";
+      import * as sciter from "@sciter";
+
+      Window.this.caption = document.$("head > title").value;
+
+      document.$("#kind").innerText = ".JS";
+      document.$("#machine").innerText = env.machineName();
+      document.$("#version").innerText = sciter.VERSION;
+      document.$("#revision").innerText = sciter.REVISION;
+
+      document.on("click", "button#ti2py", function() {
+        var answer = Window.this.xcall("PythonCall", Window.this.caption);
+        document.$("body").append(<h1#test>script -&gt; python: {answer}</h1>);
+      })
+
+      async function async_call(name, ...args) {
+        return new Promise((resolve, reject) => {
+          return Window.this.xcall(name, args, resolve, reject);
+        });
+      }
+
+      document.on("click", "#async", async function() {
+        console.log("calling AsyncTask");
+        try {
+          let r = await async_call("AsyncTask", 1, 2);
+          console.log("async result", r);
+        } catch (e) {
+          console.log("async exception", e);
+        }
+      })
+
+      document.on("click", "#thread", function() {
+        console.log("calling AsyncThread");
+        try {
+          let r = Window.this.xcall("AsyncThread", 1, 2);
+          console.log("thread result", r);
+        } catch (e) {
+          console.log("thread exception", e);
+        }
+      })
+
     </script>
   </head>
 <body>
 
   <h1>Pythonic Sciter Application</h1>
+  <p>Sciter<span id="kind" /> version <span id="version">x.x.x</span> rev <span id="revision">N</span>.</p>
   <p>Running on <em #machine /> machine</p>
-  
+
   <button #append>Append</button>
   <button #open>Open</button>
   <button #ti2py>Call python</button>
   <button #py2ti>Call script</button>
+  <button #async>Async call</button>
+  <button #thread>Thread call</button>
   <select>
     <option>First</option>
     <option>Second</option>
diff --git a/examples/pysciter.py b/examples/pysciter.py
index bec68ec..9c2a30b 100644
--- a/examples/pysciter.py
+++ b/examples/pysciter.py
@@ -11,7 +11,7 @@ def __init__(self):
     def on_subscription(self, groups):
         # subscribing only for scripting calls and document events
         from sciter.event import EVENT_GROUPS
-        return EVENT_GROUPS.HANDLE_BEHAVIOR_EVENT | EVENT_GROUPS.HANDLE_SCRIPTING_METHOD_CALL
+        return EVENT_GROUPS.HANDLE_BEHAVIOR_EVENT | EVENT_GROUPS.HANDLE_SCRIPTING_METHOD_CALL | EVENT_GROUPS.HANDLE_METHOD_CALL
 
     def on_script_call(self, name, args):
         # script calls
@@ -19,10 +19,12 @@ def on_script_call(self, name, args):
         return self.dispatch(name, args)
 
 
-    ## @name Following functions called from scripts:
+    ## @name The following functions are called from scripts:
+    @sciter.script
     def PythonCall(self, arg):
         return "Pythonic window (%s)" % str(arg)
 
+    @sciter.script
     def GetNativeApi(self):
 
         def on_add(a, b):
@@ -30,13 +32,14 @@ def on_add(a, b):
 
         def on_sub(a, b):
             raise Exception("sub(%d,%d) raised exception" % (a, b))
-                
+
         api = { 'add': on_add,              # plain function
-                'sub': on_sub,              # raised exception will propagated to script 
+                'sub': on_sub,              # raised exception will propagated to script
                 'mul': lambda a,b: a * b,   # lambdas support
                 }
         return api
 
+    @sciter.script
     def ScriptCallTest(self):
         print("calling 'hello'")
         answer = self.call_function('hello', "hello, python")
@@ -75,14 +78,35 @@ def ScriptCallTest(self):
             print("answer: ", str(e))
         return True
 
+    @sciter.script(convert=True, threading=True)
+    def AsyncThread(self, a: int, b: int) -> int:
+        print("Handler.AsyncThread(%s, %s) for %s" % (repr(a), repr(b), repr(self)))
+        print("sleeping for 10 s")
+        import time
+        time.sleep(10)
+        print("resume")
+        return a + b
+
+    @sciter.script(convert=True, promise=True)
+    def AsyncTask(self, a: int, b: int) -> int:
+        print("Handler.AsyncTask(%s, %s) for %s" % (repr(a), repr(b), repr(self)))
+        print("sleeping for 10 s")
+        import time
+        time.sleep(10)
+        print("resume")
+        return a + b
+    pass
     ## @}
 
 # end
 
 
 if __name__ == '__main__':
+    sciter.runtime_features(allow_sysinfo=True, file_io=True)
+
     import os
     htm = os.path.join(os.path.dirname(__file__), 'pysciter.htm')
     frame = Frame()
+    frame.set_dispatch_options(raw_handlers=False)
     frame.load_file(htm)
     frame.run_app()
diff --git a/sciter/__init__.py b/sciter/__init__.py
index 68548f2..3901201 100644
--- a/sciter/__init__.py
+++ b/sciter/__init__.py
@@ -1,7 +1,23 @@
-"""Sciter bindings for Python."""
+"""Sciter bindings for Python.
+
+Read about library at github: https://github.com/sciter-sdk/pysciter.
+
+This component uses Sciter Engine,
+copyright Terra Informatica Software, Inc.
+(http://terrainformatica.com/).
+
+:license: MIT
+
+Bindings library licensed under [MIT license](https://opensource.org/licenses/MIT).
+Sciter Engine has the [own license terms](https://sciter.com/prices/)
+and [end used license agreement](https://github.com/c-smile/sciter-sdk/blob/master/license.htm)
+for SDK usage.
+
+"""
 
 from .capi.scapi import SciterAPI
 from .capi.sctypes import SCITER_WIN, SCITER_OSX, SCITER_LNX
+from .capi.scdef import SCITER_RT_OPTIONS
 
 from .value import value as Value
 from .window import Window
@@ -22,16 +38,90 @@ def version(as_str=False):
     return ".".join(map(str, ver)) if as_str else ver
 
 
-def script(name=None):
+def version_num():
+    """Return version of Sciter engine as 0x03030107 number."""
+    # However, `4.0.2.5257` can't be represented as a 32-bit number, we return `0x04_00_02_00` instead.
+    a, b, c, _ = version()
+    return (a << 24) | (b << 16) | (c << 8) | (0)
+
+def api_version():
+    """Return Sciter API version number, since 4.4.0.3."""
+    # `0x0000_0001` in regular builds
+    # `0x0001_0001` in windowless versions.
+    return api.version
+
+def is_windowless():
+    """Returns True for windowless builds."""
+    return api_version() >= 0x00010001
+
+def set_option(option, value):
+    """Set various sciter engine global options, see the SCITER_RT_OPTIONS."""
+    ok = api.SciterSetOption(None, option, value)
+    if not ok:
+        raise SciterError("Could not set option " + str(option) + "=" + str(value))
+    return True
+
+def runtime_features(file_io=True, socket_io=True, allow_eval=True, allow_sysinfo=True):
+    """Set runtime features that have been disabled by default since 4.2.5.0"""
+    from .capi.scdef import SCRIPT_RUNTIME_FEATURES
+    flags = 0
+    if file_io:
+        flags += SCRIPT_RUNTIME_FEATURES.ALLOW_FILE_IO
+    if socket_io:
+        flags += SCRIPT_RUNTIME_FEATURES.ALLOW_SOCKET_IO
+    if allow_eval:
+        flags += SCRIPT_RUNTIME_FEATURES.ALLOW_EVAL
+    if allow_sysinfo:
+        flags += SCRIPT_RUNTIME_FEATURES.ALLOW_SYSINFO
+    return set_option(SCITER_RT_OPTIONS.SCITER_SET_SCRIPT_RUNTIME_FEATURES, flags)
+
+def script(name=None, convert=True, safe=True, threading=False, promise=False):
     """Annotation decorator for the functions that called from script."""
     # @script def -> script(def)
     # @script('name') def -> script(name)(def)
+
+    # `convert`: Convert Sciter values to Python types
+    # `safe`: Pass exceptions to Sciter or ignore them
+    # `threading`: Call the handler in a separate thread (concurrent.futures.ThreadPoolExecutor)
+    # `promise`: Call the handler in a separate thread as a promise
+    if threading and promise:
+        raise SciterError("Don't mix `threading` and `promise` in @script")
+
     def decorator(func):
         attr = True if name is None else name
         func._from_sciter = attr
+        func._sciter_cfg = dict(name=name, convert=convert, safe=safe, threading=threading, promise=promise)
         return func
-    if isinstance(name, str):
+
+    # script('name')
+    if name is None or isinstance(name, str):
         return decorator
+
+    # script(def)
+    func = name
+    name = None
+    return decorator(func)
+
+def async_script(name=None, convert=True, safe=True):
+    """Annotation decorator for async functions that called from script."""
+    # @async_script def -> async_script(def)
+    # @async_script('name') def -> async_script(name)(def)
+
+    # `convert`: Convert Sciter values to Python types
+    # `safe`: Pass exceptions to Sciter or ignore them
+    # `promise`: Call the handler in a separate thread as a promise (always true)
+
+    def decorator(func):
+        attr = True if name is None else name
+        func._from_sciter = attr
+        func._sciter_cfg = dict(name=name, convert=convert, safe=safe, threading=False, promise=True)
+        return func
+
+    # async_script('name')
+    if name is None or isinstance(name, str):
+        return decorator
+
+    # async_script(def)
     func = name
     name = None
     return decorator(func)
diff --git a/sciter/capi/scapi.py b/sciter/capi/scapi.py
index 3a1e0aa..02138d4 100644
--- a/sciter/capi/scapi.py
+++ b/sciter/capi/scapi.py
@@ -9,6 +9,7 @@
 from sciter.capi.sctiscript import HVM, tiscript_native_interface
 from sciter.capi.scgraphics import LPSciterGraphicsAPI
 from sciter.capi.screquest import LPSciterRequestAPI
+from sciter.capi.scmsg import SCITER_X_MSG
 
 
 #
@@ -20,8 +21,11 @@
 SciterDataReadyAsync = SCFN(BOOL, HWINDOW, LPCWSTR, LPCBYTE, UINT, LPVOID)
 
 if SCITER_WIN:
-    SciterProc = SCFN(LRESULT, HWINDOW, UINT, WPARAM, LPARAM)
-    SciterProcND = SCFN(LRESULT, HWINDOW, UINT, WPARAM, LPARAM, POINTER(BOOL))
+	SciterProc = SCFN(LRESULT, HWINDOW, UINT, WPARAM, LPARAM)
+	SciterProcND = SCFN(LRESULT, HWINDOW, UINT, WPARAM, LPARAM, POINTER(BOOL))
+else:
+	SciterProc = c_void_p
+	SciterProcND = c_void_p
 
 SciterLoadFile = SCFN(BOOL, HWINDOW, LPCWSTR)
 SciterLoadHtml = SCFN(BOOL, HWINDOW, LPCBYTE, UINT, LPCWSTR)
@@ -38,25 +42,35 @@
 SciterUpdateWindow = SCFN(VOID, HWINDOW)
 
 if SCITER_WIN:
-    SciterTranslateMessage = SCFN(BOOL, POINTER(MSG))
+	SciterTranslateMessage = SCFN(BOOL, POINTER(MSG))
+else:
+	SciterTranslateMessage = c_void_p
 
 SciterSetOption = SCFN(BOOL, HWINDOW, UINT, UINT_PTR)
 SciterGetPPI = SCFN(VOID, HWINDOW, POINTER(UINT), POINTER(UINT))
 SciterGetViewExpando = SCFN(BOOL, HWINDOW, POINTER(SCITER_VALUE))
 
 if SCITER_WIN:
-    SciterRenderD2D = SCFN(BOOL, HWINDOW, POINTER(ID2D1RenderTarget))
-    SciterD2DFactory = SCFN(BOOL, POINTER(ID2D1Factory))
-    SciterDWFactory = SCFN(BOOL, POINTER(IDWriteFactory))
+	SciterRenderD2D = SCFN(BOOL, HWINDOW, POINTER(ID2D1RenderTarget))
+	SciterD2DFactory = SCFN(BOOL, POINTER(ID2D1Factory))
+	SciterDWFactory = SCFN(BOOL, POINTER(IDWriteFactory))
+else:
+	SciterRenderD2D = c_void_p
+	SciterD2DFactory = c_void_p
+	SciterDWFactory = c_void_p
 
 SciterGraphicsCaps = SCFN(BOOL, LPUINT)
 SciterSetHomeURL = SCFN(BOOL, HWINDOW, LPCWSTR)
 
 if SCITER_OSX:
-    SciterCreateNSView = SCFN(HWINDOW, LPRECT)
+	SciterCreateNSView = SCFN(HWINDOW, LPRECT)
+else:
+	SciterCreateNSView = c_void_p
 
 if SCITER_LNX:
-    SciterCreateWidget = SCFN(HWINDOW, LPRECT)
+	SciterCreateWidget = SCFN(HWINDOW, LPRECT)
+else:
+	SciterCreateWidget = c_void_p
 
 
 SciterCreateWindow = SCFN(HWINDOW, UINT, LPRECT, SciterWindowDelegate, LPVOID, HWINDOW)
@@ -98,11 +112,11 @@
 SciterSelectElementsW = SCFN(SCDOM_RESULT, HELEMENT, LPCWSTR, SciterElementCallback, LPVOID)
 SciterSelectParent = SCFN(SCDOM_RESULT, HELEMENT, LPCSTR, UINT, POINTER(HELEMENT))
 SciterSelectParentW = SCFN(SCDOM_RESULT, HELEMENT, LPCWSTR, UINT, POINTER(HELEMENT))
-SciterSetElementHtml = SCFN(SCDOM_RESULT, HELEMENT, POINTER(BYTE), UINT, UINT)
+SciterSetElementHtml = SCFN(SCDOM_RESULT, HELEMENT, LPCBYTE, UINT, UINT)
 SciterGetElementUID = SCFN(SCDOM_RESULT, HELEMENT, POINTER(UINT))
 SciterGetElementByUID = SCFN(SCDOM_RESULT, HWINDOW, UINT, POINTER(HELEMENT))
 SciterShowPopup = SCFN(SCDOM_RESULT, HELEMENT, HELEMENT, UINT)
-SciterShowPopupAt = SCFN(SCDOM_RESULT, HELEMENT, POINT, BOOL)
+SciterShowPopupAt = SCFN(SCDOM_RESULT, HELEMENT, POINT, UINT)
 SciterHidePopup = SCFN(SCDOM_RESULT, HELEMENT)
 SciterGetElementState = SCFN(SCDOM_RESULT, HELEMENT, POINTER(UINT))
 SciterSetElementState = SCFN(SCDOM_RESULT, HELEMENT, UINT, UINT, BOOL)
@@ -217,11 +231,18 @@
 GetSciterGraphicsAPI = SCFN(LPSciterGraphicsAPI)
 GetSciterRequestAPI = SCFN(LPSciterRequestAPI)
 
+SciterProcX = SCFN(BOOL, HWINDOW, POINTER(SCITER_X_MSG))
+
+
+# DirectX API
 if SCITER_WIN:
-    # DirectX API
-    SciterCreateOnDirectXWindow = SCFN(BOOL, HWINDOW, POINTER(IDXGISwapChain))
-    SciterRenderOnDirectXWindow = SCFN(BOOL, HWINDOW, HELEMENT, BOOL)
-    SciterRenderOnDirectXTexture = SCFN(BOOL, HWINDOW, HELEMENT, POINTER(IDXGISurface))
+	SciterCreateOnDirectXWindow = SCFN(BOOL, HWINDOW, POINTER(IDXGISwapChain))
+	SciterRenderOnDirectXWindow = SCFN(BOOL, HWINDOW, HELEMENT, BOOL)
+	SciterRenderOnDirectXTexture = SCFN(BOOL, HWINDOW, HELEMENT, POINTER(IDXGISurface))
+else:
+	SciterCreateOnDirectXWindow = c_void_p
+	SciterRenderOnDirectXWindow = c_void_p
+	SciterRenderOnDirectXTexture = c_void_p
 
 
 class ISciterAPI(Structure):
@@ -436,10 +457,41 @@ class ISciterAPI(Structure):
         "SciterCreateOnDirectXWindow",
         "SciterRenderOnDirectXWindow",
         "SciterRenderOnDirectXTexture",
+
+        # since 4.0.0.0
+        "SciterProcX",
+
+        # since 4.4.2.14
+        "SciterAtomValue",
+        "SciterAtomNameCB",
+
+        # since 4.4.2.16
+        "SciterSetGlobalAsset",
+
+        # since 4.4.4.7
+        "SciterGetElementAsset",
+
+        # since 4.4.4.6 (yet disabled)
+        "SciterSetVariable",
+        "SciterGetVariable",
+
+        # since 4.4.5.4
+        "SciterElementUnwrap",
+        "SciterElementWrap",
+        "SciterNodeUnwrap",
+        "SciterNodeWrap",
+
         ]
     # END OF ISciterAPI.
 
     def _make_fields(names):
+        #
+        # Patch the ISciterAPI structure.
+        #
+        # This works by conditionally defining the function types first in the global scope,
+        # then defining *all* the possible API names in a single array,
+        # and filtering the array eliminating those that don't exist in the global context.
+        #
         context = globals()
         fields = [(name, context[name]) for name in names if name in context]
         fields.insert(0, ("version", UINT))
@@ -449,7 +501,7 @@ def _make_fields(names):
 # end
 
 SCITER_LOAD_ERROR = """%s%s was not found in PATH.
-  Please verify that Sciter SDK is installed and its binaries (SDK/bin, bin.osx or bin.gtk) available at the path.""" % (SCITER_DLL_NAME, SCITER_DLL_EXT)
+  Please verify that Sciter SDK is installed and its binaries (SDK/bin, bin.osx or bin.gtk) are available in the path.""" % (SCITER_DLL_NAME, SCITER_DLL_EXT)
 
 
 def SciterAPI():
@@ -457,25 +509,78 @@ def SciterAPI():
     if hasattr(SciterAPI, "_api"):
         return SciterAPI._api
 
+    import sys
+    import ctypes
+
     scdll = None
+    errors = []
+
     if SCITER_WIN:
+        # load 4.x version by default
+        # note: somehow `ctypes.WinDLL(dllname)` does not work in Python 3.8 anymore;
+        # now we use the full path if found.
+        import ctypes.util
         try:
-            scdll = WinDLL(SCITER_DLL_NAME)
-        except OSError:
-            pass
+            dll = ctypes.util.find_library(SCITER_DLL_NAME)
+            if not dll:
+                dll = SCITER_DLL_NAME
+            scdll = ctypes.WinDLL(dll)
+        except OSError as e:
+            errors.append("'%s': %s" % (dll, str(e)))
+
+            # try to find 3.x version
+            try:
+                dllname = "sciter64.dll" if sys.maxsize > 2**32 else "sciter32.dll"
+                dll = ctypes.util.find_library(dllname)
+                if not dll:
+                    dll = dllname
+                scdll = ctypes.WinDLL(dll)
+            except OSError as e:
+                errors.append("'%s': %s" % (dll, str(e)))
 
     else:
         # same behavior for OSX & Linux
-        import ctypes.util
-        sclib = ctypes.util.find_library(SCITER_DLL_NAME)
-        if sclib:
+        def find_sciter(dllname):
+            import ctypes.util
+            dllfile = dllname + SCITER_DLL_EXT
+            dllpath = ctypes.util.find_library(dllname)
+            if not dllpath:
+                # try $LD_LIBRARY_PATH
+                def find_in_path(dllname, envname):
+                    import os
+                    if envname in os.environ:
+                        for directory in os.environ[envname].split(os.pathsep):
+                            fname = os.path.join(directory, dllname)
+                            if os.path.isfile(fname):
+                                return fname
+                    return None
+
+                dllpath = find_in_path(dllfile, 'DYLD_LIBRARY_PATH' if SCITER_OSX else 'LD_LIBRARY_PATH')
+
+                # try $PATH
+                if not dllpath:
+                    dllpath = find_in_path(dllfile, 'PATH')
+
+            if not dllpath:
+                # last chance: try to load .so
+                dllpath = dllfile
             try:
-                scdll = ctypes.CDLL(sclib, ctypes.RTLD_LOCAL)
-            except OSError:
-                pass
+                RTLD_LAZY = 1
+                return ctypes.CDLL(dllpath, ctypes.RTLD_LOCAL | RTLD_LAZY)
+            except OSError as e:
+                errors.append(str(e))
+                return None
+
+        # try default name (4.1.4+)
+        scdll = find_sciter(SCITER_DLL_NAME)
+
+        if SCITER_LNX and scdll is None:
+            # try the old name
+            import sys
+            scdll = find_sciter("libsciter-gtk-64" if sys.maxsize > 2**32 else "libsciter-gtk-32")
 
     if not scdll:
-        raise ImportError(SCITER_LOAD_ERROR)
+        raise ImportError(SCITER_LOAD_ERROR + "\n" + "\n".join(errors))
 
     scdll.SciterAPI.restype = POINTER(ISciterAPI)
     SciterAPI._api = scdll.SciterAPI().contents
diff --git a/sciter/capi/scbehavior.py b/sciter/capi/scbehavior.py
index 61cae29..bbe97ae 100644
--- a/sciter/capi/scbehavior.py
+++ b/sciter/capi/scbehavior.py
@@ -28,6 +28,7 @@ class EVENT_GROUPS(enum.IntEnum):
     HANDLE_TISCRIPT_METHOD_CALL = 0x0800   # behavior specific methods using direct tiscript::value's */
     HANDLE_EXCHANGE = 0x1000  # system drag-n-drop */
     HANDLE_GESTURE = 0x2000  # touch input events */
+    HANDLE_SOM = 0x8000  # som_asset_t request */
     HANDLE_ALL = 0xFFFF  # all of them */
     SUBSCRIPTIONS_REQUEST = 0xFFFFFFFF  # special value for getting subscription flags */
 
@@ -99,7 +100,7 @@ class MOUSE_PARAMS(ctypes.Structure):
         ("cursor_type", UINT),   # CURSOR_TYPE to set, see CURSOR_TYPE
         ("is_on_icon", BOOL),    # mouse is over icon (foreground-image, foreground-repeat:no-repeat)
         ("dragging", HELEMENT),      # element that is being dragged over, this field is not NULL if (cmd & DRAGGING) != 0
-        ("dragging_mode", UINT), # see DRAGGING_TYPE. 
+        ("dragging_mode", UINT), # see DRAGGING_TYPE.
     ]
 
 
@@ -164,15 +165,26 @@ class SCROLL_EVENTS(enum.IntEnum):
     SCROLL_POS,
     SCROLL_SLIDER_RELEASED,
     SCROLL_CORNER_PRESSED,
-    SCROLL_CORNER_RELEASED) = range(10)
+    SCROLL_CORNER_RELEASED,
+    SCROLL_SLIDER_PRESSED) = range(11)
+
+
+class SCROLL_SOURCE(enum.IntEnum):
+    (SCROLL_SOURCE_UNKNOWN,
+    SCROLL_SOURCE_KEYBOARD,     # `SCROLL_PARAMS::reason` contains a key code
+    SCROLL_SOURCE_SCROLLBAR,    # `SCROLL_PARAMS::reason` contains a `SCROLLBAR_PART` enum
+    SCROLL_SOURCE_ANIMATOR,
+    ) = range(4)
 
 
 class SCROLL_PARAMS(ctypes.Structure):
     _fields_ = [
-        ("cmd", UINT),           # SCROLL_EVENTS
-        ("target", HELEMENT),        # target element
+        ("cmd", UINT),          # SCROLL_EVENTS
+        ("target", HELEMENT),   # target element
         ("pos", INT),           # scroll position if SCROLL_POS
-        ("vertical", BOOL),      # true if from vertical scrollbar
+        ("vertical", BOOL),     # true if from vertical scrollbar
+        ("source", UINT),       # SCROLL_SOURCE
+        ("reason", UINT),       # SCROLLBAR_PART or key code, see SCROLL_SOURCE
     ]
 
 
@@ -223,6 +235,7 @@ class DRAW_EVENTS(enum.IntEnum):
     DRAW_BACKGROUND = 0
     DRAW_CONTENT = 1
     DRAW_FOREGROUND = 2
+    DRAW_OUTLINE = 3
 
 
 class DRAW_PARAMS(ctypes.Structure):
@@ -297,10 +310,10 @@ class BEHAVIOR_EVENTS(enum.IntEnum):
                                  # is sent for example by behavior:richtext when caret position/selection has changed.
 
     FORM_SUBMIT = 0x96             # behavior:form detected submission event. BEHAVIOR_EVENT_PARAMS::data field contains data to be posted.
-                                 # BEHAVIOR_EVENT_PARAMS::data is of type T_MAP in this case key/value pairs of data that is about 
+                                 # BEHAVIOR_EVENT_PARAMS::data is of type T_MAP in this case key/value pairs of data that is about
                                  # to be submitted. You can modify the data or discard submission by returning true from the handler.
     FORM_RESET = 0x97              # behavior:form detected reset event (from button type=reset). BEHAVIOR_EVENT_PARAMS::data field contains data to be reset.
-                                 # BEHAVIOR_EVENT_PARAMS::data is of type T_MAP in this case key/value pairs of data that is about 
+                                 # BEHAVIOR_EVENT_PARAMS::data is of type T_MAP in this case key/value pairs of data that is about
                                  # to be rest. You can modify the data or discard reset by returning true from the handler.
 
     DOCUMENT_COMPLETE = 0x98       # document in behavior:frame or root document is complete.
@@ -319,7 +332,8 @@ class BEHAVIOR_EVENTS(enum.IntEnum):
     DOCUMENT_CREATED  = 0xC0       # document created, script namespace initialized. target -> the document
     DOCUMENT_CLOSE_REQUEST = 0xC1  # document is about to be closed, to cancel closing do: evt.data = sciter::value("cancel");
     DOCUMENT_CLOSE    = 0xC2       # last notification before document removal from the DOM
-    DOCUMENT_READY    = 0xC3       # document has got DOM structure, styles and behaviors of DOM elements. Script loading run is complete at this moment. 
+    DOCUMENT_READY    = 0xC3       # document has got DOM structure, styles and behaviors of DOM elements. Script loading run is complete at this moment.
+    DOCUMENT_PARSED   = 0xC4       # document just finished parsing - has got DOM structure. This event is generated before the `DOCUMENT_READY`. Since 4.0.3.
 
     VIDEO_INITIALIZED = 0xD1       # <video> "ready" notification
     VIDEO_STARTED     = 0xD2       # <video> playback started notification
@@ -338,6 +352,8 @@ class BEHAVIOR_EVENTS(enum.IntEnum):
     PAGINATION_PAGE    = 0xE1      # behavior:pager paginated page no, reason -> page no
     PAGINATION_ENDS    = 0xE2      # behavior:pager end pagination, reason -> total pages
 
+    CUSTOM             = 0xF0      # event with custom name, since 4.2.8.0
+
     FIRST_APPLICATION_EVENT_CODE = 0x100
     # all custom event codes shall be greater
     # than this number. All codes below this will be used
@@ -347,10 +363,11 @@ class BEHAVIOR_EVENTS(enum.IntEnum):
     # HTMLayoutSend/PostEvent API.
 
 
-class EVENT_REASON(enum.IntEnum):
+class CLICK_REASON(enum.IntEnum):
     BY_MOUSE_CLICK = 0
     BY_KEY_CLICK = 1
     SYNTHESIZED = 2  # synthesized, programmatically generated.
+    BY_MOUSE_ON_ICON = 3
 
 
 class EDIT_CHANGED_REASON(enum.IntEnum):
@@ -358,6 +375,7 @@ class EDIT_CHANGED_REASON(enum.IntEnum):
     BY_INS_CHARS = 1  # character range insertion, clipboard
     BY_DEL_CHAR = 2   # single char deletion
     BY_DEL_CHARS = 3  # character range deletion (selection)
+    BY_UNDO_REDO = 4  # undo/redo
 
 
 class BEHAVIOR_EVENT_PARAMS(ctypes.Structure):
@@ -366,10 +384,11 @@ class BEHAVIOR_EVENT_PARAMS(ctypes.Structure):
         ("heTarget", HELEMENT),     # target element handler, in MENU_ITEM_CLICK this is owner element that caused this menu - e.g. context menu owner
                                     # In scripting this field named as Event.owner
         ("he", HELEMENT),           # source element e.g. in SELECTION_CHANGED it is new selected <option>, in MENU_ITEM_CLICK it is menu item (LI) element
-        ("reason", UINT_PTR),       # EVENT_REASON or EDIT_CHANGED_REASON - UI action causing change.
+        ("reason", UINT_PTR),       # CLICK_REASON or EDIT_CHANGED_REASON - UI action causing change.
                                     # In case of custom event notifications this may be any
                                     # application specific value.
         ("data", SCITER_VALUE),     # auxiliary data accompanied with the event. E.g. FORM_SUBMIT event is using this field to pass collection of values.
+        ("name", LPCWSTR),          # name of a custom event (when `cmd` is `CUSTOM`), since 4.2.8.0
     ]
 
 
@@ -398,8 +417,8 @@ class BEHAVIOR_METHOD_IDENTIFIERS(enum.IntEnum):
     TEXT_EDIT_CHAR_POS_AT_XY = 11       # p - TEXT_EDIT_CHAR_POS_AT_XY_PARAMS
 
     IS_EMPTY      = 0xFC        # p - IS_EMPTY_PARAMS  # set VALUE_PARAMS::is_empty (false/true) reflects :empty state of the element.
-    GET_VALUE     = 0xFD        # p - VALUE_PARAMS 
-    SET_VALUE     = 0xFE        # p - VALUE_PARAMS 
+    GET_VALUE     = 0xFD        # p - VALUE_PARAMS
+    SET_VALUE     = 0xFE        # p - VALUE_PARAMS
 
     FIRST_APPLICATION_METHOD_ID = 0x100
 
diff --git a/sciter/capi/scdef.py b/sciter/capi/scdef.py
index 68f61de..8e7678f 100644
--- a/sciter/capi/scdef.py
+++ b/sciter/capi/scdef.py
@@ -16,7 +16,7 @@ class LOAD_RESULT(enum.IntEnum):
     LOAD_DISCARD = 1  # discard request completely
     LOAD_DELAYED = 2  # data will be delivered later by the host application.
 
-    LOAD_MYSELF = 3   # Use sciter-x-request.h[pp] API functions with SCN_LOAD_DATA::requestId handle .
+    LOAD_MYSELF = 3   # Use sciter-x-request.h[pp] API functions with SCN_LOAD_DATA::requestId handle.
 
 
 class SciterNotification(enum.IntEnum):
@@ -27,26 +27,30 @@ class SciterNotification(enum.IntEnum):
     SC_ENGINE_DESTROYED = 0x05
     SC_POSTED_NOTIFICATION = 0x06
     SC_GRAPHICS_CRITICAL_FAILURE = 0x07
+    SC_KEYBOARD_REQUEST = 0x08
+    SC_INVALIDATE_RECT = 0x09
 
 
 class SCITER_RT_OPTIONS(enum.IntEnum):
-    SCITER_SMOOTH_SCROLL = 1       # value:TRUE - enable, value:FALSE - disable, enabled by default
-    SCITER_CONNECTION_TIMEOUT = 2  # value: milliseconds, connection timeout of http client
-    SCITER_HTTPS_ERROR = 3         # value: 0 - drop connection, 1 - use builtin dialog, 2 - accept connection silently
+    """Sciter engine options (global or per-window)."""
+    SCITER_SMOOTH_SCROLL = 1       # value: TRUE - enable, value: FALSE - disable, enabled by default
+    SCITER_CONNECTION_TIMEOUT = 2  # global; value: milliseconds, connection timeout of http client
+    SCITER_HTTPS_ERROR = 3         # global; value: 0 - drop connection, 1 - use builtin dialog, 2 - accept connection silently
     SCITER_FONT_SMOOTHING = 4      # value: 0 - system default, 1 - no smoothing, 2 - std smoothing, 3 - clear type
 
     SCITER_TRANSPARENT_WINDOW = 6  # Windows Aero support, value:
                                    # 0 - normal drawing,
                                    # 1 - window has transparent background after calls DwmExtendFrameIntoClientArea() or DwmEnableBlurBehindWindow().
-    SCITER_SET_GPU_BLACKLIST  = 7  # hWnd = NULL,
+    SCITER_SET_GPU_BLACKLIST  = 7  # global;
                                    # value = LPCBYTE, json - GPU black list, see: gpu-blacklist.json resource.
-    SCITER_SET_SCRIPT_RUNTIME_FEATURES = 8,  # value - combination of SCRIPT_RUNTIME_FEATURES flags.
-    SCITER_SET_GFX_LAYER = 9       # hWnd = NULL, value - GFX_LAYER
-    SCITER_SET_DEBUG_MODE = 10     # hWnd, value - TRUE/FALSE
-    SCITER_SET_UX_THEMING = 11     # hWnd = NULL, value - BOOL, TRUE - the engine will use "unisex" theme that is common for all platforms. 
+    SCITER_SET_SCRIPT_RUNTIME_FEATURES = 8,  # global or window; value - combination of SCRIPT_RUNTIME_FEATURES flags.
+    SCITER_SET_GFX_LAYER = 9       # global; value - GFX_LAYER
+    SCITER_SET_DEBUG_MODE = 10     # global or window; value - TRUE/FALSE
+    SCITER_SET_UX_THEMING = 11     # global; value - BOOL, TRUE - the engine will use "unisex" theme that is common for all platforms.
                                    # That UX theme is not using OS primitives for rendering input elements. Use it if you want exactly
                                    # the same (modulo fonts) look-n-feel on all platforms.
-    SCITER_ALPHA_WINDOW  = 12      #  hWnd, value - TRUE/FALSE - window uses per pixel alpha (e.g. WS_EX_LAYERED/UpdateLayeredWindow() window)
+    SCITER_ALPHA_WINDOW  = 12      # hWnd, value - TRUE/FALSE - window uses per pixel alpha (e.g. WS_EX_LAYERED/UpdateLayeredWindow() window)
+    SCITER_SET_INIT_SCRIPT = 13    # hWnd - N/A , value LPCSTR - UTF-8 encoded script source to be loaded into each view before any other script execution.
 
 
 class SCRIPT_RUNTIME_FEATURES(enum.IntEnum):
@@ -57,10 +61,22 @@ class SCRIPT_RUNTIME_FEATURES(enum.IntEnum):
 
 
 class GFX_LAYER(enum.IntEnum):
-    GFX_LAYER_GDI      = 1
-    GFX_LAYER_WARP     = 2
-    GFX_LAYER_D2D      = 3
-    GFX_LAYER_AUTO     = 0xFFFF
+    AUTO          = 0xFFFF
+    CPU           = 1
+
+    if SCITER_WIN:
+        GDI       = 1
+    elif SCITER_LNX:
+        CG        = 1
+    elif SCITER_OSX:
+        CAIRO     = 1
+
+    if SCITER_WIN:
+        WARP      = 2
+        D2D       = 3
+
+    SKIA_CPU      = 4
+    SKIA_OPENGL   = 5
 
 
 class OUTPUT_SUBSYTEMS(enum.IntEnum):
@@ -82,7 +98,7 @@ class SCITER_CREATE_WINDOW_FLAGS(enum.IntEnum):
     SW_RESIZEABLE = (1 << 2)    # has resizeable frame
     SW_TOOL       = (1 << 3)    # is tool window
     SW_CONTROLS   = (1 << 4)    # has minimize / maximize buttons
-    SW_GLASSY     = (1 << 5)    # glassy window ( DwmExtendFrameIntoClientArea on windows )
+    SW_GLASSY     = (1 << 5)    # glassy window - supports "Acrylic" on Windows and "Vibrant" on MacOS.
     SW_ALPHA      = (1 << 6)    # transparent window ( e.g. WS_EX_LAYERED on Windows )
     SW_MAIN       = (1 << 7)    # main window of the app, will terminate the app on close
     SW_POPUP      = (1 << 8)    # the window is created as topmost window.
@@ -139,6 +155,22 @@ class SCN_ATTACH_BEHAVIOR(Structure):
         ("elementTag", LPVOID),
     ]
 
+class SCN_KEYBOARD_REQUEST(Structure):
+    """."""
+    _fields_ = [
+        ("code", c_uint),
+        ("hwnd", HWINDOW),
+        ("keyboardMode", c_uint)
+    ]
+
+class SCN_INVALIDATE_RECT(Structure):
+    """."""
+    _fields_ = [
+        ("code", c_uint),
+        ("hwnd", HWINDOW),
+        ("invalidRect", RECT)
+    ]
+
 
 LPSCITER_CALLBACK_NOTIFICATION = POINTER(SCITER_CALLBACK_NOTIFICATION)
 SciterHostCallback = SC_CALLBACK(UINT, LPSCITER_CALLBACK_NOTIFICATION, LPVOID)
@@ -165,6 +197,8 @@ class SCN_ATTACH_BEHAVIOR(Structure):
 NATIVE_FUNCTOR_INVOKE = CFUNCTYPE(VOID, LPVOID, UINT, PSCITER_VALUE, PSCITER_VALUE)
 NATIVE_FUNCTOR_RELEASE = CFUNCTYPE(VOID, LPVOID)
 
+ELEMENT_BITMAP_RECEIVER = SC_CALLBACK(VOID, LPCBYTE, INT, INT, UINT, UINT, LPVOID)
+
 
 class StringReceiver():
     """LPCWSTR_RECEIVER wrapper."""
diff --git a/sciter/capi/scdom.py b/sciter/capi/scdom.py
index f37fb0c..6451aee 100644
--- a/sciter/capi/scdom.py
+++ b/sciter/capi/scdom.py
@@ -157,59 +157,54 @@ class REQUEST_TYPE(enum.IntEnum):
 
 
 class CTL_TYPE(enum.IntEnum):
-    """DOM control type."""
-    (CTL_NO,              # This dom element has no behavior at all.
-    CTL_UNKNOWN,          # This dom element has behavior but its type is unknown.
-    CTL_EDIT,             # Single line edit box.
-    CTL_NUMERIC,          # Numeric input with optional spin buttons.
-    CTL_CLICKABLE,        # toolbar button, behavior:clickable.
-    CTL_BUTTON,           # Command button.
-    CTL_CHECKBOX,         # CheckBox (button).
-    CTL_RADIO,            # OptionBox (button).
-    CTL_SELECT_SINGLE,    # Single select, ListBox or TreeView.
-    CTL_SELECT_MULTIPLE,  # Multiselectable select, ListBox or TreeView.
-    CTL_DD_SELECT,        # Dropdown single select.
-    CTL_TEXTAREA,         # Multiline TextBox.
-    CTL_HTMLAREA,         # WYSIWYG HTML editor.
-    CTL_PASSWORD,         # Password input element.
-    CTL_PROGRESS,         # Progress element.
-    CTL_SLIDER,           # Slider input element.
-    CTL_DECIMAL,          # Decimal number input element.
-    CTL_CURRENCY,         # Currency input element.
-    CTL_SCROLLBAR,
-
-    CTL_HYPERLINK,
-
-    CTL_MENUBAR,
-    CTL_MENU,
-    CTL_MENUBUTTON,
-
-    CTL_CALENDAR,
-    CTL_DATE,
-    CTL_TIME,
-
-    CTL_FRAME,
-    CTL_FRAMESET,
-
-    CTL_GRAPHICS,
-    CTL_SPRITE,
-
-    CTL_LIST,
-    CTL_RICHTEXT,
-    CTL_TOOLTIP,
-
-    CTL_HIDDEN,
-    CTL_URL,            # URL input element.
-    CTL_TOOLBAR,
-
-    CTL_FORM,
-    CTL_FILE,           # file input element.
-    CTL_PATH,           # path input element.
-    CTL_WINDOW,         # has HWND attached to it
-
-    CTL_LABEL,            
-    CTL_IMAGE) = range(42) # image/object.  
-
-
-assert (CTL_TYPE.CTL_NO == 0)
-assert (CTL_TYPE.CTL_IMAGE == 41)
+    """DOM control type. Note: it was changed in 4.0.3."""
+    CTL_NO = 0                # This dom element has no behavior at all.
+    CTL_UNKNOWN = 1           # This dom element has behavior but its type is unknown.
+    CTL_EDIT = 2              # Single line edit box.
+    CTL_NUMERIC = 3           # Numeric input with optional spin buttons.
+    CTL_CLICKABLE = 4         # toolbar button, behavior:clickable.
+    CTL_BUTTON = 5            # Command button.
+    CTL_CHECKBOX = 6          # CheckBox (button).
+    CTL_RADIO = 7             # OptionBox (button).
+    CTL_SELECT_SINGLE = 8     # Single select, ListBox or TreeView.
+    CTL_SELECT_MULTIPLE = 9   # Multiselectable select, ListBox or TreeView.
+    CTL_DD_SELECT = 10        # Dropdown single select.
+    CTL_TEXTAREA = 11         # Multiline TextBox.
+    CTL_HTMLAREA = 12         # HTML selection behavior.
+    CTL_PASSWORD = 13         # Password input element.
+    CTL_PROGRESS = 14         # Progress element.
+    CTL_SLIDER = 15           # Slider input element.
+    CTL_DECIMAL = 16          # Decimal number input element.
+    CTL_CURRENCY = 17         # Currency input element.
+    CTL_SCROLLBAR = 18
+    CTL_LIST = 19
+    CTL_RICHTEXT = 20
+    CTL_CALENDAR = 21
+    CTL_DATE = 22
+    CTL_TIME = 23
+    CTL_FILE = 24             # file input element.
+    CTL_PATH = 25             # path input element.
+
+    CTL_LAST_INPUT = 26
+
+    CTL_HYPERLINK = CTL_LAST_INPUT
+    CTL_FORM = 27
+
+    CTL_MENUBAR = 28
+    CTL_MENU = 29
+    CTL_MENUBUTTON = 30
+
+    CTL_FRAME = 31
+    CTL_FRAMESET = 32
+
+    CTL_TOOLTIP = 33
+
+    CTL_HIDDEN = 34
+    CTL_URL = 35              # URL input element.
+    CTL_TOOLBAR = 36
+
+    CTL_WINDOW = 37           # has HWND attached to it
+
+    CTL_LABEL = 38
+    CTL_IMAGE = 39            # image/video object.
+    CTL_PLAINTEXT = 40        # Multiline TextBox + colorizer.
diff --git a/sciter/capi/scgraphics.py b/sciter/capi/scgraphics.py
index 88c98d9..48102a4 100644
--- a/sciter/capi/scgraphics.py
+++ b/sciter/capi/scgraphics.py
@@ -3,8 +3,8 @@
 Incomplete.
 """
 import enum
-from ctypes import *
 
+from ctypes import Structure, POINTER, c_void_p
 from sciter.capi.sctypes import SCFN, UINT, BOOL
 
 HGFX = c_void_p
@@ -29,6 +29,7 @@ class SciterGraphicsAPI(Structure):
     """Sciter Graphics ABI."""
     _fields_ = [
         ("imageCreate", imageCreate),
+        # TODO: rest of Graphics API
     ]
 
 
diff --git a/sciter/capi/scmsg.py b/sciter/capi/scmsg.py
new file mode 100644
index 0000000..a117524
--- /dev/null
+++ b/sciter/capi/scmsg.py
@@ -0,0 +1,123 @@
+"""Message definitions to be passed to the SciterProcX function.
+
+"""
+import enum
+
+from ctypes import Structure, Union, c_void_p
+from sciter.capi.sctypes import UINT, BOOL, HDC, POINT
+from sciter.capi.scdef import ELEMENT_BITMAP_RECEIVER
+from sciter.capi.scdom import HELEMENT
+from sciter.capi.scbehavior import MOUSE_BUTTONS, MOUSE_EVENTS, KEYBOARD_STATES
+
+class SCITER_X_MSG_CODE(enum.IntEnum):
+    """SCITER_X_MSG message/function identifier."""
+    SXM_CREATE = 0
+    SXM_DESTROY = 1
+    SXM_SIZE = 2
+    SXM_PAINT = 3
+    SXM_RESOLUTION = 4
+    SXM_HEARTBIT = 5
+    SXM_MOUSE = 6
+    SXM_KEY = 7
+    SXM_FOCUS = 8
+# end
+
+
+class SCITER_X_MSG(Structure):
+    """Common header of message structures passed to SciterProcX."""
+    _fields_ = [
+        ("msg", UINT),  # SCITER_X_MSG_CODE
+    ]
+
+
+class SCITER_X_MSG_CREATE(Structure):
+    """Create event passed to Sciter."""
+    _fields_ = [
+        ("header", SCITER_X_MSG),
+        ("backend", UINT),
+        ("transparent", BOOL),
+    ]
+
+
+class SCITER_X_MSG_DESTROY(Structure):
+    """Destroy event passed to Sciter."""
+    _fields_ = [
+        ("header", SCITER_X_MSG),
+    ]
+
+
+class SCITER_X_MSG_SIZE(Structure):
+    _fields_ = [
+        ("header", SCITER_X_MSG),
+        ("width", UINT),
+        ("height", UINT),
+    ]
+
+class SCITER_X_MSG_RESOLUTION(Structure):
+    _fields_ = [
+        ("header", SCITER_X_MSG),
+        ("pixelsPerInch", UINT),
+    ]
+
+class SCITER_X_MSG_MOUSE(Structure):
+    _fields_ = [
+        ("header", SCITER_X_MSG),
+        #("button", UINT),      # this field has been reordered in 4.4.0.3
+        ("event", UINT),        # MOUSE_EVENTS
+        ("button", UINT),       # MOUSE_BUTTONS
+        ("modifiers", UINT),    # KEYBOARD_STATES
+        ("pos", POINT),
+    ]
+
+class SCITER_X_MSG_KEY(Structure):
+    _fields_ = [
+        ("header", SCITER_X_MSG),
+        #("button", UINT),      # this field has been reordered in 4.4.0.3
+        ("event", UINT),        # MOUSE_EVENTS
+        ("code", UINT),         # key scan code
+        ("modifiers", UINT),    # KEYBOARD_STATES
+    ]
+
+class SCITER_X_MSG_FOCUS(Structure):
+    _fields_ = [
+        ("header", SCITER_X_MSG),
+        ("got", BOOL),
+    ]
+
+class SCITER_X_MSG_HEARTBIT(Structure):
+    _fields_ = [
+        ("header", SCITER_X_MSG),
+        ("time", UINT),
+
+    ]
+
+class SCITER_PAINT_TARGET_TYPE(enum.IntEnum):
+    SPT_DEFAULT = 0     # default rendering target - window surface
+    SPT_RECEIVER = 1    # target::receiver fields are valid
+    SPT_DC = 2          # target::hdc is valid
+    SPT_OPENGL = 3      # target is not used - caller shall set current context on its side
+    SPT_OPENGLES = 4    # target is not used - caller shall set current context on its side
+
+
+class SCITER_X_MSG_PAINT_RECEIVER(Structure):
+    _fields_ = [
+        ("param", c_void_p),
+        ("callback", ELEMENT_BITMAP_RECEIVER),
+    ]
+
+
+class SCITER_X_MSG_PAINT_TARGET(Union):
+    _fields_ = [
+        ("hdc", HDC),
+        ("receiver", SCITER_X_MSG_PAINT_RECEIVER),
+    ]
+
+
+class SCITER_X_MSG_PAINT(Structure):
+    _fields_ = [
+        ("header", SCITER_X_MSG),
+        ("element", HELEMENT),          # layer #HELEMENT, can be NULL if whole tree (document) needs to be rendered.
+        ("isFore", BOOL),               # if element is not null tells if that element is fore-layer.
+        ("targetType", UINT),           # one of SCITER_PAINT_TARGET_TYPE values.
+        ("target", SCITER_X_MSG_PAINT_TARGET)
+    ]
diff --git a/sciter/capi/screquest.py b/sciter/capi/screquest.py
index 814b3e9..4cfaa60 100644
--- a/sciter/capi/screquest.py
+++ b/sciter/capi/screquest.py
@@ -56,6 +56,7 @@ class SciterRequestAPI(Structure):
     _fields_ = [
         ("RequestUse", RequestUse),
         ("RequestUnUse", RequestUnUse),
+        # TODO: rest of Request API
     ]
 
 LPSciterRequestAPI = POINTER(SciterRequestAPI)
diff --git a/sciter/capi/sctiscript.py b/sciter/capi/sctiscript.py
index 5a52e81..3bd385c 100644
--- a/sciter/capi/sctiscript.py
+++ b/sciter/capi/sctiscript.py
@@ -12,4 +12,5 @@ class tiscript_native_interface(ctypes.Structure):
     """."""
     _fields_ = [
         ("create_vm", ctypes.c_void_p),
+        # TODO: rest of TIScript API
         ]
diff --git a/sciter/capi/sctypes.py b/sciter/capi/sctypes.py
index ca8f565..b9c4bb9 100644
--- a/sciter/capi/sctypes.py
+++ b/sciter/capi/sctypes.py
@@ -20,6 +20,9 @@
 
 def utf16tostr(addr, size=-1):
     """Read UTF-16 string from memory and encode as python string."""
+    if addr is None:
+        return None
+
     cb = size if size > 0 else 32
     bstr = ctypes.string_at(addr, cb)
     if size >= 0:
@@ -47,7 +50,7 @@ def utf16tostr(addr, size=-1):
 
 class c_utf16_p(ctypes.c_char_p):
     """A ctypes wrapper for UTF-16 string pointer."""
-    # Taken from http://stackoverflow.com/a/35507014/736762, thanks to @eryksun.
+    # Taken from https://stackoverflow.com/a/35507014/736762, thanks to @eryksun.
     def __init__(self, value=None):
         super(c_utf16_p, self).__init__()
         if value is not None:
@@ -79,7 +82,7 @@ def _check_retval_(cls, result):
 
 class UTF16LEField(object):
     """Structure member wrapper for UTF-16 string pointers."""
-    # Taken from http://stackoverflow.com/a/35507014/736762, thanks to @eryksun.
+    # Taken from https://stackoverflow.com/a/35507014/736762, thanks to @eryksun.
     def __init__(self, name):
         self.name = name
 
@@ -97,13 +100,15 @@ def __set__(self, obj, value):
 
 
 if SCITER_WIN:
-    SCITER_DLL_NAME = "sciter64" if sys.maxsize > 2**32 else "sciter32"
+    # sciter.dll since 4.0.0.0
+    SCITER_DLL_NAME = "sciter"
     SCITER_DLL_EXT = ".dll"
 
     SCFN = ctypes.WINFUNCTYPE
     SC_CALLBACK = ctypes.WINFUNCTYPE
 
     HWINDOW = c_void_p  # HWND
+    HDC = c_void_p      # HDC
 
     BOOL = c_int32
     LPCWSTR = LPWSTR = ctypes.c_wchar_p
@@ -117,26 +122,30 @@ def __set__(self, obj, value):
 
 elif SCITER_OSX:
     # sciter-osx-32 since 3.3.1.8
-    SCITER_DLL_NAME = "sciter-osx-64" if sys.maxsize > 2**32 else "sciter-osx-32"
+    # libsciter since 4.4.6.3
+    SCITER_DLL_NAME = "libsciter"
     SCITER_DLL_EXT = ".dylib"
 
     SCFN = ctypes.CFUNCTYPE
     SC_CALLBACK = ctypes.CFUNCTYPE
 
     HWINDOW = c_void_p  # NSView*
+    HDC = c_void_p      # CGContextRef
 
     BOOL = c_byte
     LPCWSTR = LPWSTR = c_utf16_p
 
 elif SCITER_LNX:
-    assert sys.maxsize > 2**32, "Only 64-bit build supported."
-    SCITER_DLL_NAME = "sciter-gtk-64" if sys.maxsize > 2**32 else "sciter-gtk-32"
+    # libsciter since 3.3.1.7
+    # libsciter-gtk.so instead of libsciter-gtk-64.so since 4.1.4
+    SCITER_DLL_NAME = "libsciter-gtk"
     SCITER_DLL_EXT = ".so"
 
     SCFN = ctypes.CFUNCTYPE
     SC_CALLBACK = ctypes.CFUNCTYPE
 
     HWINDOW = c_void_p  # GtkWidget*
+    HDC = c_void_p      # cairo_t
 
     BOOL = c_byte
     LPCWSTR = LPWSTR = c_utf16_p
diff --git a/sciter/capi/scvalue.py b/sciter/capi/scvalue.py
index 7604f63..7f2fc3e 100644
--- a/sciter/capi/scvalue.py
+++ b/sciter/capi/scvalue.py
@@ -42,6 +42,11 @@ class VALUE_TYPE(enum.IntEnum):
     T_BYTES = 12       # sequence of bytes - e.g. image data
     T_OBJECT = 13      # scripting object proxy (TISCRIPT/SCITER)
     T_DOM_OBJECT = 14  # DOM object (CSSS!), use get_object_data to get HELEMENT
+    T_RESOURCE = 15    #
+    T_RANGE = 16       # integer range N..M
+    T_DURATION = 17    # time duration in seconds, stored as float
+    T_ANGLE = 18       # angle value in radians, stored as float
+    T_COLOR = 19       # color value, stored as 0xAABBGGRR integer
 
 
 class VALUE_UNIT_TYPE(enum.IntEnum):
@@ -61,8 +66,8 @@ class VALUE_UNIT_TYPE(enum.IntEnum):
     UT_PC = 12              # picas (1 pica = 12 points).
     UT_DIP = 13
     reserved3 = 14
-    UT_COLOR = 15           # color in int
-    UT_URL = 16             # url in string
+    reserved4 = 15
+    UT_URL = 22             # url in string
 
 
 class VALUE_UNIT_TYPE_DATE(enum.IntEnum):
@@ -77,7 +82,7 @@ class VALUE_UNIT_TYPE_OBJECT(enum.IntEnum):
     """Sciter object subtype."""
     UT_OBJECT_ARRAY  = 0  # type T_OBJECT of type Array
     UT_OBJECT_OBJECT = 1  # type T_OBJECT of type Object
-    UT_OBJECT_CLASS  = 2  # type T_OBJECT of type Type (class or namespace)
+    UT_OBJECT_CLASS  = 2  # type T_OBJECT of type Class (class or namespace)
     UT_OBJECT_NATIVE = 3  # type T_OBJECT of native Type with data slot (LPVOID)
     UT_OBJECT_FUNCTION= 4 # type T_OBJECT of type Function
     UT_OBJECT_ERROR = 5   # type T_OBJECT of type Error
@@ -88,6 +93,7 @@ class VALUE_UNIT_TYPE_STRING(enum.IntEnum):
     UT_STRING_STRING = 0       # string
     UT_STRING_ERROR = 1        # is an error string
     UT_STRING_SECURE = 2       # secure string ("wiped" on destroy)
+    UT_STRING_FILE = 0xfffe    #
     UT_STRING_SYMBOL = 0xffff  # symbol in tiscript sense
 
 
diff --git a/sciter/dom.py b/sciter/dom.py
index a839f88..fbad0a5 100644
--- a/sciter/dom.py
+++ b/sciter/dom.py
@@ -7,11 +7,32 @@
 
 from sciter.capi.scdom import *
 from sciter.capi.screquest import SciterResourceType
-from sciter.capi.scbehavior import BEHAVIOR_EVENTS, EVENT_REASON
+from sciter.capi.scbehavior import BEHAVIOR_EVENTS, CLICK_REASON
 
 _api = sciter.SciterAPI()
 
 # TODO: behaviors support, create behavior
+#
+# Not implemented or not used APIs:
+#
+# SciterAttachHwndToElement
+# SciterCallBehaviorMethod
+# SciterCombineURL
+# SciterControlGetType
+# SciterGetElementIntrinsicHeight
+# SciterGetElementIntrinsicWidths
+# SciterGetElementNamespace
+# SciterGetElementType
+# SciterGetObject
+# SciterHidePopup
+# SciterNodeCastFromElement
+# SciterSelectElementsW
+# SciterSelectParentW
+# SciterShowPopup
+# SciterShowPopupAt
+# SciterSortElements
+# SciterTraverseUIEvent
+#
 
 
 class DomError(sciter.error.SciterError):
@@ -40,7 +61,7 @@ def create(cls, text, kind=NODE_TYPE.NT_TEXT):
             ok = _api.SciterCreateTextNode(text, len(text), ctypes.byref(rv))
         elif kind == NODE_TYPE.NT_COMMENT:
             ok = _api.SciterCreateCommentNode(text, len(text), ctypes.byref(rv))
-        self._throw_if(ok)
+        Node._throw_if(ok)
         return Node(rv)
 
     def __init__(self, node=None):
@@ -100,7 +121,7 @@ def _use(self, h):
 
     def _unuse(self):
         if self.h:
-            ok = _api.SciterNodeRelease(self.h)
+            _api.SciterNodeRelease(self.h)
             self.h = None
             self._as_parameter_ = self.h
         pass
@@ -256,14 +277,16 @@ def from_window(cls, hwnd):
         """Get root DOM element of the Sciter document."""
         p = HELEMENT()
         ok = _api.SciterGetRootElement(hwnd, ctypes.byref(p))
-        return Element(p)
+        Element._throw_if(ok)
+        return Element(p) if p else None
 
     @classmethod
     def from_focus(cls, hwnd):
         """Get focus DOM element of the Sciter document."""
         p = HELEMENT()
         ok = _api.SciterGetFocusElement(hwnd, ctypes.byref(p))
-        return Element(p)
+        Element._throw_if(ok)
+        return Element(p) if p else None
 
     @classmethod
     def from_point(cls, hwnd, x, y):
@@ -271,7 +294,8 @@ def from_point(cls, hwnd, x, y):
         p = HELEMENT()
         pt = sciter.capi.sctypes.POINT(x, y)
         ok = _api.SciterFindElement(hwnd, pt, ctypes.byref(p))
-        return Element(p)
+        Element._throw_if(ok)
+        return Element(p) if p else None
 
     @classmethod
     def from_highlighted(cls, hwnd):
@@ -282,11 +306,12 @@ def from_highlighted(cls, hwnd):
         return Element(p) if p else None
 
     @classmethod
-    def from_uid(cls, uid: int):
+    def from_uid(cls, hwnd, uid: int):
         """Get element handle by its UID."""
         p = HELEMENT()
         ok = _api.SciterGetElementByUID(hwnd, uid, ctypes.byref(p))
-        return Element(p)
+        Element._throw_if(ok)
+        return Element(p) if p else None
 
     # instance methods
     def __init__(self, node=None):
@@ -316,6 +341,7 @@ def _use(self, h):
     def _unuse(self):
         if self.h:
             ok = _api.Sciter_UnuseElement(self.h)
+            self._throw_if(ok)
             self.h = None
             self._as_parameter_ = self.h
         pass
@@ -389,6 +415,7 @@ def get_uid(self):
         """Get element UID - identifier suitable for storage."""
         n = ctypes.c_uint()
         ok = _api.SciterGetElementUID(self, ctypes.byref(n))
+        self._throw_if(ok)
         return n.value
 
     def get_tag(self):
@@ -480,13 +507,13 @@ def request_html(self, url: str, initiator=None):
         """Request HTML data download for this element."""
         return self.request_data(url, SciterResourceType.RT_DATA_HTML, initiator)
 
-    def send_request(self, url: str, params=None, method='GET', async=False, data_type=SciterResourceType.RT_DATA_HTML):
+    def send_request(self, url: str, params=None, method='GET', send_async=False, data_type=SciterResourceType.RT_DATA_HTML):
         """Send HTTP GET or POST request for the element."""
         if method not in ('GET', 'POST'):
             raise ValueError("Only GET or POST supported here.")
 
         # GET_ASYNC = 0, GET_SYNC = 2, POST_ASYNC = 1, POST_SYNC = 3
-        method_type = 2 if async is False else 0
+        method_type = 2 if send_async is False else 0
         method_type += 1 if method == 'POST' else 0
 
         if params:
@@ -503,26 +530,26 @@ def send_request(self, url: str, params=None, method='GET', async=False, data_ty
         self._throw_if(ok)
         return self
 
-    def send_event(self, code: BEHAVIOR_EVENTS, reason=EVENT_REASON.SYNTHESIZED, source=None):
+    def send_event(self, code: BEHAVIOR_EVENTS, reason=CLICK_REASON.SYNTHESIZED, source=None):
         """Send sinking/bubbling event to the child/parent chain of the element."""
         handled = sciter.capi.sctypes.BOOL()
         ok = _api.SciterSendEvent(self, code, source if source else self.h, reason, ctypes.byref(handled))
         self._throw_if(ok)
         return handled != False
 
-    def post_event(self, code: BEHAVIOR_EVENTS, reason=EVENT_REASON.SYNTHESIZED, source=None):
+    def post_event(self, code: BEHAVIOR_EVENTS, reason=CLICK_REASON.SYNTHESIZED, source=None):
         """Post sinking/bubbling event to the child/parent chain of the element."""
         ok = _api.SciterPostEvent(self, code, source if source else self.h, reason)
         self._throw_if(ok)
         return self
 
-    def fire_event(self, source, target, code: BEHAVIOR_EVENTS, reason=EVENT_REASON.SYNTHESIZED, post=True, data=None):
-        """Send or post sinking/bubbling event to the child/parent chain of he element."""
+    def fire_event(self, code: BEHAVIOR_EVENTS, reason=CLICK_REASON.SYNTHESIZED, source=None, post=True, data=None):
+        """Send or post sinking/bubbling event to the child/parent chain of the element."""
         params = sciter.capi.scbehavior.BEHAVIOR_EVENT_PARAMS()
         params.cmd = code
         params.reason = reason
-        params.he = source
-        params.heTarget = target
+        params.he = source if source else self.h
+        params.heTarget = self.h
         if data is not None:
             sciter.Value.pack_to(params.data, data)
         handled = sciter.capi.sctypes.BOOL()
@@ -541,7 +568,7 @@ def eval_script(self, script: str, name=None):
     def call_function(self, name: str, *args):
         """Call scripting function defined in the namespace of the element (a.k.a. global function)."""
         rv = sciter.Value()
-        argc, argv, this = sciter.Value.pack_args(*args)
+        argc, argv, _ = sciter.Value.pack_args(*args)
         ok = _api.SciterCallScriptingFunction(self, name.encode('utf-8'), argv, argc, rv)
         sciter.Value.raise_from(rv, ok == SCDOM_RESULT.SCDOM_OK, name)
         self._throw_if(ok)
@@ -550,7 +577,7 @@ def call_function(self, name: str, *args):
     def call_method(self, name: str, *args):
         """Call scripting method defined for the element."""
         rv = sciter.Value()
-        argc, argv, this = sciter.Value.pack_args(*args)
+        argc, argv, _ = sciter.Value.pack_args(*args)
         ok = _api.SciterCallScriptingMethod(self, name.encode('utf-8'), argv, argc, rv)
         sciter.Value.raise_from(rv, ok == SCDOM_RESULT.SCDOM_OK, name)
         self._throw_if(ok)
@@ -573,7 +600,7 @@ def attribute_count(self):
 
     def attribute_name(self, n):
         """Get attribute name by its index."""
-        cb = sciter.capi.scdef.StringReceiver('wchar')
+        cb = sciter.capi.scdef.StringReceiver('char')
         ok = _api.SciterGetNthAttributeNameCB(self, n, cb, None)
         self._throw_if(ok)
         return cb.text
@@ -612,6 +639,12 @@ def toggle_attribute(self, name: str, isset: bool, val=''):
             self.remove_attribute(name)
         return self
 
+    def clear_attributes(self):
+        """Remove all attributes from the element."""
+        ok = _api.SciterClearAttributes(self)
+        self._throw_if(ok)
+        return self
+
 
     ## @name Style attributes:
 
@@ -624,7 +657,7 @@ def style_attribute(self, name: str):
 
     def set_style_attribute(self, name: str, val: str):
         """Set style attribute."""
-        ok = _api.SciterSetStyleAttribute(name.encode('utf-8'), val)
+        ok = _api.SciterSetStyleAttribute(self, name.encode('utf-8'), val)
         self._throw_if(ok)
         return self
 
@@ -640,7 +673,7 @@ def set_state(self, set_bits, clear_bits=0, update=True):
     def state(self):
         """Get UI state bits of the element as set of ELEMENT_STATE_BITS."""
         n = ctypes.c_uint()
-        ok = SciterGetElementState(self, ctypes.byref(n))
+        ok = _api.SciterGetElementState(self, ctypes.byref(n))
         self._throw_if(ok)
         return ELEMENT_STATE_BITS(n.value)
 
@@ -680,9 +713,9 @@ def root(self):
 
     def parent(self):
         """Get parent element."""
-        # TODO: check root: return None or Error
         p = HELEMENT()
         ok = _api.SciterGetParentElement(self, ctypes.byref(p))
+        self._throw_if(ok)
         return Element(p) if p else None
 
     def index(self):
@@ -698,7 +731,7 @@ def next_sibling(self):
         dad = self.parent()
         if not dad or idx >= len(dad):
             return None
-        return dad[idx]
+        return dad[idx]     # pylint: disable=unsubscriptable-object
 
     def prev_sibling(self):
         """Get previous sibling element."""
@@ -706,21 +739,28 @@ def prev_sibling(self):
         dad = self.parent()
         if not dad or idx < 0 or idx >= len(dad):
             return None
-        return dad[idx]
+        return dad[idx]     # pylint: disable=unsubscriptable-object
 
     def first_sibling(self):
         """Get first sibling element."""
         dad = self.parent()
         if not dad or len(dad) == 0:
             return None
-        return dad[0]
+        return dad[0]     # pylint: disable=unsubscriptable-object
 
     def last_sibling(self):
         """Get last sibling element."""
         dad = self.parent()
         if not dad or len(dad) == 0:
             return None
-        return dad[len(dad)-1]
+        return dad[len(dad)-1]     # pylint: disable=unsubscriptable-object
+
+    def children_count(self):
+        """Get number of child elements."""
+        n = ctypes.c_uint()
+        ok = _api.SciterGetChildrenCount(self, ctypes.byref(n))
+        self._throw_if(ok)
+        return n.value
 
     def insert(self, child, index: int):
         """Insert element at index position of this element."""
@@ -885,6 +925,6 @@ def _throw_if(code):
             return
         import inspect
         context = inspect.stack()[1][3]
-        raise DomError(code, "Node." + context)
+        raise DomError(code, "Element." + context)
 
     pass
diff --git a/sciter/event.py b/sciter/event.py
index 36b4360..e93d5ca 100644
--- a/sciter/event.py
+++ b/sciter/event.py
@@ -1,7 +1,6 @@
 """Behaviors support (a.k.a windowless controls)."""
 
 import ctypes
-
 import sciter.capi.scdef
 
 from sciter.capi.scbehavior import *
@@ -14,34 +13,34 @@ class EventHandler:
     """DOM event handler which can be attached to any DOM element."""
 
     ALL_EVENTS = EVENT_GROUPS.HANDLE_ALL
-    DEFAULT_EVENTS = EVENT_GROUPS.HANDLE_INITIALIZATION | EVENT_GROUPS.HANDLE_SIZE | EVENT_GROUPS.HANDLE_BEHAVIOR_EVENT | EVENT_GROUPS.HANDLE_SCRIPTING_METHOD_CALL
+    DEFAULT_EVENTS = EVENT_GROUPS.HANDLE_BEHAVIOR_EVENT | EVENT_GROUPS.HANDLE_SCRIPTING_METHOD_CALL | EVENT_GROUPS.HANDLE_METHOD_CALL
 
-    def __init__(self, window=None, element=None, subscription=DEFAULT_EVENTS):
+    def __init__(self, window=None, element=None, subscription=None):
         """Attach event handler to dom::element or sciter::window."""
         super().__init__()
-        self.subscription = subscription
+        self.subscription = subscription if subscription is not None else EventHandler.DEFAULT_EVENTS
         self.element = None
         self._attached_to_window = None
         self._attached_to_element = None
         self._dispatcher = dict()
+        self._executor = None
         self.set_dispatch_options()
         if window or element:
             self.attach(window, element, subscription)
-        pass
 
     def __del__(self):
         assert(not self.element)
         pass
 
-    def attach(self, window=None, element=None, subscription=DEFAULT_EVENTS):
+    def attach(self, window=None, element=None, subscription=None):
         """Attach event handler to dom::element or sciter::window."""
         assert(window or element)
-        self.subscription = subscription
+        self.subscription = subscription if subscription is not None else EventHandler.DEFAULT_EVENTS
         self._event_handler_proc = sciter.capi.scdef.ElementEventProc(self._element_proc)
         tag = id(self)
         if window:
             self._attached_to_window = window
-            ok = _api.SciterWindowAttachEventHandler(window, self._event_handler_proc, tag, subscription)
+            ok = _api.SciterWindowAttachEventHandler(window, self._event_handler_proc, tag, self.subscription)
             if ok != SCDOM_RESULT.SCDOM_OK:
                 raise sciter.SciterError("Could not attach to window")
         elif element:
@@ -54,6 +53,9 @@ def attach(self, window=None, element=None, subscription=DEFAULT_EVENTS):
     def detach(self):
         """Detach event handler from dom::element or sciter::window."""
         tag = id(self)
+        if self._executor is not None:
+            self._executor.shutdown()
+            self._executor = None
         if self._attached_to_window:
             ok = _api.SciterWindowDetachEventHandler(self._attached_to_window, self._event_handler_proc, tag)
             if ok != SCDOM_RESULT.SCDOM_OK:
@@ -68,15 +70,16 @@ def detach(self):
 
     def dispatch(self, name, args):
         """Route script call to python handler directly."""
-        fn = getattr(self, name, None)
-        if fn is not None:
-            return fn(*args)
+        # fn = getattr(self, name, None)
+        # if fn is not None:
+        #     return fn(*args)
         pass
 
-    def set_dispatch_options(self, enable=True, require_attribute=True, dynamic_handlers=False):
-        """Set a various script dispatch options."""
+    def set_dispatch_options(self, enable=True, require_attribute=True, dynamic_handlers=False, raw_handlers=True):
+        """Set the various script dispatch options."""
         self._dispatcher['enabled'] = enable                # enable or disable dispatching of script calls to class handlers
         self._dispatcher['runtime'] = dynamic_handlers      # class handlers may be added at runtime, so we won't cache it
+        self._dispatcher['static'] = raw_handlers           # `self.on_script_call` is always called
         self._dispatcher['require'] = require_attribute     # class handlers require @sciter.script attribute
         self._dispatcher['handlers'] = {}
         self._dispatcher_update(True)
@@ -108,11 +111,11 @@ def on_subscription(self, groups: EVENT_GROUPS):
         return self.subscription
 
     def on_script_call(self, name: str, args: list):
-        """Script calls from CSSS! script and TIScript."""
+        """Script calls from CSSS! script and TIScript. Arguments are Sciter types. Return something to prevent @script handlers to be executed."""
         # Return something except None to indicate that function handled (e.g. found).
         pass
 
-    def on_event(self, source: HELEMENT, target: HELEMENT, code: BEHAVIOR_EVENTS, phase: PHASE_MASK, reason: EVENT_REASON):
+    def on_event(self, source: HELEMENT, target: HELEMENT, code: BEHAVIOR_EVENTS, phase: PHASE_MASK, reason: CLICK_REASON):
         """Notification event from builtin behaviors."""
         pass
 
@@ -120,6 +123,30 @@ def on_data_arrived(self, nm: DATA_ARRIVED_PARAMS):
         """Requested data has been delivered."""
         pass
 
+    def on_timer(self, timerId):
+        """Timer tick. Return `True` to continue timer, `False` to stop (default behavior)."""
+        pass
+
+    def on_mouse(self, params: MOUSE_PARAMS):
+        """Mouse event."""
+        pass
+
+    def on_key(self, params: KEY_PARAMS):
+        """Keyboard event."""
+        pass
+
+    def on_focus(self, params: FOCUS_PARAMS):
+        """Element focus get/loose event."""
+        pass
+
+    def on_draw(self, params: DRAW_PARAMS):
+        """Element draw event. Return `True` for custom drawing, `False` for default drawing."""
+        pass
+
+    def on_size(self):
+        """Element resize event."""
+        pass
+
     ## @}
 
     def _document_ready(self, target):
@@ -147,31 +174,131 @@ def _dispatcher_update(self, force=False):
     def _on_script_call(self, f):
         # update handlers on every call if needed
         self._dispatcher_update()
+
         fname = f.name.decode('utf-8')
         fn = self._dispatcher['handlers'].get(fname)
-        args = sciter.Value.unpack_from(f.argv, f.argc)
-        try:
-            if fn:
-                handler_called = True
+        call_raw = self._dispatcher['static']
+        rv = None
+        value_args = None
+
+        # call raw handler first if configured
+        if call_raw == 'always':
+            try:
+                value_args = [sciter.Value(f.argv[i]) for i in range(f.argc)]
+                # pylint: disable=assignment-from-none,assignment-from-no-return
+                # because the `self.on_` methods can be overloaded
+                rv = self.on_script_call(fname, value_args)
+            except Exception as e:
+                rv = self.script_exception_handler(fname, e)
+
+        # if not handled, call decorated method
+        if rv is None and fn:
+            cfg = getattr(fn, '_sciter_cfg', {})
+            skip_exception = not cfg.get('safe', True)
+            if cfg.get('threading') and cfg.get('promise'):
+                raise sciter.SciterError("Don't mix `threading` and `promise` in @script")
+            try:
+                if cfg.get('convert'):
+                    args = sciter.Value.unpack_from(f.argv, f.argc)
+                else:
+                    args = [sciter.Value(f.argv[i]) for i in range(f.argc)]
+
+                if cfg.get('threading'):
+                    # submit this handler to a separate thread
+                    # syntax:
+                    # `root.xcall(name, ...args)`
+
+                    def on_thread_done(fut):
+                        if fut.cancelled():
+                            return
+
+                        exc = fut.exception()
+                        if exc is None:
+                            # no exception, but nowhere to send the result
+                            fut.result()
+                        else:
+                            # there was an exception, call the exception hook
+                            self.script_exception_handler(fname, exc)
+                        pass
+
+                    if self._executor is None:
+                        from concurrent.futures import ThreadPoolExecutor
+                        self._executor = ThreadPoolExecutor()
+
+                    # submit to the executor and wait for completion
+                    fut = self._executor.submit(fn, *args)
+                    fut.add_done_callback(on_thread_done)
+                    return True
+
+                elif cfg.get('promise'):
+                    # submit this promise handler to a separate thread
+                    # syntax:
+                    # `root.xcall(name, [args], resolve, reject)`
+                    jsargs = args[0]
+                    jsresolve = args[1]
+                    jsreject = args[2]
+
+                    def on_task_done(fut):
+                        if fut.cancelled():
+                            return
+
+                        exc = fut.exception()
+                        if exc is None:
+                            # no exception, resolve the promise
+                            rv = fut.result()
+                            jsresolve(rv)
+                        else:
+                            # there was an exception, reject the promise
+                            # note: reject expects a value, not an error
+                            exc = self.script_exception_handler(fname, exc)
+                            if skip_exception:
+                                jsresolve(str(exc))
+                            else:
+                                jsreject(str(exc))
+                        pass
+
+                    if self._executor is None:
+                        from concurrent.futures import ThreadPoolExecutor
+                        self._executor = ThreadPoolExecutor()
+
+                    # submit to the executor and wait for completion
+                    fut = self._executor.submit(fn, *jsargs)
+                    fut.add_done_callback(on_task_done)
+                    return True
+
                 rv = fn(*args)
-            else:
-                handler_called = False
-                rv = self.on_script_call(fname, args)
-        except Exception as e:
-            rv = e
-        if handler_called or rv is not None:
+            except Exception as e:
+                exc = self.script_exception_handler(fname, e)
+                rv = str(exc) if skip_exception else exc
+
+        # if not handled by @script, call the raw handler
+        if not fn and call_raw == True:
+            try:
+                value_args = [sciter.Value(f.argv[i]) for i in range(f.argc)]
+                # pylint: disable=assignment-from-none,assignment-from-no-return
+                # because the `self.on_` methods can be overloaded
+                rv = self.on_script_call(fname, value_args)
+            except Exception as e:
+                rv = self.script_exception_handler(fname, e)
+
+        # if handled, pack result for Sciter
+        if fn or rv is not None:
             sciter.Value.pack_to(f.result, rv)
             return True
         return False
 
     # event handler native callback
     def _element_proc(self, tag, he, evt, params):
+        # pylint: disable=assignment-from-none,assignment-from-no-return
+        # because the `self.on_` methods can be overloaded
+
         he = HELEMENT(he)
         if evt == EVENT_GROUPS.SUBSCRIPTIONS_REQUEST:
             p = ctypes.cast(params, ctypes.POINTER(ctypes.c_uint))
-            subscribed = self.on_subscription(p.contents)
+            request = p.contents.value
+            subscribed = self.on_subscription(request)
             if subscribed is not None:
-                p.contents = ctypes.c_ulong(int(subscribed))
+                p[0] = int(subscribed)
                 return True
 
         elif evt == EVENT_GROUPS.HANDLE_INITIALIZATION:
@@ -201,7 +328,7 @@ def _element_proc(self, tag, he, evt, params):
 
             code = (m.cmd & 0xFFF)
             phase = PHASE_MASK(m.cmd & 0xFFFFF000)
-            reason = m.reason                   # reason can be EVENT_REASON or EDIT_CHANGED_REASON, so leave it as int
+            reason = m.reason                   # reason can be CLICK_REASON or EDIT_CHANGED_REASON, so leave it as int
             try:
                 event = BEHAVIOR_EVENTS(code)   # not all codes enumerated in BEHAVIOR_EVENTS :-\
             except ValueError:
@@ -220,5 +347,44 @@ def _element_proc(self, tag, he, evt, params):
             handled = self.on_data_arrived(p.contents)
             return handled or False
 
+        elif evt == EVENT_GROUPS.HANDLE_DRAW:
+            p = ctypes.cast(params, ctypes.POINTER(DRAW_PARAMS))
+            handled = self.on_draw(p.contents)
+            return handled or False
+
+        elif evt == EVENT_GROUPS.HANDLE_MOUSE:
+            p = ctypes.cast(params, ctypes.POINTER(MOUSE_PARAMS))
+            handled = self.on_mouse(p.contents)
+            return handled or False
+
+        elif evt == EVENT_GROUPS.HANDLE_KEY:
+            p = ctypes.cast(params, ctypes.POINTER(KEY_PARAMS))
+            handled = self.on_key(p.contents)
+            return handled or False
+
+        elif evt == EVENT_GROUPS.HANDLE_FOCUS:
+            p = ctypes.cast(params, ctypes.POINTER(FOCUS_PARAMS))
+            handled = self.on_focus(p.contents)
+            return handled or False
+
+        elif evt == EVENT_GROUPS.HANDLE_TIMER:
+            p = ctypes.cast(params, ctypes.POINTER(TIMER_PARAMS))
+            handled = self.on_timer(p.contents.timerId)
+            return handled or False
+
+        elif evt == EVENT_GROUPS.HANDLE_SIZE:
+            handled = self.on_size()
+            return handled or False
+
         return False
+
+    def script_exception_handler(self, func_name, exception):
+        """
+        By default, just prints exception traceback to stderr and then returns it.
+        Can be overridden to change script exception handling.
+        """
+        import traceback
+        traceback.print_tb(exception.__traceback__)
+        return exception
+
     pass
diff --git a/sciter/host.py b/sciter/host.py
index 965aa02..a5dab57 100644
--- a/sciter/host.py
+++ b/sciter/host.py
@@ -9,6 +9,8 @@
 import sciter
 import sciter.dom
 
+import sys
+
 _api = sciter.SciterAPI()
 
 
@@ -36,11 +38,16 @@ def setup_callback(self, hwnd):
         _api.SciterSetCallback(hwnd, self._sciter_handler_proc, ctypes.c_void_p(0))
         pass
 
-    def setup_debug(self, hwnd=None):
+    def setup_debug(self, hwnd=None, debug_windows=True, debug_output=True):
         """Setup debug output function for specific window or globally."""
-        ok = _api.SciterSetOption(hwnd, SCITER_RT_OPTIONS.SCITER_SET_DEBUG_MODE, True)
+        ok = _api.SciterSetOption(hwnd, SCITER_RT_OPTIONS.SCITER_SET_DEBUG_MODE, debug_windows)
+        if not ok:
+            raise sciter.SciterError("Could not set debug mode")
         self._sciter_debug_proc = DEBUG_OUTPUT_PROC(self.on_debug_output)
-        _api.SciterSetupDebugOutput(hwnd, None, self._sciter_debug_proc)
+        if debug_output:
+            _api.SciterSetupDebugOutput(hwnd, None, self._sciter_debug_proc)
+        else:
+            _api.SciterSetupDebugOutput(hwnd, None, DEBUG_OUTPUT_PROC(0))
         pass
 
     def set_option(self, option, value):
@@ -57,7 +64,7 @@ def set_home_url(self, url: str):
         """Set sciter window home url."""
         ok = _api.SciterSetHomeURL(self.hwnd, url)
         if not ok:
-            raise sciter.SciterError("Could not home url " + str(url))
+            raise sciter.SciterError("Could not set home url " + str(url))
         return self
 
     def set_media_type(self, media_type: str):
@@ -124,6 +131,7 @@ def get_root(self):
         """Get window root DOM element."""
         he = sciter.dom.HELEMENT()
         ok = _api.SciterGetRootElement(self.hwnd, ctypes.byref(he))
+        sciter.dom.Element._throw_if(ok)
         return sciter.dom.Element(he) if he else None
 
     def eval_script(self, script: str, name=None):
@@ -136,7 +144,7 @@ def eval_script(self, script: str, name=None):
     def call_function(self, name: str, *args):
         """Call scripting function defined in the global namespace."""
         rv = sciter.Value()
-        argc, argv, this = sciter.Value.pack_args(*args)
+        argc, argv, _ = sciter.Value.pack_args(*args)
         ok = _api.SciterCall(self.hwnd, name.encode('utf-8'), argc, argv, rv)
         sciter.Value.raise_from(rv, ok != False, name)
         return rv
@@ -145,7 +153,7 @@ def data_ready(self, uri: str, data: bytes, request_id=None, hwnd=None):
         """This function is used as response to SCN_LOAD_DATA request."""
         if not hwnd:
             hwnd = self.hwnd
-        if request_id:
+        if request_id is not None:
             ok = _api.SciterDataReadyAsync(hwnd, uri, data, len(data), request_id)
         else:
             ok = _api.SciterDataReady(hwnd, uri, data, len(data))
@@ -175,11 +183,14 @@ def on_debug_output(self, tag, subsystem, severity, text, text_len):
             text = text.value
         message = text.replace("\r", "\n").rstrip()
         if message:
-            print("{}:{}: {}".format(sevname, sysname, message))
+            destination = sys.stdout if sevname == 'info' else sys.stderr
+            print("{}:{}: {}".format(sevname, sysname, message), file=destination)
         pass
 
     def handle_notification(self, pnm, param):
         """Sciter notification handler."""
+        # pylint: disable=assignment-from-none,assignment-from-no-return
+        # because the `self.on_` methods can be overloaded
         rv = 0
         nm = pnm.contents
         if nm.code == SciterNotification.SC_LOAD_DATA:
diff --git a/sciter/platform.py b/sciter/platform.py
index 71953c4..fbbf894 100644
--- a/sciter/platform.py
+++ b/sciter/platform.py
@@ -13,7 +13,17 @@
     class WindowsWindow:
         """Win32 window."""
 
+        _initialized = False
+
+        def __init__(self):
+            super().__init__()
+            self.hwnd = None
+            pass
+
         def _create(self, flags, rect, parent):
+            if not WindowsWindow._initialized:
+                ctypes.windll.ole32.OleInitialize(None)
+                WindowsWindow._initialized = True
             if rect is None:
                 rect = sciter.capi.sctypes.RECT()
             self._msg_delegate = sciter.capi.scdef.SciterWindowDelegate(self._on_msg_delegate)
@@ -47,6 +57,9 @@ def get_title(self):
             ctypes.windll.user32.GetWindowTextW(self.hwnd, title, cb)
             return title
 
+        def minimal_menu(self):
+            pass
+
         def run_app(self):
             """Run the main app message loop until window been closed."""
             msg = MSG()
@@ -66,6 +79,8 @@ def on_message(self, hwnd, msg, wparam, lparam):
             pass
 
         def _on_msg_delegate(self, hwnd, msg, wparam, lparam, pparam, phandled):
+            # pylint: disable=assignment-from-none,assignment-from-no-return
+            # because the `self.on_` methods can be overloaded
             rv = self.on_message(hwnd, msg, wparam, lparam)
             if rv is not None:
                 phandled.contents = 1  # True
@@ -86,10 +101,15 @@ def __init__(self):
             super().__init__()
             self.objc = ObjC()
             self.nsApp = None
-            self._msg_delegate = None
+            self.hwnd = None
+            self.window_flags = None
 
             NSApplication = self.objc.getClass('NSApplication')
             self.nsApp = self.objc(NSApplication, 'sharedApplication')
+
+			# By default, unbundled apps start with `NSApplicationActivationPolicyProhibited` (no dock, no menu).
+            # Bundled apps start with `NSApplicationActivationPolicyRegular`
+            self.objc(self.nsApp, 'setActivationPolicy:', 0)
             pass
 
         def _create(self, flags, rect, parent):
@@ -138,6 +158,29 @@ def get_title(self):
             nstitle = self.objc(self._window(), 'title')
             return self.objc.fromNSString(nstitle)
 
+        def minimal_menu(self):
+            NSMenu = self.objc.getClass('NSMenu')
+            NSMenuItem = self.objc.getClass('NSMenuItem')
+
+            menubar = self.objc.alloc(NSMenu)
+            menubar_item = self.objc.alloc(NSMenuItem)
+
+            self.objc(menubar, 'addItem:', menubar_item)
+            self.objc(self.nsApp, 'setMainMenu:', menubar)
+
+            title = self.objc.toNSString('Quit')
+            key = self.objc.toNSString('q')
+            action = ctypes.c_void_p(self.objc.getSEL('terminate:'))
+
+            quit = self.objc(self.objc(NSMenuItem, 'alloc'), 'initWithTitle:action:keyEquivalent:', title, action, key)
+            self.objc(quit, 'autorelease')
+
+            appmenu = self.objc.alloc(NSMenu)
+            self.objc(appmenu, 'addItem:', quit)
+            self.objc(menubar_item, 'setSubmenu:', appmenu)
+
+            pass
+
         def run_app(self):
             """Run the main app message loop until window been closed."""
             self.objc(self.nsApp, 'run')
@@ -195,7 +238,7 @@ def __init__(self):
             #  const UniChar * CFStringGetCharactersPtr ( CFStringRef theString );
             self.cf.CFStringGetCharactersPtr.restype = ctypes.c_char_p
             self.cf.CFStringGetCharactersPtr.argtypes = [ctypes.c_void_p]
-            
+
             # CFStringGetLength
             self.cf.CFStringGetLength.restype = ctypes.c_int
             self.cf.CFStringGetLength.argtypes = [ctypes.c_void_p]
@@ -222,7 +265,7 @@ def fromNSString(self, nsString):
 
         def toNSString(self, string: str):
             kCFStringEncodingUTF8 = 0x08000100
-            return ctypes.c_void_p(self.cf.CFStringCreateWithCString(None, string.encode('utf8'), kCFStringEncodingUTF8))
+            return ctypes.c_void_p(self.cf.CFStringCreateWithCString(None, string.encode('utf-8'), kCFStringEncodingUTF8))
 
         def getClass(self, name):
             # NSSound = objc.getClass('NSSound')
@@ -232,6 +275,10 @@ def new(self, cls):
             # sound = objc.new(NSSound)
             return self.call(self.call(cls, 'alloc'), 'init')
 
+        def alloc(self, cls):
+            # sound = objc.alloc(NSSound)
+            return self.call(self.call(cls, 'new'), 'autorelease')
+
         def getSEL(self, name):
             return self.dll.sel_registerName(name.encode('utf-8'))
 
@@ -245,7 +292,9 @@ def __call__(self, obj, method, *args, **kwargs):
 
         def call(self, obj, method, *args, **kwargs):
             # objc.call(NSSound, 'alloc')
-            objc = self.dll
+            # NB: `objc_msgSend.argtypes` has only 2 arguments specified.
+            # Extra args should be `c_void_p`!
+
             restype = kwargs.get('cast', ctypes.c_void_p)
             typename = kwargs.get('type')
             if typename is not None and hasattr(ctypes, typename):
@@ -267,6 +316,21 @@ def _init_lib():
         lib = ctypes.cdll.LoadLibrary(ctypes.util.find_library('gtk-3'))
         _init_lib._dll = lib
 
+        lib.gtk_widget_get_toplevel.restype = LPCVOID
+        lib.gtk_widget_get_toplevel.argtypes = [LPCVOID]
+
+        lib.gtk_init.argtypes = [LPCVOID, LPCVOID]
+        lib.gtk_widget_get_toplevel.argtypes = [LPCVOID]
+        lib.gtk_widget_hide.argtypes = [LPCVOID]
+        lib.gtk_window_iconify.argtypes = [LPCVOID]
+        lib.gtk_window_maximize.argtypes = [LPCVOID]
+        lib.gtk_window_present.argtypes = [LPCVOID]
+        lib.gtk_window_close.argtypes = [LPCVOID]
+        lib.gtk_window_get_title.argtypes = [LPCVOID]
+        lib.gtk_window_set_title.argtypes = [LPCVOID, LPCSTR]
+        lib.gtk_main.argtypes = []
+        lib.gtk_main_quit.argtypes = []
+
         lib.gtk_init(None, None)
         return lib
 
@@ -276,6 +340,7 @@ class LinuxWindow:
 
         def __init__(self):
             super().__init__()
+            self.hwnd = None
             self._gtk = _init_lib()
             pass
 
@@ -322,6 +387,9 @@ def get_title(self):
             self._gtk.gtk_window_get_title.restype = ctypes.c_char_p
             return self._gtk.gtk_window_get_title(self._window()).decode('utf-8')
 
+        def minimal_menu(self):
+            pass
+
         def run_app(self):
             """Run the main app message loop until window been closed."""
             self._gtk.gtk_main()
diff --git a/sciter/value.py b/sciter/value.py
index a9e4753..f202a5f 100644
--- a/sciter/value.py
+++ b/sciter/value.py
@@ -1,6 +1,7 @@
 """Python interface to sciter::value."""
 
-import inspect
+# TODO: Date support.
+
 import ctypes
 
 import sciter
@@ -23,13 +24,16 @@
                  VALUE_TYPE.T_ARRAY: list,
                  VALUE_TYPE.T_MAP: dict,
                  VALUE_TYPE.T_BYTES: bytes,
+                 VALUE_TYPE.T_DURATION: float,
+                 VALUE_TYPE.T_ANGLE: float,
+                 VALUE_TYPE.T_COLOR: int,
                  }
 
-_value_type_names = [name.lower()[2:] for name, val in VALUE_TYPE.__members__.items()]
+_value_type_names = {val: name.lower()[2:] for name, val in VALUE_TYPE.__members__.items()}
 
 
 def _subtype_name(subtype):
-    return [name.split('_')[-1].lower() for name, val in subtype.__members__.items()]
+    return {val: name.split('_')[-1].lower() for name, val in subtype.__members__.items()}
 
 _value_subtypes = {VALUE_TYPE.T_LENGTH: _subtype_name(VALUE_UNIT_TYPE),
                    VALUE_TYPE.T_DATE: _subtype_name(VALUE_UNIT_TYPE_DATE),
@@ -87,6 +91,29 @@ def secure_string(cls, val):
         rv._assign_str(val, VALUE_UNIT_TYPE_STRING.UT_STRING_SECURE)
         return rv
 
+    @classmethod
+    def color(cls, val):
+        """Make explicit value of color type, in 0xAABBGGRR form."""
+        rv = value()
+        ok = _api.ValueIntDataSet(rv, val, VALUE_TYPE.T_COLOR, 0)
+        rv._throw_if(ok)
+        return rv
+
+    @classmethod
+    def duration(cls, val):
+        """Make explicit duration value, in seconds."""
+        rv = value()
+        ok = _api.ValueFloatDataSet(rv, val, VALUE_TYPE.T_DURATION, 0)
+        rv._throw_if(ok)
+        return rv
+
+    @classmethod
+    def angle(cls, val):
+        """Make explicit angle value, in radians."""
+        rv = value()
+        ok = _api.ValueFloatDataSet(rv, val, VALUE_TYPE.T_ANGLE, 0)
+        rv._throw_if(ok)
+        return rv
 
     ## @name Value methods:
 
@@ -95,7 +122,7 @@ def __init__(self, val=None):
         super().__init__()
         self.data = SCITER_VALUE()
         self.ptr = ctypes.pointer(self.data)
-        self._as_parameter_ = self.ptr
+        self._as_parameter_ = self.ptr  # allows instance to be used as ctypes parameter
         _api.ValueInit(self.ptr)
         if val is not None:
             self.set_value(val)
@@ -113,14 +140,14 @@ def __call__(self, *args, **kwargs):
     def __repr__(self):
         """Machine-like value visualization."""
         t = VALUE_TYPE(self.data.t)
-        tname = _value_type_names[self.data.t]
+        tname = _value_type_names.get(self.data.t, hex(self.data.t))
         if t in (VALUE_TYPE.T_UNDEFINED, VALUE_TYPE.T_NULL):
             return "<%s>" % (tname)
 
         if self.data.u != 0:
             subtypes = _value_subtypes.get(t)
             if subtypes:
-                tname = tname + ':' + subtypes[self.data.u]
+                tname = tname + ':' + subtypes.get(self.data.u, hex(self.data.u))
 
         return "<%s: %s>" % (tname, str(self))
 
@@ -128,6 +155,7 @@ def __str__(self):
         """Human-like value representation."""
         copy = self.copy()
         ok = _api.ValueToString(copy, VALUE_STRING_CVT_TYPE.CVT_JSON_LITERAL)
+        self._throw_if(ok)
         return copy.get_value()
 
     def __bool__(self):
@@ -219,6 +247,8 @@ def isolate(self):
         """Convert T_OBJECT value types to T_MAP or T_ARRAY.
 
         It will convert all object-arrays to plain JSON arrays – removing all references of script objects.
+
+        Also must be used if you need to pass values between different threads.
         """
         ok = _api.ValueIsolate(self)
         self._throw_if(ok)
@@ -269,14 +299,14 @@ def values(self):
     def append(self, val):
         """Append value to the end of T_ARRAY sciter::value."""
         xval = value(val)
-        ok = _api.ValueNthElementSet(self.length(), xval)
+        ok = _api.ValueNthElementValueSet(self.length(), xval)
         self._throw_if(ok)
         return self
 
     def insert(self, i, val):
         """Insert or set value at given index of T_ARRAY, T_MAP, T_FUNCTION and T_OBJECT sciter::value."""
         xval = value(val)
-        ok = _api.ValueNthElementSet(i, xval)
+        ok = _api.ValueNthElementValueSet(i, xval)
         self._throw_if(ok)
         return self
 
@@ -300,14 +330,12 @@ def items(self):
         if not self.get_type() in (VALUE_TYPE.T_MAP, VALUE_TYPE.T_FUNCTION, VALUE_TYPE.T_OBJECT):
             raise AttributeError("'%s' has no attribute '%s'" % (self.get_type(), 'items'))
         r = []
-        for n in range(self.length()):
-            xkey = value()
-            xval = value()
-            ok = _api.ValueNthElementKey(self, n, xkey)
-            self._throw_if(ok)
-            ok = _api.ValueNthElementValue(self, n, xval)
-            self._throw_if(ok)
-            r.append((xkey, xval))
+        def on_element(param, key, val):
+            r.append((value(key.contents), value(val.contents)))
+            return True
+        scfunc = sciter.capi.scdef.KeyValueCallback(on_element)
+        ok = _api.ValueEnumElements(self, scfunc, None)
+        self._throw_if(ok)
         return tuple(r)
 
 
@@ -360,7 +388,7 @@ def is_bytes(self):
 
     def is_string(self):
         """."""
-        t, u = self.get_type(with_unit=True)
+        t, _ = self.get_type(with_unit=True)
         return t == VALUE_TYPE.T_STRING
 
     def is_error_string(self):
@@ -373,6 +401,21 @@ def is_symbol(self):
         t, u = self.get_type(with_unit=True)
         return t == VALUE_TYPE.T_STRING and u == VALUE_UNIT_TYPE_STRING.UT_STRING_SYMBOL
 
+    def is_color(self):
+        """."""
+        t = self.get_type()
+        return t == VALUE_TYPE.T_COLOR
+
+    def is_duration(self):
+        """."""
+        t = self.get_type()
+        return t == VALUE_TYPE.T_DURATION
+
+    def is_angle(self):
+        """."""
+        t = self.get_type()
+        return t == VALUE_TYPE.T_ANGLE
+
     def is_array(self):
         """."""
         t = self.get_type()
@@ -383,6 +426,46 @@ def is_map(self):
         t = self.get_type()
         return t == VALUE_TYPE.T_MAP
 
+    def is_object(self):
+        """."""
+        t = self.get_type()
+        return t == VALUE_TYPE.T_OBJECT
+
+    def is_function(self):
+        """."""
+        t = self.get_type()
+        return t == VALUE_TYPE.T_FUNCTION
+
+    def is_native_function(self):
+        """."""
+        return _api.ValueIsNativeFunctor(self) != 0
+
+    def is_object_function(self):
+        """."""
+        t, u = self.get_type(with_unit=True)
+        return t == VALUE_TYPE.T_OBJECT and u == VALUE_UNIT_TYPE_OBJECT.UT_OBJECT_FUNCTION
+
+    def is_object_error(self):
+        """."""
+        t, u = self.get_type(with_unit=True)
+        return t == VALUE_TYPE.T_OBJECT and u == VALUE_UNIT_TYPE_OBJECT.UT_OBJECT_ERROR
+
+    def is_varray(self):
+        """."""
+        return self.is_array()
+
+    def is_vmap(self):
+        """."""
+        return self.is_map() or self.is_object_map()
+
+    def is_vfunction(self):
+        """."""
+        return self.is_function() or self.is_object_function() or self.is_native_function()
+
+    def is_verror(self):
+        """."""
+        return self.is_error_string() or self.is_object_error()
+
     def get_type(self, py=False, with_unit=False):
         """Return python type or sciter type with (optionally) unit subtype of sciter::value."""
         t = VALUE_TYPE(self.data.t)
@@ -450,9 +533,37 @@ def get_value(self):
             return self._get_list()
         elif t == VALUE_TYPE.T_MAP:
             return self._get_dict()
-        else:
-            raise TypeError(str(t) + " is unsupported python type")
-        pass
+        elif t == VALUE_TYPE.T_DURATION:
+            v = ctypes.c_double()
+            ok = _api.ValueFloatData(self, byref(v))
+            self._throw_if(ok)
+            return float(v.value)
+        elif t == VALUE_TYPE.T_ANGLE:
+            v = ctypes.c_double()
+            ok = _api.ValueFloatData(self, byref(v))
+            self._throw_if(ok)
+            return float(v.value)
+        elif t == VALUE_TYPE.T_COLOR:
+            v = ctypes.c_int32()
+            ok = _api.ValueIntData(self, byref(v))
+            self._throw_if(ok)
+            return int(v.value)
+        elif t == VALUE_TYPE.T_OBJECT:
+            u = self.data.u
+            if u == VALUE_UNIT_TYPE_OBJECT.UT_OBJECT_ARRAY:
+                return self._get_list()
+            elif u == VALUE_UNIT_TYPE_OBJECT.UT_OBJECT_OBJECT:
+                return self._get_dict()
+            pass
+
+        if self.is_vfunction():
+            # return functions as is
+            return self
+
+        # unsupported:
+        t, u = self.get_type(with_unit=True)
+        u = str(u).rpartition('.')[2]
+        raise TypeError("%s (%s) is unsupported python type" % (str(t), str(u)))
 
     def set_value(self, val):
         """Set Python object to the sciter::value.
@@ -495,25 +606,32 @@ def set_value(self, val):
         elif isinstance(val, (value, SCITER_VALUE)):
             ok = _api.ValueCopy(self, val)
             self._throw_if(ok)
-        elif inspect.isroutine(val):
+        elif self._is_callable(val):
             ok = self._assign_function(val)
             self._throw_if(ok)
         else:
             raise TypeError(str(type(val)) + " is unsupported sciter type")
         pass
 
+    def _is_callable(self, val):
+        # `callable` was removed in 3.0 until 3.2
+        try:
+            return callable(val)
+        except:
+            import inspect
+            return inspect.isroutine(val)
+        pass
+
     def _assign_list(self, val):
-        # special case to explicitly make an empty array
-        json = '[]'
-        ok = _api.ValueFromString(self, json, len(json), VALUE_STRING_CVT_TYPE.CVT_JSON_LITERAL)
+        # explicit array creation since 3.3.2.6
+        ok = _api.ValueIntDataSet(self, len(val), VALUE_TYPE.T_ARRAY, 0)
         for i, v in enumerate(val):
             self[i] = v
         return ok
 
     def _assign_dict(self, val):
-        # special case to explicitly make an empty dict
-        json = '{}'
-        ok = _api.ValueFromString(self, json, len(json), VALUE_STRING_CVT_TYPE.CVT_JSON_LITERAL)
+        # explicit map creation since 3.3.2.6
+        ok = _api.ValueIntDataSet(self, 0, VALUE_TYPE.T_MAP, 0)
         for k, v in val.items():
             self[k] = v
         return ok
@@ -572,11 +690,9 @@ def pack_to(scval, val):
     def pack_args(*args, **kwargs):
         """Pack arguments tuple as SCITER_VALUE array."""
         argc = len(args)
-        args_type = SCITER_VALUE * argc
-        argv = args_type()
+        argv = value_array(length=argc)
         for i, v in enumerate(args):
-            sv = sciter.Value(v)
-            sv.copy_to(argv[i])
+            argv[i] = v
         this = value(kwargs.get('this'))
         return (argc, argv, this)
 
@@ -594,6 +710,50 @@ def raise_from(error_val, success: bool, name: str):
 # end
 
 
+class value_array:
+    """
+    Wrapper for SCITER_VALUE Array
+    """
+
+    def __init__(self, length: int):
+        """Return a new sciter value array wrapped object."""
+        super().__init__()
+        self.data = (SCITER_VALUE * length)()
+        self.ptr = ctypes.pointer(self.data)
+
+    @property
+    def _as_parameter_(self):
+        """
+        Called when passing this object as parameter to a ctypes function.
+        """
+        return ctypes.cast(self.ptr, ctypes.POINTER(SCITER_VALUE))
+
+    def __getitem__(self, index) -> value:
+        """Get value from array."""
+        return value(self.data[index])
+
+    def __setitem__(self, index: int, val):
+        """Set value to array."""
+        if not isinstance(val, value):
+            val = value(val)
+        val.copy_to(self.data[index])
+
+    def __len__(self) -> int:
+        """Get array length."""
+        return len(self.data)
+
+    def __iter__(self):
+        """Get array iterator."""
+        return iter(self.data)
+
+    def __del__(self):
+        """Clear array from memory"""
+        for val in self:
+            # clear all values in array
+            _api.ValueClear(val)
+        _api.ValueClear(self)  # clear array itself
+
+
 _native_cache = []
 
 
diff --git a/sciter/window.py b/sciter/window.py
index 319cfd0..98cd36f 100644
--- a/sciter/window.py
+++ b/sciter/window.py
@@ -12,7 +12,7 @@
 class Window(sciter.platform.BaseWindow, sciter.host.Host, sciter.event.EventHandler):
     """Basic Sciter window."""
 
-    def __init__(self, ismain=False, ispopup=False, ischild=False, resizeable=True, parent=None, uni_theme=False, debug=True):
+    def __init__(self, ismain=False, ispopup=False, ischild=False, resizeable=True, parent=None, uni_theme=False, debug=True, pos=None, size=None, subscription=None):
         """Create a new window and setup the sciter and dom callbacks."""
         super().__init__()
         from sciter.capi.scdef import SCITER_CREATE_WINDOW_FLAGS
@@ -21,7 +21,7 @@ def __init__(self, ismain=False, ispopup=False, ischild=False, resizeable=True,
         if resizeable:
             flags = flags | SCITER_CREATE_WINDOW_FLAGS.SW_RESIZEABLE
         if ismain:
-            flags = flags | SCITER_CREATE_WINDOW_FLAGS.SW_TITLEBAR
+            flags = flags | SCITER_CREATE_WINDOW_FLAGS.SW_MAIN | SCITER_CREATE_WINDOW_FLAGS.SW_TITLEBAR
         elif ispopup:
             flags = flags | SCITER_CREATE_WINDOW_FLAGS.SW_POPUP
         elif ischild:
@@ -32,16 +32,35 @@ def __init__(self, ismain=False, ispopup=False, ischild=False, resizeable=True,
 
         if debug:
             flags = flags | SCITER_CREATE_WINDOW_FLAGS.SW_ENABLE_DEBUG
+
+        # New windows can be inspectable.
+        # Debug messages will be printed always.
+        # If you need to disable the debug output,
+        # either call `frame.setup_debug(debug_output=False)`
+        # or override `Host.on_debug_output`.
+        self.setup_debug(debug_windows=debug, debug_output=True)
+
         self.window_flags = flags
         self._title_changed = False
-        self.hwnd = self._create(flags, rect=None, parent=None)
+
+        rect = sciter.capi.sctypes.RECT()
+        if pos is not None:
+            rect.left = pos[0]
+            rect.top = pos[1]
+            if size is None:
+                raise ValueError("`size` is required if `pos` is provided!")
+        if size is not None:
+            rect.right = rect.left + size[0]
+            rect.bottom = rect.top + size[1]
+        if not pos and not size:
+            rect = None
+
+        self.hwnd = self._create(flags, rect=rect, parent=parent)
         if not self.hwnd:
             raise sciter.SciterError("Could not create window")
 
-        if debug:
-            self.setup_debug()
         self.setup_callback(self.hwnd)
-        self.attach(window=self.hwnd)
+        self.attach(window=self.hwnd, subscription=subscription)
         pass
 
     def collapse(self, hide=False):
@@ -65,6 +84,10 @@ def get_title(self):
         """Get native window title."""
         return super().get_title()
 
+    def minimal_menu(self):
+        """Construct a minimal menu with a Quit item. Vital for macOS."""
+        return super().minimal_menu()
+
     def run_app(self, show=True):
         """Show window and run the main app message loop until window been closed."""
         if show:
@@ -87,11 +110,4 @@ def _document_ready(self, target):
             self.set_title(title.get_text())
         pass
 
-    def document_close(self):
-        # Quit application if main window was closed
-        if self.window_flags & sciter.capi.scdef.SCITER_CREATE_WINDOW_FLAGS.SW_TITLEBAR:
-            self.quit_app()
-        super().document_close()
-        pass
-
     pass
diff --git a/setup.cfg b/setup.cfg
index cb75a5d..6d16e1b 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,3 +1,3 @@
 [flake8]
 max-line-length = 160
-ignore = F403,N801,E123,D203
+ignore = F403,F405,N801,E123,D203
diff --git a/setup.py b/setup.py
index 3a5c8d1..293f078 100644
--- a/setup.py
+++ b/setup.py
@@ -1,3 +1,6 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
 try:
     from setuptools import setup
 except ImportError:
@@ -7,37 +10,86 @@
     'name': 'PySciter',
     'author': 'pravic',
     'author_email': 'ehysta@gmail.com',
-    'description': 'Python bindings for the Sciter - Embeddable HTML/CSS/script engine.',
-    'url': 'http://github.com/pravic/pysciter/',
-    'download_url': 'http://github.com/pravic/pysciter/',
-    'version': '0.4',
+    'description': 'Python bindings for the Sciter - Embeddable HTML/CSS/script engine (cross-platform desktop GUI toolkit).',
+    'url': 'https://github.com/sciter-sdk/pysciter/',
+    'download_url': 'https://github.com/sciter-sdk/pysciter/releases',
+    'bugtrack_url': 'https://github.com/sciter-sdk/pysciter/issues',
+    'version': '0.6.9',
+    'platforms': ['Windows', 'Linux', 'MacOS X', ],
     'packages': ['sciter', 'sciter.capi'],
     'install_requires': [''],
     'scripts': [],
-    'keywords': ['gui', 'sciter', 'tiscript', 'htmlayout', 'html', 'css', 'web', 'cross-platform',],
+    'keywords': ['gui', 'sciter', 'javascript', 'tiscript', 'htmlayout', 'html', 'css', 'web', 'cross-platform', ],
     'license': 'MIT',
     'classifiers': [
-        'Development Status :: 4 - Beta',
+        'Development Status :: 5 - Production/Stable',
         'Intended Audience :: Developers',
         'Operating System :: OS Independent',
-        'Operating System :: MacOS :: MacOS X',
         'Operating System :: Microsoft :: Windows :: Windows XP',
         'Operating System :: Microsoft :: Windows :: Windows Vista',
         'Operating System :: Microsoft :: Windows :: Windows 7',
+        'Operating System :: Microsoft :: Windows :: Windows 10',
+        'Operating System :: MacOS :: MacOS X',
         'Operating System :: POSIX :: Linux',
         'Environment :: MacOS X',
         'Environment :: Win32 (MS Windows)',
         'Environment :: X11 Applications :: GTK',
+        'Environment :: Web Environment',
+        'Programming Language :: C++',
+        'Programming Language :: JavaScript',
         'Programming Language :: Python',
-        'Programming Language :: Python :: 2',
-        'Programming Language :: Python :: 2.7',
         'Programming Language :: Python :: 3',
-        'Programming Language :: Python :: 3.5',
-        'Topic :: Software Development :: User Interfaces',
+        'Topic :: Desktop Environment',
+        'Topic :: Software Development',
         'Topic :: Software Development :: Libraries :: Application Frameworks',
         'Topic :: Software Development :: Libraries :: Python Modules',
+        'Topic :: Software Development :: User Interfaces',
         'Topic :: Software Development :: Widget Sets',
     ],
+    'long_description': """
+Introduction
+============
+Sciter (https://sciter.com) is an embeddable HTML/CSS/script engine with GPU accelerated rendering for desktop application UI.
+It's a compact, single dll/dylib/so file (4-8 mb), engine without any additional dependencies.
+
+Sciter uses Direct2D GPU accelerated graphics on modern Windows versions and GDI+ on XP.
+On OS X, it uses standard CoreGraphics primitives, while the Linux version uses Cairo.
+
+Sciter uses HTML5 set of elements, implements CSS level 2.1 in full, plus the most popular features of CSS level 3.
+It also contains custom CSS extensions that are required to support desktop UI cases.
+For example, flex units and various layout managers.
+
+Check the `screenshot gallery <https://github.com/oskca/sciter#sciter-desktop-ui-examples>`_ of the desktop UI examples.
+
+
+Installation
+============
+
+For installation instructions and usage examples please refer to `github project page <https://github.com/sciter-sdk/pysciter#getting-started>`_.
+
+
+Compatibility
+=============
+
+PySciter requires Python 3.x.
+
+Sciter works on:
+
+- Microsoft Windows XP and above (x86/x64)
+- macOS v 10.7 and above (64-bit)
+- Linux/GTK (GTK v 3.0 and above, 64-bit only)
+- Raspberry Pi
+
+
+Feedback and getting involved
+=============================
+- PySciter Code Repository: https://github.com/sciter-sdk/pysciter
+- Issue tracker: https://github.com/sciter-sdk/pysciter/issues
+- Sciter official website: https://sciter.com
+- Sciter forum: https://sciter.com/forums/
+- Sciter SDK: https://github.com/c-smile/sciter-sdk
+
+""",
 }
 
 setup(**config)
diff --git a/tests/test_value.py b/tests/test_value.py
index 9e39c0b..6165384 100644
--- a/tests/test_value.py
+++ b/tests/test_value.py
@@ -113,12 +113,26 @@ def test_11bytes(self):
                 xval = value(item)
                 with self.assertRaises(TypeError):
                     bval = bytes(xval)
+                    print(bval)
                 continue
         item = b'hello, world'
         xval = value(item)
         self.assertEqual(bytes(xval), bytes(item))
         pass
 
+    def test_11func(self):
+        def inner_fn():
+            return 17
+
+        v = value(inner_fn)
+        r = v.call()
+        self.assertEqual(r, 17)
+
+        v = value(lambda x, y: x + y)
+        r = v.call(42, 1)
+        self.assertEqual(r, 43)
+        pass
+
     def test_12len(self):
         items = [[], {}, [3, 4], {'5': 5, '6': 6}]
         for item in items:
@@ -157,7 +171,7 @@ def test_14getitem(self):
 
             self.assertEqual(xval[0], value(3))
             self.assertEqual(xval[1], value(4))
-            self.assertEqual(xval[-1],value(5))
+            self.assertEqual(xval[-1], value(5))
 
             with self.assertRaises(IndexError):
                 r = xval[20]
@@ -172,6 +186,7 @@ def test_14getitem(self):
 
             with self.assertRaises(KeyError):
                 r = xval['not exist']
+                print(r)
         pass
 
     def test_16setitem(self):
@@ -250,6 +265,58 @@ def test_19explicit(self):
         self.assertTrue(xval.is_error_string())
         self.assertEqual(xval.get_value(), 'error')  # doesn't raise exception.
 
+        if sciter.version_num() > 0x04000100:
+            # color
+            xval = value.color(0x0000FFFF)      # yellow R255, G255, B000 in 0xAABBGGRR form
+            self.assertTrue(xval.is_color())
+            self.assertEqual(xval.get_value(), 0x0000FFFF)
+
+            # duration
+            xval = value.duration(12.5)
+            self.assertTrue(xval.is_duration())
+            self.assertEqual(xval.get_value(), 12.5)
+
+            # angle
+            xval = value.angle(1.0)
+            self.assertTrue(xval.is_angle())
+            self.assertEqual(xval.get_value(), 1.0)
+            pass
+        pass
+
+    def test_20clear(self):
+        xval = value()
+        self.assertTrue(xval.is_undefined())
+        xval.clear()
+        self.assertTrue(xval.is_undefined())
+
+        xval = value.null()
+        self.assertTrue(xval.is_null())
+        xval.clear()
+        self.assertTrue(xval.is_undefined())
+
+        xval = value.symbol('hello')
+        self.assertTrue(xval.is_symbol())
+        xval.clear()
+        self.assertTrue(xval.is_undefined())
+
+        xval = value(17)
+        self.assertTrue(xval.is_int())
+        xval.clear()
+        self.assertTrue(xval.is_undefined())
+
+        pass
+
+    def test_21items(self):
+        item = {'5': 5, '6': 6}
+        xval = value(item)
+        self.assertEqual(len(xval), 2)
+
+        items = list(item.items())
+        xitems = list(xval.items())
+
+        self.assertEqual(len(xitems), 2)
+        self.assertEqual(xitems[0][0].get_value(), items[0][0])
+        self.assertEqual(xitems[1][1].get_value(), items[1][1])
         pass
 
     # Sequence operations