Initial import of Dillo
273
.github/workflows/build.yml
vendored
Normal file
@ -0,0 +1,273 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches: master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ubuntu-latest-html-tests:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
with:
|
||||||
|
fetch-depth: 1
|
||||||
|
- name: Install dependencies
|
||||||
|
run: sudo apt install -y libfltk1.3-dev libssl-dev libpng-dev libjpeg-dev libwebp-dev xvfb x11-apps x11-utils imagemagick
|
||||||
|
|
||||||
|
- name: autogen
|
||||||
|
run: ./autogen.sh
|
||||||
|
- name: Make install dir
|
||||||
|
run: mkdir install build
|
||||||
|
- name: configure
|
||||||
|
run: cd build && ../configure --prefix=$(readlink -f ../install) --enable-html-tests
|
||||||
|
- name: make
|
||||||
|
run: cd build && make
|
||||||
|
- name: make install
|
||||||
|
run: cd build && make install
|
||||||
|
- name: Copy config to .dillo
|
||||||
|
run: |
|
||||||
|
mkdir -p ~/.dillo/
|
||||||
|
cp install/etc/dillo/* ~/.dillo/
|
||||||
|
- name: make check
|
||||||
|
run: |
|
||||||
|
export DILLOBIN=$(readlink -f install/bin/dillo)
|
||||||
|
cd build && make check || (cat test/html/test-suite.log; false)
|
||||||
|
export DILLOBIN=
|
||||||
|
- name: Check release fits in a floppy disk of 1.44 MB
|
||||||
|
run: |
|
||||||
|
cd build
|
||||||
|
make dist-gzip
|
||||||
|
size=$(stat -c %s dillo-*.tar.gz)
|
||||||
|
floppy=$((1474560 - 32*1024)) # Leave room for FAT table
|
||||||
|
echo "Floppy occupation: $(($size * 100 / $floppy)) %"
|
||||||
|
if [ $size -lt $floppy ]; then
|
||||||
|
echo 'OK: Fits in floopy disk'
|
||||||
|
else
|
||||||
|
echo "FAIL: Release size too big: $size / $floppy"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
- name: make distcheck (with HTML tests)
|
||||||
|
run: |
|
||||||
|
export DILLOBIN=
|
||||||
|
mkdir build-distcheck install-distcheck
|
||||||
|
cd build-distcheck && ../configure --prefix=$(readlink -f ../install-distcheck) --enable-html-tests
|
||||||
|
make distcheck DISTCHECK_CONFIGURE_FLAGS=--enable-html-tests
|
||||||
|
# - name: Remove pipes
|
||||||
|
# run: find test/html -type p -delete || true
|
||||||
|
# - name: Archive production artifacts
|
||||||
|
# uses: actions/upload-artifact@v3
|
||||||
|
# with:
|
||||||
|
# name: upload-html-test-results
|
||||||
|
# path: |
|
||||||
|
# build/test/html
|
||||||
|
ubuntu-latest-no-tls:
|
||||||
|
needs: ubuntu-latest-html-tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
with:
|
||||||
|
fetch-depth: 1
|
||||||
|
- name: Install dependencies
|
||||||
|
run: sudo apt install -y libfltk1.3-dev
|
||||||
|
- name: autogen
|
||||||
|
run: ./autogen.sh
|
||||||
|
- name: configure
|
||||||
|
run: ./configure --disable-tls
|
||||||
|
- name: make
|
||||||
|
run: make
|
||||||
|
- name: make check
|
||||||
|
run: make check
|
||||||
|
- name: make distcheck
|
||||||
|
run: make distcheck
|
||||||
|
ubuntu-latest-mbedtls2:
|
||||||
|
needs: ubuntu-latest-html-tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
with:
|
||||||
|
fetch-depth: 1
|
||||||
|
- name: Install dependencies
|
||||||
|
run: sudo apt install -y libfltk1.3-dev libmbedtls-dev
|
||||||
|
- name: autogen
|
||||||
|
run: ./autogen.sh
|
||||||
|
- name: configure
|
||||||
|
run: ./configure --disable-openssl
|
||||||
|
- name: make
|
||||||
|
run: make
|
||||||
|
- name: make check
|
||||||
|
run: make check
|
||||||
|
- name: make distcheck
|
||||||
|
run: make distcheck
|
||||||
|
ubuntu-latest-openssl-3:
|
||||||
|
needs: ubuntu-latest-html-tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
with:
|
||||||
|
fetch-depth: 1
|
||||||
|
- name: Install dependencies
|
||||||
|
run: sudo apt install -y libfltk1.3-dev libssl-dev
|
||||||
|
- name: autogen
|
||||||
|
run: ./autogen.sh
|
||||||
|
- name: configure
|
||||||
|
run: ./configure --disable-mbedtls
|
||||||
|
- name: make
|
||||||
|
run: make
|
||||||
|
- name: make check
|
||||||
|
run: make check
|
||||||
|
- name: make distcheck
|
||||||
|
run: make distcheck
|
||||||
|
ubuntu-latest-with-old-std:
|
||||||
|
needs: ubuntu-latest-html-tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
with:
|
||||||
|
fetch-depth: 1
|
||||||
|
- name: Install dependencies
|
||||||
|
run: sudo apt install -y libfltk1.3-dev libssl-dev
|
||||||
|
- name: autogen
|
||||||
|
run: ./autogen.sh
|
||||||
|
- name: configure
|
||||||
|
# Make sure we build with the C++11 standard, so we fail to build on newer
|
||||||
|
# features.
|
||||||
|
run: ./configure --disable-mbedtls CFLAGS="-Werror" CXXFLAGS="-Werror -std=c++11"
|
||||||
|
- name: make
|
||||||
|
run: make
|
||||||
|
- name: make check
|
||||||
|
run: make check
|
||||||
|
ubuntu-20-04-openssl-1-1:
|
||||||
|
needs: ubuntu-latest-html-tests
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
with:
|
||||||
|
fetch-depth: 1
|
||||||
|
- name: Install dependencies
|
||||||
|
run: sudo apt install -y libfltk1.3-dev libssl-dev
|
||||||
|
- name: autogen
|
||||||
|
run: ./autogen.sh
|
||||||
|
- name: configure
|
||||||
|
run: ./configure --disable-mbedtls
|
||||||
|
- name: make
|
||||||
|
run: make
|
||||||
|
- name: make check
|
||||||
|
run: make check
|
||||||
|
- name: make distcheck
|
||||||
|
run: make distcheck
|
||||||
|
alpine-mbedtls-3_6_0:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: jirutka/setup-alpine@v1
|
||||||
|
with:
|
||||||
|
packages: >
|
||||||
|
build-base
|
||||||
|
autoconf
|
||||||
|
automake
|
||||||
|
fltk-dev
|
||||||
|
libpng-dev
|
||||||
|
libjpeg-turbo-dev
|
||||||
|
libwebp-dev
|
||||||
|
mbedtls-dev
|
||||||
|
- run: |
|
||||||
|
./autogen.sh
|
||||||
|
./configure
|
||||||
|
make
|
||||||
|
make check
|
||||||
|
shell: alpine.sh {0}
|
||||||
|
macOS-13-openssl-1-1:
|
||||||
|
needs: ubuntu-latest-html-tests
|
||||||
|
runs-on: macos-13
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
with:
|
||||||
|
fetch-depth: 1
|
||||||
|
- name: Install dependencies
|
||||||
|
run: brew install autoconf automake fltk@1.3
|
||||||
|
- name: autogen
|
||||||
|
run: ./autogen.sh
|
||||||
|
- name: configure
|
||||||
|
run: |
|
||||||
|
export PATH="/usr/local/opt/fltk@1.3/bin:$PATH"
|
||||||
|
./configure --disable-mbedtls
|
||||||
|
- name: make
|
||||||
|
run: make
|
||||||
|
- name: make check
|
||||||
|
run: make check
|
||||||
|
- name: make distcheck
|
||||||
|
run: |
|
||||||
|
export PATH="/usr/local/opt/fltk@1.3/bin:$PATH"
|
||||||
|
make distcheck
|
||||||
|
macOS-13-openssl-3:
|
||||||
|
needs: ubuntu-latest-html-tests
|
||||||
|
runs-on: macos-13
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
with:
|
||||||
|
fetch-depth: 1
|
||||||
|
- name: Remove old OpenSSL 1.1
|
||||||
|
run: brew uninstall openssl@1.1
|
||||||
|
- name: Install dependencies
|
||||||
|
run: brew install autoconf automake fltk@1.3 openssl@3
|
||||||
|
- name: autogen
|
||||||
|
run: ./autogen.sh
|
||||||
|
- name: configure
|
||||||
|
run: |
|
||||||
|
export PATH="/usr/local/opt/fltk@1.3/bin:$PATH"
|
||||||
|
./configure --disable-mbedtls
|
||||||
|
- name: make
|
||||||
|
run: make
|
||||||
|
- name: make check
|
||||||
|
run: make check
|
||||||
|
- name: make distcheck
|
||||||
|
run: |
|
||||||
|
export PATH="/usr/local/opt/fltk@1.3/bin:$PATH"
|
||||||
|
make distcheck
|
||||||
|
freebsd-14-openssl-3:
|
||||||
|
needs: ubuntu-latest-html-tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: FreeBSD VM build
|
||||||
|
id: test
|
||||||
|
uses: vmactions/freebsd-vm@v1
|
||||||
|
with:
|
||||||
|
release: "14.0"
|
||||||
|
usesh: true
|
||||||
|
prepare: |
|
||||||
|
set -x
|
||||||
|
pkg install -y automake fltk
|
||||||
|
run: |
|
||||||
|
set -x
|
||||||
|
pwd
|
||||||
|
freebsd-version
|
||||||
|
./autogen.sh
|
||||||
|
./configure CPPFLAGS='-I/usr/local/include' LDFLAGS='-L/usr/local/lib'
|
||||||
|
cat config.log
|
||||||
|
make
|
||||||
|
make check
|
||||||
|
ldd src/dillo
|
||||||
|
windows-mbedtls:
|
||||||
|
needs: ubuntu-latest-html-tests
|
||||||
|
runs-on: windows-latest
|
||||||
|
steps:
|
||||||
|
- run: git config --global core.autocrlf input
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 1
|
||||||
|
- uses: cygwin/cygwin-install-action@master
|
||||||
|
with:
|
||||||
|
packages: gcc-core gcc-g++ autoconf automake make zlib-devel mbedtls-devel libfltk-devel libiconv-devel libpng-devel libjpeg-devel libwebp-devel libgif-devel
|
||||||
|
- shell: C:\cygwin\bin\bash.exe --login --norc -eo pipefail -o igncr '{0}'
|
||||||
|
run: |
|
||||||
|
set -x
|
||||||
|
cd ${GITHUB_WORKSPACE}
|
||||||
|
pwd
|
||||||
|
ls -l
|
||||||
|
./autogen.sh
|
||||||
|
./configure
|
||||||
|
make
|
||||||
|
make check
|
||||||
|
ls -l src/dillo
|
||||||
47
.gitignore
vendored
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
*.a
|
||||||
|
*.o
|
||||||
|
config.*
|
||||||
|
autom4te.cache
|
||||||
|
Makefile
|
||||||
|
Makefile.in
|
||||||
|
configure
|
||||||
|
compile
|
||||||
|
build
|
||||||
|
aclocal.m4
|
||||||
|
depcomp
|
||||||
|
install-sh
|
||||||
|
stamp-h1
|
||||||
|
missing
|
||||||
|
tags
|
||||||
|
/src/dillo
|
||||||
|
/doc/dillo.1
|
||||||
|
/dpi/[^/]*\.dpi
|
||||||
|
/dpid/dpid
|
||||||
|
/dpid/dpidc
|
||||||
|
/dpid/dpidrc
|
||||||
|
/test/containers
|
||||||
|
/test/cookies
|
||||||
|
/test/dw-anchors-test
|
||||||
|
/test/dw-border-test
|
||||||
|
/test/dw-example
|
||||||
|
/test/dw-find-test
|
||||||
|
/test/dw-float-test
|
||||||
|
/test/dw-image-background
|
||||||
|
/test/dw-images-scaled
|
||||||
|
/test/dw-images-scaled2
|
||||||
|
/test/dw-images-simple
|
||||||
|
/test/dw-imgbuf-mem-test
|
||||||
|
/test/dw-links
|
||||||
|
/test/dw-links2
|
||||||
|
/test/dw-lists
|
||||||
|
/test/dw-resource-test
|
||||||
|
/test/dw-simple-container-test
|
||||||
|
/test/dw-table
|
||||||
|
/test/dw-table-aligned
|
||||||
|
/test/dw-ui-test
|
||||||
|
/test/identity
|
||||||
|
/test/liang
|
||||||
|
/test/notsosimplevector
|
||||||
|
/test/shapes
|
||||||
|
/test/trie
|
||||||
|
/test/unicode-test
|
||||||
12
.rtfl
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[rtfl-obj-1.0]:0:0:class-color:dw\:\:*:#c0ff80
|
||||||
|
[rtfl-obj-1.0]:0:0:class-color:dw\:\:fltk\:\:*:#c0c0ff
|
||||||
|
[rtfl-obj-1.0]:0:0:class-color:dw\:\:core\:\:*:#ffa0a0
|
||||||
|
[rtfl-obj-1.0]:0:0:class-color:dw\:\:core\:\:style\:\:*:#ffe0a0
|
||||||
|
[rtfl-obj-1.0]:0:0:class-color:dw\:\:core\:\:SizeParams:#e0e0a0
|
||||||
|
[rtfl-obj-1.0]:0:0:class-color:dw\:\:Image:#80ffa0
|
||||||
|
[rtfl-obj-1.0]:0:0:class-color:dw\:\:Textblock:#f0ff80
|
||||||
|
[rtfl-obj-1.0]:0:0:class-color:dw\:\:OutOfFlowMgr:#d0ff80
|
||||||
|
[rtfl-obj-1.0]:0:0:class-color:dw\:\:AlignedTextblock:#e0ff80
|
||||||
|
[rtfl-obj-1.0]:0:0:class-color:dw\:\:ListItem:#b0ff80
|
||||||
|
[rtfl-obj-1.0]:0:0:class-color:dw\:\:TableCell:#80ff80
|
||||||
|
[rtfl-obj-1.0]:0:0:class-color:dw\:\:Table:#80ffc0
|
||||||
103
AUTHORS
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
_________________________________________________________________
|
||||||
|
|
||||||
|
Flenser project's team
|
||||||
|
_________________________________________________________________
|
||||||
|
|
||||||
|
Project maintainer:
|
||||||
|
* ADAM David Alan Martin
|
||||||
|
|
||||||
|
Core Developers:
|
||||||
|
* ADAM David Alan Martin
|
||||||
|
|
||||||
|
------------------
|
||||||
|
Forked from Dillo
|
||||||
|
------------------
|
||||||
|
|
||||||
|
_________________________________________________________________
|
||||||
|
|
||||||
|
Dillo project's team
|
||||||
|
_________________________________________________________________
|
||||||
|
|
||||||
|
Project maintainer:
|
||||||
|
* Jorge Arellano Cid
|
||||||
|
|
||||||
|
Core Developers:
|
||||||
|
* Jorge Arellano Cid
|
||||||
|
* Sebastian Geerken
|
||||||
|
* Johannes Hofmann
|
||||||
|
* Place (aka corvid)
|
||||||
|
* Luca Rota
|
||||||
|
|
||||||
|
Steady developers:
|
||||||
|
* Livio Baldini
|
||||||
|
* Eric Gaudet
|
||||||
|
* Jeremy Henty
|
||||||
|
* Jörgen Viksell
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
* Lars Clausen
|
||||||
|
* Ferdi Franceschini
|
||||||
|
* Matthias Franz
|
||||||
|
* Geoff Lane
|
||||||
|
* Sammy Mannaert
|
||||||
|
* James McCollough
|
||||||
|
* Justus Winter
|
||||||
|
|
||||||
|
Patches:
|
||||||
|
* Philip Blundell
|
||||||
|
* Francis Daly
|
||||||
|
* Sam Dennis
|
||||||
|
* Melvin Hadasht
|
||||||
|
* Madis Janson
|
||||||
|
* Frank de Lange
|
||||||
|
* Andrew McPherson
|
||||||
|
* Sean 'Shaleh' Perry
|
||||||
|
* Marcos Ramírez
|
||||||
|
* Adam Sampson
|
||||||
|
* Andreas Schweitzer
|
||||||
|
* Dominic Wong
|
||||||
|
_________________________________________________________________
|
||||||
|
|
||||||
|
Web site logo:
|
||||||
|
* Eric Gaudet
|
||||||
|
* Jarrod Henry
|
||||||
|
|
||||||
|
Gzilla author:
|
||||||
|
* Raph Levien
|
||||||
|
_________________________________________________________________
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-------------------
|
||||||
|
Up to gzilla-0.1.7:
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Raph Levien <raph@acm.org> is the author of gzilla.
|
||||||
|
|
||||||
|
Christoph Thompson <obituary@freshmeat.net> and Tomas O"gren <stric@ing.umu.se>
|
||||||
|
added pixmaps for the buttons and the code encessary to make them work.
|
||||||
|
They also reorganized the source tree and gave general advice and tips.
|
||||||
|
(Tomas will be pleased to find that my personal bookmarks file is no longer
|
||||||
|
hard-coded into Gzilla. :))
|
||||||
|
|
||||||
|
Ian Main <slow@intergate.bc.ca> did the bookmarks.
|
||||||
|
|
||||||
|
Thanks to Otto Hammersmith, David Mosberger-Tang, and Peter Mattis for
|
||||||
|
patches.
|
||||||
|
|
||||||
|
Contributions are always welcome!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---------------
|
||||||
|
Non-Dillo code:
|
||||||
|
---------------
|
||||||
|
|
||||||
|
* dw/fltkcomplexbutton.(cc|hh) contain code from the FLTK project whose
|
||||||
|
copyright is held by Bill Spitzak and others.
|
||||||
|
* src/binaryconst.h contains code by Tom Torfs that the author placed in the
|
||||||
|
public domain.
|
||||||
|
* src/md5.[ch] contain code by L. Peter Deutsch whose copyright is held by
|
||||||
|
Aladdin Enterprises.
|
||||||
|
* src/tipwin.cc contains code by Greg Ercolano.
|
||||||
674
COPYING
Normal file
@ -0,0 +1,674 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
the GNU General Public License is intended to guarantee your freedom to
|
||||||
|
share and change all versions of a program--to make sure it remains free
|
||||||
|
software for all its users. We, the Free Software Foundation, use the
|
||||||
|
GNU General Public License for most of our software; it applies also to
|
||||||
|
any other work released this way by its authors. You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to prevent others from denying you
|
||||||
|
these rights or asking you to surrender the rights. Therefore, you have
|
||||||
|
certain responsibilities if you distribute copies of the software, or if
|
||||||
|
you modify it: responsibilities to respect the freedom of others.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must pass on to the recipients the same
|
||||||
|
freedoms that you received. You must make sure that they, too, receive
|
||||||
|
or can get the source code. And you must show them these terms so they
|
||||||
|
know their rights.
|
||||||
|
|
||||||
|
Developers that use the GNU GPL protect your rights with two steps:
|
||||||
|
(1) assert copyright on the software, and (2) offer you this License
|
||||||
|
giving you legal permission to copy, distribute and/or modify it.
|
||||||
|
|
||||||
|
For the developers' and authors' protection, the GPL clearly explains
|
||||||
|
that there is no warranty for this free software. For both users' and
|
||||||
|
authors' sake, the GPL requires that modified versions be marked as
|
||||||
|
changed, so that their problems will not be attributed erroneously to
|
||||||
|
authors of previous versions.
|
||||||
|
|
||||||
|
Some devices are designed to deny users access to install or run
|
||||||
|
modified versions of the software inside them, although the manufacturer
|
||||||
|
can do so. This is fundamentally incompatible with the aim of
|
||||||
|
protecting users' freedom to change the software. The systematic
|
||||||
|
pattern of such abuse occurs in the area of products for individuals to
|
||||||
|
use, which is precisely where it is most unacceptable. Therefore, we
|
||||||
|
have designed this version of the GPL to prohibit the practice for those
|
||||||
|
products. If such problems arise substantially in other domains, we
|
||||||
|
stand ready to extend this provision to those domains in future versions
|
||||||
|
of the GPL, as needed to protect the freedom of users.
|
||||||
|
|
||||||
|
Finally, every program is threatened constantly by software patents.
|
||||||
|
States should not allow patents to restrict development and use of
|
||||||
|
software on general-purpose computers, but in those that do, we wish to
|
||||||
|
avoid the special danger that patents applied to a free program could
|
||||||
|
make it effectively proprietary. To prevent this, the GPL assures that
|
||||||
|
patents cannot be used to render the program non-free.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work
|
||||||
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Use with the GNU Affero General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU Affero General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the special requirements of the GNU Affero General Public License,
|
||||||
|
section 13, concerning interaction through a network will apply to the
|
||||||
|
combination as such.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
state the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program does terminal interaction, make it output a short
|
||||||
|
notice like this when it starts in an interactive mode:
|
||||||
|
|
||||||
|
<program> Copyright (C) <year> <name of author>
|
||||||
|
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, your program's commands
|
||||||
|
might be different; for a GUI interface, you would use an "about box".
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
|
For more information on this, and how to apply and follow the GNU GPL, see
|
||||||
|
<http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
The GNU General Public License does not permit incorporating your program
|
||||||
|
into proprietary programs. If your program is a subroutine library, you
|
||||||
|
may consider it more useful to permit linking proprietary applications with
|
||||||
|
the library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License. But first, please read
|
||||||
|
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||||
1
FUNDING.yml
Normal file
@ -0,0 +1 @@
|
|||||||
|
liberapay: dillo
|
||||||
365
INSTALL
Normal file
@ -0,0 +1,365 @@
|
|||||||
|
Installation Instructions
|
||||||
|
*************************
|
||||||
|
|
||||||
|
Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
|
||||||
|
2006, 2007, 2008, 2009 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
Copying and distribution of this file, with or without modification,
|
||||||
|
are permitted in any medium without royalty provided the copyright
|
||||||
|
notice and this notice are preserved. This file is offered as-is,
|
||||||
|
without warranty of any kind.
|
||||||
|
|
||||||
|
Basic Installation
|
||||||
|
==================
|
||||||
|
|
||||||
|
Briefly, the shell commands `./configure; make; make install' should
|
||||||
|
configure, build, and install this package. The following
|
||||||
|
more-detailed instructions are generic; see the `README' file for
|
||||||
|
instructions specific to this package. Some packages provide this
|
||||||
|
`INSTALL' file but do not implement all of the features documented
|
||||||
|
below. The lack of an optional feature in a given package is not
|
||||||
|
necessarily a bug. More recommendations for GNU packages can be found
|
||||||
|
in *note Makefile Conventions: (standards)Makefile Conventions.
|
||||||
|
|
||||||
|
The `configure' shell script attempts to guess correct values for
|
||||||
|
various system-dependent variables used during compilation. It uses
|
||||||
|
those values to create a `Makefile' in each directory of the package.
|
||||||
|
It may also create one or more `.h' files containing system-dependent
|
||||||
|
definitions. Finally, it creates a shell script `config.status' that
|
||||||
|
you can run in the future to recreate the current configuration, and a
|
||||||
|
file `config.log' containing compiler output (useful mainly for
|
||||||
|
debugging `configure').
|
||||||
|
|
||||||
|
It can also use an optional file (typically called `config.cache'
|
||||||
|
and enabled with `--cache-file=config.cache' or simply `-C') that saves
|
||||||
|
the results of its tests to speed up reconfiguring. Caching is
|
||||||
|
disabled by default to prevent problems with accidental use of stale
|
||||||
|
cache files.
|
||||||
|
|
||||||
|
If you need to do unusual things to compile the package, please try
|
||||||
|
to figure out how `configure' could check whether to do them, and mail
|
||||||
|
diffs or instructions to the address given in the `README' so they can
|
||||||
|
be considered for the next release. If you are using the cache, and at
|
||||||
|
some point `config.cache' contains results you don't want to keep, you
|
||||||
|
may remove or edit it.
|
||||||
|
|
||||||
|
The file `configure.ac' (or `configure.in') is used to create
|
||||||
|
`configure' by a program called `autoconf'. You need `configure.ac' if
|
||||||
|
you want to change it or regenerate `configure' using a newer version
|
||||||
|
of `autoconf'.
|
||||||
|
|
||||||
|
The simplest way to compile this package is:
|
||||||
|
|
||||||
|
1. `cd' to the directory containing the package's source code and type
|
||||||
|
`./configure' to configure the package for your system.
|
||||||
|
|
||||||
|
Running `configure' might take a while. While running, it prints
|
||||||
|
some messages telling which features it is checking for.
|
||||||
|
|
||||||
|
2. Type `make' to compile the package.
|
||||||
|
|
||||||
|
3. Optionally, type `make check' to run any self-tests that come with
|
||||||
|
the package, generally using the just-built uninstalled binaries.
|
||||||
|
|
||||||
|
4. Type `make install' to install the programs and any data files and
|
||||||
|
documentation. When installing into a prefix owned by root, it is
|
||||||
|
recommended that the package be configured and built as a regular
|
||||||
|
user, and only the `make install' phase executed with root
|
||||||
|
privileges.
|
||||||
|
|
||||||
|
5. Optionally, type `make installcheck' to repeat any self-tests, but
|
||||||
|
this time using the binaries in their final installed location.
|
||||||
|
This target does not install anything. Running this target as a
|
||||||
|
regular user, particularly if the prior `make install' required
|
||||||
|
root privileges, verifies that the installation completed
|
||||||
|
correctly.
|
||||||
|
|
||||||
|
6. You can remove the program binaries and object files from the
|
||||||
|
source code directory by typing `make clean'. To also remove the
|
||||||
|
files that `configure' created (so you can compile the package for
|
||||||
|
a different kind of computer), type `make distclean'. There is
|
||||||
|
also a `make maintainer-clean' target, but that is intended mainly
|
||||||
|
for the package's developers. If you use it, you may have to get
|
||||||
|
all sorts of other programs in order to regenerate files that came
|
||||||
|
with the distribution.
|
||||||
|
|
||||||
|
7. Often, you can also type `make uninstall' to remove the installed
|
||||||
|
files again. In practice, not all packages have tested that
|
||||||
|
uninstallation works correctly, even though it is required by the
|
||||||
|
GNU Coding Standards.
|
||||||
|
|
||||||
|
8. Some packages, particularly those that use Automake, provide `make
|
||||||
|
distcheck', which can by used by developers to test that all other
|
||||||
|
targets like `make install' and `make uninstall' work correctly.
|
||||||
|
This target is generally not run by end users.
|
||||||
|
|
||||||
|
Compilers and Options
|
||||||
|
=====================
|
||||||
|
|
||||||
|
Some systems require unusual options for compilation or linking that
|
||||||
|
the `configure' script does not know about. Run `./configure --help'
|
||||||
|
for details on some of the pertinent environment variables.
|
||||||
|
|
||||||
|
You can give `configure' initial values for configuration parameters
|
||||||
|
by setting variables in the command line or in the environment. Here
|
||||||
|
is an example:
|
||||||
|
|
||||||
|
./configure CC=c99 CFLAGS=-g LIBS=-lposix
|
||||||
|
|
||||||
|
*Note Defining Variables::, for more details.
|
||||||
|
|
||||||
|
Compiling For Multiple Architectures
|
||||||
|
====================================
|
||||||
|
|
||||||
|
You can compile the package for more than one kind of computer at the
|
||||||
|
same time, by placing the object files for each architecture in their
|
||||||
|
own directory. To do this, you can use GNU `make'. `cd' to the
|
||||||
|
directory where you want the object files and executables to go and run
|
||||||
|
the `configure' script. `configure' automatically checks for the
|
||||||
|
source code in the directory that `configure' is in and in `..'. This
|
||||||
|
is known as a "VPATH" build.
|
||||||
|
|
||||||
|
With a non-GNU `make', it is safer to compile the package for one
|
||||||
|
architecture at a time in the source code directory. After you have
|
||||||
|
installed the package for one architecture, use `make distclean' before
|
||||||
|
reconfiguring for another architecture.
|
||||||
|
|
||||||
|
On MacOS X 10.5 and later systems, you can create libraries and
|
||||||
|
executables that work on multiple system types--known as "fat" or
|
||||||
|
"universal" binaries--by specifying multiple `-arch' options to the
|
||||||
|
compiler but only a single `-arch' option to the preprocessor. Like
|
||||||
|
this:
|
||||||
|
|
||||||
|
./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
|
||||||
|
CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
|
||||||
|
CPP="gcc -E" CXXCPP="g++ -E"
|
||||||
|
|
||||||
|
This is not guaranteed to produce working output in all cases, you
|
||||||
|
may have to build one architecture at a time and combine the results
|
||||||
|
using the `lipo' tool if you have problems.
|
||||||
|
|
||||||
|
Installation Names
|
||||||
|
==================
|
||||||
|
|
||||||
|
By default, `make install' installs the package's commands under
|
||||||
|
`/usr/local/bin', include files under `/usr/local/include', etc. You
|
||||||
|
can specify an installation prefix other than `/usr/local' by giving
|
||||||
|
`configure' the option `--prefix=PREFIX', where PREFIX must be an
|
||||||
|
absolute file name.
|
||||||
|
|
||||||
|
You can specify separate installation prefixes for
|
||||||
|
architecture-specific files and architecture-independent files. If you
|
||||||
|
pass the option `--exec-prefix=PREFIX' to `configure', the package uses
|
||||||
|
PREFIX as the prefix for installing programs and libraries.
|
||||||
|
Documentation and other data files still use the regular prefix.
|
||||||
|
|
||||||
|
In addition, if you use an unusual directory layout you can give
|
||||||
|
options like `--bindir=DIR' to specify different values for particular
|
||||||
|
kinds of files. Run `configure --help' for a list of the directories
|
||||||
|
you can set and what kinds of files go in them. In general, the
|
||||||
|
default for these options is expressed in terms of `${prefix}', so that
|
||||||
|
specifying just `--prefix' will affect all of the other directory
|
||||||
|
specifications that were not explicitly provided.
|
||||||
|
|
||||||
|
The most portable way to affect installation locations is to pass the
|
||||||
|
correct locations to `configure'; however, many packages provide one or
|
||||||
|
both of the following shortcuts of passing variable assignments to the
|
||||||
|
`make install' command line to change installation locations without
|
||||||
|
having to reconfigure or recompile.
|
||||||
|
|
||||||
|
The first method involves providing an override variable for each
|
||||||
|
affected directory. For example, `make install
|
||||||
|
prefix=/alternate/directory' will choose an alternate location for all
|
||||||
|
directory configuration variables that were expressed in terms of
|
||||||
|
`${prefix}'. Any directories that were specified during `configure',
|
||||||
|
but not in terms of `${prefix}', must each be overridden at install
|
||||||
|
time for the entire installation to be relocated. The approach of
|
||||||
|
makefile variable overrides for each directory variable is required by
|
||||||
|
the GNU Coding Standards, and ideally causes no recompilation.
|
||||||
|
However, some platforms have known limitations with the semantics of
|
||||||
|
shared libraries that end up requiring recompilation when using this
|
||||||
|
method, particularly noticeable in packages that use GNU Libtool.
|
||||||
|
|
||||||
|
The second method involves providing the `DESTDIR' variable. For
|
||||||
|
example, `make install DESTDIR=/alternate/directory' will prepend
|
||||||
|
`/alternate/directory' before all installation names. The approach of
|
||||||
|
`DESTDIR' overrides is not required by the GNU Coding Standards, and
|
||||||
|
does not work on platforms that have drive letters. On the other hand,
|
||||||
|
it does better at avoiding recompilation issues, and works well even
|
||||||
|
when some directory options were not specified in terms of `${prefix}'
|
||||||
|
at `configure' time.
|
||||||
|
|
||||||
|
Optional Features
|
||||||
|
=================
|
||||||
|
|
||||||
|
If the package supports it, you can cause programs to be installed
|
||||||
|
with an extra prefix or suffix on their names by giving `configure' the
|
||||||
|
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
|
||||||
|
|
||||||
|
Some packages pay attention to `--enable-FEATURE' options to
|
||||||
|
`configure', where FEATURE indicates an optional part of the package.
|
||||||
|
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
|
||||||
|
is something like `gnu-as' or `x' (for the X Window System). The
|
||||||
|
`README' should mention any `--enable-' and `--with-' options that the
|
||||||
|
package recognizes.
|
||||||
|
|
||||||
|
For packages that use the X Window System, `configure' can usually
|
||||||
|
find the X include and library files automatically, but if it doesn't,
|
||||||
|
you can use the `configure' options `--x-includes=DIR' and
|
||||||
|
`--x-libraries=DIR' to specify their locations.
|
||||||
|
|
||||||
|
Some packages offer the ability to configure how verbose the
|
||||||
|
execution of `make' will be. For these packages, running `./configure
|
||||||
|
--enable-silent-rules' sets the default to minimal output, which can be
|
||||||
|
overridden with `make V=1'; while running `./configure
|
||||||
|
--disable-silent-rules' sets the default to verbose, which can be
|
||||||
|
overridden with `make V=0'.
|
||||||
|
|
||||||
|
Particular systems
|
||||||
|
==================
|
||||||
|
|
||||||
|
On HP-UX, the default C compiler is not ANSI C compatible. If GNU
|
||||||
|
CC is not installed, it is recommended to use the following options in
|
||||||
|
order to use an ANSI C compiler:
|
||||||
|
|
||||||
|
./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
|
||||||
|
|
||||||
|
and if that doesn't work, install pre-built binaries of GCC for HP-UX.
|
||||||
|
|
||||||
|
On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
|
||||||
|
parse its `<wchar.h>' header file. The option `-nodtk' can be used as
|
||||||
|
a workaround. If GNU CC is not installed, it is therefore recommended
|
||||||
|
to try
|
||||||
|
|
||||||
|
./configure CC="cc"
|
||||||
|
|
||||||
|
and if that doesn't work, try
|
||||||
|
|
||||||
|
./configure CC="cc -nodtk"
|
||||||
|
|
||||||
|
On Solaris, don't put `/usr/ucb' early in your `PATH'. This
|
||||||
|
directory contains several dysfunctional programs; working variants of
|
||||||
|
these programs are available in `/usr/bin'. So, if you need `/usr/ucb'
|
||||||
|
in your `PATH', put it _after_ `/usr/bin'.
|
||||||
|
|
||||||
|
On Haiku, software installed for all users goes in `/boot/common',
|
||||||
|
not `/usr/local'. It is recommended to use the following options:
|
||||||
|
|
||||||
|
./configure --prefix=/boot/common
|
||||||
|
|
||||||
|
Specifying the System Type
|
||||||
|
==========================
|
||||||
|
|
||||||
|
There may be some features `configure' cannot figure out
|
||||||
|
automatically, but needs to determine by the type of machine the package
|
||||||
|
will run on. Usually, assuming the package is built to be run on the
|
||||||
|
_same_ architectures, `configure' can figure that out, but if it prints
|
||||||
|
a message saying it cannot guess the machine type, give it the
|
||||||
|
`--build=TYPE' option. TYPE can either be a short name for the system
|
||||||
|
type, such as `sun4', or a canonical name which has the form:
|
||||||
|
|
||||||
|
CPU-COMPANY-SYSTEM
|
||||||
|
|
||||||
|
where SYSTEM can have one of these forms:
|
||||||
|
|
||||||
|
OS
|
||||||
|
KERNEL-OS
|
||||||
|
|
||||||
|
See the file `config.sub' for the possible values of each field. If
|
||||||
|
`config.sub' isn't included in this package, then this package doesn't
|
||||||
|
need to know the machine type.
|
||||||
|
|
||||||
|
If you are _building_ compiler tools for cross-compiling, you should
|
||||||
|
use the option `--target=TYPE' to select the type of system they will
|
||||||
|
produce code for.
|
||||||
|
|
||||||
|
If you want to _use_ a cross compiler, that generates code for a
|
||||||
|
platform different from the build platform, you should specify the
|
||||||
|
"host" platform (i.e., that on which the generated programs will
|
||||||
|
eventually be run) with `--host=TYPE'.
|
||||||
|
|
||||||
|
Sharing Defaults
|
||||||
|
================
|
||||||
|
|
||||||
|
If you want to set default values for `configure' scripts to share,
|
||||||
|
you can create a site shell script called `config.site' that gives
|
||||||
|
default values for variables like `CC', `cache_file', and `prefix'.
|
||||||
|
`configure' looks for `PREFIX/share/config.site' if it exists, then
|
||||||
|
`PREFIX/etc/config.site' if it exists. Or, you can set the
|
||||||
|
`CONFIG_SITE' environment variable to the location of the site script.
|
||||||
|
A warning: not all `configure' scripts look for a site script.
|
||||||
|
|
||||||
|
Defining Variables
|
||||||
|
==================
|
||||||
|
|
||||||
|
Variables not defined in a site shell script can be set in the
|
||||||
|
environment passed to `configure'. However, some packages may run
|
||||||
|
configure again during the build, and the customized values of these
|
||||||
|
variables may be lost. In order to avoid this problem, you should set
|
||||||
|
them in the `configure' command line, using `VAR=value'. For example:
|
||||||
|
|
||||||
|
./configure CC=/usr/local2/bin/gcc
|
||||||
|
|
||||||
|
causes the specified `gcc' to be used as the C compiler (unless it is
|
||||||
|
overridden in the site shell script).
|
||||||
|
|
||||||
|
Unfortunately, this technique does not work for `CONFIG_SHELL' due to
|
||||||
|
an Autoconf bug. Until the bug is fixed you can use this workaround:
|
||||||
|
|
||||||
|
CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
|
||||||
|
|
||||||
|
`configure' Invocation
|
||||||
|
======================
|
||||||
|
|
||||||
|
`configure' recognizes the following options to control how it
|
||||||
|
operates.
|
||||||
|
|
||||||
|
`--help'
|
||||||
|
`-h'
|
||||||
|
Print a summary of all of the options to `configure', and exit.
|
||||||
|
|
||||||
|
`--help=short'
|
||||||
|
`--help=recursive'
|
||||||
|
Print a summary of the options unique to this package's
|
||||||
|
`configure', and exit. The `short' variant lists options used
|
||||||
|
only in the top level, while the `recursive' variant lists options
|
||||||
|
also present in any nested packages.
|
||||||
|
|
||||||
|
`--version'
|
||||||
|
`-V'
|
||||||
|
Print the version of Autoconf used to generate the `configure'
|
||||||
|
script, and exit.
|
||||||
|
|
||||||
|
`--cache-file=FILE'
|
||||||
|
Enable the cache: use and save the results of the tests in FILE,
|
||||||
|
traditionally `config.cache'. FILE defaults to `/dev/null' to
|
||||||
|
disable caching.
|
||||||
|
|
||||||
|
`--config-cache'
|
||||||
|
`-C'
|
||||||
|
Alias for `--cache-file=config.cache'.
|
||||||
|
|
||||||
|
`--quiet'
|
||||||
|
`--silent'
|
||||||
|
`-q'
|
||||||
|
Do not print messages saying which checks are being made. To
|
||||||
|
suppress all normal output, redirect it to `/dev/null' (any error
|
||||||
|
messages will still be shown).
|
||||||
|
|
||||||
|
`--srcdir=DIR'
|
||||||
|
Look for the package's source code in directory DIR. Usually
|
||||||
|
`configure' can determine that directory automatically.
|
||||||
|
|
||||||
|
`--prefix=DIR'
|
||||||
|
Use DIR as the installation prefix. *note Installation Names::
|
||||||
|
for more details, including other options available for fine-tuning
|
||||||
|
the installation locations.
|
||||||
|
|
||||||
|
`--no-create'
|
||||||
|
`-n'
|
||||||
|
Run the configure checks, but stop before creating any output
|
||||||
|
files.
|
||||||
|
|
||||||
|
`configure' also accepts some other, not widely useful, options. Run
|
||||||
|
`configure --help' for more details.
|
||||||
|
|
||||||
18
Makefile.am
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
SUBDIRS = lout dw dlib dpip src doc dpid dpi test
|
||||||
|
|
||||||
|
dist_bin_SCRIPTS = dillo-install-hyphenation
|
||||||
|
|
||||||
|
EXTRA_DIST = README.md Doxyfile dillorc install-dpi-local d_size.h
|
||||||
|
|
||||||
|
sysconf_DATA = dillorc
|
||||||
|
|
||||||
|
desktopdir = $(datadir)/applications
|
||||||
|
desktop_DATA = dillo.desktop
|
||||||
|
|
||||||
|
appicon48dir = $(datadir)/icons/hicolor/48x48/apps/
|
||||||
|
appicon48_DATA = icons/48x48/dillo.png
|
||||||
|
|
||||||
|
appicon128dir = $(datadir)/icons/hicolor/128x128/apps/
|
||||||
|
appicon128_DATA = icons/128x128/dillo.png
|
||||||
|
|
||||||
|
EXTRA_DIST += $(desktop_DATA) $(appicon48_DATA) $(appicon128_DATA)
|
||||||
57
NEWS
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
----
|
||||||
|
NEWS
|
||||||
|
----
|
||||||
|
|
||||||
|
Nov 2000:
|
||||||
|
|
||||||
|
Introduced a new design layer between the IO and the Dw.
|
||||||
|
|
||||||
|
March 2001:
|
||||||
|
|
||||||
|
Finally the new dillo widget is ready! (dillo >= 0.4.0).
|
||||||
|
0.4.0 is able to cope with low resolution depths.
|
||||||
|
|
||||||
|
Apr 2002:
|
||||||
|
|
||||||
|
We moved to: http://dillo.cipsga.org.br/
|
||||||
|
|
||||||
|
Dec 2002
|
||||||
|
|
||||||
|
We moved to: http://dillo.auriga.wearlab.de/
|
||||||
|
|
||||||
|
Jun 2003
|
||||||
|
|
||||||
|
http://www.dillo.org/ (hosted at the wearlab!)
|
||||||
|
|
||||||
|
Sep 2007
|
||||||
|
|
||||||
|
The new FLTK2-based dillo code is released! (under GPL3)
|
||||||
|
|
||||||
|
Jul 2011
|
||||||
|
|
||||||
|
Dillo switches to fltk-1.3.0.
|
||||||
|
dillo-2.2.1, the last of the 2.x series is released.
|
||||||
|
|
||||||
|
Sep 2011
|
||||||
|
|
||||||
|
dillo-3.0, the first of the 3.x series is released.
|
||||||
|
|
||||||
|
dillo-3.0.1, a bugfix release for the new 3.x series is out.
|
||||||
|
|
||||||
|
Mar 2013
|
||||||
|
|
||||||
|
dillo-3.0.3, a new release for the new 3.x series is out.
|
||||||
|
|
||||||
|
Apr 2014
|
||||||
|
|
||||||
|
dillo-3.0.4, a new release for the new 3.x series is out.
|
||||||
|
|
||||||
|
Dec 2014
|
||||||
|
|
||||||
|
dillo-3.0.4.1, a new release for the new 3.x series is out.
|
||||||
|
|
||||||
|
|
||||||
|
Jorge.-
|
||||||
|
jcid@dillo.org
|
||||||
|
Project maintainer, core developer, patcher, you name it! :-)
|
||||||
|
|
||||||
36
README.dillo.md
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# Dillo web browser
|
||||||
|
|
||||||
|
Dillo is a multi-platform graphical web browser, known for its speed and
|
||||||
|
small footprint, that is developed with a focus on personal security and
|
||||||
|
privacy. It is built with the [FLTK 1.3 GUI toolkit](http://fltk.org).
|
||||||
|
|
||||||
|
Screenshot of the [Dillo Website][dillo] rendered in Dillo:
|
||||||
|
|
||||||
|
[dillo]: https://dillo-browser.github.io/
|
||||||
|
|
||||||
|
<p align="center"><img src="doc/dillo.png" width="60%" /></p>
|
||||||
|
|
||||||
|
To install Dillo follow the [installation guide](doc/install.md).
|
||||||
|
|
||||||
|
This repository contains mostly the original code of Dillo with some
|
||||||
|
minor patches. Additional patches or pull requests are welcome.
|
||||||
|
|
||||||
|
See also other related forks: [dillo-plus][dillo-plus],
|
||||||
|
[dilloNG][dilloNG], [D+ browser][dplus-browser] and [Mobilized
|
||||||
|
Dillo][mobilized].
|
||||||
|
|
||||||
|
[dillo-plus]: https://github.com/crossbowerbt/dillo-plus
|
||||||
|
[dilloNG]: https://github.com/w00fpack/dilloNG
|
||||||
|
[dplus-browser]: https://sourceforge.net/projects/dplus-browser/
|
||||||
|
[mobilized]: https://www.toomanyatoms.com/software/mobilized_dillo.html
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
> As of December 2023, the host `dillo.org` is [no longer under control][gone]
|
||||||
|
> of Dillo developers. A copy of the old website is archived in
|
||||||
|
> [GitHub Pages][old] and the [Wayback Machine (May 2022)][may].
|
||||||
|
|
||||||
|
[gone]: https://dillo-browser.github.io/dillo.org.html
|
||||||
|
[old]: https://dillo-browser.github.io/old/
|
||||||
|
[may]: http://web.archive.org/web/20220508022123/https://www.dillo.org/
|
||||||
|
|
||||||
|
Forked from dillo on the fbd719f93ab659fec6c42952e76f5e5b971728be commit.
|
||||||
59
autogen.sh
Executable file
@ -0,0 +1,59 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# Script to generate configure&make stuff
|
||||||
|
#
|
||||||
|
|
||||||
|
#-----------------------------------------------------
|
||||||
|
# If defined, get these programs from the environment
|
||||||
|
#
|
||||||
|
: ${ACLOCAL:=aclocal}
|
||||||
|
: ${AUTOHEADER:=autoheader}
|
||||||
|
: ${AUTOCONF:=autoconf}
|
||||||
|
: ${AUTOMAKE:=automake}
|
||||||
|
|
||||||
|
#-------------------------
|
||||||
|
# Required binaries check
|
||||||
|
#
|
||||||
|
check_bin_file(){
|
||||||
|
command -v $1 > /dev/null 2>&1
|
||||||
|
if [ $? = 0 ]; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
#------
|
||||||
|
# Main
|
||||||
|
#
|
||||||
|
|
||||||
|
#clear
|
||||||
|
ERR="no"
|
||||||
|
for cmd in "$ACLOCAL" "$AUTOHEADER" "$AUTOCONF" "$AUTOMAKE"
|
||||||
|
do
|
||||||
|
if check_bin_file "$cmd"
|
||||||
|
then
|
||||||
|
echo -e "$cmd \tfound"
|
||||||
|
else
|
||||||
|
echo -e "$cmd \tNOT found"
|
||||||
|
ERR="yes"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if test $ERR = "yes"
|
||||||
|
then
|
||||||
|
echo
|
||||||
|
echo "ERROR: to run this program you need the following installed"
|
||||||
|
echo " $ACLOCAL $AUTOHEADER $AUTOCONF $AUTOMAKE"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[Checks passed]"
|
||||||
|
echo "Generating..."
|
||||||
|
|
||||||
|
"$ACLOCAL"
|
||||||
|
"$AUTOHEADER"
|
||||||
|
"$AUTOCONF"
|
||||||
|
"$AUTOMAKE" -a
|
||||||
|
|
||||||
798
configure.ac
Normal file
@ -0,0 +1,798 @@
|
|||||||
|
dnl Process this file with aclocal, autoconf and automake.
|
||||||
|
|
||||||
|
AC_INIT([dillo], [3.2.0])
|
||||||
|
|
||||||
|
dnl Detect the canonical target build environment
|
||||||
|
AC_CANONICAL_TARGET
|
||||||
|
|
||||||
|
AM_INIT_AUTOMAKE
|
||||||
|
AC_CONFIG_SRCDIR([src/dillo.cc])
|
||||||
|
AC_CONFIG_HEADERS([config.h])
|
||||||
|
|
||||||
|
sysconfdir=${sysconfdir}/${PACKAGE}
|
||||||
|
|
||||||
|
dnl Options
|
||||||
|
|
||||||
|
AC_ARG_WITH([jpeg-lib],
|
||||||
|
[AS_HELP_STRING([--with-jpeg-lib=DIR], [Specify where to find libjpeg])],
|
||||||
|
LIBJPEG_LIBDIR=$withval)
|
||||||
|
|
||||||
|
AC_ARG_WITH([jpeg-inc],
|
||||||
|
[AS_HELP_STRING([--with-jpeg-inc=DIR], [Specify where to find libjpeg headers])],
|
||||||
|
LIBJPEG_INCDIR=$withval)
|
||||||
|
|
||||||
|
AC_ARG_ENABLE([efence],
|
||||||
|
[AS_HELP_STRING([--enable-efence], [Try to compile and run with Electric Fence])],
|
||||||
|
[enable_efence=$enableval],
|
||||||
|
[enable_efence=no])
|
||||||
|
|
||||||
|
AC_ARG_ENABLE([gprof],
|
||||||
|
[AS_HELP_STRING([--enable-gprof], [Try to compile and run with profiling enabled])],
|
||||||
|
[enable_gprof=$enableval],
|
||||||
|
[enable_gprof=no])
|
||||||
|
|
||||||
|
AC_ARG_ENABLE([insure],
|
||||||
|
[AS_HELP_STRING([--enable-insure], [Try to compile and run with Insure++])],
|
||||||
|
[enable_insure=$enableval],
|
||||||
|
[enable_insure=no])
|
||||||
|
|
||||||
|
AC_ARG_ENABLE([ipv6],
|
||||||
|
[AS_HELP_STRING([--enable-ipv6], [Build with support for IPv6])],
|
||||||
|
[enable_ipv6=$enableval],
|
||||||
|
[enable_ipv6=no])
|
||||||
|
|
||||||
|
AC_ARG_ENABLE([cookies],
|
||||||
|
[AS_HELP_STRING([--disable-cookies], [Dont compile support for cookies])],
|
||||||
|
[enable_cookies=$enableval],
|
||||||
|
[enable_cookies=yes])
|
||||||
|
|
||||||
|
AC_ARG_ENABLE([png],
|
||||||
|
[AS_HELP_STRING([--disable-png], [Disable support for PNG images])],
|
||||||
|
[enable_png=$enableval],
|
||||||
|
[enable_png=yes])
|
||||||
|
|
||||||
|
AC_ARG_ENABLE([webp],
|
||||||
|
[AS_HELP_STRING([--disable-webp], [Disable support for WEBP images])],
|
||||||
|
[enable_webp=$enableval],
|
||||||
|
[enable_webp=yes])
|
||||||
|
|
||||||
|
AC_ARG_ENABLE([jpeg],
|
||||||
|
[AS_HELP_STRING([--disable-jpeg], [Disable support for JPEG images])],
|
||||||
|
[enable_jpeg=$enableval],
|
||||||
|
[enable_jpeg=yes])
|
||||||
|
|
||||||
|
AC_ARG_ENABLE([gif],
|
||||||
|
[AS_HELP_STRING([--disable-gif], [Disable support for GIF images])],
|
||||||
|
[enable_gif=$enableval],
|
||||||
|
[enable_gif=yes])
|
||||||
|
|
||||||
|
AC_ARG_ENABLE([svg],
|
||||||
|
[AS_HELP_STRING([--disable-svg], [Disable support for SVG images])],
|
||||||
|
[enable_svg=$enableval],
|
||||||
|
[enable_svg=yes])
|
||||||
|
|
||||||
|
AC_ARG_ENABLE([threaded-dns],
|
||||||
|
[AS_HELP_STRING([--disable-threaded-dns], [Disable the advantage of a reentrant resolver library])],
|
||||||
|
[enable_threaded_dns=$enableval],
|
||||||
|
[enable_threaded_dns=yes])
|
||||||
|
|
||||||
|
AC_ARG_ENABLE([rtfl],
|
||||||
|
[AS_HELP_STRING([--enable-rtfl], [Print low-level RTFL messages for debugging the renderer (very large slowdown)])],
|
||||||
|
[enable_rtfl=$enableval],
|
||||||
|
[enable_rtfl=no])
|
||||||
|
|
||||||
|
AC_ARG_ENABLE([xembed],
|
||||||
|
[AS_HELP_STRING([--disable-xembed], [Disable support for X11 XEmbed])],
|
||||||
|
[enable_xembed=$enableval],
|
||||||
|
[enable_xembed=yes])
|
||||||
|
|
||||||
|
AC_ARG_ENABLE([tls],
|
||||||
|
[AS_HELP_STRING([--disable-tls], [Disable TLS support (for HTTPS)])],
|
||||||
|
[enable_tls=$enableval],
|
||||||
|
[enable_tls=yes])
|
||||||
|
|
||||||
|
dnl Deprecated, if given it will cause an error
|
||||||
|
AC_ARG_ENABLE([ssl],
|
||||||
|
[AS_HELP_STRING([--disable-ssl], [Disable TLS support (deprecated, use --disable-tls)])],
|
||||||
|
[enable_ssl=$enableval],
|
||||||
|
[enable_ssl=])
|
||||||
|
|
||||||
|
AC_ARG_ENABLE([openssl],
|
||||||
|
[AS_HELP_STRING([--disable-openssl], [Disable support for OpenSSL])],
|
||||||
|
[enable_openssl=$enableval],
|
||||||
|
[enable_openssl=yes])
|
||||||
|
|
||||||
|
AC_ARG_ENABLE([mbedtls],
|
||||||
|
[AS_HELP_STRING([--disable-mbedtls], [Disable support for mbedTLS])],
|
||||||
|
[enable_mbedtls=$enableval],
|
||||||
|
[enable_mbedtls=yes])
|
||||||
|
|
||||||
|
AC_ARG_WITH([ca-certs-file],
|
||||||
|
[AS_HELP_STRING([--with-ca-certs-file=FILE], [Specify where to find a bundle of trusted CA certificates for TLS])],
|
||||||
|
CA_CERTS_FILE=$withval)
|
||||||
|
|
||||||
|
AC_ARG_WITH([ca-certs-dir],
|
||||||
|
[AS_HELP_STRING([--with-ca-certs-dir=DIR], [Specify where to find a directory containing trusted CA certificates for TLS])],
|
||||||
|
CA_CERTS_DIR=$withval)
|
||||||
|
|
||||||
|
|
||||||
|
AC_ARG_ENABLE([html-tests],
|
||||||
|
[AS_HELP_STRING([--enable-html-tests], [Enable HTML tests (requires xvfb, xwd and imagemagick)])],
|
||||||
|
[enable_html_tests=$enableval],
|
||||||
|
[enable_html_tests=no])
|
||||||
|
|
||||||
|
if test "x$enable_ssl" = "xno"; then
|
||||||
|
AC_MSG_ERROR([The flag --disable-ssl is deprecated, use --disable-tls])
|
||||||
|
fi
|
||||||
|
dnl Also catch --enable-ssl=maybe
|
||||||
|
if test "x$enable_ssl" != "x"; then
|
||||||
|
AC_MSG_ERROR([The flag --enable-ssl is deprecated, use --enable-tls])
|
||||||
|
fi
|
||||||
|
|
||||||
|
AC_PROG_CC
|
||||||
|
AC_PROG_CXX
|
||||||
|
AC_PROG_RANLIB
|
||||||
|
AC_PROG_CPP
|
||||||
|
|
||||||
|
AC_MSG_CHECKING(if C++ compiler '$CXX' works)
|
||||||
|
AC_LANG_PUSH([C++])
|
||||||
|
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
|
||||||
|
#ifndef __cplusplus
|
||||||
|
#error "No C++ support, AC_PROG_CXX failed"
|
||||||
|
#endif
|
||||||
|
]])],
|
||||||
|
[AC_MSG_RESULT([yes])],
|
||||||
|
[AC_MSG_RESULT(no)
|
||||||
|
AC_MSG_FAILURE([C++ compiler doesn't work])]
|
||||||
|
)
|
||||||
|
AC_LANG_POP([C++])
|
||||||
|
|
||||||
|
dnl ----------------------------
|
||||||
|
dnl Check our char and int types
|
||||||
|
dnl ----------------------------
|
||||||
|
dnl
|
||||||
|
AC_CHECK_SIZEOF(char)
|
||||||
|
AC_CHECK_SIZEOF(short)
|
||||||
|
AC_CHECK_SIZEOF(long)
|
||||||
|
AC_CHECK_SIZEOF(int)
|
||||||
|
AC_CHECK_SIZEOF(void *)
|
||||||
|
|
||||||
|
AC_TYPE_INT16_T
|
||||||
|
AC_TYPE_UINT16_T
|
||||||
|
AC_TYPE_INT32_T
|
||||||
|
AC_TYPE_UINT32_T
|
||||||
|
|
||||||
|
dnl -----------------------------------------------------------------
|
||||||
|
dnl Check for absolute path of working directory.
|
||||||
|
dnl This is needed for RTFL, to get full the full paths of the source
|
||||||
|
dnl file names
|
||||||
|
dnl -----------------------------------------------------------------
|
||||||
|
dnl
|
||||||
|
BASE_CUR_WORKING_DIR=`pwd`
|
||||||
|
|
||||||
|
dnl ------------------------------------
|
||||||
|
dnl Check for socket libs (AIX, Solaris)
|
||||||
|
dnl ------------------------------------
|
||||||
|
dnl
|
||||||
|
AC_CHECK_FUNCS(gethostbyname,,
|
||||||
|
[AC_CHECK_LIB(nsl,gethostbyname,,[AC_CHECK_LIB(socket,gethostbyname)])])
|
||||||
|
AC_CHECK_FUNCS(setsockopt,,[AC_CHECK_LIB(socket,setsockopt)])
|
||||||
|
|
||||||
|
dnl --------------------
|
||||||
|
dnl Checks for socklen_t
|
||||||
|
dnl --------------------
|
||||||
|
dnl
|
||||||
|
AC_MSG_CHECKING([for socklen_t])
|
||||||
|
ac_cv_socklen_t=""
|
||||||
|
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
]],[[
|
||||||
|
socklen_t a=0;
|
||||||
|
getsockname(0,(struct sockaddr*)0, &a);
|
||||||
|
]])],
|
||||||
|
ac_cv_socklen_t="socklen_t",
|
||||||
|
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
]],[[
|
||||||
|
int a=0;
|
||||||
|
getsockname(0,(struct sockaddr*)0, &a);
|
||||||
|
]])],
|
||||||
|
ac_cv_socklen_t="int",
|
||||||
|
ac_cv_socklen_t="size_t"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
AC_MSG_RESULT($ac_cv_socklen_t)
|
||||||
|
if test "$ac_cv_socklen_t" != "socklen_t"; then
|
||||||
|
AC_DEFINE_UNQUOTED([socklen_t], [$ac_cv_socklen_t],
|
||||||
|
[Define the real type of socklen_t])
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
dnl -------------------------
|
||||||
|
dnl Test for FLTK 1.3 library
|
||||||
|
dnl -------------------------
|
||||||
|
dnl
|
||||||
|
dnl For debugging and to be user friendly
|
||||||
|
AC_PATH_PROG(FLTK_CONFIG,fltk-config)
|
||||||
|
AC_MSG_CHECKING([FLTK 1.3])
|
||||||
|
fltk_version="`$FLTK_CONFIG --version 2>/dev/null`"
|
||||||
|
case $fltk_version in
|
||||||
|
1.3.*) AC_MSG_RESULT(yes)
|
||||||
|
LIBFLTK_CXXFLAGS=`$FLTK_CONFIG --cxxflags`
|
||||||
|
LIBFLTK_CFLAGS=`$FLTK_CONFIG --cflags`
|
||||||
|
LIBFLTK_LIBS=`$FLTK_CONFIG --ldflags`;;
|
||||||
|
1.4.*) AC_MSG_RESULT(no)
|
||||||
|
AC_MSG_ERROR([FLTK $fltk_version not supported yet; use FLTK 1.3]);;
|
||||||
|
?*) AC_MSG_RESULT(no)
|
||||||
|
AC_MSG_ERROR(FLTK 1.3 required; version found: $fltk_version);;
|
||||||
|
*) AC_MSG_RESULT(no)
|
||||||
|
AC_MSG_ERROR(FLTK 1.3 required; fltk-config not found)
|
||||||
|
esac
|
||||||
|
|
||||||
|
dnl -----------------------------------
|
||||||
|
dnl Test for X11 (only on some systems)
|
||||||
|
dnl -----------------------------------
|
||||||
|
AC_MSG_CHECKING([whether to link to X11])
|
||||||
|
AC_LANG_PUSH([C++])
|
||||||
|
old_libs=$LIBS
|
||||||
|
old_cxxflags=$CXXFLAGS
|
||||||
|
LIBS=$LIBFLTK_LIBS
|
||||||
|
CXXFLAGS=$LIBFLTK_CXXFLAGS
|
||||||
|
AC_RUN_IFELSE([AC_LANG_PROGRAM([[
|
||||||
|
#define FL_INTERNALS
|
||||||
|
#include <FL/x.H>
|
||||||
|
]],[[
|
||||||
|
#ifdef X_PROTOCOL
|
||||||
|
return 0;
|
||||||
|
#else
|
||||||
|
return 1;
|
||||||
|
#endif
|
||||||
|
]])], [AC_MSG_RESULT(yes)
|
||||||
|
LIBX11_LIBS="-lX11"],
|
||||||
|
[AC_MSG_RESULT(no)],
|
||||||
|
[AC_MSG_RESULT(no)
|
||||||
|
AC_MSG_WARN([*** Test for X11 not possible when cross-compiling. ***])])
|
||||||
|
CXXFLAGS=$old_cxxflags
|
||||||
|
LIBS=$old_libs
|
||||||
|
AC_LANG_POP([C++])
|
||||||
|
|
||||||
|
dnl ----------------
|
||||||
|
dnl Test for libjpeg
|
||||||
|
dnl ----------------
|
||||||
|
dnl
|
||||||
|
jpeg_ok=no
|
||||||
|
if test "x$enable_jpeg" = "xyes"; then
|
||||||
|
AC_CHECK_HEADER(jpeglib.h, jpeg_ok=yes, jpeg_ok=no)
|
||||||
|
|
||||||
|
if test "x$jpeg_ok" = "xyes"; then
|
||||||
|
old_libs="$LIBS"
|
||||||
|
AC_CHECK_LIB(jpeg, jpeg_destroy_decompress, jpeg_ok=yes, jpeg_ok=no)
|
||||||
|
LIBS="$old_libs"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "x$jpeg_ok" = "xyes"; then
|
||||||
|
LIBJPEG_LIBS="-ljpeg"
|
||||||
|
if test -n "$LIBJPEG_LIBDIR"; then
|
||||||
|
LIBJPEG_LDFLAGS="-L$LIBJPEG_LIBDIR"
|
||||||
|
fi
|
||||||
|
if test -n "$LIBJPEG_INCDIR"; then
|
||||||
|
LIBJPEG_CPPFLAGS="-I$LIBJPEG_INCDIR"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
AC_MSG_WARN([*** No libjpeg found. Disabling jpeg images.***])
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "x$jpeg_ok" = "xyes"; then
|
||||||
|
AC_DEFINE([ENABLE_JPEG], [1], [Enable JPEG images])
|
||||||
|
fi
|
||||||
|
|
||||||
|
dnl ----------------
|
||||||
|
dnl Test for libwebp
|
||||||
|
dnl ----------------
|
||||||
|
dnl
|
||||||
|
if test "x$enable_webp" = "xyes"; then
|
||||||
|
AC_CHECK_HEADER(webp/decode.h, webp_ok=yes, webp_ok=no)
|
||||||
|
|
||||||
|
if test "x$webp_ok" = "xyes"; then
|
||||||
|
old_libs="$LIBS"
|
||||||
|
AC_CHECK_LIB(webp, WebPGetInfo, webp_ok=yes, webp_ok=no)
|
||||||
|
LIBS="$old_libs"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "x$webp_ok" = "xyes"; then
|
||||||
|
LIBWEBP_LIBS="-lwebp"
|
||||||
|
else
|
||||||
|
AC_MSG_WARN([*** No libwebp found. Disabling webp images.***])
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "x$webp_ok" = "xyes"; then
|
||||||
|
AC_DEFINE([ENABLE_WEBP], [1], [Enable webp images])
|
||||||
|
fi
|
||||||
|
|
||||||
|
dnl -------------
|
||||||
|
dnl Test for zlib
|
||||||
|
dnl -------------
|
||||||
|
dnl
|
||||||
|
AC_CHECK_HEADER(zlib.h, libz_ok=yes, libz_ok=no)
|
||||||
|
|
||||||
|
if test "x$libz_ok" = "xyes"; then
|
||||||
|
old_libs="$LIBS"
|
||||||
|
AC_CHECK_LIB(z, zlibVersion, libz_ok=yes, libz_ok=no)
|
||||||
|
LIBS="$old_libs"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "x$libz_ok" = xyes; then
|
||||||
|
LIBZ_LIBS="-lz"
|
||||||
|
else
|
||||||
|
AC_MSG_ERROR(zlib must be installed!)
|
||||||
|
fi
|
||||||
|
|
||||||
|
dnl ---------------
|
||||||
|
dnl Test for libpng
|
||||||
|
dnl ---------------
|
||||||
|
dnl
|
||||||
|
png_ok="no"
|
||||||
|
if test "x$enable_png" = "xyes"; then
|
||||||
|
AC_MSG_CHECKING([for libpng-config])
|
||||||
|
|
||||||
|
dnl Check if the user hasn't set the variable $PNG_CONFIG
|
||||||
|
if test -z "$PNG_CONFIG"; then
|
||||||
|
PNG_CONFIG=`command -v libpng16-config`
|
||||||
|
if test -z "$PNG_CONFIG"; then
|
||||||
|
PNG_CONFIG=`command -v libpng14-config`
|
||||||
|
fi
|
||||||
|
if test -z "$PNG_CONFIG"; then
|
||||||
|
PNG_CONFIG=`command -v libpng12-config`
|
||||||
|
fi
|
||||||
|
if test -z "$PNG_CONFIG"; then
|
||||||
|
PNG_CONFIG=`command -v libpng-config`
|
||||||
|
fi
|
||||||
|
if test -z "$PNG_CONFIG"; then
|
||||||
|
PNG_CONFIG=`command -v libpng10-config`
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
dnl Check if the libpng-config script was found and is executable
|
||||||
|
if test -n "$PNG_CONFIG" && test -x "$PNG_CONFIG"; then
|
||||||
|
AC_MSG_RESULT([$PNG_CONFIG])
|
||||||
|
png_ok="yes"
|
||||||
|
else
|
||||||
|
AC_MSG_RESULT([missing])
|
||||||
|
png_ok="no"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "x$png_ok" = "xyes"; then
|
||||||
|
dnl For debugging and to be user friendly
|
||||||
|
AC_MSG_CHECKING([for libpng version])
|
||||||
|
png_version=`$PNG_CONFIG --version`
|
||||||
|
case $png_version in
|
||||||
|
1.[[0246]].*) AC_MSG_RESULT([$png_version]) ;;
|
||||||
|
*) AC_MSG_RESULT([$png_version (unrecognised version)]) ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
dnl Try to use options that are supported by all libpng-config versions...
|
||||||
|
LIBPNG_CFLAGS=`$PNG_CONFIG --cflags`
|
||||||
|
LIBPNG_LIBS=`$PNG_CONFIG --ldflags`
|
||||||
|
case $png_version in
|
||||||
|
1.2.4*) LIBPNG_LIBS="$LIBPNG_LIBS `$PNG_CONFIG --libs`" ;;
|
||||||
|
esac
|
||||||
|
else
|
||||||
|
dnl Try to find libpng even though libpng-config wasn't found
|
||||||
|
AC_CHECK_HEADERS(png.h libpng/png.h, png_ok=yes && break, png_ok=no)
|
||||||
|
|
||||||
|
if test "x$png_ok" = "xyes"; then
|
||||||
|
old_libs="$LIBS"
|
||||||
|
AC_CHECK_LIB(png, png_sig_cmp, png_ok=yes, png_ok=no, $LIBZ_LIBS -lm)
|
||||||
|
LIBS="$old_libs"
|
||||||
|
|
||||||
|
if test "x$png_ok" = "xyes"; then
|
||||||
|
LIBPNG_LIBS="-lpng -lm"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "x$png_ok" = "xno"; then
|
||||||
|
AC_MSG_WARN([*** No libpng found. Disabling PNG images ***])
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "x$png_ok" = "xyes"; then
|
||||||
|
AC_DEFINE([ENABLE_PNG], [1], [Enable PNG images])
|
||||||
|
fi
|
||||||
|
|
||||||
|
dnl Check if support for GIF images should be compiled in
|
||||||
|
if test "x$enable_gif" = "xyes"; then
|
||||||
|
AC_DEFINE([ENABLE_GIF], [1], [Enable GIF images])
|
||||||
|
fi
|
||||||
|
|
||||||
|
dnl Check if support for SVG images should be compiled in
|
||||||
|
if test "x$enable_svg" = "xyes"; then
|
||||||
|
AC_DEFINE([ENABLE_SVG], [1], [Enable SVG images])
|
||||||
|
else
|
||||||
|
AC_MSG_NOTICE([Disabling SVG])
|
||||||
|
fi
|
||||||
|
|
||||||
|
dnl --------------------------
|
||||||
|
dnl Test for support for SSL/TLS
|
||||||
|
dnl --------------------------
|
||||||
|
dnl
|
||||||
|
|
||||||
|
tls_ok="no"
|
||||||
|
tls_impl="none"
|
||||||
|
if test "x$enable_tls" = "xyes"; then
|
||||||
|
if test "x$enable_openssl" = "xyes"; then
|
||||||
|
dnl Search for OpenSSL headers first
|
||||||
|
AC_CHECK_HEADER(openssl/ssl.h, openssl_ok=yes, openssl_ok=no)
|
||||||
|
|
||||||
|
dnl If the headers are found, try to link with -lssl and -lcrypto
|
||||||
|
if test "x$openssl_ok" = "xyes"; then
|
||||||
|
old_libs="$LIBS"
|
||||||
|
AC_CHECK_LIB(ssl, SSL_write, openssl_ok=yes, openssl_ok=no, -lcrypto)
|
||||||
|
LIBS="$old_libs"
|
||||||
|
fi
|
||||||
|
|
||||||
|
dnl If all went good, set OpenSSL
|
||||||
|
if test "x$openssl_ok" = "xyes"; then
|
||||||
|
AC_MSG_NOTICE([Using OpenSSL as TLS library.])
|
||||||
|
tls_impl="OpenSSL"
|
||||||
|
AC_DEFINE([HAVE_OPENSSL], [1], [OpenSSL works])
|
||||||
|
LIBSSL_LIBS="-lcrypto -lssl"
|
||||||
|
else
|
||||||
|
AC_MSG_NOTICE([Cannot find OpenSSL, trying mbedTLS...])
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
AC_MSG_NOTICE([Skipping OpenSSL search, as it is disabled])
|
||||||
|
fi
|
||||||
|
|
||||||
|
dnl Try to find mbedTLS if OpenSSL failed or is disabled
|
||||||
|
if test "x$enable_mbedtls" = "xyes"; then
|
||||||
|
if test "x$openssl_ok" != "xyes"; then
|
||||||
|
dnl In mbed TLS 2.3.0, ssl.h needs platform.h but fails to include it.
|
||||||
|
AC_CHECK_HEADER(mbedtls/ssl.h, mbedtls_ok=yes, mbedtls_ok=no, [#include <mbedtls/platform.h>])
|
||||||
|
|
||||||
|
dnl If the headers are found, try to link with mbedTLS
|
||||||
|
if test "x$mbedtls_ok" = "xyes"; then
|
||||||
|
old_libs="$LIBS"
|
||||||
|
AC_CHECK_LIB(mbedtls, mbedtls_ssl_init, mbedtls_ok=yes, mbedtls_ok=no, -lmbedx509 -lmbedcrypto)
|
||||||
|
LIBS="$old_libs"
|
||||||
|
fi
|
||||||
|
|
||||||
|
dnl If it went good, use it, otherwise disable TLS support
|
||||||
|
if test "x$mbedtls_ok" = "xyes"; then
|
||||||
|
AC_MSG_NOTICE([Using mbedTLS as TLS library.])
|
||||||
|
tls_impl="mbedTLS"
|
||||||
|
AC_DEFINE([HAVE_MBEDTLS], [1], [mbedTLS works])
|
||||||
|
LIBSSL_LIBS="-lmbedtls -lmbedx509 -lmbedcrypto"
|
||||||
|
else
|
||||||
|
AC_MSG_NOTICE([Cannot find mbedTLS])
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
AC_MSG_NOTICE([Skipping mbedTLS search, as it is disabled])
|
||||||
|
fi
|
||||||
|
|
||||||
|
dnl Only need one that works
|
||||||
|
if test "x$openssl_ok" = "xyes" -o "x$mbedtls_ok" = "xyes"; then
|
||||||
|
tls_ok="yes"
|
||||||
|
AC_DEFINE([ENABLE_TLS], [1], [Enable TLS support])
|
||||||
|
else
|
||||||
|
AC_MSG_ERROR([No TLS library available])
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
AM_CONDITIONAL([USE_OPENSSL], [test "x$openssl_ok" = "xyes"])
|
||||||
|
AM_CONDITIONAL([USE_MBEDTLS], [test "x$mbedtls_ok" = "xyes"])
|
||||||
|
|
||||||
|
dnl ----------------------------------------------------------------
|
||||||
|
dnl Test for iconv implementation first in libiconv and then in libc
|
||||||
|
dnl ----------------------------------------------------------------
|
||||||
|
AC_CHECK_HEADER(iconv.h, iconv_ok=yes, iconv_ok=no)
|
||||||
|
if test "x$iconv_ok" = "xyes"; then
|
||||||
|
dnl First try to link with the libiconv library if available, otherwise
|
||||||
|
dnl fall back to the implementation of libc. It is required to be done
|
||||||
|
dnl in this order because FreeBSD distributes libiconv headers in
|
||||||
|
dnl /usr/include/local/iconv.h, in the same place the headers for FLTK
|
||||||
|
dnl are placed. If we select to link with libc, the headers provided by
|
||||||
|
dnl libiconv will be used while linking with libc. This causes a linkage
|
||||||
|
dnl error.
|
||||||
|
AC_CHECK_LIB(iconv, iconv_open, LIBICONV_LIBS="-liconv",
|
||||||
|
AC_CHECK_LIB(c, iconv_open, LIBICONV_LIBS="", iconv_ok=no))
|
||||||
|
fi
|
||||||
|
if test "x$iconv_ok" = "xno"; then
|
||||||
|
dnl Test for OpenBSD
|
||||||
|
old_libs="$LIBS"
|
||||||
|
LIBS="$old_libs -liconv"
|
||||||
|
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
|
||||||
|
#include <iconv.h>
|
||||||
|
]],[[
|
||||||
|
iconv_open("","");
|
||||||
|
]])],
|
||||||
|
iconv_ok=yes,iconv_ok=no)
|
||||||
|
LIBS="$old_libs"
|
||||||
|
if test "x$iconv_ok" = "xyes"; then
|
||||||
|
LIBICONV_LIBS="-liconv"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "x$iconv_ok" = "xno"; then
|
||||||
|
AC_MSG_ERROR(libiconv must be installed!)
|
||||||
|
fi
|
||||||
|
|
||||||
|
dnl ----------------------
|
||||||
|
dnl Check if we need to
|
||||||
|
dnl support the old
|
||||||
|
dnl iconv interface
|
||||||
|
dnl ----------------------
|
||||||
|
if test "x$iconv_ok" = "xyes"; then
|
||||||
|
old_libs="$LIBS"
|
||||||
|
LIBS="$old_libs $LIBICONV_LIBS"
|
||||||
|
old_cflags="$CFLAGS"
|
||||||
|
CFLAGS="$CFLAGS -Werror"
|
||||||
|
AC_LANG_PUSH([C++])
|
||||||
|
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
|
||||||
|
#include <iconv.h>
|
||||||
|
]],[[
|
||||||
|
const char *inPtr;
|
||||||
|
char *outPtr;
|
||||||
|
size_t inLeft = 0, outRoom = 0;
|
||||||
|
iconv_t encoder = iconv_open("ASCII", "UTF-8");
|
||||||
|
iconv(encoder, &inPtr, &inLeft, &outPtr, &outRoom);
|
||||||
|
]])],
|
||||||
|
iconv_old=yes,iconv_old=no)
|
||||||
|
AC_LANG_POP([C++])
|
||||||
|
LIBS="$old_libs"
|
||||||
|
CFLAGS="$old_cflags"
|
||||||
|
|
||||||
|
if test "x$iconv_old" = "xyes"; then
|
||||||
|
AC_DEFINE([inbuf_t], [const char], [Use const char pointers for older libiconv])
|
||||||
|
else
|
||||||
|
AC_DEFINE([inbuf_t], [char], [Use char pointers for newer libiconv])
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
dnl ----------------------
|
||||||
|
dnl Test for POSIX threads
|
||||||
|
dnl ----------------------
|
||||||
|
dnl
|
||||||
|
if test -z "$LIBPTHREAD_LIBS"; then
|
||||||
|
case $target in
|
||||||
|
*-*-linux*|*-*-solaris*)
|
||||||
|
old_libs="$LIBS"
|
||||||
|
AC_CHECK_LIB(pthread, pthread_create, LIBPTHREAD_LIBS="-lpthread")
|
||||||
|
LIBS="$old_libs"
|
||||||
|
;;
|
||||||
|
|
||||||
|
*-*-osf1*)
|
||||||
|
AC_MSG_CHECKING(whether pthreads work)
|
||||||
|
LIBPTHREAD_LIBS="-lpthread -lexc -ldb"
|
||||||
|
AC_MSG_WARN([*** _Untested pthreads_ try setting LIBPTHREAD_LIBS manually if it doesn't work ***])
|
||||||
|
;;
|
||||||
|
|
||||||
|
*-*-minix*)
|
||||||
|
AC_MSG_NOTICE([Minix detected, skipping pthread detection])
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
AC_MSG_CHECKING(whether threads work with -pthread)
|
||||||
|
LDSAVEFLAGS=$LDFLAGS
|
||||||
|
LDFLAGS="$LDFLAGS -pthread"
|
||||||
|
AC_LINK_IFELSE([AC_LANG_CALL([],[pthread_create])],
|
||||||
|
pthread_ok=yes, pthread_ok=no)
|
||||||
|
LDFLAGS=$LDSAVEFLAGS
|
||||||
|
|
||||||
|
if test "x$pthread_ok" = "xyes"; then
|
||||||
|
AC_MSG_RESULT(yes)
|
||||||
|
LIBPTHREAD_LDFLAGS="-pthread"
|
||||||
|
else
|
||||||
|
AC_MSG_RESULT(no. Now we will try some libraries.)
|
||||||
|
|
||||||
|
AC_SEARCH_LIBS(pthread_create, pthread,
|
||||||
|
LIBPTHREADS_LIBS="-lpthread",
|
||||||
|
AC_SEARCH_LIBS(pthread_create, pthreads,
|
||||||
|
LIBPTHREADS_LIBS="-lpthreads",
|
||||||
|
AC_SEARCH_LIBS(pthread_create, c_r,
|
||||||
|
LIBPTHREADS_LIBS="-lc_r", thread_ok=no)))
|
||||||
|
|
||||||
|
if test "x$thread_ok" = "xno"; then
|
||||||
|
AC_MSG_WARN([*** No pthreads found. ***])
|
||||||
|
AC_MSG_ERROR([*** Try setting LIBPTHREAD_LIBS manually to point to your pthreads library. ***])
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
AC_MSG_WARN([found a way to link threads, but it may not work...])
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
dnl ----------
|
||||||
|
dnl HTML tests
|
||||||
|
dnl ----------
|
||||||
|
dnl
|
||||||
|
html_tests_ok=no
|
||||||
|
if test "x$enable_html_tests" = "xyes" ; then
|
||||||
|
html_tests_ok=yes
|
||||||
|
AC_CHECK_PROG(xvfb_ok, Xvfb, yes, no)
|
||||||
|
AC_CHECK_PROG(xwd_ok, xwd, yes, no)
|
||||||
|
AC_CHECK_PROG(xwininfo_ok, xwininfo, yes, no)
|
||||||
|
AC_CHECK_PROG(convert_ok, convert, yes, no)
|
||||||
|
|
||||||
|
if test "x$xvfb_ok" != "xyes"; then
|
||||||
|
html_tests_ok=no
|
||||||
|
fi
|
||||||
|
if test "x$xwd_ok" != "xyes"; then
|
||||||
|
html_tests_ok=no
|
||||||
|
fi
|
||||||
|
if test "x$xwininfo_ok" != "xyes"; then
|
||||||
|
html_tests_ok=no
|
||||||
|
fi
|
||||||
|
if test "x$convert_ok" != "xyes"; then
|
||||||
|
html_tests_ok=no
|
||||||
|
fi
|
||||||
|
if test "x$html_tests_ok" != "xyes"; then
|
||||||
|
AC_MSG_ERROR([Cannot find all tools to enable HTML tests])
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
AM_CONDITIONAL([ENABLE_HTML_TESTS], [test "x$html_tests_ok" = "xyes"])
|
||||||
|
|
||||||
|
dnl --------------------
|
||||||
|
dnl Command line options
|
||||||
|
dnl --------------------
|
||||||
|
dnl
|
||||||
|
if test "x$enable_cookies" = "xno" ; then
|
||||||
|
CFLAGS="$CFLAGS -DDISABLE_COOKIES"
|
||||||
|
CXXFLAGS="$CXXFLAGS -DDISABLE_COOKIES"
|
||||||
|
fi
|
||||||
|
if test "x$enable_ipv6" = "xyes" ; then
|
||||||
|
CFLAGS="$CFLAGS -DENABLE_IPV6"
|
||||||
|
fi
|
||||||
|
if test "x$enable_efence" = "xyes" ; then
|
||||||
|
LIBS="-lefence $LIBS"
|
||||||
|
fi
|
||||||
|
if test "x$enable_gprof" = "xyes" ; then
|
||||||
|
CFLAGS="$CFLAGS -pg"
|
||||||
|
CXXFLAGS="$CXXFLAGS -pg"
|
||||||
|
fi
|
||||||
|
if test "x$enable_insure" = "xyes" ; then
|
||||||
|
CC="insure -Zoi \"compiler $CC\""
|
||||||
|
LIBS="$LIBS -lstdc++-2-libc6.1-1-2.9.0"
|
||||||
|
fi
|
||||||
|
if test "x$enable_threaded_dns" = "xyes" ; then
|
||||||
|
CFLAGS="$CFLAGS -DD_DNS_THREADED"
|
||||||
|
fi
|
||||||
|
if test "x$enable_rtfl" = "xyes" ; then
|
||||||
|
CXXFLAGS="$CXXFLAGS -DDBG_RTFL"
|
||||||
|
fi
|
||||||
|
if test "x$enable_xembed" = "xno" ; then
|
||||||
|
CXXFLAGS="$CFLAGS -DDISABLE_XEMBED"
|
||||||
|
fi
|
||||||
|
|
||||||
|
dnl -----------------------
|
||||||
|
dnl Checks for header files
|
||||||
|
dnl -----------------------
|
||||||
|
dnl
|
||||||
|
AC_CHECK_HEADERS(fcntl.h unistd.h sys/uio.h)
|
||||||
|
|
||||||
|
dnl --------------------------
|
||||||
|
dnl Check for compiler options
|
||||||
|
dnl --------------------------
|
||||||
|
dnl
|
||||||
|
if eval "test x$GCC = xyes"; then
|
||||||
|
if test "`echo $CFLAGS | grep '\-D_REENTRANT' 2> /dev/null`" = ""; then
|
||||||
|
CFLAGS="$CFLAGS -D_REENTRANT"
|
||||||
|
fi
|
||||||
|
if test "`echo $CFLAGS | grep '\-D_THREAD_SAFE' 2> /dev/null`" = ""; then
|
||||||
|
CFLAGS="$CFLAGS -D_THREAD_SAFE"
|
||||||
|
fi
|
||||||
|
if test "`echo $CFLAGS | grep '\-Wall' 2> /dev/null`" = ""; then
|
||||||
|
CFLAGS="$CFLAGS -Wall"
|
||||||
|
fi
|
||||||
|
if test "`echo $CFLAGS | grep -e '-W ' -e '-W$' 2> /dev/null`" = ""; then
|
||||||
|
CFLAGS="$CFLAGS -W"
|
||||||
|
fi
|
||||||
|
if test "`echo $CFLAGS | grep '\-Wno-unused-parameter' 2> /dev/null`" = ""; then
|
||||||
|
CFLAGS="$CFLAGS -Wno-unused-parameter"
|
||||||
|
fi
|
||||||
|
CFLAGS="$CFLAGS -pedantic -std=c99 -D_POSIX_C_SOURCE=200112L"
|
||||||
|
fi
|
||||||
|
|
||||||
|
dnl -----------
|
||||||
|
dnl CXX options
|
||||||
|
dnl -----------
|
||||||
|
dnl
|
||||||
|
|
||||||
|
if eval "test x$GCC = xyes"; then
|
||||||
|
CXXFLAGS="$CXXFLAGS -Wall -W -Wno-unused-parameter -fno-rtti -fno-exceptions -pedantic -D_POSIX_C_SOURCE=200112L"
|
||||||
|
fi
|
||||||
|
|
||||||
|
dnl ----------------------------------
|
||||||
|
dnl Check if we can use the git commit
|
||||||
|
dnl ----------------------------------
|
||||||
|
git_ok=no
|
||||||
|
AC_CHECK_PROG(git_found, git, yes, no)
|
||||||
|
if test "x$git_found" = "xyes" ; then
|
||||||
|
AC_MSG_CHECKING([.git directory exists])
|
||||||
|
if test -d "$srcdir/.git"; then
|
||||||
|
AC_MSG_RESULT([yes])
|
||||||
|
AC_MSG_CHECKING(git describe works)
|
||||||
|
git_version=`git --work-tree="$srcdir" describe --dirty`
|
||||||
|
if test "x$git_version" = "x"; then
|
||||||
|
AC_MSG_RESULT(no)
|
||||||
|
else
|
||||||
|
git_ok=yes
|
||||||
|
AC_MSG_RESULT([yes ($git_version)])
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
AC_MSG_RESULT([no])
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
AM_CONDITIONAL([GIT_AVAILABLE], [test "x$git_ok" = "xyes"])
|
||||||
|
|
||||||
|
AC_SUBST(BASE_CUR_WORKING_DIR)
|
||||||
|
AC_SUBST(LIBJPEG_LIBS)
|
||||||
|
AC_SUBST(LIBJPEG_LDFLAGS)
|
||||||
|
AC_SUBST(LIBJPEG_CPPFLAGS)
|
||||||
|
AC_SUBST(LIBPNG_LIBS)
|
||||||
|
AC_SUBST(LIBPNG_CFLAGS)
|
||||||
|
AC_SUBST(LIBWEBP_LIBS)
|
||||||
|
AC_SUBST(LIBZ_LIBS)
|
||||||
|
AC_SUBST(LIBSSL_LIBS)
|
||||||
|
AC_SUBST(LIBPTHREAD_LIBS)
|
||||||
|
AC_SUBST(LIBPTHREAD_LDFLAGS)
|
||||||
|
AC_SUBST(LIBFLTK_CXXFLAGS)
|
||||||
|
AC_SUBST(LIBFLTK_CFLAGS)
|
||||||
|
AC_SUBST(LIBFLTK_LIBS)
|
||||||
|
AC_SUBST(LIBICONV_LIBS)
|
||||||
|
AC_SUBST(LIBX11_LIBS)
|
||||||
|
AC_SUBST(CA_CERTS_FILE)
|
||||||
|
AC_SUBST(CA_CERTS_DIR)
|
||||||
|
AC_SUBST(datadir)
|
||||||
|
|
||||||
|
AC_CONFIG_FILES([
|
||||||
|
Makefile
|
||||||
|
dlib/Makefile
|
||||||
|
dpip/Makefile
|
||||||
|
dpid/Makefile
|
||||||
|
dpi/Makefile
|
||||||
|
doc/Makefile
|
||||||
|
dw/Makefile
|
||||||
|
lout/Makefile
|
||||||
|
src/Makefile
|
||||||
|
src/IO/Makefile
|
||||||
|
test/Makefile
|
||||||
|
test/unit/Makefile
|
||||||
|
test/dw/Makefile
|
||||||
|
test/html/Makefile
|
||||||
|
])
|
||||||
|
|
||||||
|
AC_OUTPUT
|
||||||
|
|
||||||
|
_AS_ECHO([])
|
||||||
|
_AS_ECHO([Configuration summary:])
|
||||||
|
_AS_ECHO([])
|
||||||
|
_AS_ECHO([ CC : ${CC}])
|
||||||
|
_AS_ECHO([ CFLAGS : ${CFLAGS}])
|
||||||
|
_AS_ECHO([ CXX : ${CXX}])
|
||||||
|
_AS_ECHO([ CXXFLAGS: ${CXXFLAGS}])
|
||||||
|
_AS_ECHO([])
|
||||||
|
_AS_ECHO([ TLS enabled: ${tls_ok}])
|
||||||
|
_AS_ECHO([ TLS library: ${tls_impl}])
|
||||||
|
_AS_ECHO([ TLS flags : ${LIBSSL_LIBS}])
|
||||||
|
_AS_ECHO([])
|
||||||
|
_AS_ECHO([ Cookies enabled: ${enable_cookies}])
|
||||||
|
_AS_ECHO([ XEmbed enabled : ${enable_xembed}])
|
||||||
|
_AS_ECHO([ RTFL enabled : ${enable_rtfl}])
|
||||||
|
_AS_ECHO([ JPEG enabled : ${jpeg_ok}])
|
||||||
|
_AS_ECHO([ PNG enabled : ${png_ok}])
|
||||||
|
_AS_ECHO([ GIF enabled : ${enable_gif}])
|
||||||
|
_AS_ECHO([ SVG enabled : ${enable_svg}])
|
||||||
|
_AS_ECHO([ WEBP enabled : ${enable_webp}])
|
||||||
|
_AS_ECHO([])
|
||||||
|
_AS_ECHO([ HTML tests : ${html_tests_ok}])
|
||||||
|
_AS_ECHO([])
|
||||||
24
d_size.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#ifndef __D_SIZE_H__
|
||||||
|
#define __D_SIZE_H__
|
||||||
|
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_STDINT_H
|
||||||
|
#include <stdint.h>
|
||||||
|
#else
|
||||||
|
#ifdef HAVE_INTTYPES_H
|
||||||
|
#include <inttypes.h>
|
||||||
|
#else
|
||||||
|
/* config.h defines {int,uint}*_t */
|
||||||
|
#endif /* HAVE_INTTYPES_H */
|
||||||
|
#endif /*HAVE_STDINT_H */
|
||||||
|
|
||||||
|
typedef unsigned char uchar_t;
|
||||||
|
typedef unsigned short ushort_t;
|
||||||
|
typedef unsigned long ulong_t;
|
||||||
|
typedef unsigned int uint_t;
|
||||||
|
typedef unsigned char bool_t;
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* __D_SIZE_H__ */
|
||||||
153
devdoc/CCCwork.txt
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
Last review: August 04, 2009 --jcid
|
||||||
|
|
||||||
|
|
||||||
|
----------------------------
|
||||||
|
Internal working for the CCC
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
|
||||||
|
HTTP protocol
|
||||||
|
-------------
|
||||||
|
|
||||||
|
|
||||||
|
Query: |
|
||||||
|
.
|
||||||
|
1B --> 1B 1B --> 1B --> | -------------.
|
||||||
|
.----. .----. .----. . |
|
||||||
|
I |Capi| |http| | IO | | |
|
||||||
|
'----' '----' '----' . |
|
||||||
|
1F <-- 1F 1F <-- 1F | V
|
||||||
|
.
|
||||||
|
| [Server]
|
||||||
|
Answer: .
|
||||||
|
|
||||||
|
2B --> 2B 2B --> 2B | |
|
||||||
|
.----. .----. .----. . |
|
||||||
|
II |Capi| |Dpi | | IO | | |
|
||||||
|
'----' '----' '----' . |
|
||||||
|
2F <-- 2F 2F <-- 2F <-- | <------------'
|
||||||
|
.
|
||||||
|
|
|
||||||
|
|
||||||
|
* a_Capi_open_url() builds both the Answer and Query chains at
|
||||||
|
once (Answer first then Query), to ensure a uniform structure
|
||||||
|
that avoids complexity (e.g. race conditions).
|
||||||
|
|
||||||
|
* Http_get() sets a callback for the DNS hostname resolve.
|
||||||
|
Normally it comes later, but may also by issued immediately if
|
||||||
|
the hostname is cached.
|
||||||
|
|
||||||
|
* The socket FD is passed by means of OpSend by the http module
|
||||||
|
once the remote IP is known and the socket is connected.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Function calls for HTTP CCC
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
a_Capi_open_url
|
||||||
|
if (reload)
|
||||||
|
Capi OpStart 2B (answer) [Capi] --> [dpi] --> [IO]
|
||||||
|
Capi OpStart 1B (query) [Capi] --> [http] --> [IO]
|
||||||
|
Http_get
|
||||||
|
a_Cache_open_url
|
||||||
|
if URL_E2EReload -> prepare reload
|
||||||
|
if cached
|
||||||
|
client enqueue
|
||||||
|
delayed process queue
|
||||||
|
else
|
||||||
|
Cache_entry_add
|
||||||
|
client enqueue
|
||||||
|
|
||||||
|
-//->
|
||||||
|
a_Http_dns_cb
|
||||||
|
Http_connect_socket
|
||||||
|
OpSend FD, BCK
|
||||||
|
OpSend FD, FWD
|
||||||
|
Http_send_query
|
||||||
|
a_Http_make_query_str
|
||||||
|
OpSend, BCK
|
||||||
|
IO_submit
|
||||||
|
a_IOwatch_add_fd (DIO_WRITE, ...)
|
||||||
|
|
||||||
|
|
||||||
|
Note about 'web' structures. They're created using a_Web_new().
|
||||||
|
The web.c module keeps a list of valid webs, so anytime you're
|
||||||
|
unsure of a weak reference to 'web', it can be checked with
|
||||||
|
a_Web_valid(web).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
------------
|
||||||
|
Dpi protocol
|
||||||
|
------------
|
||||||
|
|
||||||
|
|
||||||
|
Query: |
|
||||||
|
.
|
||||||
|
1B --> 1B 1B --> 1B --> | -------------.
|
||||||
|
.----. .----. .----. . |
|
||||||
|
I |Capi| |Dpi | | IO | | |
|
||||||
|
'----' '----' '----' . |
|
||||||
|
1F <-- 1F 1F <-- 1F | V
|
||||||
|
.
|
||||||
|
| [Server]
|
||||||
|
.
|
||||||
|
Answer (same as HTTP): | |
|
||||||
|
. |
|
||||||
|
2B --> 2B 2B --> 2B | |
|
||||||
|
.----. .----. .----. . |
|
||||||
|
II |Capi| |Dpi | | IO | | |
|
||||||
|
'----' '----' '----' . |
|
||||||
|
2F <-- 2F 2F <-- 2F <-- | <------------'
|
||||||
|
.
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
CCC Construction:
|
||||||
|
|
||||||
|
a_Capi_open_url() calls a_Capi_dpi_send_cmd() when the URL
|
||||||
|
belongs to a dpi and it is not cached.
|
||||||
|
|
||||||
|
a_Capi_dpi_send_cmd() builds both the Answer and Query chains
|
||||||
|
at once (Answer first then Query), in the same way as HTTP does.
|
||||||
|
Note that the answer chain is the same for both, and the query
|
||||||
|
chain only differs in the module in the middle ([http] or [dpi]).
|
||||||
|
|
||||||
|
|
||||||
|
Function calls for DPI CCC
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
a_Capi_open_url
|
||||||
|
a_Capi_dpi_send_cmd
|
||||||
|
Capi OpStart 2B (answer) [Capi] --> [dpi] --> [IO]
|
||||||
|
Capi OpStart 1B (query) [Capi] --> [http] --> [IO]
|
||||||
|
a_Cache_open_url
|
||||||
|
[...]
|
||||||
|
|
||||||
|
|
||||||
|
Normal termination:
|
||||||
|
|
||||||
|
When the dpi server is done, it closes the FD, and OpEnd flows
|
||||||
|
from IO to Capi (answer branch). When in Capi, capi propagates
|
||||||
|
OpEnd to the query branch.
|
||||||
|
|
||||||
|
Abnormal termination:
|
||||||
|
|
||||||
|
The transfer may be aborted by a_Capi_conn_abort_by_url(). The
|
||||||
|
OpAbort is not yet standardized and has an ad-hoc implementation.
|
||||||
|
One idea is to have OpAbort always propagate BCK and then FWD and
|
||||||
|
to jump into the other chain when it gets to [Capi].
|
||||||
|
|
||||||
|
|
||||||
|
Debugging CCC
|
||||||
|
-------------
|
||||||
|
|
||||||
|
A simple way to "look" inside it, is to "#define VERBOSE 1" in
|
||||||
|
chain.c, and then to follow its work with a printed copy of the
|
||||||
|
diagrams in this document.
|
||||||
|
|
||||||
|
Each new data request generates a CCC, so if you want to debug,
|
||||||
|
it's good to refine the testcase to the minimum possible number
|
||||||
|
of connections.
|
||||||
|
|
||||||
166
devdoc/Cache.txt
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
June 2000, --Jcid
|
||||||
|
Last update: Jul 09
|
||||||
|
|
||||||
|
-------
|
||||||
|
CACHE
|
||||||
|
-------
|
||||||
|
|
||||||
|
The cache module is the main abstraction layer between
|
||||||
|
rendering and networking.
|
||||||
|
|
||||||
|
The capi module acts as a discriminating wrapper which either
|
||||||
|
calls the cache or the dpi routines depending on the type of
|
||||||
|
request.
|
||||||
|
|
||||||
|
Every URL must be requested using a_Capi_open_url, which
|
||||||
|
sends the request to the cache if the data is cached, to dillo's
|
||||||
|
http module for http: URLs, and through dillo's DPI system for
|
||||||
|
other URLs.
|
||||||
|
|
||||||
|
Here we'll document non dpi requests.
|
||||||
|
|
||||||
|
|
||||||
|
----------------
|
||||||
|
CACHE PHILOSOPHY
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Dillo's cache is very simple; every single resource that's
|
||||||
|
retrieved (URL) is kept in memory. NOTHING is saved to disk.
|
||||||
|
This is mainly for three reasons:
|
||||||
|
|
||||||
|
- Dillo encourages personal privacy and it assures there'll be
|
||||||
|
no recorded tracks of the sites you visited.
|
||||||
|
|
||||||
|
- The Network is full of intermediate transparent proxys that
|
||||||
|
serve as caches.
|
||||||
|
|
||||||
|
- If you still want to have cached stuff, you can install an
|
||||||
|
external cache server (such as WWWOFFLE), and benefit from it.
|
||||||
|
|
||||||
|
|
||||||
|
---------------
|
||||||
|
CACHE STRUCTURE
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Currently, dillo's cache code is spread in different sources:
|
||||||
|
mainly in cache.[ch], dicache.[ch] and it uses some other
|
||||||
|
functions from mime.c and web.cc.
|
||||||
|
|
||||||
|
Cache.c is the principal source, and it also is the one
|
||||||
|
responsible for processing cache-clients (held in a queue).
|
||||||
|
Dicache.c is the interface to the decompressed RGB representations
|
||||||
|
of currently-displayed images held in DW's imgbuf.
|
||||||
|
|
||||||
|
mime.c and web.cc are used for secondary tasks such as
|
||||||
|
assigning the right "viewer" or "decoder" for a given URL.
|
||||||
|
|
||||||
|
|
||||||
|
----------------
|
||||||
|
A bit of history
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Some time ago, the cache functions, URL retrieval and
|
||||||
|
external protocols were a whole mess of mixed code, and it was
|
||||||
|
getting REALLY hard to fix, improve or extend the functionality.
|
||||||
|
The main idea of this "layering" is to make code-portions as
|
||||||
|
independent as possible so they can be understood, fixed,
|
||||||
|
improved or replaced without affecting the rest of the browser.
|
||||||
|
|
||||||
|
An interesting part of the process is that, as resources are
|
||||||
|
retrieved, the client (dillo in this case) doesn't know the
|
||||||
|
Content-Type of the resource at request-time. It only becomes known
|
||||||
|
when the resource header is retrieved (think of http). This
|
||||||
|
happens when the cache has control, so the cache sets the
|
||||||
|
proper viewer for it (unless the Callback function was already
|
||||||
|
specified with the URL request).
|
||||||
|
|
||||||
|
You'll find a good example in http.c.
|
||||||
|
|
||||||
|
Note: All resources received by the cache have HTTP-style headers.
|
||||||
|
The file/data/ftp DPIs generate these headers when sending their
|
||||||
|
non-HTTP resources. Most importantly, a Content-Type header is
|
||||||
|
generated based on file extension or file contents.
|
||||||
|
|
||||||
|
|
||||||
|
-------------
|
||||||
|
Cache clients
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Cache clients MUST use a_Capi_open_url to request an URL. The
|
||||||
|
client structure and the callback-function prototype are defined,
|
||||||
|
in cache.h, as follows:
|
||||||
|
|
||||||
|
struct _CacheClient {
|
||||||
|
int Key; /* Primary Key for this client */
|
||||||
|
const DilloUrl *Url; /* Pointer to a cache entry Url */
|
||||||
|
int Version; /* Dicache version of this Url (0 if not used) */
|
||||||
|
void *Buf; /* Pointer to cache-data */
|
||||||
|
uint_t BufSize; /* Valid size of cache-data */
|
||||||
|
CA_Callback_t Callback; /* Client function */
|
||||||
|
void *CbData; /* Client function data */
|
||||||
|
void *Web; /* Pointer to the Web structure of our client */
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void (*CA_Callback_t)(int Op, CacheClient_t *Client);
|
||||||
|
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
* Op is the operation that the callback is asked to perform
|
||||||
|
by the cache. { CA_Send | CA_Close | CA_Abort }.
|
||||||
|
|
||||||
|
* Client: The Client structure that originated the request.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--------------------------
|
||||||
|
Key-functions descriptions
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||||
|
int a_Cache_open_url(void *Web, CA_Callback_t Call, void *CbData)
|
||||||
|
|
||||||
|
if Web->url is not cached
|
||||||
|
Create a cache-entry for that URL
|
||||||
|
Send client to cache queue
|
||||||
|
else
|
||||||
|
Feed our client with cached data
|
||||||
|
|
||||||
|
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||||
|
|
||||||
|
----------------------
|
||||||
|
Redirections mechanism
|
||||||
|
(HTTP 30x answers)
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
This is by no means complete. It's a work in progress.
|
||||||
|
|
||||||
|
Whenever an URL is served under an HTTP 30x header, its cache
|
||||||
|
entry is flagged with 'CA_Redirect'. If it's a 301 answer, the
|
||||||
|
additional 'CA_ForceRedirect' flag is also set, if it's a 302
|
||||||
|
answer, 'CA_TempRedirect' is also set (this happens inside the
|
||||||
|
Cache_parse_header() function).
|
||||||
|
|
||||||
|
Later on, in Cache_process_queue(), when the entry is flagged
|
||||||
|
with 'CA_Redirect' Cache_redirect() is called.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-----------
|
||||||
|
Notes
|
||||||
|
-----------
|
||||||
|
|
||||||
|
The whole process is asynchronous and very complex. I'll try
|
||||||
|
to document it in more detail later (source is commented).
|
||||||
|
Currently I have a drawing to understand it; hope the ASCII
|
||||||
|
translation serves the same as the original.
|
||||||
|
If you're planning to understand the cache process thoroughly,
|
||||||
|
write me a note and I will assign higher priority to further
|
||||||
|
improvement of this doc.
|
||||||
|
Hope this helps!
|
||||||
|
|
||||||
|
|
||||||
96
devdoc/Dillo.txt
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
"Eliminate the guesswork and quality goes up."
|
||||||
|
|
||||||
|
|
||||||
|
-------
|
||||||
|
DILLO
|
||||||
|
-------
|
||||||
|
|
||||||
|
These notes are written with a view to make it less hard, not
|
||||||
|
easier yet ;), to get into Dillo development.
|
||||||
|
When I first got into it, I was totally unaware of the browser
|
||||||
|
internals. Now that I've made my way deep into the core of it,
|
||||||
|
(we rewrote it 90% and modified the rest), is time to write some
|
||||||
|
documentation, just to make a less steep learning curve for new
|
||||||
|
developers.
|
||||||
|
|
||||||
|
--Jcid
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--------
|
||||||
|
OVERVIEW
|
||||||
|
--------
|
||||||
|
|
||||||
|
Dillo can be viewed as the sum of five main parts:
|
||||||
|
|
||||||
|
1.- Dillo Widget: A custom widget, FLTK-based, that holds the
|
||||||
|
necessary data structures and mechanisms for graphical rendering.
|
||||||
|
(Described in Dw*.txt, dw*.c files among the sources.)
|
||||||
|
|
||||||
|
2.- Dillo Cache: Integrated with a signal driven Input/Output
|
||||||
|
engine that handles file descriptor activity, the cache acts as
|
||||||
|
the main abstraction layer between rendering and networking.
|
||||||
|
Every URL, whether cached or not, must be retrieved using
|
||||||
|
a_Capi_open_url (Described briefly in Cache.txt, source
|
||||||
|
contained in capi.c).
|
||||||
|
IO is described in IO.txt (recommended), source in src/IO/.
|
||||||
|
|
||||||
|
3.- The HTML parser: A streamed parser that joins the Dillo
|
||||||
|
Widget and the Cache functionality to make browsing possible
|
||||||
|
(Described in HtmlParser.txt, source mainly inside html.cc).
|
||||||
|
|
||||||
|
4.- Image processing code: The part that handles image
|
||||||
|
retrieval, decoding, caching and displaying. (Described in
|
||||||
|
Images.txt. Sources: image.c, dw/image.cc, dicache.c, gif.c,
|
||||||
|
jpeg.c and png.c)
|
||||||
|
|
||||||
|
5.- The dpi framework: a gateway to interface the browser with
|
||||||
|
external programs (Example: the bookmarks server plugin).
|
||||||
|
Dpi spec: https://dillo-browser.github.io/old/dpi1.html
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------
|
||||||
|
HOW IS THE PAGE RENDERED?
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
(A short description of the internal function calling process)
|
||||||
|
|
||||||
|
When the user requests a new URL, a_UIcmd_open_url
|
||||||
|
is queried to do the job; it calls a_Nav_push (The highest level
|
||||||
|
URL dispatcher); a_Nav_push updates current browsing history and
|
||||||
|
calls Nav_open_url. Nav_open_url closes all open connections by
|
||||||
|
calling a_Bw_stop_clients, and then calls
|
||||||
|
a_Capi_open_url which calls a_Cache_open_url (or the dpi module if
|
||||||
|
this gateway is used).
|
||||||
|
|
||||||
|
If Cache_entry_search hits (due to a cached url :), the client is
|
||||||
|
fed with cached data, but if the URL isn't cached yet, a new CCC
|
||||||
|
(Concomitant Control Chain) is created and committed to fetch the
|
||||||
|
URL.
|
||||||
|
|
||||||
|
The next CCC link is dynamically assigned by examining the
|
||||||
|
URL's protocol. It can be a_Http_ccc or a_Dpi_ccc.
|
||||||
|
|
||||||
|
If we have an HTTP URL, a_Http_ccc will succeed, and the http
|
||||||
|
module will be linked; it will create the proper HTTP query and
|
||||||
|
link the IO module to submit and deliver the answer.
|
||||||
|
|
||||||
|
Note that as the Content-Type of the URL is not always known
|
||||||
|
in advance, the answering branch decides where to dispatch it to
|
||||||
|
upon HTTP header arrival.
|
||||||
|
|
||||||
|
|
||||||
|
What happens then?
|
||||||
|
|
||||||
|
Well, the html parser gets fed, and proper functions are
|
||||||
|
called for each tag (to parse and call the appropriate methods)
|
||||||
|
and the whole page is constructed in a streamed way.
|
||||||
|
Somewhere in the middle of it, resize and repaint functions
|
||||||
|
are activated and idle functions draw to screen what has been
|
||||||
|
processed.
|
||||||
|
|
||||||
|
(The process for images is described in Images.txt)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
331
devdoc/Dpid.txt
Normal file
@ -0,0 +1,331 @@
|
|||||||
|
Aug 2003, Jorge Arellano Cid,
|
||||||
|
Ferdi Franceschini --
|
||||||
|
Last update: Nov 2009
|
||||||
|
|
||||||
|
|
||||||
|
------
|
||||||
|
dpid
|
||||||
|
------
|
||||||
|
|
||||||
|
-------------
|
||||||
|
Nomenclature:
|
||||||
|
-------------
|
||||||
|
|
||||||
|
dpi:
|
||||||
|
generic term referring to dillo's plugin system (version1).
|
||||||
|
|
||||||
|
dpi1:
|
||||||
|
specific term for dillo's plugin spec version 1.
|
||||||
|
at: https://dillo-browser.github.io/old/dpi1.html
|
||||||
|
|
||||||
|
dpi program:
|
||||||
|
any plugin program itself.
|
||||||
|
|
||||||
|
dpi framework:
|
||||||
|
the code base inside and outside dillo that makes dpi1
|
||||||
|
working possible (it doesn't include dpi programs).
|
||||||
|
|
||||||
|
dpip:
|
||||||
|
dillo plugin protocol. The HTML/XML like set of command tags
|
||||||
|
and information that goes inside the communication sockets.
|
||||||
|
Note: not yet fully defined, but functional.
|
||||||
|
Note2: it was designed to be extensible.
|
||||||
|
|
||||||
|
dpid:
|
||||||
|
dillo plugin daemon.
|
||||||
|
|
||||||
|
server plugin:
|
||||||
|
A plugin that is capable of accepting connections on a socket. Dpid will
|
||||||
|
never run more than one instance of a server plugin at a time.
|
||||||
|
|
||||||
|
filter plugin:
|
||||||
|
A dpi program that reads from stdin and writes to stdout, and that
|
||||||
|
exits after its task is done (they don't remain as server plugins).
|
||||||
|
Warning, dpid will run multiple instances of filter plugins if requested.
|
||||||
|
|
||||||
|
-----------
|
||||||
|
About dpid:
|
||||||
|
-----------
|
||||||
|
|
||||||
|
* dpid is a program which manages dpi connections.
|
||||||
|
* dpid is a daemon that serves dillo using IDS sockets.
|
||||||
|
* dpid launches dpi programs and arranges socket communication
|
||||||
|
between the dpi program and dillo.
|
||||||
|
|
||||||
|
The concept and motivation is similar to that of inetd. The
|
||||||
|
plugin manager (dpid) listens for a service request on a socket
|
||||||
|
and returns the socket/port pair of a plugin that handles the
|
||||||
|
service. It also watches sockets of inactive plugins and starts
|
||||||
|
them when a connection is requested.
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
What's the problem with managing dpi programs inside dillo?
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
That's the other way to handle it, but it started to show some
|
||||||
|
problems (briefly outlined here):
|
||||||
|
|
||||||
|
* When having two or more running instances of Dillo, one
|
||||||
|
should prevail, and take control of dpi managing (but all
|
||||||
|
dillos carry the managing code).
|
||||||
|
* If the managing-dillo exits, it must pass control to another
|
||||||
|
instance, or leave it void if there's no other dillo running!
|
||||||
|
* The need to synchronize all the running instances of
|
||||||
|
dillo arises.
|
||||||
|
* If the controlling instance finishes and quits, all the
|
||||||
|
dpi-program PIDs are lost.
|
||||||
|
* Terminating hanged dpis is hard if it's not done with signals
|
||||||
|
(PIDs)
|
||||||
|
* Forks can be expensive (Dillo had to fork its dpis).
|
||||||
|
* When a managing dillo exits, the new one is no longer the
|
||||||
|
parent of the forked dpis.
|
||||||
|
* If Unix domain sockets for the dpis were to be named
|
||||||
|
randomly, it gets very hard to recover their names if the
|
||||||
|
controlling instance of dillo exits and another must "take
|
||||||
|
over" the managing.
|
||||||
|
* It increments dillo's core size.
|
||||||
|
* If dillo hangs/crashes, dpi activity is lost (e.g. downloads)
|
||||||
|
* ...
|
||||||
|
|
||||||
|
That's why the managing daemon scheme was chosen.
|
||||||
|
|
||||||
|
|
||||||
|
----------------------
|
||||||
|
What does dpid handle?
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
It solves all the above mentioned shortcomings and also can do:
|
||||||
|
|
||||||
|
* Multiple dillos:
|
||||||
|
dpid can communicate and serve more than one instance
|
||||||
|
of dillo.
|
||||||
|
|
||||||
|
* Multiple dillo windows:
|
||||||
|
two or more windows of the same dillo instance accessing dpis
|
||||||
|
at the same time.
|
||||||
|
|
||||||
|
* Different implementations of the same service
|
||||||
|
dpi programs ("dpis") are just an implementation of a
|
||||||
|
service. There's no problem in implementing a different one
|
||||||
|
for the same service (e.g. downloads).
|
||||||
|
|
||||||
|
* Upgrading a service:
|
||||||
|
to a new version or implementation without requiring
|
||||||
|
patching dillo's core or even bringing down the dpid.
|
||||||
|
|
||||||
|
|
||||||
|
And finally, being aware that this design can support the
|
||||||
|
following functions is very helpful:
|
||||||
|
|
||||||
|
SCHEME Example
|
||||||
|
------------------------------------------------------------
|
||||||
|
* "one demand/one response" man, preferences, ...
|
||||||
|
* "resident while working" downloads, mp3, ...
|
||||||
|
* "resident until exit request" bookmarks, ...
|
||||||
|
|
||||||
|
* "one client only" cd burner, ...
|
||||||
|
* "one client per instance" man, ...
|
||||||
|
* "multiple clients/one instance" downloads, cookies ...
|
||||||
|
|
||||||
|
|
||||||
|
--------
|
||||||
|
Features
|
||||||
|
--------
|
||||||
|
* Dpi programs go in: "EPREFIX/dillo/dpi" or "~/.dillo/dpi". The binaries
|
||||||
|
are named <name>.dpi as "bookmarks.dpi" and <name>.filter.dpi as in
|
||||||
|
"hello.filter.dpi". The ".filter" plugins simply read from stdin
|
||||||
|
and write to stdout.
|
||||||
|
* Register/update/remove dpis from list of available dpis when a
|
||||||
|
'register_all' command is received.
|
||||||
|
* dpid terminates when it receives a 'DpiBye' command.
|
||||||
|
* dpis can be terminated with a 'DpiBye' command.
|
||||||
|
* dpidc control program for dpid, currently allows register and stop.
|
||||||
|
|
||||||
|
|
||||||
|
-----
|
||||||
|
todo:
|
||||||
|
-----
|
||||||
|
|
||||||
|
These features are already designed, waiting for implementation:
|
||||||
|
|
||||||
|
* dpidc remove // May be not necessary after all...
|
||||||
|
|
||||||
|
|
||||||
|
-----------------
|
||||||
|
How does it work?
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
o on startup dpid reads dpidrc for the path to the dpi directory
|
||||||
|
(usually EPREFIX/lib/dillo/dpi). ~/.dillo/dpi is scanned first.
|
||||||
|
|
||||||
|
o both directories are scanned for the list of available plugins.
|
||||||
|
~/.dillo/dpi overrides system-wide dpis.
|
||||||
|
|
||||||
|
o next it creates internet domain sockets for the available plugins and
|
||||||
|
then listens for service requests on its own socket,
|
||||||
|
and for connections to the sockets of inactive plugins.
|
||||||
|
|
||||||
|
o dpid returns the port of a plugin's socket when a client (dillo)
|
||||||
|
requests a service.
|
||||||
|
|
||||||
|
o if the requested plugin is a 'server' then
|
||||||
|
1) dpid stops watching the socket for activity
|
||||||
|
2) forks and starts the plugin
|
||||||
|
3) resumes watching the socket when the plugin exits
|
||||||
|
|
||||||
|
o if the requested plugin is a 'filter' then
|
||||||
|
1) dpid accepts the connection
|
||||||
|
2) maps the socket fd to stdin/stdout (with dup2)
|
||||||
|
3) forks and starts the plugin
|
||||||
|
4) continues to watch the socket for new connections
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---------------------------
|
||||||
|
dpi service process diagram
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
These drawings should be worth a thousand words! :)
|
||||||
|
|
||||||
|
|
||||||
|
(I)
|
||||||
|
.--- s1 s2 s3 ... sn
|
||||||
|
|
|
||||||
|
[dpid] [dillo]
|
||||||
|
|
|
||||||
|
'--- srs
|
||||||
|
|
||||||
|
The dpid is running listening on several sockets.
|
||||||
|
|
||||||
|
|
||||||
|
(II)
|
||||||
|
.--- s1 s2 s3 ... sn
|
||||||
|
|
|
||||||
|
[dpid] [dillo]
|
||||||
|
| |
|
||||||
|
'--- srs ------------------'
|
||||||
|
|
||||||
|
dillo needs a service so it connects to the service request
|
||||||
|
socket of the dpid (srs) and asks for the socket name of the
|
||||||
|
required plugin (using dpip).
|
||||||
|
|
||||||
|
|
||||||
|
(III)
|
||||||
|
.--- s1 s2 s3 ... sn
|
||||||
|
| |
|
||||||
|
[dpid] | [dillo]
|
||||||
|
| | |
|
||||||
|
'--- srs '---------------'
|
||||||
|
|
||||||
|
then it connects to that socket (s3, still serviced by dpid!)
|
||||||
|
|
||||||
|
|
||||||
|
(IV)
|
||||||
|
.--- s1 s2 s3 ... sn
|
||||||
|
| |
|
||||||
|
.[dpid] | [dillo]
|
||||||
|
. | | |
|
||||||
|
. '--- srs '---------------'
|
||||||
|
.
|
||||||
|
.............[dpi program]
|
||||||
|
|
||||||
|
when s3 has activity (incoming data), dpid forks the dpi
|
||||||
|
program for it...
|
||||||
|
|
||||||
|
|
||||||
|
(V)
|
||||||
|
.--- s1 s2 (s3) ... sn
|
||||||
|
|
|
||||||
|
[dpid] [dillo]
|
||||||
|
| |
|
||||||
|
'--- srs .---------------'
|
||||||
|
|
|
||||||
|
[dpi program]
|
||||||
|
|
||||||
|
... and lets it "to take over" the socket.
|
||||||
|
|
||||||
|
Once there's a socket channel for dpi and dillo, the whole
|
||||||
|
communication process takes place until the task is done. When
|
||||||
|
the dpi program exits, dpid resumes listening on the socket (s3).
|
||||||
|
|
||||||
|
|
||||||
|
--------------------------------
|
||||||
|
So, how do I make my own plugin?
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
Maybe the simplest way to get started is to understand a few
|
||||||
|
concepts and then to use the hands-on method by using/modifying
|
||||||
|
the hello dpi. It's designed as an example to get developers
|
||||||
|
started.
|
||||||
|
|
||||||
|
---------
|
||||||
|
Concepts:
|
||||||
|
---------
|
||||||
|
|
||||||
|
* Dillo plugins work by communicating two processes: dillo
|
||||||
|
and the dpi.
|
||||||
|
* The underlying protocol (DPIP) has a uniform API which is
|
||||||
|
powerful enough for both blocking and nonblocking IO, and
|
||||||
|
filter or server dpis.
|
||||||
|
* The simplest example is one-request one-answer (for example
|
||||||
|
dillo asks for a URL and the dpi sends it). You'll find
|
||||||
|
this and more complex examples in hello.c
|
||||||
|
|
||||||
|
First, you should get familiar with the hello dpi as a user:
|
||||||
|
|
||||||
|
$dillo dpi:/hello/
|
||||||
|
|
||||||
|
Once you've played enough with it, start reading the well
|
||||||
|
commented code in hello.c and start making changes!
|
||||||
|
|
||||||
|
|
||||||
|
---------------
|
||||||
|
Debugging a dpi
|
||||||
|
---------------
|
||||||
|
|
||||||
|
The simplest way is to add printf-like feedback using the MSG*
|
||||||
|
macros. You can start the dpid by hand on a terminal to force
|
||||||
|
messages to go there. Filter dpis use sdterr and server dpis
|
||||||
|
stdout.
|
||||||
|
|
||||||
|
Sometimes more complex dpis need more than MSG*. In this case
|
||||||
|
you can use gdb like this.
|
||||||
|
|
||||||
|
1.- Add an sleep(20) statement just after the dpi starts.
|
||||||
|
2.- Start dillo and issue a request for your dpi. This will
|
||||||
|
get your dpi started.
|
||||||
|
3.- Standing in the dpi source directory:
|
||||||
|
ps aux|grep dpi
|
||||||
|
4.- Take note of the dpi's PID and start gdb, then:
|
||||||
|
(gdb) attach <PID>
|
||||||
|
5.- Continue from there...
|
||||||
|
|
||||||
|
|
||||||
|
------------
|
||||||
|
Final Notes:
|
||||||
|
------------
|
||||||
|
|
||||||
|
1.- If you already understand the hello dpi and want to try
|
||||||
|
something more advanced:
|
||||||
|
|
||||||
|
* bookmarks.c is a good example of a blocking server
|
||||||
|
* file.c is an advanced example of a server handling multiple
|
||||||
|
non-blocking connections with select().
|
||||||
|
|
||||||
|
2.- Multiple instances of a filter plugin may be run
|
||||||
|
concurrently, this could be a problem if your plugin records data
|
||||||
|
in a file, however it is safe if you simply write to stdout.
|
||||||
|
Alternatively you could write a 'server' plugin instead as they
|
||||||
|
are guaranteed not to run concurrently.
|
||||||
|
|
||||||
|
3.- The hardest part is to try to modify the dpi framework code
|
||||||
|
inside dillo; you have been warned! It already supports a lot of
|
||||||
|
functionality, but if you need to do some very custom stuff, try
|
||||||
|
extending the "chat" command, or asking in dillo-dev.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<
|
||||||
|
|
||||||
124
devdoc/HtmlParser.txt
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
October 2001, --Jcid
|
||||||
|
Last update: Jul 2009
|
||||||
|
|
||||||
|
---------------
|
||||||
|
THE HTML PARSER
|
||||||
|
---------------
|
||||||
|
|
||||||
|
|
||||||
|
Dillo's parser is more than just a HTML parser, it does XHTML
|
||||||
|
and plain text also. It has parsing 'modes' that define its
|
||||||
|
behaviour while working:
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
DILLO_HTML_PARSE_MODE_INIT = 0,
|
||||||
|
DILLO_HTML_PARSE_MODE_STASH,
|
||||||
|
DILLO_HTML_PARSE_MODE_STASH_AND_BODY,
|
||||||
|
DILLO_HTML_PARSE_MODE_BODY,
|
||||||
|
DILLO_HTML_PARSE_MODE_VERBATIM,
|
||||||
|
DILLO_HTML_PARSE_MODE_PRE
|
||||||
|
} DilloHtmlParseMode;
|
||||||
|
|
||||||
|
|
||||||
|
The parser works upon a token-grained basis, i.e., the data
|
||||||
|
stream is parsed into tokens and the parser is fed with them. The
|
||||||
|
process is simple: whenever the cache has new data, it is
|
||||||
|
passed to Html_write, which groups data into tokens and calls the
|
||||||
|
appropriate functions for the token type (tag, space, or word).
|
||||||
|
|
||||||
|
Note: when in DILLO_HTML_PARSE_MODE_VERBATIM, the parser
|
||||||
|
doesn't try to split the data stream into tokens anymore; it
|
||||||
|
simply collects until the closing tag.
|
||||||
|
|
||||||
|
------
|
||||||
|
TOKENS
|
||||||
|
------
|
||||||
|
|
||||||
|
* A chunk of WHITE SPACE --> Html_process_space
|
||||||
|
|
||||||
|
|
||||||
|
* TAG --> Html_process_tag
|
||||||
|
|
||||||
|
The tag-start is defined by two adjacent characters:
|
||||||
|
|
||||||
|
first : '<'
|
||||||
|
second: ALPHA | '/' | '!' | '?'
|
||||||
|
|
||||||
|
Note: comments are discarded ( <!-- ... --> )
|
||||||
|
|
||||||
|
|
||||||
|
The tag's end is not as easy to find, nor to deal with!:
|
||||||
|
|
||||||
|
1) The HTML 4.01 sec. 3.2.2 states that "Attribute/value
|
||||||
|
pairs appear before the final '>' of an element's start tag",
|
||||||
|
but it doesn't define how to discriminate the "final" '>'.
|
||||||
|
|
||||||
|
2) '<' and '>' should be escaped as '<' and '>' inside
|
||||||
|
attribute values.
|
||||||
|
|
||||||
|
3) The XML SPEC for XHTML states:
|
||||||
|
AttrValue ::== '"' ([^<&"] | Reference)* '"' |
|
||||||
|
"'" ([^<&'] | Reference)* "'"
|
||||||
|
|
||||||
|
Current parser honors the XML SPEC.
|
||||||
|
|
||||||
|
As it's a common mistake for human authors to mistype or
|
||||||
|
forget one of the quote marks of an attribute value; the
|
||||||
|
parser solves the problem with a look-ahead technique
|
||||||
|
(otherwise the parser could skip significant amounts of
|
||||||
|
properly-written HTML).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* WORD --> Html_process_word
|
||||||
|
|
||||||
|
A word is anything that doesn't start with SPACE, that's
|
||||||
|
outside of a tag, up to the first SPACE or tag start.
|
||||||
|
|
||||||
|
SPACE = ' ' | \n | \r | \t | \f | \v
|
||||||
|
|
||||||
|
|
||||||
|
-----------------
|
||||||
|
THE PARSING STACK
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
The parsing state of the document is kept in a stack:
|
||||||
|
|
||||||
|
class DilloHtml {
|
||||||
|
[...]
|
||||||
|
lout::misc::SimpleVector<DilloHtmlState> *stack;
|
||||||
|
[...]
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _DilloHtmlState {
|
||||||
|
CssPropertyList *table_cell_props;
|
||||||
|
DilloHtmlParseMode parse_mode;
|
||||||
|
DilloHtmlTableMode table_mode;
|
||||||
|
bool cell_text_align_set;
|
||||||
|
DilloHtmlListMode list_type;
|
||||||
|
int list_number;
|
||||||
|
|
||||||
|
/* TagInfo index for the tag that's being processed */
|
||||||
|
int tag_idx;
|
||||||
|
|
||||||
|
dw::core::Widget *textblock, *table;
|
||||||
|
|
||||||
|
/* This is used to align list items (especially in enumerated lists) */
|
||||||
|
dw::core::Widget *ref_list_item;
|
||||||
|
|
||||||
|
/* This is used for list items etc; if it is set to TRUE, breaks
|
||||||
|
have to be "handed over" (see Html_add_indented and
|
||||||
|
Html_eventually_pop_dw). */
|
||||||
|
bool hand_over_break;
|
||||||
|
};
|
||||||
|
|
||||||
|
Basically, when a TAG is processed, a new state is pushed into
|
||||||
|
the 'stack' and its 'style' is set to reflect the desired
|
||||||
|
appearance (details in DwStyle.txt).
|
||||||
|
|
||||||
|
That way, when a word is processed later (added to the Dw), all
|
||||||
|
the information is within the top state.
|
||||||
|
|
||||||
|
Closing TAGs just pop the stack.
|
||||||
|
|
||||||
|
|
||||||
468
devdoc/IO.txt
Normal file
@ -0,0 +1,468 @@
|
|||||||
|
|
||||||
|
This is the updated base of a paper I wrote with Horst.
|
||||||
|
It provides a good introduction to Dillo's internals.
|
||||||
|
(Highly recommended if you plan to patch or develop in Dillo)
|
||||||
|
|
||||||
|
It may not be exactly accurate (it's quite old), but it explains
|
||||||
|
the theory behind in some detail, so it's more than recommended
|
||||||
|
reading material.
|
||||||
|
--Jcid
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------
|
||||||
|
Parallel network programming of the Dillo web browser
|
||||||
|
-----------------------------------------------------
|
||||||
|
|
||||||
|
Jorge Arellano-Cid <jcid@dillo.org>
|
||||||
|
Horst H. von Brand <vonbrand@inf.utfsm.cl>
|
||||||
|
|
||||||
|
|
||||||
|
--------
|
||||||
|
Abstract
|
||||||
|
--------
|
||||||
|
|
||||||
|
Network programs face several delay sources when sending or
|
||||||
|
retrieving data. This is particularly problematic in programs
|
||||||
|
which interact directly with the user, most notably web browsers.
|
||||||
|
We present a hybrid approach using threads communicated through
|
||||||
|
pipes and signal driven I/O, which allows a non-blocking main
|
||||||
|
thread and overlapping waiting times.
|
||||||
|
|
||||||
|
|
||||||
|
------------
|
||||||
|
Introduction
|
||||||
|
------------
|
||||||
|
|
||||||
|
The Dillo project didn't start from scratch but mainly working
|
||||||
|
on the code base of gzilla (a light web browser written by Raph
|
||||||
|
Levien). As the project went by, the code of the whole source was
|
||||||
|
standardized, and the networking engine was replaced with a new,
|
||||||
|
faster design. Later, the parser was changed, the streamed
|
||||||
|
handling of data and its error control was put under the control
|
||||||
|
of the CCC (Concomitant Control Chain), and the old widget system
|
||||||
|
was replaced with a new one (Dw). The source code is currently
|
||||||
|
regarded as "very stable beta", and is available at
|
||||||
|
<https://github.com/dillo-browser/dillo>. Dillo is a project
|
||||||
|
licensed under the GNU General Public License.
|
||||||
|
|
||||||
|
This paper covers basic design aspects of the hybrid approach
|
||||||
|
that the Dillo web browser uses to solve several latency
|
||||||
|
problems. After introducing the main delay-sources, the main
|
||||||
|
points of the hybrid design will be addressed.
|
||||||
|
|
||||||
|
|
||||||
|
-------------
|
||||||
|
Delay sources
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Network programs face several delay-sources while sending or
|
||||||
|
retrieving data. In the particular case of a web browser, they
|
||||||
|
are found in:
|
||||||
|
|
||||||
|
DNS querying:
|
||||||
|
The time required to solve a name.
|
||||||
|
|
||||||
|
Initiating the TCP connection:
|
||||||
|
The three way handshake of the TCP protocol.
|
||||||
|
|
||||||
|
Sending the query:
|
||||||
|
The time spent uploading queries to the remote server.
|
||||||
|
|
||||||
|
Retrieving data:
|
||||||
|
The time spent expecting and receiving the query answer.
|
||||||
|
|
||||||
|
Closing the TCP connection:
|
||||||
|
The four packet-sending closing sequence of the TCP protocol.
|
||||||
|
|
||||||
|
In a WAN context, every single item of this list has an
|
||||||
|
associated delay that is non deterministic and often measured in
|
||||||
|
seconds. If we add several connections per browsed page (each one
|
||||||
|
requiring at least the 4 last steps), the total latency can be
|
||||||
|
considerable.
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------
|
||||||
|
The traditional (blocking) approach
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
|
The main problems with the blocking approach are:
|
||||||
|
|
||||||
|
When issuing an operation that can't be completed
|
||||||
|
immediately, the process is put to sleep waiting for
|
||||||
|
completion, and the program doesn't do any other
|
||||||
|
processing in the meantime.
|
||||||
|
|
||||||
|
When waiting for a specific socket operation to complete,
|
||||||
|
packets that belong to other connections may be arriving,
|
||||||
|
and have to wait for service.
|
||||||
|
|
||||||
|
Web browsers handle many small transactions,
|
||||||
|
if waiting times are not overlapped
|
||||||
|
the latency perceived by the user can be very annoying.
|
||||||
|
|
||||||
|
If the user interface is just put to sleep during network
|
||||||
|
operations, the program becomes unresponsive, confusing
|
||||||
|
and perhaps alarming the user.
|
||||||
|
|
||||||
|
Not overlapping waiting times and processing makes
|
||||||
|
graphical rendering (which is arguably the central function
|
||||||
|
of a browser) unnecessarily slow.
|
||||||
|
|
||||||
|
|
||||||
|
---------------------
|
||||||
|
Dillo's hybrid design
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Dillo uses threads and signal driven I/O extensively to
|
||||||
|
overlap waiting times and computation. Handling the user
|
||||||
|
interface in a thread that never blocks gives a good interactive
|
||||||
|
``feel.'' The use of GTK+, a sophisticated widget framework for
|
||||||
|
graphical user interfaces, helped very much to accomplish this
|
||||||
|
goal. All the interface, rendering and I/O engine was built upon
|
||||||
|
its facilities.
|
||||||
|
|
||||||
|
The design is said to be ``hybrid'' because it uses threads
|
||||||
|
for DNS querying and reading local files, and signal driven I/O
|
||||||
|
for TCP connections. The threaded DNS scheme is potentially
|
||||||
|
concurrent (this depends on underlying hardware), while the I/O
|
||||||
|
handling (both local files and remote connections) is
|
||||||
|
definitively parallel.
|
||||||
|
|
||||||
|
To simplify the structure of the browser, local files are
|
||||||
|
encapsulated into HTTP streams and presented to the rest of the
|
||||||
|
browser as such, in exactly the same way a remote connection is
|
||||||
|
handled. To create this illusion, a thread is launched. This
|
||||||
|
thread opens a pipe to the browser, it then synthesizes an
|
||||||
|
appropriate HTTP header, sends it together with the file to the
|
||||||
|
browser proper. In this way, all the browser sees is a handle,
|
||||||
|
the data on it can come from a remote connection or from a local
|
||||||
|
file.
|
||||||
|
|
||||||
|
To handle a remote connection is more complex. In this case,
|
||||||
|
the browser asks the cache manager for the URL. The name in the
|
||||||
|
URL has to be resolved through the DNS engine, a socket TCP
|
||||||
|
connection must be established, the HTTP request has to be sent,
|
||||||
|
and finally the result retrieved. Each of the steps mentioned
|
||||||
|
could give rise to errors, which have to be handled and somehow
|
||||||
|
communicated to the rest of the program. For performance reasons,
|
||||||
|
it is critical that responses are cached locally, so the remote
|
||||||
|
connection doesn't directly hand over the data to the browser;
|
||||||
|
the response is passed to the cache manager which then relays it
|
||||||
|
to the rest of the browser. The DNS engine caches DNS responses,
|
||||||
|
and either answers them from the cache or by querying the DNS.
|
||||||
|
Querying is done in a separate thread, so that the rest of the
|
||||||
|
browser isn't blocked by long waits here.
|
||||||
|
|
||||||
|
The activities mentioned do not happen strictly in the order
|
||||||
|
stated above. It is even possible that several URLs are being
|
||||||
|
handled at the same time, in order to overlap waiting and
|
||||||
|
downloading. The functions called directly from the user
|
||||||
|
interface have to return quickly to maintain interactive
|
||||||
|
response. Sometimes they return connection handlers that haven't
|
||||||
|
been completely set up yet. As stated, I/O is signal-driven, when
|
||||||
|
one of the descriptors is ready for data transfer (reading or
|
||||||
|
writing), it wakes up the I/O engine.
|
||||||
|
|
||||||
|
Data transfer between threads inside the browser is handled by
|
||||||
|
pipes, shared memory is little used. This almost obviates the
|
||||||
|
need for explicit synchronization, which is one of the main areas
|
||||||
|
of complexity and bugs in concurrent programs. Dillo handles its
|
||||||
|
threads in a way that its developers can think of it as running
|
||||||
|
on a single thread of control. This is accomplished by making the
|
||||||
|
DNS engine call-backs happen within the main thread, and by
|
||||||
|
isolating file loading with pipes.
|
||||||
|
|
||||||
|
Using threads in this way has three big advantages:
|
||||||
|
|
||||||
|
The browser doesn't block when one of its child threads
|
||||||
|
blocks. In particular, the user interface is responsive
|
||||||
|
even while resolving a name or downloading a file.
|
||||||
|
|
||||||
|
Developers don't need to deal with complex concurrent
|
||||||
|
concerns. Concurrency is hard to handle, and few developers
|
||||||
|
are adept at this. This gives access a much larger pool of
|
||||||
|
potential developers, something which can be critical
|
||||||
|
in an open-source development project.
|
||||||
|
|
||||||
|
By making the code mostly sequential, debugging the code
|
||||||
|
with traditional tools like gdb is possible. Debugging
|
||||||
|
parallel programs is very hard, and appropriate tools are
|
||||||
|
hard to come by.
|
||||||
|
|
||||||
|
Because of simplicity and portability concerns, DNS querying
|
||||||
|
is done in a separate thread. The standard C library doesn't
|
||||||
|
provide a function for making DNS queries that don't block. The
|
||||||
|
alternative is to implement a new, custom DNS querying function
|
||||||
|
that doesn't block. This is certainly a complex task, integrating
|
||||||
|
this mechanism into the thread structure of the program is much
|
||||||
|
simpler.
|
||||||
|
|
||||||
|
Using a thread and a pipe to read a local file adds a
|
||||||
|
buffering step to the process (and a certain latency), but it has
|
||||||
|
a couple of significative advantages:
|
||||||
|
|
||||||
|
By handling local files in the same way as remote
|
||||||
|
connections, a significant amount of code is reused.
|
||||||
|
|
||||||
|
A preprocessing step of the file data can be added easily,
|
||||||
|
if needed. In fact, the file is encapsulated into an HTTP
|
||||||
|
data stream.
|
||||||
|
|
||||||
|
|
||||||
|
-----------
|
||||||
|
DNS queries
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Dillo handles DNS queries with threads, letting a child thread
|
||||||
|
wait until the DNS server answers the request. When the answer
|
||||||
|
arrives, a call-back function is called, and the program resumes
|
||||||
|
what it was doing at DNS-request time. The interesting thing is
|
||||||
|
that the call-back happens in the main thread, while the child
|
||||||
|
thread simply exits when done. This is implemented through a
|
||||||
|
server-channel design.
|
||||||
|
|
||||||
|
|
||||||
|
The server channel
|
||||||
|
------------------
|
||||||
|
|
||||||
|
There is one thread for each channel, and each channel can
|
||||||
|
have multiple clients. When the program requests an IP address,
|
||||||
|
the server first looks for a cached match; if it hits, the client
|
||||||
|
call-back is invoked immediately, but if not, the client is put
|
||||||
|
into a queue, a thread is spawned to query the DNS, and a GTK+
|
||||||
|
idle client is set to poll the channel 5~times per second for
|
||||||
|
completion, and when it finally succeeds, every client of that
|
||||||
|
channel is serviced.
|
||||||
|
|
||||||
|
This scheme allows all the further processing to continue on
|
||||||
|
the same thread it began: the main thread.
|
||||||
|
|
||||||
|
|
||||||
|
------------------------
|
||||||
|
Handling TCP connections
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
Establishing a TCP connection requires the well known
|
||||||
|
three-way handshake packet-sending sequence. Depending on network
|
||||||
|
traffic and several other issues, significant delay can occur at
|
||||||
|
this phase.
|
||||||
|
|
||||||
|
Dillo handles the connection by a non blocking socket scheme.
|
||||||
|
Basically, a socket file descriptor of AF_INET type is requested
|
||||||
|
and set to non-blocking I/O. When the DNS server has resolved the
|
||||||
|
name, the socket connection process begins by calling connect(2);
|
||||||
|
{We use the Unix convention of identifying the manual section
|
||||||
|
where the concept is described, in this case
|
||||||
|
section 2 (system calls).}
|
||||||
|
which returns immediately with an EINPROGRESS error.
|
||||||
|
|
||||||
|
After the connection reaches the EINPROGRESS ``state,'' the
|
||||||
|
socket waits in background until connection succeeds (or fails),
|
||||||
|
when that happens, a callback function is awaked to perform the
|
||||||
|
following steps: set the I/O engine to send the query and expect
|
||||||
|
its answer (both in background).
|
||||||
|
|
||||||
|
The advantage of this scheme is that every required step is
|
||||||
|
quickly done without blocking the browser. Finally, the socket
|
||||||
|
will generate a signal whenever I/O is possible.
|
||||||
|
|
||||||
|
|
||||||
|
----------------
|
||||||
|
Handling queries
|
||||||
|
----------------
|
||||||
|
|
||||||
|
In the case of a HTTP URL, queries typically translate into a
|
||||||
|
short transmission (the HTTP query) and a lengthy retrieval
|
||||||
|
process. Queries are not always short though, specially when
|
||||||
|
requesting forms (all the form data is attached within the
|
||||||
|
query), and also when requesting CGI programs.
|
||||||
|
|
||||||
|
Regardless of query length, query sending is handled in
|
||||||
|
background. The thread that was initiated at TCP connecting time
|
||||||
|
has all the transmission framework already set up; at this point,
|
||||||
|
packet sending is just a matter of waiting for the
|
||||||
|
write signal (G_IO_OUT) to come and then sending the data. When
|
||||||
|
the socket gets ready for transmission, the data is sent using
|
||||||
|
IO_write.
|
||||||
|
|
||||||
|
|
||||||
|
--------------
|
||||||
|
Receiving data
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Although conceptually similar to sending queries, retrieving
|
||||||
|
data is very different as the data received can easily exceed the
|
||||||
|
size of the query by many orders of magnitude (for example when
|
||||||
|
downloading images or files). This is one of the main sources of
|
||||||
|
latency, the retrieval can take several seconds or even minutes
|
||||||
|
when downloading large files.
|
||||||
|
|
||||||
|
The data retrieving process for a single file, that began by
|
||||||
|
setting up the expecting framework at TCP connecting time, simply
|
||||||
|
waits for the read signal (G_IO_IN). When it happens, the
|
||||||
|
low-level I/O engine gets called, the data is read into
|
||||||
|
pre-allocated buffers and the appropriate call-backs are
|
||||||
|
performed. Technically, whenever a G_IO_IN event is generated,
|
||||||
|
data is received from the socket file descriptor, by using the
|
||||||
|
IO_read function. This iterative process finishes upon EOF (or on
|
||||||
|
an error condition).
|
||||||
|
|
||||||
|
|
||||||
|
----------------------
|
||||||
|
Closing the connection
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
Closing a TCP connection requires four data segments, not an
|
||||||
|
impressive amount but twice the round trip time, which can be
|
||||||
|
substantial. When data retrieval finishes, socket closing is
|
||||||
|
triggered. There's nothing but a IO_close_fd call on the socket's
|
||||||
|
file descriptor. This process was originally designed to split
|
||||||
|
the four segment close into two partial closes, one when query
|
||||||
|
sending is done and the other when all data is in. This scheme is
|
||||||
|
not currently used because the write close also stops the reading
|
||||||
|
part.
|
||||||
|
|
||||||
|
|
||||||
|
The low-level I/O engine
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
Dillo I/O is carried out in the background. This is achieved
|
||||||
|
by using low level file descriptors and signals. Anytime a file
|
||||||
|
descriptor shows activity, a signal is raised and the signal
|
||||||
|
handler takes care of the I/O.
|
||||||
|
|
||||||
|
The low-level I/O engine ("I/O engine" from here on) was
|
||||||
|
designed as an internal abstraction layer for background file
|
||||||
|
descriptor activity. It is intended to be used by the cache
|
||||||
|
module only; higher level routines should ask the cache for its
|
||||||
|
URLs. Every operation that is meant to be carried out in
|
||||||
|
background should be handled by the I/O engine. In the case of
|
||||||
|
TCP sockets, they are created and submitted to the I/O engine for
|
||||||
|
any further processing.
|
||||||
|
|
||||||
|
The submitting process (client) must fill a request structure
|
||||||
|
and let the I/O engine handle the file descriptor activity, until
|
||||||
|
it receives a call-back for finally processing the data. This is
|
||||||
|
better understood by examining the request structure:
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
gint Key; /* Primary Key (for klist) */
|
||||||
|
gint Op; /* IORead | IOWrite | IOWrites */
|
||||||
|
gint FD; /* Current File Descriptor */
|
||||||
|
gint Flags; /* Flag array */
|
||||||
|
glong Status; /* Number of bytes read, or -errno code */
|
||||||
|
|
||||||
|
void *Buf; /* Buffer place */
|
||||||
|
size_t BufSize; /* Buffer length */
|
||||||
|
void *BufStart; /* PRIVATE: only used inside IO.c! */
|
||||||
|
|
||||||
|
void *ExtData; /* External data reference (not used by IO.c) */
|
||||||
|
void *Info; /* CCC Info structure for this IO */
|
||||||
|
GIOChannel *GioCh; /* IO channel */
|
||||||
|
guint watch_id; /* glib's event source id */
|
||||||
|
} IOData_t;
|
||||||
|
|
||||||
|
To request an I/O operation, this structure must be filled and
|
||||||
|
passed to the I/O engine.
|
||||||
|
|
||||||
|
'Op' and 'Buf' and 'BufSize' MUST be provided.
|
||||||
|
|
||||||
|
'ExtData' MAY be provided.
|
||||||
|
|
||||||
|
'Status', 'FD' and 'GioCh' are set by I/O engine internal
|
||||||
|
routines.
|
||||||
|
|
||||||
|
When there is new data in the file descriptor, 'IO_callback'
|
||||||
|
gets called (by glib). Only after the I/O engine finishes
|
||||||
|
processing the data are the upper layers notified.
|
||||||
|
|
||||||
|
|
||||||
|
The I/O engine transfer buffer
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
The 'Buf' and 'BufSize' fields of the request structure
|
||||||
|
provide the transfer buffer for each operation. This buffer must
|
||||||
|
be set by the client (to increase performance by avoiding copying
|
||||||
|
data).
|
||||||
|
|
||||||
|
On reads, the client specifies the amount and where to place
|
||||||
|
the retrieved data; on writes, it specifies the amount and source
|
||||||
|
of the data segment that is to be sent. Although this scheme
|
||||||
|
increases complexity, it has proven very fast and powerful. For
|
||||||
|
instance, when the size of a document is known in advance, a
|
||||||
|
buffer for all the data can be allocated at once, eliminating the
|
||||||
|
need for multiple memory reallocations. Even more, if the size is
|
||||||
|
known and the data transfer is taking the form of multiple small
|
||||||
|
chunks of data, the client only needs to update 'Buf' and
|
||||||
|
BufSize' to point to the next byte in its large preallocated
|
||||||
|
reception buffer (by adding the chunk size to 'Buf'). On the
|
||||||
|
other hand, if the size of the transfer isn't known in advance,
|
||||||
|
the reception buffer can remain untouched until the connection
|
||||||
|
closes, but the client must then accomplish the usual buffer
|
||||||
|
copying and reallocation.
|
||||||
|
|
||||||
|
The I/O engine also lets the client specify a full length
|
||||||
|
transfer buffer when sending data. It doesn't matter (from the
|
||||||
|
client's point of view) if the data fits in a single packet or
|
||||||
|
not, it's the I/O engine's job to divide it into smaller chunks
|
||||||
|
if needed and to perform the operation accordingly.
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------
|
||||||
|
Handling multiple simultaneous connections
|
||||||
|
------------------------------------------
|
||||||
|
|
||||||
|
The previous sections describe the internal work for a single
|
||||||
|
connection, the I/O engine handles several of them in parallel.
|
||||||
|
This is the normal downloading behavior of a web page. Normally,
|
||||||
|
after retrieving the main document (HTML code), several
|
||||||
|
references to other files (typically images) and sometimes even
|
||||||
|
to other sites (mostly advertising today) are found inside the
|
||||||
|
page. In order to parse and complete the page rendering, those
|
||||||
|
other documents must be fetched and displayed, so it is not
|
||||||
|
uncommon to have multiple downloading connections (every one
|
||||||
|
requiring the whole fetching process) happening at the same time.
|
||||||
|
|
||||||
|
Even though socket activity can reach a hectic pace, the
|
||||||
|
browser never blocks. Note also that the I/O engine is the one
|
||||||
|
that directs the execution flow of the program by triggering a
|
||||||
|
call-back chain whenever a file descriptor operation succeeds or
|
||||||
|
fails.
|
||||||
|
|
||||||
|
A key point for this multiple call-back chained I/O engine is
|
||||||
|
that every single function in the chain must be guaranteed to
|
||||||
|
return quickly. Otherwise, the whole system blocks until it
|
||||||
|
returns.
|
||||||
|
|
||||||
|
|
||||||
|
-----------
|
||||||
|
Conclusions
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Dillo is currently in very stable beta state. It already shows
|
||||||
|
impressive performance, and its interactive ``feel'' is much
|
||||||
|
better than that of other web browsers.
|
||||||
|
|
||||||
|
The modular structure of Dillo, and its reliance on GTK1 allow
|
||||||
|
it to be very small. Not every feature of HTML-4.01 has been
|
||||||
|
implemented yet, but no significant problems are foreseen in
|
||||||
|
doing this.
|
||||||
|
|
||||||
|
The fact that Dillo's central I/O engine is written using
|
||||||
|
advanced features of POSIX and TCP/IP networking makes its
|
||||||
|
performance possible, but on the other hand this also means that
|
||||||
|
only a fraction of the interested hackers are able to work on it.
|
||||||
|
|
||||||
|
A simple code base is critical when trying to attract hackers
|
||||||
|
to work on a project like this one. Using the GTK+ framework
|
||||||
|
helped both in creating the graphical user interface and in
|
||||||
|
handling the concurrency inside the browser. By having threads
|
||||||
|
communicate through pipes the need for explicit synchronization
|
||||||
|
is almost completely eliminated, and with it most of the
|
||||||
|
complexity of concurrent programming disappears.
|
||||||
|
|
||||||
|
A clean, strictly applied layering approach based on clear
|
||||||
|
abstractions is vital in each programming project. A good,
|
||||||
|
supportive framework is of much help here.
|
||||||
|
|
||||||
|
|
||||||
129
devdoc/Images.txt
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
January 2009, --Jcid
|
||||||
|
|
||||||
|
Update June 2015: See also doc/dw-images-and-backgrounds.doc, or
|
||||||
|
../html/dw-images-and-backgrounds.html (generated by doxygen).
|
||||||
|
|
||||||
|
------
|
||||||
|
IMAGES
|
||||||
|
------
|
||||||
|
|
||||||
|
* When a image tag is found within a HTML page, Html_tag_open_img
|
||||||
|
handles it by:
|
||||||
|
|
||||||
|
- Parsing & getting attribute values.
|
||||||
|
- Creating a new image structure (DilloImage) and its
|
||||||
|
associated widget (DwImage).
|
||||||
|
i.e. If 'Image' is the var for the structure, then
|
||||||
|
'Image->dw' is the widget.
|
||||||
|
- Requesting the image to be fed by the cache.
|
||||||
|
- Sending some info to the browser interface.
|
||||||
|
|
||||||
|
* The cache can either request the image data from the net, or
|
||||||
|
feed it directly from the dicache (decompressed image cache).
|
||||||
|
|
||||||
|
* Both processes are somewhat different because the first one
|
||||||
|
requires to decode the image data into RGB format, and the second
|
||||||
|
one has the whole data already decoded.
|
||||||
|
|
||||||
|
* Regardless of the RGB-data feeding method, the decoded data is
|
||||||
|
passed to the widget (DwImage) and drawn in a streamed way.
|
||||||
|
Note that INDEXED images are also decoded into RGB format.
|
||||||
|
|
||||||
|
Html_tag_open_img // IMG element processing
|
||||||
|
Html_add_new_image // Read attributes, create image, add to HTML page
|
||||||
|
a_Image_new // Create a 'DilloImage' data structure, to coordinate
|
||||||
|
// decoded image-data transfer to an 'Imgbuf'.
|
||||||
|
Html_add_widget // Adds the dw::Image to the page
|
||||||
|
Html_load_image // Tells cache to retrieve image
|
||||||
|
|
||||||
|
|
||||||
|
---------------------
|
||||||
|
Fetching from the net
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
* a_Capi_open_url initiates the resource request, and when
|
||||||
|
finally the answer arrives, the HTTP header is examined for MIME
|
||||||
|
type and either the GIF or PNG or JPEG decoder is set to handle
|
||||||
|
the incoming data stream.
|
||||||
|
|
||||||
|
Decoding functions:
|
||||||
|
a_Gif_callback, a_Jpeg_callback and a_Png_callback.
|
||||||
|
|
||||||
|
* The decoding function calls the following dicache methods as
|
||||||
|
the data is processed (listed in order):
|
||||||
|
|
||||||
|
a_Dicache_set_parms
|
||||||
|
a_Dicache_set_cmap (only for indexed-GIF images)
|
||||||
|
a_Dicache_write
|
||||||
|
a_Dicache_new_scan (MAY be called here or after set_cmap)
|
||||||
|
a_Dicache_close
|
||||||
|
|
||||||
|
|
||||||
|
* The dicache methods call the necessary functions to connect
|
||||||
|
with the widget code. This is done by calling image.c functions:
|
||||||
|
|
||||||
|
a_Image_set_parms
|
||||||
|
a_Image_set_cmap
|
||||||
|
a_Image_write
|
||||||
|
a_Image_new_scan
|
||||||
|
a_Image_close
|
||||||
|
|
||||||
|
* The functions in image.c make the required Dw calls.
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------
|
||||||
|
Fetching from the dicache
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
* a_Capi_open_url() tests the cache for the image, and the cache,
|
||||||
|
via a_Cache_open_url(), enqueues a client for it, without asking
|
||||||
|
the network for the data. When the client queue is processed (a
|
||||||
|
bit later), Dicache_image() is set as the callback.
|
||||||
|
|
||||||
|
* When Dicache_image() is called, it sets the proper image data
|
||||||
|
decoder (RGB) and its data structure based on the entry's Type.
|
||||||
|
Then it substitutes itself with a_Dicache_callback() as the
|
||||||
|
handling function, and gets out of the way.
|
||||||
|
|
||||||
|
* Thenceforth the rest of the functions calls is driven by
|
||||||
|
a_Dicache_callback().
|
||||||
|
|
||||||
|
|
||||||
|
-----------
|
||||||
|
Misc. notes
|
||||||
|
-----------
|
||||||
|
|
||||||
|
* Repeated images generate new cache clients, but they may share
|
||||||
|
the imgbuf.
|
||||||
|
Note: Currently there's no proper support for transparent
|
||||||
|
images (i.e. decode to RGBA), but most of the time they render
|
||||||
|
the background color OK. This is: when first loaded, repeated
|
||||||
|
images share a background color, but when cached they render
|
||||||
|
correctly ;-). There's no point in trying to fix this because the
|
||||||
|
correct solution is to decode to RGBA and let the toolkit (FLTK)
|
||||||
|
handle the transparency.
|
||||||
|
|
||||||
|
* The first cache-client callback (Dicache_image()) is set when
|
||||||
|
the Content-type of the image is got.
|
||||||
|
|
||||||
|
* Later on, when there's a shared imgbuf, the dicache's logic
|
||||||
|
avoids decoding it multiple times and reuses what's already done.
|
||||||
|
|
||||||
|
* The dicache-entry and the Image structure hold bit arrays that
|
||||||
|
represent which rows have been decoded.
|
||||||
|
|
||||||
|
* The image processing can be found in the following sources:
|
||||||
|
|
||||||
|
- image.{cc,hh}
|
||||||
|
- dicache.[ch]
|
||||||
|
- gif.[ch], png.[ch], jpeg.[ch]
|
||||||
|
- dw/image.{cc,hh}
|
||||||
|
|
||||||
|
* Bear in mind that there are four data structures for image
|
||||||
|
code:
|
||||||
|
|
||||||
|
- DilloImage (image.hh)
|
||||||
|
- DICacheEntry (dicache.h)
|
||||||
|
- dw::Image (class Image in dw/image.hh)
|
||||||
|
- core::Imgbuf (imgbuf.hh)
|
||||||
|
|
||||||
127
devdoc/NC_design.txt
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
|
||||||
|
_________________________________________________________________
|
||||||
|
|
||||||
|
Naming&Coding design
|
||||||
|
_________________________________________________________________
|
||||||
|
|
||||||
|
Dillo's code is divided into modules. For instance: bookmark, cache,
|
||||||
|
dicache, gif.
|
||||||
|
|
||||||
|
Let's think of a module named "menu", then:
|
||||||
|
* Every internal routine of the module, should start with "Menu_"
|
||||||
|
prefix.
|
||||||
|
* "Menu_" prefixed functions are not meant to be called from outside
|
||||||
|
the module.
|
||||||
|
* If the function is to be exported to other modules (i.e. it will
|
||||||
|
be called from the outside), it should be wrapped with an "a_"
|
||||||
|
prefix.
|
||||||
|
|
||||||
|
For instance: if the function name is "Menu_create", then it's an
|
||||||
|
internal function, but if we need to call it from the outside, then it
|
||||||
|
should be renamed to "a_Menu_create".
|
||||||
|
|
||||||
|
Why the "a_" prefix?
|
||||||
|
Because of historical reasons.
|
||||||
|
And "a_Menu_create" reads better than "d_Menu_create" because the
|
||||||
|
first one suggests "a Menu create" function!
|
||||||
|
|
||||||
|
Another way of understanding this is thinking of "a_" prefixed
|
||||||
|
functions as Dillo's internal library, and the rest ("Menu_" prefixed
|
||||||
|
in our example) as a private module-library.
|
||||||
|
|
||||||
|
Indentation:
|
||||||
|
|
||||||
|
Source code must be indented with 3 blank spaces, no Tabs.
|
||||||
|
Why?
|
||||||
|
Because different editors expand or treat tabs in several ways; 8
|
||||||
|
spaces being the most common, but that makes code really wide and
|
||||||
|
we'll try to keep it within the 80 columns bounds (printer friendly).
|
||||||
|
|
||||||
|
You can use: indent -kr -sc -i3 -bad -nbbo -nut -l79 myfile.c
|
||||||
|
|
||||||
|
Function commenting:
|
||||||
|
|
||||||
|
Every single function of the module should start with a short comment
|
||||||
|
that explains its purpose; three lines must be enough, but if you
|
||||||
|
think it requires more, enlarge it.
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Try finding the url in the cache. If it hits, send the contents
|
||||||
|
* to the caller. If it misses, set up a new connection.
|
||||||
|
*/
|
||||||
|
int a_Cache_open_url(const char *url, void *Data)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
...
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
We also have the BUG:, TODO:, and WORKAROUND: tags.
|
||||||
|
Use them within source code comments to spot hidden issues. For
|
||||||
|
instance:
|
||||||
|
|
||||||
|
/* BUG: this counter is not accurate */
|
||||||
|
++i;
|
||||||
|
|
||||||
|
/* TODO: get color from the right place */
|
||||||
|
a = color;
|
||||||
|
|
||||||
|
/* WORKAROUND: the canonical way of doing it doesn't work yet. */
|
||||||
|
++a; ++a; ++a;
|
||||||
|
|
||||||
|
Function length:
|
||||||
|
|
||||||
|
Let's try to keep functions within the 45 lines boundary. This eases
|
||||||
|
code reading, following, understanding and maintenance.
|
||||||
|
|
||||||
|
Functions with a single exit:
|
||||||
|
|
||||||
|
It's much easier to follow and maintain functions with a single exit
|
||||||
|
point at the bottom (instead of multiple returns). The exception to
|
||||||
|
the rule are calls like dReturn_if_fail() at its head.
|
||||||
|
|
||||||
|
dlib functions:
|
||||||
|
|
||||||
|
* Dillo uses dlib extensively in its C sources. Before starting
|
||||||
|
to code something new, a good starting point is to check what
|
||||||
|
this library has to offer (check dlib/dlib.h).
|
||||||
|
* Memory management must be done using dNew, dNew0, dMalloc, dFree
|
||||||
|
and their relatives.
|
||||||
|
* For debugging purposes and error catching (not for normal flow):
|
||||||
|
dReturn_if_fail, dReturn_val_if_fail etc. are encouraged.
|
||||||
|
* The MSG macro is extensively used to output additional information
|
||||||
|
to the calling terminal.
|
||||||
|
|
||||||
|
_________________________________________________________________
|
||||||
|
|
||||||
|
C++
|
||||||
|
|
||||||
|
Source code in C++ should follow the same rules with these exceptions:
|
||||||
|
|
||||||
|
* Class method names are camel-cased and start with lowercase
|
||||||
|
e.g. appendInputMultipart
|
||||||
|
* Classes and types start uppercased
|
||||||
|
e.g. class DilloHtmlReceiver
|
||||||
|
* Class methods don't need to prefix its module name
|
||||||
|
e.g. links->get()
|
||||||
|
|
||||||
|
We also try to keep the C++ relatively simple. Dillo does use
|
||||||
|
inheritance and templates, but that's about all.
|
||||||
|
|
||||||
|
_________________________________________________________________
|
||||||
|
|
||||||
|
What do we get with this?
|
||||||
|
|
||||||
|
* A clear module API for Dillo; every function prefixed "a_" is to
|
||||||
|
be used outside the module.
|
||||||
|
* A way to identify where the function came from (the
|
||||||
|
capitalized word is the module name).
|
||||||
|
* An inner ADT (Abstract data type) for the module that can be
|
||||||
|
isolated, tested and replaced independently.
|
||||||
|
* A two stage instance for bug-fixing. You can change the exported
|
||||||
|
function algorithms while someone else fixes the internal
|
||||||
|
module-ADT!
|
||||||
|
* A coding standard ;)
|
||||||
|
_________________________________________________________________
|
||||||
|
|
||||||
|
Naming&Coding design by Jorge Arellano Cid
|
||||||
51
devdoc/README
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
README: Last update Jul 2009
|
||||||
|
|
||||||
|
These documents cover dillo's internals.
|
||||||
|
For user help, see https://dillo-browser.github.io/old/dillo3-help.html
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
These documents need a review.
|
||||||
|
*.txt were current with Dillo1, but many have since become more or
|
||||||
|
less out-of-date.
|
||||||
|
*.doc are doxygen source for the Dillo Widget (dw) component, and
|
||||||
|
were written for Dillo2.
|
||||||
|
|
||||||
|
They will give you an overview of what's going on, but take them
|
||||||
|
with a pinch of salt.
|
||||||
|
|
||||||
|
Of course I'd like to have *.txt as doxygen files too!
|
||||||
|
If somebody wants to make this conversion, please let me know
|
||||||
|
to assign higher priority to updating these docs.
|
||||||
|
|
||||||
|
--
|
||||||
|
Jorge.-
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------
|
||||||
|
FILE DESCRIPTION STATE
|
||||||
|
--------------------------------------------------------------------------
|
||||||
|
NC_design.txt Naming&Coding design (Be sure to Current
|
||||||
|
read it before any other doc)
|
||||||
|
Dillo.txt General overview of the program Current
|
||||||
|
IO.txt Extensive introduction Current
|
||||||
|
Cache.txt Informative description Current
|
||||||
|
Images.txt Image handling and processing Current
|
||||||
|
HtmlParser.txt A versatile parser Current
|
||||||
|
Dw.txt The New Dillo Widget (Overview) Current
|
||||||
|
Imgbuf.txt Image buffers Pending
|
||||||
|
Selection.txt Selections, and link activation Current (?)
|
||||||
|
Cookies.txt Explains how to enable cookies Current
|
||||||
|
Dpid.txt Dillo plugin daemon Current
|
||||||
|
--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
* BTW, there's a small program (srch) within the src/ dir. It searches
|
||||||
|
tokens within the whole code (*.[ch]). It has proven very useful.
|
||||||
|
Ex: ./srch a_Image_write
|
||||||
|
./srch todo:
|
||||||
|
|
||||||
|
* Please submit your patches with 'hg diff'.
|
||||||
|
|
||||||
|
|
||||||
|
Happy coding!
|
||||||
|
--Jcid
|
||||||
2669
devdoc/doxygen-awesome.css
Normal file
105
devdoc/dw-changes.doc
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
/** \page dw-changes Changes to the GTK+-based Release Version
|
||||||
|
|
||||||
|
<h2>Changes in Dw</h2>
|
||||||
|
|
||||||
|
Related to the FLTK port, there have been many changes, this is a
|
||||||
|
(hopefully complete) list:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li> Rendering abstraction, read \ref dw-overview and \ref dw-layout-views
|
||||||
|
for details. Some important changes:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li> The underlying platform (e.g. the UI toolkit) is fully abstract,
|
||||||
|
there are several platform independent structures replacing
|
||||||
|
GTK+ structures, e.g. dw::core::Event.
|
||||||
|
|
||||||
|
<li> The central class managing the widget tree is not anymore
|
||||||
|
GtkDwViewport, but dw::core::Layout.
|
||||||
|
|
||||||
|
<li> Drawing is done via dw::core::View, a pointer is passed to
|
||||||
|
dw::core::Widget::draw.
|
||||||
|
|
||||||
|
<li> The distinction between viewport coordinates and canvas
|
||||||
|
coordinates (formerly world coordinates) has been mostly
|
||||||
|
removed. (Only for views, it sometimes plays a role, see
|
||||||
|
\ref dw-layout-views).
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<li> Cursors have been moved to dw::core::style, see
|
||||||
|
dw::core::style::Style::cursor. dw::core::Widget::setCursor is now
|
||||||
|
protected (and so only called by widget implementations).
|
||||||
|
|
||||||
|
<li> World coordinates are now called canvas coordinates.
|
||||||
|
|
||||||
|
<li> There is now a distinction between dw::core::style::StyleAttrs and
|
||||||
|
dw::core::style::Style.
|
||||||
|
|
||||||
|
<li> There is no base class for container widgets anymore. The former
|
||||||
|
DwContainer::for_all has been removed, instead this functionality
|
||||||
|
is now done via iterators (dw::core::Widget::iterator,
|
||||||
|
dw::core::Iterator).
|
||||||
|
|
||||||
|
<li> DwPage is now called dw::Textblock, and DwAlignedPage
|
||||||
|
dw::AlignedTextblock.
|
||||||
|
|
||||||
|
<li> dw::Textblock, all sub classes of it, and dw::Table do not read
|
||||||
|
"limit_text_width" from the preferences, but get it as an argument.
|
||||||
|
(May change again.)
|
||||||
|
|
||||||
|
<li> dw::Table has been rewritten.
|
||||||
|
|
||||||
|
<li> Instead of border_spacing in the old DwStyle, there are two attributes,
|
||||||
|
dw::core::style::Style::hBorderSpacing and
|
||||||
|
dw::core::style::Style::vBorderSpacing, since CSS allows to specify
|
||||||
|
two values. Without CSS, both attributes should have the same value.
|
||||||
|
|
||||||
|
<li> Images are handled differently, see \ref dw-images-and-backgrounds.
|
||||||
|
|
||||||
|
<li> Embedded UI widgets (formerly GtkWidget's) are handled differently,
|
||||||
|
see dw::core::ui.
|
||||||
|
|
||||||
|
<li> DwButton has been removed, instead, embedded UI widgets are used. See
|
||||||
|
dw::core::ui and dw::core::ui::ComplexButtonResource.
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
Dw is now written C++, the transition should be obvious. All "Dw"
|
||||||
|
prefixes have been removed, instead, namespaces are used now:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>dw::core contains the core,
|
||||||
|
<li>dw::core::style styles,
|
||||||
|
<li>dw::core::ui embedded UI resources,
|
||||||
|
<li>dw::fltk classes related to FLTK, and
|
||||||
|
<li>::dw the widgets.
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Documentation</h2>
|
||||||
|
|
||||||
|
The old documentation has been moved to:
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr><th colspan="2">Old <th>New
|
||||||
|
<tr><td rowspan="2">Dw.txt
|
||||||
|
<td>general part <td>\ref dw-overview, \ref dw-usage,
|
||||||
|
\ref dw-layout-widgets,
|
||||||
|
\ref dw-widget-sizes
|
||||||
|
<tr><td>remarks on specific widgets <td>respective source files: dw::Bullet,
|
||||||
|
dw::core::ui::Embed
|
||||||
|
<tr><td rowspan="2">DwImage.txt
|
||||||
|
<td>signals <td>dw::core::Layout::LinkReceiver
|
||||||
|
<tr><td>rest <td>dw::Image,
|
||||||
|
\ref dw-images-and-backgrounds
|
||||||
|
<tr><td colspan="2">Imgbuf.txt <td>dw::core::Imgbuf,
|
||||||
|
\ref dw-images-and-backgrounds
|
||||||
|
<tr><td colspan="2">DwPage.txt <td>dw::Textblock
|
||||||
|
<tr><td colspan="2">DwRender.txt <td>\ref dw-overview, \ref dw-layout-views,
|
||||||
|
dw::core::ui
|
||||||
|
<tr><td colspan="2">DwStyle.txt <td>dw::core::style
|
||||||
|
<tr><td colspan="2">DwTable.txt <td>dw::Table
|
||||||
|
<tr><td colspan="2">DwWidget.txt <td>dw::core::Widget, \ref dw-layout-widgets,
|
||||||
|
\ref dw-widget-sizes
|
||||||
|
<tr><td colspan="2">Selection.txt <td>dw::core::SelectionState
|
||||||
|
</table>
|
||||||
|
|
||||||
|
*/
|
||||||
BIN
devdoc/dw-example-screenshot.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
63
devdoc/dw-fixed-positions.doc
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/** \page dw-fixed-positions Fixed positions
|
||||||
|
|
||||||
|
In some cases, widgets or widget content must be positioned relative
|
||||||
|
to the viewport. As in the CSS specification, these positions will be
|
||||||
|
called "fixed positions". This must not be confused with "fixedly
|
||||||
|
positioned elements" (see \ref dw-out-of-flow), which are a special
|
||||||
|
case of fixed positions.
|
||||||
|
|
||||||
|
|
||||||
|
Applications
|
||||||
|
============
|
||||||
|
|
||||||
|
As defined by CSS
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
- "position: fixed"; see \ref dw-out-of-flow.
|
||||||
|
- "background-attachment: fixed"; see \ref dw-images-and-backgrounds.
|
||||||
|
|
||||||
|
Idea for tables
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Often, tables have a header, which contains information necessary to
|
||||||
|
interpret the columns in the table body. For this, HTML defines the elements
|
||||||
|
<thead> and <tbody>
|
||||||
|
<sup><a href="#note-table-footer" id="ref-table-footer">[1]</a></sup>.
|
||||||
|
|
||||||
|
For large tables, the problem occurs that the table header gets out of
|
||||||
|
the reader's view. In paged media, where a large table covers multiple
|
||||||
|
pages, this is often solved by *repeating* the table header on each
|
||||||
|
page occupied by the table. When using a viewport, a table larger than
|
||||||
|
the viewport could be displayed like this:
|
||||||
|
|
||||||
|
1. If the top of the table is within the viewport, show the table
|
||||||
|
header at the usual position.
|
||||||
|
2. As soon as top of the table gets above the top border of the
|
||||||
|
viewport, keep the table header at the viewport top, so that it is
|
||||||
|
still visible (this means, it moves down, relative to the
|
||||||
|
*canvas*). This way, the header is still visible, so our objective
|
||||||
|
is achieved.
|
||||||
|
3. When scrolling further down, at some point the table body gets out
|
||||||
|
of the viewport again, and so should the table header.
|
||||||
|
|
||||||
|
(Some images would be nice.)
|
||||||
|
|
||||||
|
These ideas should be considered when developing a design for fixed
|
||||||
|
positions.
|
||||||
|
|
||||||
|
|
||||||
|
Design sketch
|
||||||
|
==============
|
||||||
|
|
||||||
|
[...]
|
||||||
|
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
<sup><a href="#ref-table-footer" id="note-table-footer">[1]</a></sup>
|
||||||
|
... and also <tfoot>, which is not discussed here, for reasons
|
||||||
|
of simplicity. However, it is obvious that <tfoot> should be
|
||||||
|
dealt with in an analogue way as <thead>.
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
BIN
devdoc/dw-floats-01.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
209
devdoc/dw-grows.doc
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
/** \page dw-grows GROWS - Grand Redesign Of Widget Sizes
|
||||||
|
|
||||||
|
<div style="border: 2px solid #ffff00; margin: 1em 0;
|
||||||
|
padding: 0.5em 1em; background-color: #ffffe0">The complex "widget
|
||||||
|
sizes" is currently divided into three documents: \ref
|
||||||
|
dw-widget-sizes, **Grand Redesign Of Widget Sizes** (this document),
|
||||||
|
and \ref dw-size-request-pos. Furthermore, there are some notes in
|
||||||
|
\ref dw-miscellaneous.</div>
|
||||||
|
|
||||||
|
This paper describes (will describe) some design changes to
|
||||||
|
calculating widget sizes. Goals are:
|
||||||
|
|
||||||
|
- Simplification of widget size calculation by the parent widget;
|
||||||
|
dw::Textblock::calcWidgetSize, dw::OutOfFlowMgr::ensureFloatSize
|
||||||
|
etc. should become simpler or perhaps even obsolete.
|
||||||
|
|
||||||
|
- Making the implementation of some features possible:
|
||||||
|
|
||||||
|
- *max-width*, *max-height*, *min-width*, *min-height*;
|
||||||
|
- correct aspect ratio for images with only one percentage size defined;
|
||||||
|
- *display: inline-block*;
|
||||||
|
- <button>.
|
||||||
|
|
||||||
|
|
||||||
|
A short sketch
|
||||||
|
==============
|
||||||
|
|
||||||
|
**dw::core::Widget::sizeRequest and dw::core::Widget::getExtremes will
|
||||||
|
return final results.** The caller does not have to correct the size,
|
||||||
|
e. g. when percentages are defined. As an example,
|
||||||
|
dw::Textblock::calcWidgetSize has already become much simpler.
|
||||||
|
|
||||||
|
**A new hierarchy, *container*:** Aside from dw::core::Widget::parent
|
||||||
|
and dw::core::Widget::generator, there is a third hierarchy
|
||||||
|
dw::core::Widget::container, which is (unlike *generator*) always a
|
||||||
|
direct ancestor, and represents what in CSS is called *containing
|
||||||
|
block*. Containers are important to define the "context size", which
|
||||||
|
is (not solely) used for percentage sizes.
|
||||||
|
|
||||||
|
(There is another "containing block", dw::Textblock::containingBlock;
|
||||||
|
these may be consolidated some day.)
|
||||||
|
|
||||||
|
**The process of size calculation is split between the widget itself
|
||||||
|
and its container:**
|
||||||
|
|
||||||
|
- The container provides some abstract methods:
|
||||||
|
dw::core::Widget::getAvailWidthOfChild,
|
||||||
|
dw::core::Widget::getAvailHeightOfChild,
|
||||||
|
dw::core::Widget::correctRequisitionOfChild, and
|
||||||
|
dw::core::Widget::correctExtremesOfChild, which can be used in the
|
||||||
|
actual implementation of dw::core::Widget::sizeRequestImpl;
|
||||||
|
different containers with different ways how to arrange their
|
||||||
|
children will implement these methods in a different way. (Simple
|
||||||
|
example: the *available width* for children within a textblock is
|
||||||
|
the *available width* for the textblock itself, minus
|
||||||
|
margin/border/padding; on the other hand, it is completely different
|
||||||
|
for children of tables, for which a complex column width calculation
|
||||||
|
is used.)
|
||||||
|
|
||||||
|
- The actual size calculation is, however, controlled by the widget
|
||||||
|
itself, which only *uses* these methods above.
|
||||||
|
|
||||||
|
<div style="border: 2px solid #ffff00; margin-top: 0.5em;
|
||||||
|
margin-bottom: 0.5em; padding: 0.5em 1em; background-color: #ffffe0">
|
||||||
|
<b>Update:</b> This is not fully correct; the parents are also involved
|
||||||
|
for calculating available widths and heights, at least when CSS 'width'
|
||||||
|
and 'height' are not set.</div>
|
||||||
|
|
||||||
|
**Size hints are removed.** Instead, the container methods in the
|
||||||
|
previous paragraph are used. Changes of container sizes (especially
|
||||||
|
viewport the size) are handled in a different way.
|
||||||
|
|
||||||
|
**Extremes are extended by intrinsic values.** In some cases (see
|
||||||
|
dw::Table::forceCalcCellSizes, case *minWidth* > *totalWidth*, for an
|
||||||
|
example) it is useful to know about minimal and maximal width of a
|
||||||
|
widget independent of CSS attributes. For this, dw::core::Extremes is
|
||||||
|
extended by:
|
||||||
|
|
||||||
|
- dw::core::Extremes::minWidthIntrinsic and
|
||||||
|
- dw::core::Extremes::maxWidthIntrinsic.
|
||||||
|
|
||||||
|
The rules for the calculation:
|
||||||
|
|
||||||
|
1. If a widget has no children, it calculates *minWidthIntrinsic* and
|
||||||
|
*maxWidthIntrinsic* as those values not affected by CSS hints.
|
||||||
|
(dw::core::Widget::correctExtremes will not change these values.)
|
||||||
|
2. A widget must calculate *minWidthIntrinsic* and *maxWidthIntrinsic*
|
||||||
|
from *minWidthIntrinsic* and *maxWidthIntrinsic* of its children,
|
||||||
|
and *minWidth* and *maxWidth* from *minWidth* and *maxWidth* of its
|
||||||
|
children.
|
||||||
|
3. At the end, *minWidth* and *maxWidth* of a widget are corrected by
|
||||||
|
CSS attributes. (dw::core::Widget::correctExtremes will do this.)
|
||||||
|
|
||||||
|
<div style="border: 2px solid #ffff00; margin-top: 0.5em;
|
||||||
|
margin-bottom: 0.5em; padding: 0.5em 1em; background-color: #ffffe0">
|
||||||
|
<b>Notice:</b> Currently, dw::core::Widget::getExtremesImpl must
|
||||||
|
set all four members in dw::core::Extremes; this may change.</div>
|
||||||
|
|
||||||
|
Another **extension of extremes: *adjustmentWidth*.** This is used as
|
||||||
|
minimum for the width, when "adjust_min_width" (or,
|
||||||
|
"adjust_table_min_width", respectively) is set.
|
||||||
|
|
||||||
|
The rules for the calculation:
|
||||||
|
|
||||||
|
1. If a widget has no children, it can choose a suitable value,
|
||||||
|
typically based on dw::core::Extremes::minWidth and
|
||||||
|
dw::core::Extremes::minWidthIntrinsic.
|
||||||
|
2. A widget must calculate *adjustmentWidth* from *adjustmentWidth* of
|
||||||
|
its children.
|
||||||
|
|
||||||
|
*Note:* An implementation of dw::core::Widget::getExtremesImpl may set
|
||||||
|
this value *after* calling dw::core::Widget::correctExtremesOfChild,
|
||||||
|
so that it cannot be used for the correction of extremes. In this case
|
||||||
|
*useAdjustmentWidth = false* should be passed to
|
||||||
|
dw::core::Widget::correctExtremesOfChild. On the other hand, if known
|
||||||
|
before, *useAdjustmentWidth* should be set to *true*.
|
||||||
|
|
||||||
|
Rules for *new* methods related to resizing
|
||||||
|
===========================================
|
||||||
|
|
||||||
|
- Of course, *sizeRequestImpl* may (should) call *correctRequisition*,
|
||||||
|
and *getExtremesImpl* may (should) call *correctExtremes*.
|
||||||
|
|
||||||
|
- *sizeRequestImpl* (and *correctRequisition*) is allowed to call
|
||||||
|
*getAvailWidth* and *getAvailHeight* with *forceValue* set, but
|
||||||
|
*getExtremesImpl* (and *correctExtremes*) is allowed to call these
|
||||||
|
only with *forceValue* unset.
|
||||||
|
|
||||||
|
- For this reason, *sizeRequestImpl* is indeed allowed to call
|
||||||
|
*getExtremes* (dw::Table does so), but the opposite
|
||||||
|
(*getExtremesImpl* calling *sizeRequest*) is not allowed
|
||||||
|
anymore. (Before GROWS, the standard implementation
|
||||||
|
dw::core::Widget::getExtremesImpl did so.)
|
||||||
|
|
||||||
|
- Finally, *getAvailWidth* and *getAvailHeight* may call
|
||||||
|
*getExtremes*, if and only if *forceValue* is set.
|
||||||
|
|
||||||
|
Here is a diagram showing all permitted dependencies:
|
||||||
|
|
||||||
|
\dot
|
||||||
|
digraph G {
|
||||||
|
node [shape=record, fontname=Helvetica, fontsize=10, color="#c0c0c0"];
|
||||||
|
edge [arrowhead="open", arrowtail="none", color="#404040"];
|
||||||
|
|
||||||
|
"sizeRequest[Impl]" -> "getExtremes[Impl]";
|
||||||
|
"sizeRequest[Impl]" -> correctRequisition;
|
||||||
|
"getExtremes[Impl]" -> correctExtremes;
|
||||||
|
"sizeRequest[Impl]" -> "getAvail[Width|Height] (true)";
|
||||||
|
"getExtremes[Impl]" -> "getAvail[Width|Height] (false)";
|
||||||
|
correctRequisition -> "getAvail[Width|Height] (true)";
|
||||||
|
correctExtremes -> "getAvail[Width|Height] (false)";
|
||||||
|
"getAvail[Width|Height] (true)" -> "getExtremes[Impl]";
|
||||||
|
}
|
||||||
|
\enddot
|
||||||
|
|
||||||
|
Open issues
|
||||||
|
===========
|
||||||
|
|
||||||
|
**Do CSS size dimensions override intrinsic sizes in all cases?** If a
|
||||||
|
textblock needs at least, say, 100 pixels width so that the text can
|
||||||
|
be read, but has a specification "width: 50px", should half of the
|
||||||
|
text be invisible? Or should the width be corrected again to 100
|
||||||
|
pixels?
|
||||||
|
|
||||||
|
Currently, in the CSS size specification is honoured in all cases,
|
||||||
|
with one exception: see dw::Textblock::sizeRequestImpl and see
|
||||||
|
dw::Textblock::getExtremesImpl (the time when
|
||||||
|
dw::core::Widget::correctRequisition and
|
||||||
|
dw::core::Widget::correctExtremes, respectively, is called).
|
||||||
|
|
||||||
|
*Not* honouring the CSS size specification in all cases could improve
|
||||||
|
readability in some cases, so this could depend on a user preference.
|
||||||
|
|
||||||
|
**Update:** There is now a dillorc option <tt>adjust_min_width</tt>,
|
||||||
|
which is implemented for widths, but not heights (since it is based on
|
||||||
|
width extremes, but there are currently no height extremes).
|
||||||
|
|
||||||
|
Another problem is that in most cases, there is no clippping, so that
|
||||||
|
contents may exceed the allocation of the widget, but redrawing is not
|
||||||
|
necessarily triggered.
|
||||||
|
|
||||||
|
**Percentage values for margins and paddings, as well as negative
|
||||||
|
margins** are interesting applications, but have not been considered
|
||||||
|
yet. For negative margins, a new attribute
|
||||||
|
dw::core::Widget::extraSpace could solve the problem of widgets
|
||||||
|
sticking out of the allocation of parent.
|
||||||
|
|
||||||
|
**Clarify percentage heights.** Search in widget.cc, and compare
|
||||||
|
section 10.5 ('height') of the CSS 2.1 specification to section 10.2
|
||||||
|
('width').
|
||||||
|
|
||||||
|
**Fast queue resize does not work fully.** Example: run
|
||||||
|
*test/dw-simple-container-test* (dw_simple_container_test.cc), resize
|
||||||
|
(best maximize) the window and follow (e. g. by using RTFL) what
|
||||||
|
happens in consequence of dw::core::Layout::viewportSizeChanged. The
|
||||||
|
dw::SimpleContainer in the middle is not affected, so only the two
|
||||||
|
dw::Textblock's (at the top and at the bottom) call queueResize with
|
||||||
|
*fast = true*, and so get *NEEDS_RESIZE* set; but since it is not set
|
||||||
|
for the dw::SimpleContainer, *sizeRequest* is never called for the
|
||||||
|
bottom dw::Textblock.
|
||||||
|
|
||||||
|
There does not seem to be a real case for this problem in dillo, since
|
||||||
|
all widgets which may contain other widgets (except
|
||||||
|
dw::SimpleContainer, which is not used outside tests) use the
|
||||||
|
available width and height (dw::core::Widget::usesAvailWidth and
|
||||||
|
dw::core::Widget::usesAvailHeight), and so are always affected by
|
||||||
|
viewport size changes.
|
||||||
|
|
||||||
|
*/
|
||||||
235
devdoc/dw-images-and-backgrounds.doc
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
/** \page dw-images-and-backgrounds Images and Backgrounds in Dw
|
||||||
|
|
||||||
|
Image Buffers
|
||||||
|
=============
|
||||||
|
|
||||||
|
Representation of the image data is done by dw::core::Imgbuf, see
|
||||||
|
there for details. Drawing is done by dw::core::View
|
||||||
|
(dw::core::View::drawImage).
|
||||||
|
|
||||||
|
Since dw::core::Imgbuf provides memory management based on reference
|
||||||
|
counting, there may be an 1-to-n relation from image renderers (image
|
||||||
|
widgets or backgrounds, see below) and dw::core::Imgbuf. Since
|
||||||
|
dw::core::Imgbuf does not know about renderers, but just provides
|
||||||
|
rendering functionality, the caller must (typically after calling
|
||||||
|
dw::core::Imgbuf::copyRow) notify all renderers connected to the
|
||||||
|
buffer.
|
||||||
|
|
||||||
|
|
||||||
|
Image Renderer
|
||||||
|
==============
|
||||||
|
|
||||||
|
Generally, there are no restrictions on how to manage
|
||||||
|
dw::core::Imgbuf; but to handle image data from web resources, the
|
||||||
|
interface dw::core::ImgRenderer should be implemented. It is again
|
||||||
|
wrapped by DilloImage (to make access from the C part possible, since
|
||||||
|
dw::core::ImgRenderer is written in C++), which is referenced by
|
||||||
|
DilloWeb. There are two positions where retrieving image data is
|
||||||
|
initiated:
|
||||||
|
|
||||||
|
- Html_load_image: for embedded images (implemented by dw::Image,
|
||||||
|
which implements dw::core::ImgRenderer);
|
||||||
|
- StyleEngine::apply (search for "case
|
||||||
|
CSS_PROPERTY_BACKGROUND_IMAGE"): for background images; there are
|
||||||
|
some implementations of dw::core::ImgRenderer within the context of
|
||||||
|
dw::core::style::StyleImage.
|
||||||
|
|
||||||
|
Both are described in detail below. Notice that the code is quite
|
||||||
|
similar; only the implementation of dw::core::ImgRenderer differs.
|
||||||
|
|
||||||
|
At this time, dw::core::ImgRenderer has got two methods (see more
|
||||||
|
documentation there):
|
||||||
|
|
||||||
|
- dw::core::ImgRenderer::setBuffer,
|
||||||
|
- dw::core::ImgRenderer::drawRow,
|
||||||
|
- dw::core::ImgRenderer::finish, and
|
||||||
|
- dw::core::ImgRenderer::fatal.
|
||||||
|
|
||||||
|
|
||||||
|
Images
|
||||||
|
======
|
||||||
|
|
||||||
|
This is the simplest renderer, displaying an image. For each row to be
|
||||||
|
drawn,
|
||||||
|
|
||||||
|
- first dw::core::Imgbuf::copyRow, and then
|
||||||
|
- for each dw::Image, dw::Image::drawRow must be called, with the same
|
||||||
|
argument (no scaling is necessary).
|
||||||
|
|
||||||
|
dw::Image automatically scales the dw::core::Imgbuf, the root buffer
|
||||||
|
should be passed to dw::Image::setBuffer.
|
||||||
|
|
||||||
|
\see dw::Image for more details.
|
||||||
|
|
||||||
|
|
||||||
|
Background Images
|
||||||
|
=================
|
||||||
|
|
||||||
|
Since background images are style resources, they are associated with
|
||||||
|
dw::core::style::Style, as dw::core::style::StyleImage, which is
|
||||||
|
handled in a similar way (reference counting etc.) as
|
||||||
|
dw::core::style::Color and dw::core::style::Font, although it is
|
||||||
|
concrete and not platform-dependant.
|
||||||
|
|
||||||
|
The actual renderer (passed to Web) is an instance of
|
||||||
|
dw::core::ImgRendererDist (distributes all calls to a set of other
|
||||||
|
instances of dw::core::ImgRenderer), which contains two kinds of
|
||||||
|
renderers:
|
||||||
|
|
||||||
|
- one instance of dw::core::style::StyleImage::StyleImgRenderer, which
|
||||||
|
does everything needed for dw::core::style::StyleImage, and
|
||||||
|
- other renderers, used externally (widgets etc.), which are added by
|
||||||
|
dw::core::style::StyleImage::putExternalImgRenderer (and removed by
|
||||||
|
dw::core::style::StyleImage::removeExternalImgRenderer).
|
||||||
|
|
||||||
|
This diagram gives an comprehensive overview:
|
||||||
|
|
||||||
|
\dot
|
||||||
|
digraph G {
|
||||||
|
node [shape=record, fontname=Helvetica, fontsize=10];
|
||||||
|
edge [arrowhead="open", dir="both", arrowtail="none",
|
||||||
|
labelfontname=Helvetica, labelfontsize=10, color="#404040",
|
||||||
|
labelfontcolor="#000080"];
|
||||||
|
fontname=Helvetica; fontsize=10;
|
||||||
|
|
||||||
|
subgraph cluster_dw_style {
|
||||||
|
style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
|
||||||
|
|
||||||
|
Style [URL="\ref dw::core::style::Style"];
|
||||||
|
StyleImage [URL="\ref dw::core::style::StyleImage"];
|
||||||
|
Imgbuf [URL="\ref dw::core::Imgbuf", color="#a0a0a0"];
|
||||||
|
StyleImgRenderer
|
||||||
|
[URL="\ref dw::core::style::StyleImage::StyleImgRenderer"];
|
||||||
|
ImgRenderer [URL="\ref dw::core::ImgRenderer", color="#ff8080"];
|
||||||
|
ImgRendererDist [URL="\ref dw::core::ImgRendererDist"];
|
||||||
|
ExternalImgRenderer
|
||||||
|
[URL="\ref dw::core::style::StyleImage::ExternalImgRenderer",
|
||||||
|
color="#a0a0a0"];
|
||||||
|
ExternalWidgetImgRenderer
|
||||||
|
[URL="\ref dw::core::style::StyleImage::ExternalWidgetImgRenderer",
|
||||||
|
color="#a0a0a0"];
|
||||||
|
}
|
||||||
|
|
||||||
|
subgraph cluster_dw_layout {
|
||||||
|
style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
|
||||||
|
|
||||||
|
Layout [URL="\ref dw::core::Layout"];
|
||||||
|
LayoutImgRenderer [URL="\ref dw::core::Layout::LayoutImgRenderer"];
|
||||||
|
}
|
||||||
|
|
||||||
|
subgraph cluster_dw_widget {
|
||||||
|
style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
|
||||||
|
|
||||||
|
Widget [URL="\ref dw::core::Widget", color="#a0a0a0"];
|
||||||
|
WidgetImgRenderer [URL="\ref dw::core::Widget::WidgetImgRenderer"];
|
||||||
|
}
|
||||||
|
|
||||||
|
subgraph cluster_dw_textblock {
|
||||||
|
style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
|
||||||
|
|
||||||
|
Textblock [URL="\ref dw::Textblock"];
|
||||||
|
Word [URL="\ref dw::Textblock::Word"];
|
||||||
|
WordImgRenderer [URL="\ref dw::Textblock::WordImgRenderer"];
|
||||||
|
SpaceImgRenderer [URL="\ref dw::Textblock::SpaceImgRenderer"];
|
||||||
|
}
|
||||||
|
|
||||||
|
Style -> StyleImage [headlabel="*", taillabel="1"];
|
||||||
|
StyleImage -> Imgbuf [headlabel="*", taillabel="1"];
|
||||||
|
StyleImage -> StyleImgRenderer [headlabel="1", taillabel="1"];
|
||||||
|
StyleImage -> ImgRendererDist [headlabel="1", taillabel="1"];
|
||||||
|
ImgRendererDist -> StyleImgRenderer [headlabel="1", taillabel="1"];
|
||||||
|
ImgRendererDist -> ImgRenderer [headlabel="1", taillabel="*"];
|
||||||
|
|
||||||
|
ImgRenderer -> ImgRendererDist [arrowhead="none", arrowtail="empty",
|
||||||
|
dir="both", style="dashed"];
|
||||||
|
ImgRenderer -> StyleImgRenderer [arrowhead="none", arrowtail="empty",
|
||||||
|
dir="both", style="dashed"];
|
||||||
|
ImgRenderer -> ExternalImgRenderer [arrowhead="none", arrowtail="empty",
|
||||||
|
dir="both", style="dashed"];
|
||||||
|
ExternalImgRenderer -> ExternalWidgetImgRenderer [arrowhead="none",
|
||||||
|
arrowtail="empty", dir="both", style="dashed"];
|
||||||
|
|
||||||
|
Layout -> LayoutImgRenderer [headlabel="1", taillabel="0..1"];
|
||||||
|
ExternalImgRenderer -> LayoutImgRenderer [arrowhead="none",
|
||||||
|
arrowtail="empty", dir="both", style="dashed"];
|
||||||
|
|
||||||
|
Widget -> WidgetImgRenderer [headlabel="1", taillabel="0..1"];
|
||||||
|
ExternalWidgetImgRenderer -> WidgetImgRenderer [arrowhead="none",
|
||||||
|
arrowtail="empty", dir="both", style="dashed"];
|
||||||
|
|
||||||
|
Textblock -> Word [headlabel="1", taillabel="*"];
|
||||||
|
Word -> WordImgRenderer [headlabel="1", taillabel="0..1"];
|
||||||
|
Word -> SpaceImgRenderer [headlabel="1", taillabel="0..1"];
|
||||||
|
ExternalWidgetImgRenderer -> WordImgRenderer [arrowhead="none",
|
||||||
|
arrowtail="empty", dir="both", style="dashed"];
|
||||||
|
WordImgRenderer -> SpaceImgRenderer [arrowhead="none", arrowtail="empty",
|
||||||
|
dir="both", style="dashed"];
|
||||||
|
}
|
||||||
|
\enddot
|
||||||
|
|
||||||
|
<center>[\ref uml-legend "legend"]</center>
|
||||||
|
|
||||||
|
|
||||||
|
Memory management
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
dw::core::style::StyleImage extends lout::signal::ObservedObject, so
|
||||||
|
that deleting this instance can be connected to code dealing with
|
||||||
|
cache clients etc. See StyleImageDeletionReceiver and how it is
|
||||||
|
attached in StyleEngine::apply ("case CSS_PROPERTY_BACKGROUND_IMAGE").
|
||||||
|
|
||||||
|
|
||||||
|
Bugs and Things Needing Improvement
|
||||||
|
===================================
|
||||||
|
|
||||||
|
(Mostly related to image backgrounds, when not otherwise mentioned.)
|
||||||
|
|
||||||
|
High Priority
|
||||||
|
-------------
|
||||||
|
|
||||||
|
**Configurability, security/privacy aspects, etc.,** which are
|
||||||
|
currently available for image widgets, should be adopted. Perhaps some
|
||||||
|
more configuration options specially for background images.
|
||||||
|
|
||||||
|
|
||||||
|
Medium Priority
|
||||||
|
---------------
|
||||||
|
|
||||||
|
**Background-attachment** is not yet implemented, and will be postponed.
|
||||||
|
|
||||||
|
**Calls to dw::core::ImgRenderer::fatal** are incomplete. As an
|
||||||
|
example, it is not called, when connecting to a server fails. (And so,
|
||||||
|
as far as I see, no cache client is started.)
|
||||||
|
|
||||||
|
|
||||||
|
Low Priority
|
||||||
|
------------
|
||||||
|
|
||||||
|
**Alpha support:** (not related to image backgrounds) currently alpha
|
||||||
|
support (and also colormap management) is done in dicache, while
|
||||||
|
dw::Image is only created with type RGB. This leads to several problems:
|
||||||
|
|
||||||
|
- One dicache entry (representing an image related to the same URL),
|
||||||
|
which has only one background color, may refer to different images
|
||||||
|
with different background colors.
|
||||||
|
- The dicache only handles background colors, not background images.
|
||||||
|
|
||||||
|
The solution is basically simple: keep alpha support out of dicache;
|
||||||
|
instead implement RGBA in dw::Image. As it seems, the main problem is
|
||||||
|
alpha support in FLTK/X11.
|
||||||
|
|
||||||
|
|
||||||
|
Solved (Must Be Documented)
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
*Drawing background images row by row may become slow. As an
|
||||||
|
alternative, dw::core::ImgRenderer::finish could be used. However,
|
||||||
|
drawing row by row could become an option.* There is now
|
||||||
|
dw::core::style::drawBackgroundLineByLine, which can be changed in the
|
||||||
|
code, and is set to *false*. The old code still exists, so changing
|
||||||
|
this to *true* activates again drawing line by line.
|
||||||
|
|
||||||
|
(For image widgets, this could also become an option: in contexts,
|
||||||
|
when image data is retrieved in a very fast way.)
|
||||||
|
|
||||||
|
*/
|
||||||
BIN
devdoc/dw-interrupted-drawing-1.png
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
BIN
devdoc/dw-interrupted-drawing-2.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
706
devdoc/dw-interrupted-drawing-2.svg
Normal file
@ -0,0 +1,706 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="210mm"
|
||||||
|
height="297mm"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.3.1 r9886"
|
||||||
|
sodipodi:docname="dw-interrupted-drawing-2.svg"
|
||||||
|
inkscape:export-filename="/home/sg/dev/dillo/dillo-grows-work-intdraw/doc/dw-interrupted-drawing-2.png"
|
||||||
|
inkscape:export-xdpi="69.620003"
|
||||||
|
inkscape:export-ydpi="69.620003">
|
||||||
|
<defs
|
||||||
|
id="defs4">
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="Arrow2Lend"
|
||||||
|
orient="auto"
|
||||||
|
refY="0.0"
|
||||||
|
refX="0.0"
|
||||||
|
id="Arrow2Lend"
|
||||||
|
style="overflow:visible;">
|
||||||
|
<path
|
||||||
|
id="path4042"
|
||||||
|
style="fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
|
||||||
|
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
||||||
|
transform="scale(1.1) rotate(180) translate(1,0)" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="Arrow2Lend"
|
||||||
|
orient="auto"
|
||||||
|
refY="0"
|
||||||
|
refX="0"
|
||||||
|
id="Arrow2Lend-2"
|
||||||
|
style="overflow:visible">
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path4042-4"
|
||||||
|
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
|
||||||
|
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||||
|
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="Arrow2Lend"
|
||||||
|
orient="auto"
|
||||||
|
refY="0"
|
||||||
|
refX="0"
|
||||||
|
id="Arrow2Lend-8"
|
||||||
|
style="overflow:visible">
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path4042-0"
|
||||||
|
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
|
||||||
|
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||||
|
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="Arrow2Lend"
|
||||||
|
orient="auto"
|
||||||
|
refY="0"
|
||||||
|
refX="0"
|
||||||
|
id="Arrow2Lend-24"
|
||||||
|
style="overflow:visible">
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path4042-2"
|
||||||
|
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
|
||||||
|
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||||
|
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="Arrow2Lend"
|
||||||
|
orient="auto"
|
||||||
|
refY="0"
|
||||||
|
refX="0"
|
||||||
|
id="Arrow2Lend-29"
|
||||||
|
style="overflow:visible">
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path4042-1"
|
||||||
|
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
|
||||||
|
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||||
|
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="Arrow2Lend"
|
||||||
|
orient="auto"
|
||||||
|
refY="0"
|
||||||
|
refX="0"
|
||||||
|
id="Arrow2Lend-5"
|
||||||
|
style="overflow:visible">
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path4042-41"
|
||||||
|
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
|
||||||
|
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||||
|
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="Arrow2Lend"
|
||||||
|
orient="auto"
|
||||||
|
refY="0"
|
||||||
|
refX="0"
|
||||||
|
id="Arrow2Lend-23"
|
||||||
|
style="overflow:visible">
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path4042-19"
|
||||||
|
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
|
||||||
|
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||||
|
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="Arrow2Lend"
|
||||||
|
orient="auto"
|
||||||
|
refY="0"
|
||||||
|
refX="0"
|
||||||
|
id="Arrow2Lend-7"
|
||||||
|
style="overflow:visible">
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path4042-9"
|
||||||
|
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
|
||||||
|
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||||
|
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="Arrow2Lend"
|
||||||
|
orient="auto"
|
||||||
|
refY="0"
|
||||||
|
refX="0"
|
||||||
|
id="Arrow2Lend-0"
|
||||||
|
style="overflow:visible">
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path4042-97"
|
||||||
|
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
|
||||||
|
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||||
|
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="Arrow2Lend"
|
||||||
|
orient="auto"
|
||||||
|
refY="0"
|
||||||
|
refX="0"
|
||||||
|
id="Arrow2Lend-0-4"
|
||||||
|
style="overflow:visible">
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path4042-97-1"
|
||||||
|
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
|
||||||
|
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||||
|
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="Arrow2Lend"
|
||||||
|
orient="auto"
|
||||||
|
refY="0"
|
||||||
|
refX="0"
|
||||||
|
id="Arrow2Lend-0-7"
|
||||||
|
style="overflow:visible">
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path4042-97-5"
|
||||||
|
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
|
||||||
|
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||||
|
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="Arrow2Lend"
|
||||||
|
orient="auto"
|
||||||
|
refY="0"
|
||||||
|
refX="0"
|
||||||
|
id="Arrow2Lend-0-8"
|
||||||
|
style="overflow:visible">
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path4042-97-8"
|
||||||
|
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
|
||||||
|
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||||
|
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="Arrow2Lend"
|
||||||
|
orient="auto"
|
||||||
|
refY="0"
|
||||||
|
refX="0"
|
||||||
|
id="Arrow2Lend-0-3"
|
||||||
|
style="overflow:visible">
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path4042-97-2"
|
||||||
|
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
|
||||||
|
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||||
|
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="Arrow2Lend"
|
||||||
|
orient="auto"
|
||||||
|
refY="0"
|
||||||
|
refX="0"
|
||||||
|
id="Arrow2Lend-1"
|
||||||
|
style="overflow:visible">
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path4042-7"
|
||||||
|
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
|
||||||
|
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||||
|
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="Arrow2Lend"
|
||||||
|
orient="auto"
|
||||||
|
refY="0"
|
||||||
|
refX="0"
|
||||||
|
id="marker5604"
|
||||||
|
style="overflow:visible">
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path5606"
|
||||||
|
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
|
||||||
|
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||||
|
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
|
||||||
|
</marker>
|
||||||
|
</defs>
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="0.35355339"
|
||||||
|
inkscape:cx="437.30872"
|
||||||
|
inkscape:cy="337.60168"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="true"
|
||||||
|
inkscape:window-width="1600"
|
||||||
|
inkscape:window-height="900"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="0">
|
||||||
|
<inkscape:grid
|
||||||
|
type="xygrid"
|
||||||
|
id="grid2985" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<metadata
|
||||||
|
id="metadata7">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Ebene 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1">
|
||||||
|
<g
|
||||||
|
id="g3977"
|
||||||
|
transform="translate(2.992126e-6,177.16535)">
|
||||||
|
<rect
|
||||||
|
y="24.803127"
|
||||||
|
x="17.716536"
|
||||||
|
height="70.866142"
|
||||||
|
width="141.73228"
|
||||||
|
id="rect2997"
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||||
|
<flowRoot
|
||||||
|
transform="translate(20.094709,-2.1798012)"
|
||||||
|
style="font-size:12px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||||
|
id="flowRoot3786"
|
||||||
|
xml:space="preserve"><flowRegion
|
||||||
|
id="flowRegion3788"><rect
|
||||||
|
y="48.270561"
|
||||||
|
x="46.467018"
|
||||||
|
height="28.284271"
|
||||||
|
width="75.256363"
|
||||||
|
id="rect3790" /></flowRegion><flowPara
|
||||||
|
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
|
||||||
|
id="flowPara3792">body</flowPara></flowRoot> </g>
|
||||||
|
<g
|
||||||
|
id="g3984"
|
||||||
|
transform="translate(2.992126e-6,177.16535)">
|
||||||
|
<rect
|
||||||
|
y="24.803127"
|
||||||
|
x="194.8819"
|
||||||
|
height="70.866142"
|
||||||
|
width="141.73228"
|
||||||
|
id="rect3767"
|
||||||
|
style="fill:#ffe0e0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||||
|
<text
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
id="text3832"
|
||||||
|
y="67.096199"
|
||||||
|
x="243.2318"
|
||||||
|
style="font-size:12px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||||
|
xml:space="preserve"><tspan
|
||||||
|
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
|
||||||
|
y="67.096199"
|
||||||
|
x="243.2318"
|
||||||
|
id="tspan3834"
|
||||||
|
sodipodi:role="line">#sc-1</tspan></text>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g3989"
|
||||||
|
transform="translate(176.96508,177.13197)">
|
||||||
|
<rect
|
||||||
|
y="24.803127"
|
||||||
|
x="372.04724"
|
||||||
|
height="70.866142"
|
||||||
|
width="141.73228"
|
||||||
|
id="rect3767-6"
|
||||||
|
style="fill:#b0ffb0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||||
|
<text
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
id="text3836"
|
||||||
|
y="67.356201"
|
||||||
|
x="425.39713"
|
||||||
|
style="font-size:20px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||||
|
xml:space="preserve"><tspan
|
||||||
|
y="67.356201"
|
||||||
|
x="425.39713"
|
||||||
|
id="tspan3838"
|
||||||
|
sodipodi:role="line"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L">#fl-1</tspan></text>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g3994"
|
||||||
|
transform="translate(-177.81225,177.39197)">
|
||||||
|
<rect
|
||||||
|
y="24.803127"
|
||||||
|
x="549.21259"
|
||||||
|
height="70.866142"
|
||||||
|
width="141.73228"
|
||||||
|
id="rect3767-2"
|
||||||
|
style="fill:#f0f0ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||||
|
<text
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
id="text3840"
|
||||||
|
y="67.096199"
|
||||||
|
x="595.92249"
|
||||||
|
style="font-size:20px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||||
|
xml:space="preserve"><tspan
|
||||||
|
y="67.096199"
|
||||||
|
x="595.92249"
|
||||||
|
id="tspan3842"
|
||||||
|
sodipodi:role="line"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L">#sc-2</tspan></text>
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||||
|
d="m 88.582679,272.83462 -2e-6,531.49607"
|
||||||
|
id="path3890"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||||
|
d="m 265.74803,272.83462 0,531.49607"
|
||||||
|
id="path3890-9"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||||
|
d="m 442.91339,272.83462 0,531.49607"
|
||||||
|
id="path3890-2"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||||
|
d="m 620.07874,272.83462 0,531.49607"
|
||||||
|
id="path3890-29"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffa0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||||
|
id="rect3931"
|
||||||
|
width="35.433071"
|
||||||
|
height="460.62991"
|
||||||
|
x="70.866142"
|
||||||
|
y="308.2677" />
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffa0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||||
|
id="rect3933"
|
||||||
|
width="35.433071"
|
||||||
|
height="354.33069"
|
||||||
|
x="248.03149"
|
||||||
|
y="343.70078" />
|
||||||
|
<flowRoot
|
||||||
|
xml:space="preserve"
|
||||||
|
id="flowRoot3935"
|
||||||
|
style="font-size:20px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||||
|
transform="translate(2.992126e-6,177.16535)"><flowRegion
|
||||||
|
id="flowRegion3937"><rect
|
||||||
|
id="rect3939"
|
||||||
|
width="835.71429"
|
||||||
|
height="147.14285"
|
||||||
|
x="-1.4285715"
|
||||||
|
y="-40.494953" /></flowRegion><flowPara
|
||||||
|
id="flowPara3941"></flowPara></flowRoot> <path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend)"
|
||||||
|
d="m 106.29921,343.70076 141.73229,0"
|
||||||
|
id="path3890-29-5"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffa0;fill-opacity:1;stroke:#000000;stroke-width:3.00000191;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||||
|
id="rect4643"
|
||||||
|
width="35.433094"
|
||||||
|
height="70.866158"
|
||||||
|
x="602.36218"
|
||||||
|
y="414.56689" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend)"
|
||||||
|
d="m 283.46457,414.56691 318.89763,0"
|
||||||
|
id="path3890-29-5-2"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:6, 6;stroke-dashoffset:0;marker-end:url(#Arrow2Lend)"
|
||||||
|
d="m 602.36221,485.43305 -318.89764,0"
|
||||||
|
id="path3890-29-5-2-0-0"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:6, 6;stroke-dashoffset:0;marker-end:url(#Arrow2Lend)"
|
||||||
|
d="m 248.0315,698.03147 -141.73229,0"
|
||||||
|
id="path3890-29-5-2-0-0-8"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffa0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||||
|
id="rect4767"
|
||||||
|
width="35.433071"
|
||||||
|
height="35.433071"
|
||||||
|
x="602.36218"
|
||||||
|
y="733.46454" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend)"
|
||||||
|
d="m 106.29921,733.46454 496.06299,10e-6"
|
||||||
|
id="path3890-29-5-4"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend)"
|
||||||
|
d="m 17.716536,308.26769 53.149608,0"
|
||||||
|
id="path3890-29-5-27"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:6, 6;stroke-dashoffset:0;marker-end:url(#Arrow2Lend)"
|
||||||
|
d="m 70.866142,768.89762 -53.149613,0"
|
||||||
|
id="path3890-29-5-2-0-0-8-4"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#ff0000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||||
|
d="m 354.33071,715.74801 318.89763,53.14961 0,0 0,0 0,0"
|
||||||
|
id="path4865"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="ccccc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#ff0000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||||
|
d="m 354.3307,768.89762 318.89764,-53.14961 0,0 0,0"
|
||||||
|
id="path4865-9"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cccc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend-0)"
|
||||||
|
d="m 88.582677,201.96848 c 17.716533,-88.58268 141.732283,-88.58268 159.448823,0"
|
||||||
|
id="path4914"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend-0)"
|
||||||
|
d="m 283.46456,201.96848 c 17.71654,-88.58268 141.73229,-88.58268 159.44883,0"
|
||||||
|
id="path4914-8"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend-0)"
|
||||||
|
d="m 88.582677,201.96848 c 35.433073,-212.598421 513.779523,-212.598423 549.212603,0"
|
||||||
|
id="path4914-0"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2, 4;stroke-dashoffset:0;marker-end:url(#Arrow2Lend-0)"
|
||||||
|
d="m 265.74802,201.96848 c 35.43308,-159.448817 301.18111,-159.448817 336.61418,0"
|
||||||
|
id="path4914-8-3"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:2,4;stroke-dashoffset:0"
|
||||||
|
d="m 318.89764,361.4173 c -17.71654,0 -35.43307,0 -35.43307,0 l 0,0"
|
||||||
|
id="path5443"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L"
|
||||||
|
x="414.18863"
|
||||||
|
y="362.88965"
|
||||||
|
id="text5445"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan5447"
|
||||||
|
x="414.18863"
|
||||||
|
y="362.88965" /></text>
|
||||||
|
<g
|
||||||
|
id="g5586">
|
||||||
|
<rect
|
||||||
|
rx="0"
|
||||||
|
ry="17.716547"
|
||||||
|
y="325.98422"
|
||||||
|
x="318.89764"
|
||||||
|
height="70.866142"
|
||||||
|
width="194.8819"
|
||||||
|
id="rect5453"
|
||||||
|
style="fill:#ffffe0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||||
|
<text
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
id="text5455"
|
||||||
|
y="354.7933"
|
||||||
|
x="418.09937"
|
||||||
|
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L"
|
||||||
|
xml:space="preserve"><tspan
|
||||||
|
style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
|
||||||
|
y="354.7933"
|
||||||
|
x="418.09937"
|
||||||
|
id="tspan5457"
|
||||||
|
sodipodi:role="line">#sc-1 detects that #fl-1</tspan><tspan
|
||||||
|
id="tspan5459"
|
||||||
|
style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
|
||||||
|
y="377.2933"
|
||||||
|
x="418.09937"
|
||||||
|
sodipodi:role="line">interrupts the drawing</tspan></text>
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2, 4;stroke-dashoffset:0"
|
||||||
|
d="m 673.22836,450.00001 c -17.71654,0 -35.43307,0 -35.43307,0 l 0,0"
|
||||||
|
id="path5443-9"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="ccc" />
|
||||||
|
<g
|
||||||
|
id="g5531"
|
||||||
|
transform="translate(-88.582663,106.29923)">
|
||||||
|
<rect
|
||||||
|
rx="0"
|
||||||
|
ry="17.716547"
|
||||||
|
y="414.56689"
|
||||||
|
x="673.22833"
|
||||||
|
height="70.866142"
|
||||||
|
width="194.8819"
|
||||||
|
id="rect5453-7"
|
||||||
|
style="fill:#ffffe0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||||
|
<text
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
id="text5455-9"
|
||||||
|
y="443.37598"
|
||||||
|
x="770.91229"
|
||||||
|
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L"
|
||||||
|
xml:space="preserve"><tspan
|
||||||
|
id="tspan5517"
|
||||||
|
style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
|
||||||
|
y="443.37598"
|
||||||
|
x="770.91229"
|
||||||
|
sodipodi:role="line">#fl-1 is drawn as</tspan><tspan
|
||||||
|
id="tspan5521"
|
||||||
|
style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
|
||||||
|
y="465.87598"
|
||||||
|
x="770.91229"
|
||||||
|
sodipodi:role="line">an interruption</tspan></text>
|
||||||
|
</g>
|
||||||
|
<flowRoot
|
||||||
|
xml:space="preserve"
|
||||||
|
id="flowRoot5523"
|
||||||
|
style="fill:black;stroke:none;stroke-opacity:1;stroke-width:1px;stroke-linejoin:miter;stroke-linecap:butt;fill-opacity:1;font-family:Nimbus Mono L;font-style:normal;font-weight:normal;font-size:20px;line-height:125%;letter-spacing:0px;word-spacing:0px;-inkscape-font-specification:Nimbus Mono L;font-stretch:normal;font-variant:normal"><flowRegion
|
||||||
|
id="flowRegion5525"><rect
|
||||||
|
id="rect5527"
|
||||||
|
width="230.37166"
|
||||||
|
height="100.60158"
|
||||||
|
x="662.54175"
|
||||||
|
y="400.53543" /></flowRegion><flowPara
|
||||||
|
id="flowPara5529"></flowPara></flowRoot> <g
|
||||||
|
id="g5699">
|
||||||
|
<rect
|
||||||
|
rx="0"
|
||||||
|
ry="17.716547"
|
||||||
|
y="502.68695"
|
||||||
|
x="318.90851"
|
||||||
|
height="70.866142"
|
||||||
|
width="194.8819"
|
||||||
|
id="rect5453-70"
|
||||||
|
style="fill:#ffffe0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||||
|
<text
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
id="text5455-7"
|
||||||
|
y="531.49603"
|
||||||
|
x="418.11023"
|
||||||
|
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L"
|
||||||
|
xml:space="preserve"><tspan
|
||||||
|
id="tspan5459-7"
|
||||||
|
style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
|
||||||
|
y="531.49603"
|
||||||
|
x="418.11023"
|
||||||
|
sodipodi:role="line">drawing of #sc-1</tspan><tspan
|
||||||
|
id="tspan5584"
|
||||||
|
style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
|
||||||
|
y="553.99603"
|
||||||
|
x="418.11023"
|
||||||
|
sodipodi:role="line">is continued</tspan></text>
|
||||||
|
</g>
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffa0;fill-opacity:1;stroke:#000000;stroke-width:3.00000191;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||||
|
id="rect4643-9"
|
||||||
|
width="35.433094"
|
||||||
|
height="70.866158"
|
||||||
|
x="425.19681"
|
||||||
|
y="591.73224" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend)"
|
||||||
|
d="m 283.46457,591.73226 141.73227,10e-6"
|
||||||
|
id="path3890-29-5-2-8"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:6, 6;stroke-dashoffset:0;marker-end:url(#Arrow2Lend)"
|
||||||
|
d="m 425.19685,662.59841 -141.73228,-10e-6"
|
||||||
|
id="path3890-29-5-2-0-0-4"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2, 4;stroke-dashoffset:0"
|
||||||
|
d="m 673.22835,751.18108 c -17.71654,0 -35.43307,0 -35.43307,0 l 0,0"
|
||||||
|
id="path5443-8-2"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<g
|
||||||
|
id="g5713"
|
||||||
|
transform="translate(-88.571786,105.83661)">
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path5443-8"
|
||||||
|
d="m 726.37796,786.61415 c -17.71654,0 -35.43307,0 -35.43307,0 l 0,0"
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2, 4;stroke-dashoffset:0" />
|
||||||
|
<rect
|
||||||
|
rx="0"
|
||||||
|
ry="17.716547"
|
||||||
|
y="715.74799"
|
||||||
|
x="673.22833"
|
||||||
|
height="70.866142"
|
||||||
|
width="194.8819"
|
||||||
|
id="rect5453-70-2"
|
||||||
|
style="fill:#ffffe0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||||
|
<text
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
id="text5455-7-4"
|
||||||
|
y="744.55707"
|
||||||
|
x="772.43005"
|
||||||
|
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L"
|
||||||
|
xml:space="preserve"><tspan
|
||||||
|
id="tspan5707"
|
||||||
|
style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
|
||||||
|
y="744.55707"
|
||||||
|
x="772.43005"
|
||||||
|
sodipodi:role="line">no need anymore</tspan><tspan
|
||||||
|
id="tspan5711"
|
||||||
|
style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
|
||||||
|
y="767.05707"
|
||||||
|
x="772.43005"
|
||||||
|
sodipodi:role="line">to draw #fl-1</tspan></text>
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2, 4;stroke-dashoffset:0"
|
||||||
|
d="m 318.89764,538.58266 c -17.71654,0 -35.43307,0 -35.43307,0 l 0,0"
|
||||||
|
id="path5443-9-0"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="ccc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2, 4;stroke-dashoffset:0"
|
||||||
|
d="m 673.22835,449.99998 0,0"
|
||||||
|
id="path5443-9-6"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:2,4;stroke-dashoffset:0"
|
||||||
|
d="m 673.22835,520.86612 c 0,-70.86614 0,-70.86614 0,-70.86614"
|
||||||
|
id="path5738"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2, 4;stroke-dashoffset:0"
|
||||||
|
d="m 673.22835,822.04722 c 0,-70.86614 0,-70.86614 0,-70.86614"
|
||||||
|
id="path5738-7"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 32 KiB |
121
devdoc/dw-interrupted-drawing.doc
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
/** \page dw-interrupted-drawing Interrupted drawing
|
||||||
|
|
||||||
|
Describing the problem
|
||||||
|
======================
|
||||||
|
|
||||||
|
Without interrupting drawing (which is described below), a widget can
|
||||||
|
define the order in which its parts (background, non-widget content,
|
||||||
|
child widgets, etc.) are drawn, but it must be drawn as a whole. There
|
||||||
|
are situations when this is not possible.
|
||||||
|
|
||||||
|
Consider the following simple HTML document:
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
#sc-1 { position: relative; z-index: 1; background: #ffe0e0; }
|
||||||
|
#fl-1 { float: right; background: #b0ffb0; }
|
||||||
|
#sc-2 { position: relative; z-index: 1; background: #f0f0ff; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="sc-1">
|
||||||
|
<div id="fl-1">
|
||||||
|
Float, line 1/3<br/>
|
||||||
|
Float, line 2/3<br/>
|
||||||
|
Float, line 3/3
|
||||||
|
</div>
|
||||||
|
Stacking Context 1
|
||||||
|
<div id="sc-2">Stacking Context 2</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
The rendering will look like this:
|
||||||
|
|
||||||
|
\image html dw-interrupted-drawing-1.png
|
||||||
|
|
||||||
|
Note the missing "Float, line 2/3" of element #fl-1, which is covered
|
||||||
|
by element #sc-2.
|
||||||
|
|
||||||
|
As described in \ref dw-out-of-flow, it has to be distinguished
|
||||||
|
between the *container* hierarchy (equivalent to the hierarchy of
|
||||||
|
dw::core::Widget.) and the the *generator* hierarchy. In the following
|
||||||
|
diagram, the former is represented by solid lines, the latter by
|
||||||
|
dotted lines:
|
||||||
|
|
||||||
|
\dot
|
||||||
|
digraph G {
|
||||||
|
node [shape=rect, fontname=Helvetica, fontsize=10];
|
||||||
|
edge [arrowhead="vee"];
|
||||||
|
|
||||||
|
"#sc-1" [fillcolor="#ffe0e0", style="filled"];
|
||||||
|
"#fl-1" [fillcolor="#b0ffb0", style="filled"];
|
||||||
|
"#sc-2" [fillcolor="#f0f0ff", style="filled"];
|
||||||
|
|
||||||
|
"body" -> "#sc-1";
|
||||||
|
"body" -> "#fl-1";
|
||||||
|
{ rank=same; "#sc-1" -> "#fl-1" [style=dotted]; }
|
||||||
|
"#sc-1" -> "#sc-2";
|
||||||
|
}
|
||||||
|
\enddot
|
||||||
|
|
||||||
|
|
||||||
|
The drawing order of the four elements (represented by widgets) is:
|
||||||
|
|
||||||
|
- body,
|
||||||
|
- #sc-1,
|
||||||
|
- #fl-1,
|
||||||
|
- #sc-2.
|
||||||
|
|
||||||
|
Since
|
||||||
|
|
||||||
|
1. #sc-2 is a child of #sc-1, but
|
||||||
|
2. #fl-1 is a child of the body, and
|
||||||
|
3. a widget can only draw its descendants (not necessary children,
|
||||||
|
but drawing siblings is not allowed),
|
||||||
|
|
||||||
|
#sc-1 cannot be drawn as a whole; instead drawing is **interrupted**
|
||||||
|
by #fl-1. This means:
|
||||||
|
|
||||||
|
1. the background and text of #sc-1 is drawn;
|
||||||
|
2. drawing of #sc-1 is **interrupted** by #fl-1 (see below for details),
|
||||||
|
3. drawing of #sc-1 is **continued**, by drawing #sc-2.
|
||||||
|
|
||||||
|
The exact control flow is described in this sequence diagram:
|
||||||
|
|
||||||
|
\image html dw-interrupted-drawing-2.png
|
||||||
|
|
||||||
|
|
||||||
|
When is drawing interrupted?
|
||||||
|
============================
|
||||||
|
|
||||||
|
A widget out of flow is regarded as part of the stacking context (see
|
||||||
|
\ref dw-stacking-context) of its *generator* (in the example above:
|
||||||
|
#fl-1 is part of the stacking context stablished by #sc-1, not the one
|
||||||
|
established by body). For this reason, a widget out of flow must, in
|
||||||
|
some cases, drawn while the *gerator* is drawn, as an
|
||||||
|
interruption. The exact rule:
|
||||||
|
|
||||||
|
A widget out of flow must be drawn as an interruption (while the
|
||||||
|
*generator* is drawn) if the stacking context of the generator (to
|
||||||
|
which this widget belongs) is in front of the stacking context of the
|
||||||
|
container (the parent widget).
|
||||||
|
|
||||||
|
See dw::oof::OOFAwareWidget::doesWidgetOOFInterruptDrawing.
|
||||||
|
|
||||||
|
|
||||||
|
How does interruption of drawing work?
|
||||||
|
======================================
|
||||||
|
|
||||||
|
When a widget detects that an other widget should be drawn as
|
||||||
|
interruption (see above), it calls dw::core::Widget::drawInterruption,
|
||||||
|
which
|
||||||
|
|
||||||
|
1. draws the widget within another "context" (area and reference
|
||||||
|
widget); for this the original drawing area
|
||||||
|
(dw::core::DrawingContext::getToplevelArea) is used.
|
||||||
|
2. Using dw::core::DrawingContext::addWidgetDrawnAsInterruption, and
|
||||||
|
checking later with
|
||||||
|
dw::core::DrawingContext::hasWidgetBeenDrawnAsInterruption prevents
|
||||||
|
these widgets from being drawn twice.
|
||||||
|
|
||||||
|
*/
|
||||||
256
devdoc/dw-layout-views.doc
Normal file
@ -0,0 +1,256 @@
|
|||||||
|
/** \page dw-layout-views Layout and Views
|
||||||
|
|
||||||
|
Rendering of Dw is done in a way resembling the model-view pattern, at
|
||||||
|
least formally. Actually, the counterpart of the model, the layout
|
||||||
|
(dw::core::Layout), does a bit more than a typical model, namely the
|
||||||
|
layouting (delegated to the widget tree, see \ref dw-layout-widgets),
|
||||||
|
and the view does a bit less than a typical view, i.e. only the actual
|
||||||
|
drawing.
|
||||||
|
|
||||||
|
Additionally, there is a structure representing common properties of
|
||||||
|
the platform. A platform is typically related to the underlying UI
|
||||||
|
toolkit, but other uses may be thought of.
|
||||||
|
|
||||||
|
This design helps to achieve two important goals:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li> Abstraction of the actual drawing, by different implementations
|
||||||
|
of dw::core::View.
|
||||||
|
|
||||||
|
<li> It makes portability simple.
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
<h2>Viewports</h2>
|
||||||
|
|
||||||
|
Although the design implies that the usage of viewports should be
|
||||||
|
fully transparent to the layout module, this cannot be fully achieved,
|
||||||
|
for the following reasons:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li> Some features, which are used on the level of dw::core::Widget,
|
||||||
|
e.g. anchors, refer to scrolling positions.
|
||||||
|
|
||||||
|
<li> Size hints (see \ref dw-layout-widgets) depend on the viewport
|
||||||
|
sizes, e.g. when the user changes the window size, and so also
|
||||||
|
the size of a viewport, the text within should be rewrapped.
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
Therefore, dw::core::Layout keeps track of the viewport size, the
|
||||||
|
viewport position, and even the thickness of the scrollbars, they are
|
||||||
|
relevant, see below for more details.
|
||||||
|
If a viewport is not used, however, the size is not defined.
|
||||||
|
|
||||||
|
Whether a given dw::core::View implementation is a viewport or not, is
|
||||||
|
defined by the return value of dw::core::View::usesViewport. If this
|
||||||
|
method returns false, the following methods need not to be implemented
|
||||||
|
at all:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li> dw::core::View::getHScrollbarThickness,
|
||||||
|
<li> dw::core::View::getVScrollbarThickness,
|
||||||
|
<li> dw::core::View::scrollTo, and
|
||||||
|
<li> dw::core::View::setViewportSize.
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Scrolling Positions</h3>
|
||||||
|
|
||||||
|
The scrolling position is the canvas position at the upper left corner
|
||||||
|
of the viewport. Views using viewports must
|
||||||
|
|
||||||
|
<ol>
|
||||||
|
<li> change this value on request (dw::core::View::scrollTo), and
|
||||||
|
<li> tell other changes to the layout, e.g. caused by user events
|
||||||
|
(dw::core::Layout::scrollPosChanged).
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
Applications of scrolling positions (anchors, test search etc.) are
|
||||||
|
handled by the layout, in a way fully transparent to the view.
|
||||||
|
|
||||||
|
<h3>Scrollbars</h3>
|
||||||
|
|
||||||
|
A feature of the viewport size model are scrollbars. There may be a
|
||||||
|
vertical scrollbar and a horizontal scrollbar, displaying the
|
||||||
|
relationship between canvas and viewport height or width,
|
||||||
|
respectively. If they are not needed, they are hidden, to save screen
|
||||||
|
space.
|
||||||
|
|
||||||
|
Since scrollbars decrease the usable space of a view, dw::core::Layout
|
||||||
|
must know how much space they take. The view returns, via
|
||||||
|
dw::core::View::getHScrollbarThickness and
|
||||||
|
dw::core::View::getVScrollbarThickness, how thick they will be, when
|
||||||
|
visible.
|
||||||
|
|
||||||
|
Viewport sizes, which denote the size of the viewport widgets, include
|
||||||
|
scrollbar thicknesses. When referring to the viewport \em excluding
|
||||||
|
the scrollbars space, we will call it "usable viewport size", this is
|
||||||
|
the area, which is used to display the canvas.
|
||||||
|
|
||||||
|
<h2>Drawing</h2>
|
||||||
|
|
||||||
|
A view must implement several drawing methods, which work on the whole
|
||||||
|
canvas. If it is necessary to convert them (e.g. into
|
||||||
|
dw::fltk::FltkViewport), this is done in a way fully transparent to
|
||||||
|
dw::core::Widget and dw::core::Layout, instead, this is done by the
|
||||||
|
view implementation.
|
||||||
|
|
||||||
|
There exist following situations:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li> A view gets an expose event: It will delegate this to the
|
||||||
|
layout (dw::core::Layout::draw), which will then pass it to the
|
||||||
|
widgets (dw::core::Widget::draw), with the view as a parameter.
|
||||||
|
Eventually, the widgets will call drawing methods of the view.
|
||||||
|
|
||||||
|
<li> A widget requests a redraw: In this case, the widget will
|
||||||
|
delegate this to the layout (dw::core::Layout::queueDraw), which
|
||||||
|
delegates it to the view (dw::core::View::queueDraw).
|
||||||
|
Typically, the view will queue these requests for efficiency.
|
||||||
|
|
||||||
|
<li> A widget requests a resize: This case is described below, in short,
|
||||||
|
dw::core::View::queueDrawTotal is called for the view.
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
If the draw method of a widget is implemented in a way that it may
|
||||||
|
draw outside of the widget's allocation, it should draw into a
|
||||||
|
<i>clipping view.</i> A clipping view is a view related to the actual
|
||||||
|
view, which guarantees that the parts drawn outside are discarded. At
|
||||||
|
the end, the clipping view is merged into the actual view. Sample
|
||||||
|
code:
|
||||||
|
|
||||||
|
\code
|
||||||
|
void Foo::draw (dw::core::View *view, dw::core::Rectangle *area)
|
||||||
|
{
|
||||||
|
// 1. Create a clipping view.
|
||||||
|
dw::core::View clipView =
|
||||||
|
view->getClippingView (allocation.x, allocation.y,
|
||||||
|
allocation.width, getHeight ());
|
||||||
|
|
||||||
|
// 2. Draw into clip_view
|
||||||
|
clipView->doSomeDrawing (...);
|
||||||
|
|
||||||
|
// 3. Draw the children, they receive the clipping view as argument.
|
||||||
|
dw::core::Rectangle *childArea
|
||||||
|
for (<all relevant children>) {
|
||||||
|
if (child->intersects (area, &childArea))
|
||||||
|
child->draw (clipView, childArea);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Merge
|
||||||
|
view->mergeClippingView (clipView);
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
A drawing process is always embedded into calls of
|
||||||
|
dw::core::View::startDrawing and dw::core::View::finishDrawing. An
|
||||||
|
implementation of this may e.g. use backing pixmaps, to prevent
|
||||||
|
flickering.
|
||||||
|
|
||||||
|
|
||||||
|
<h2>Sizes</h2>
|
||||||
|
|
||||||
|
In the simplest case, the view does not have any influence on
|
||||||
|
the canvas size, so it is told about changes of the
|
||||||
|
canvas size by a call to dw::core::View::setCanvasSize. This happens
|
||||||
|
in the following situations:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li> dw::core::Layout::addWidget,
|
||||||
|
<li> dw::core::Layout::removeWidget (called by dw::core::Widget::~Widget),
|
||||||
|
and
|
||||||
|
<li> dw::core::Layout::queueResize (called by
|
||||||
|
dw::core::Widget::queueResize, when a widget itself requests a size
|
||||||
|
change).
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Viewports</h3>
|
||||||
|
|
||||||
|
There are two cases where the viewport size changes:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li> As an reaction on a user event, e.g. when the user changes the
|
||||||
|
window size. In this case, the view delegates this
|
||||||
|
change to the layout, by calling
|
||||||
|
dw::core::Layout::viewportSizeChanged.
|
||||||
|
|
||||||
|
<li> The viewport size may also depend on the visibility of UI
|
||||||
|
widgets, which depend on the world size, e.g scrollbars,
|
||||||
|
generally called "viewport markers". This is described in a separate
|
||||||
|
section.
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
After the creation of the layout, the viewport size is undefined. When
|
||||||
|
a view is attached to a layout, and this view can already specify
|
||||||
|
its viewport size, it may call
|
||||||
|
dw::core::Layout::viewportSizeChanged within the implementation of
|
||||||
|
dw::core::Layout::setLayout. If not, it may do this as soon as the
|
||||||
|
viewport size is known.
|
||||||
|
|
||||||
|
Generally, the scrollbars have to be considered. If e.g. an HTML page
|
||||||
|
is rather small, it looks like this:
|
||||||
|
|
||||||
|
\image html dw-viewport-without-scrollbar.png
|
||||||
|
|
||||||
|
If some more data is retrieved, so that the height exceeds the
|
||||||
|
viewport size, the text has to be rewrapped, since the available width
|
||||||
|
gets smaller, due to the vertical scrollbar:
|
||||||
|
|
||||||
|
\image html dw-viewport-with-scrollbar.png
|
||||||
|
|
||||||
|
Notice the different line breaks.
|
||||||
|
|
||||||
|
This means circular dependencies between these different sizes:
|
||||||
|
|
||||||
|
<ol>
|
||||||
|
<li> Whether the scrollbars are visible or not, determines the
|
||||||
|
usable space of the viewport.
|
||||||
|
|
||||||
|
<li> From the usable space of the viewport, the size hints for the
|
||||||
|
toplevel are calculated.
|
||||||
|
|
||||||
|
<li> The size hints for the toplevel widgets may have an effect on its
|
||||||
|
size, which is actually the canvas size.
|
||||||
|
|
||||||
|
<li> The canvas size determines the visibility of the scrollbarss.
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
To make an implementation simpler, we simplify the model:
|
||||||
|
|
||||||
|
<ol>
|
||||||
|
<li> For the calls to dw::core::Widget::setAscent and
|
||||||
|
dw::core::Widget::setDescent, we will always exclude the
|
||||||
|
horizontal scrollbar thickness (i.e. assume the horizontal
|
||||||
|
scrollbar is used, although the visibility is determined correctly).
|
||||||
|
|
||||||
|
<li> For the calls to dw::core::Widget::setWidth, we will calculate
|
||||||
|
the usable viewport width, but with the general assumption, that
|
||||||
|
the widget generally gets higher.
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
This results in the following rules:
|
||||||
|
|
||||||
|
<ol>
|
||||||
|
<li> Send always (when it changes) dw::core::Layout::viewportHeight
|
||||||
|
minus the maximal value of dw::core::View::getHScrollbarThickness as
|
||||||
|
argument to dw::core::Widget::setAscent, and 0 as argument to
|
||||||
|
dw::core::Widget::setDescent.
|
||||||
|
|
||||||
|
<li> There is a flag, dw::core::Layout::canvasHeightGreater, which is set
|
||||||
|
to false in the following cases:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li> dw::core::Layout::addWidget,
|
||||||
|
<li> dw::core::Layout::removeWidget (called by dw::core::Widget::~Widget),
|
||||||
|
and
|
||||||
|
<li> dw::core::Layout::viewportSizeChanged.
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
Whenever the canvas size is calculated (dw::core::Layout::resizeIdle),
|
||||||
|
and dw::core::Layout::canvasHeightGreater is false, a test is made,
|
||||||
|
whether the widget has in the meantime grown that high, that the second
|
||||||
|
argument should be set to true (i.e. the vertical scrollbar gets visible).
|
||||||
|
As soon as and dw::core::Layout::canvasHeightGreater is true, no such test
|
||||||
|
is done anymore.
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
*/
|
||||||
267
devdoc/dw-layout-widgets.doc
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
/** \page dw-layout-widgets Layout and Widgets
|
||||||
|
|
||||||
|
Both, the layouting and the drawing is delegated to a tree of
|
||||||
|
widgets. A widget represents a given part of the document, e.g. a text
|
||||||
|
block, a table, or an image. Widgets may be nested, so layouting and
|
||||||
|
drawing may be delegated by one widget to its child widgets.
|
||||||
|
|
||||||
|
Where to define the borders of a widget, whether to combine different
|
||||||
|
widgets to one, or to split one widget into multiple ones, should be
|
||||||
|
considered based on different concerns:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li> First, there are some restrictions of Dw:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li> The allocation (this is the space a widget allocates at
|
||||||
|
a time) of a dillo widget is always rectangular, and
|
||||||
|
<li> the allocation of a child widget must be a within the allocation
|
||||||
|
of the parent widget.
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<li> Since some widgets are already rather complex, an important goal
|
||||||
|
is to keep the implementation of the widget simple.
|
||||||
|
|
||||||
|
<li> Furthermore, the granularity should not be too fine, because of the
|
||||||
|
overhead each single widget adds.
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
For CSS, there will be a document tree on top of Dw, this will be
|
||||||
|
flexible enough when mapping the document structure on the widget
|
||||||
|
structure, so you should not have the document structure in mind.
|
||||||
|
|
||||||
|
<h2>Sizes</h2>
|
||||||
|
|
||||||
|
\ref dw-widget-sizes
|
||||||
|
|
||||||
|
|
||||||
|
<h2>Styles</h2>
|
||||||
|
|
||||||
|
Each widget is assigned a style, see dw::core::style for more
|
||||||
|
information.
|
||||||
|
|
||||||
|
|
||||||
|
<h2>Iterators</h2>
|
||||||
|
|
||||||
|
Widgets must implement dw::core::Widget::iterator. There are several
|
||||||
|
common iterators:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>dw::core::EmptyIterator, and
|
||||||
|
<li>dw::core::TextIterator.
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
Both hide the constructor, use the \em create method.
|
||||||
|
|
||||||
|
These simple iterators only iterate through one widget, it does not
|
||||||
|
have to iterate recursively through child widgets. Instead, the type
|
||||||
|
dw::core::Content::WIDGET is returned, and the next call of
|
||||||
|
dw::core::Iterator::next will return the piece of contents \em after
|
||||||
|
(not within) this child widget.
|
||||||
|
|
||||||
|
This makes implementation much simpler, for recursive iteration, there
|
||||||
|
is dw::core::DeepIterator.
|
||||||
|
|
||||||
|
|
||||||
|
<h2>Anchors and Scrolling</h2>
|
||||||
|
|
||||||
|
\todo This section is not implemented yet, after the implementation,
|
||||||
|
the documentation should be reviewed.
|
||||||
|
|
||||||
|
Here is a description, what is to be done for a widget
|
||||||
|
implementation. How to jump to anchors, set scrolling positions
|
||||||
|
etc. is described in \ref dw-usage.
|
||||||
|
|
||||||
|
<h3>Anchors</h3>
|
||||||
|
|
||||||
|
Anchors are position markers, which are identified by a name, which is
|
||||||
|
unique in the widget tree. The widget must care about anchors in three
|
||||||
|
different situations:
|
||||||
|
|
||||||
|
<ol>
|
||||||
|
<li> Adding an anchor is inititiated by a specific widget method, e.g.
|
||||||
|
dw::Textblock::addAnchor. Here, dw::core::Widget::addAnchor must be
|
||||||
|
called,
|
||||||
|
|
||||||
|
<li> Whenever the position of an anchor is changed,
|
||||||
|
dw::core::Widget::changeAnchor is called (typically, this is done
|
||||||
|
in the implementation of dw::core::Widget::sizeAllocateImpl).
|
||||||
|
|
||||||
|
<li> When a widget is destroyed, the anchor must be removed, by calling
|
||||||
|
dw::core::Widget::removeAnchor.
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
All these methods are delegated to dw::core::Layout, which manages
|
||||||
|
anchors centrally. If the anchor in question has been set to jump to,
|
||||||
|
the viewport position is automatically adjusted, see \ref
|
||||||
|
dw-usage.
|
||||||
|
|
||||||
|
|
||||||
|
<h2>Drawing</h2>
|
||||||
|
|
||||||
|
In two cases, a widget has to be drawn:
|
||||||
|
|
||||||
|
<ol>
|
||||||
|
<li> as a reaction on an expose event,
|
||||||
|
<li> if the widget content has changed and it needs to be redrawn.
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
In both cases, drawing is done by the implementation of
|
||||||
|
dw::core::Widget::draw, which draws into the view.
|
||||||
|
|
||||||
|
|
||||||
|
Each view provides some primitive methods for drawing, most should be
|
||||||
|
obvious. Note that the views do not know anything about dillo
|
||||||
|
widgets, and so coordinates have to be passed as canvas coordinates.
|
||||||
|
|
||||||
|
A widget may only draw in its own allocation. If this cannot be
|
||||||
|
achieved, a <i>clipping view</i> can be used, this is described in
|
||||||
|
\ref dw-layout-views. Generally, drawing should then look like:
|
||||||
|
|
||||||
|
\code
|
||||||
|
void Foo::draw (dw::core::View *view, dw::core::Rectangle *area)
|
||||||
|
{
|
||||||
|
// 1. Create a clipping view.
|
||||||
|
dw::core::View clipView =
|
||||||
|
view->getClippingView (allocation.x, allocation.y,
|
||||||
|
allocation.width, getHeight ());
|
||||||
|
|
||||||
|
// 2. Draw into clip_view
|
||||||
|
clipView->doSomeDrawing (...);
|
||||||
|
|
||||||
|
// 3. Draw the children, they receive the clipping view as argument.
|
||||||
|
dw::core::Rectangle *childArea
|
||||||
|
for (<all relevant children>) {
|
||||||
|
if (child->intersects (area, &childArea))
|
||||||
|
child->draw (clipView, childArea);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Merge
|
||||||
|
view->mergeClippingView (clipView);
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
Clipping views are expensive, so they should be avoided when possible.
|
||||||
|
|
||||||
|
The second argument to dw::core::Widget::draw is the region, which has
|
||||||
|
to be drawn. This may (but needs not) be used for optimization.
|
||||||
|
|
||||||
|
If a widget contains child widgets, it must explicitly draw these
|
||||||
|
children (see also code example above). For this, there is the useful
|
||||||
|
method dw::core::Widget::intersects, which returns, which area of the
|
||||||
|
child must be drawn.
|
||||||
|
|
||||||
|
<h3>Explicit Redrawing</h3>
|
||||||
|
|
||||||
|
If a widget changes its contents, so that it must be redrawn, it must
|
||||||
|
call dw::core::Widget::queueDrawArea or
|
||||||
|
dw::core::Widget::queueDraw. The first variant expects a region within
|
||||||
|
the widget, the second will cause the whole widget to be redrawn. This
|
||||||
|
will cause an asynchronous call of dw::core::Widget::draw.
|
||||||
|
|
||||||
|
If only the size changes, a call to dw::core::Widget::queueResize is
|
||||||
|
sufficient, this will also queue a complete redraw (see \ref
|
||||||
|
dw-widget-sizes.)
|
||||||
|
|
||||||
|
|
||||||
|
<h2>Mouse Events</h2>
|
||||||
|
|
||||||
|
A widget may process mouse events. The view (\ref dw-layout-views)
|
||||||
|
passes mouse events to the layout, which then passes them to the
|
||||||
|
widgets. There are two kinds of mouse events:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>events returning bool, and
|
||||||
|
<li>events returning nothing (void).
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
The first group consists of:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>dw::core::Widget::buttonPressImpl,
|
||||||
|
<li>dw::core::Widget::buttonReleaseImpl, and
|
||||||
|
<li>dw::core::Widget::motionNotifyImpl.
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
For these events, a widget returns a boolean value, which denotes,
|
||||||
|
whether the widget has processed this event (true) or not (false). In
|
||||||
|
the latter case, the event is delegated according to the following
|
||||||
|
rules:
|
||||||
|
|
||||||
|
<ol>
|
||||||
|
<li> First, this event is passed to the bottom-most widget, in which
|
||||||
|
allocation the mouse position is in.
|
||||||
|
<li> If the widget does not process this event (returning false), it is
|
||||||
|
passed to the parent, and so on.
|
||||||
|
<li> The final result (whether \em any widget has processed this event) is
|
||||||
|
returned to the view.
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
The view may return this to the UI toolkit, which then interprets this
|
||||||
|
in a similar way (whether the viewport, a UI widget, has processed
|
||||||
|
this event).
|
||||||
|
|
||||||
|
These events return nothing:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>dw::core::Widget::enterNotifyImpl and
|
||||||
|
<li>dw::core::Widget::leaveNotifyImpl.
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
since they are bound to a widget.
|
||||||
|
|
||||||
|
When processing mouse events, the layout always deals with two
|
||||||
|
widgets: the widget, the mouse pointer was in, when the previous mouse
|
||||||
|
event was processed, (below called the "old widget") and the widget,
|
||||||
|
in which the mouse pointer is now ("new widget").
|
||||||
|
|
||||||
|
The following paths are calculated:
|
||||||
|
|
||||||
|
<ol>
|
||||||
|
<li> the path from the old widget to the nearest common ancestor of the old
|
||||||
|
and the new widget, and
|
||||||
|
<li> the path from this ancestor to the new widget.
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
For the widgets along these paths, dw::core::Widget::enterNotifyImpl
|
||||||
|
and dw::core::Widget::leaveNotifyImpl are called.
|
||||||
|
|
||||||
|
<h3>Signals</h3>
|
||||||
|
|
||||||
|
If a caller outside of the widget is interested in these events, he
|
||||||
|
can connect a dw::core::Layout::LinkReceiver. For those events with a
|
||||||
|
boolean return value, the results of the signal emission is regarded,
|
||||||
|
i.e. the delegation of an event to the parent of the widget can be
|
||||||
|
stopped by a signal receiver returning true, even if the widget method
|
||||||
|
returns false.
|
||||||
|
|
||||||
|
First, the widget method is called, then (in any case) the signal is
|
||||||
|
emitted.
|
||||||
|
|
||||||
|
<h3>Selection</h3>
|
||||||
|
|
||||||
|
If your widget has selectable contents, it should delegate the events
|
||||||
|
to dw::core::SelectionState (dw::core::Layout::selectionState).
|
||||||
|
|
||||||
|
|
||||||
|
<h2>Miscellaneous</h2>
|
||||||
|
|
||||||
|
<h3>Cursors</h3>
|
||||||
|
|
||||||
|
Each widget has a cursor, which is set by
|
||||||
|
dw::core::Widget::setCursor. If a cursor is assigned to a part of a
|
||||||
|
widget, this widget must process mouse events, and call
|
||||||
|
dw::core::Widget::setCursor explicitly.
|
||||||
|
|
||||||
|
(This will change, cursors should become part of
|
||||||
|
dw::core::style::Style.)
|
||||||
|
|
||||||
|
<h3>Background</h3>
|
||||||
|
|
||||||
|
Backgrounds are part of styles
|
||||||
|
(dw::core::style::Style::backgroundColor). If a widget assigns
|
||||||
|
background colors to parts of a widget (as dw::Table does for rows),
|
||||||
|
it must call dw::core::Widget::setBgColor for the children inside this
|
||||||
|
part.
|
||||||
|
|
||||||
|
*/
|
||||||
470
devdoc/dw-line-breaking.doc
Normal file
@ -0,0 +1,470 @@
|
|||||||
|
/** \page dw-line-breaking Changes in Line-Breaking and Hyphenation
|
||||||
|
|
||||||
|
<div style="border: 2px solid #ffff00; margin-bottom: 0.5em;
|
||||||
|
padding: 0.5em 1em; background-color: #ffffe0"><b>Info:</b>
|
||||||
|
Should be incorporated into dw::Textblock.</div>
|
||||||
|
|
||||||
|
Introduction
|
||||||
|
============
|
||||||
|
|
||||||
|
For the implementation of hyphenation in dillo, not only a
|
||||||
|
hyphenation algorithm was implemented, but also, the line breaking was
|
||||||
|
changed to a simple optimization per line. Aside from the improvement
|
||||||
|
by this change per se, an important aspect is the introduction of
|
||||||
|
"penalties". Before this change, dillo put all words into a line which
|
||||||
|
fitted into it; now, a "badness" is calculated for a possible
|
||||||
|
breakpoint, and the best breakpoint, i. e. the breakpoint with the
|
||||||
|
smallest value for "badness", is chosen. This can be simply refined
|
||||||
|
to define "good" and "bad" breakpoints by assigning a "penalty"; the
|
||||||
|
best breakpoint is then the one with the smallest value of "badness +
|
||||||
|
penalty". Details can be found below.
|
||||||
|
|
||||||
|
Example: Normal spaces have a penalty of 0, while hyphenation points
|
||||||
|
get a penalty of, say, 1, since hyphenation is generally considered as
|
||||||
|
a bit "ugly" and should rather be avoided. Consider a situation where
|
||||||
|
the word "dillo" could be hyphenated, with the following badnesses:
|
||||||
|
|
||||||
|
- before "dillo": 0.6;
|
||||||
|
- between "dil-" and "lo": 0.2;
|
||||||
|
- after "dillo": 0.5.
|
||||||
|
|
||||||
|
Since the penalty is added, the last value is the best one, so "dillo"
|
||||||
|
is put at the end of the line, without hyphenation.
|
||||||
|
|
||||||
|
Under other circumstances (e. g. narrower lines), the values
|
||||||
|
might be different:
|
||||||
|
|
||||||
|
- before "dillo": infinite;
|
||||||
|
- between "dil-" and "lo": 0.3;
|
||||||
|
- after "dillo": 1.5.
|
||||||
|
|
||||||
|
In this case, even the addition of the penalty makes hyphenation the
|
||||||
|
best choice.
|
||||||
|
|
||||||
|
|
||||||
|
Literature
|
||||||
|
==========
|
||||||
|
|
||||||
|
Breaking Paragraphs Into Lines
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
Although dillo does not (yet?) implement the algorithm T<sub>E</sub>X
|
||||||
|
uses for line breaking, this document shares much of the notation used
|
||||||
|
by the article *Breaking Paragraphs Into Lines* by Donald E. Knuth and
|
||||||
|
Michael F. Plass; originally published in: Software -- Practice and
|
||||||
|
Experience **11** (1981), 1119-1184; reprinted in: *Digital
|
||||||
|
Typography* by Donalt E. Knuth, CSLI Publications 1999. Anyway an
|
||||||
|
interesting reading.
|
||||||
|
|
||||||
|
Hyphenation
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Dillo uses the algorithm by Frank Liang, which is described in his
|
||||||
|
doctoral dissertation found at http://www.tug.org/docs/liang/. There
|
||||||
|
is also a description in chapter H ("Hyphenation") of *The
|
||||||
|
T<sub>E</sub>Xbook* by Donald E. Knuth, Addison-Wesley 1984.
|
||||||
|
|
||||||
|
Pattern files can be found at
|
||||||
|
http://www.ctan.org/tex-archive/language/hyphenation.
|
||||||
|
|
||||||
|
|
||||||
|
Overview of Changes
|
||||||
|
===================
|
||||||
|
|
||||||
|
Starting with this change, dw/textblock.cc has been split up; anything
|
||||||
|
related to line breaking has been moved into
|
||||||
|
dw/textblock_linebreaking.cc. This will also be done for other aspects
|
||||||
|
like floats. (Better, however, would be a clean logical split.)
|
||||||
|
|
||||||
|
An important change relates to the way that lines are added: before,
|
||||||
|
dillo would add a line as soon as a new word for this line was
|
||||||
|
added. Now, a line is added not before the *last* word of this line is
|
||||||
|
known. This has two important implications:
|
||||||
|
|
||||||
|
- Some values in dw::Textblock::Line, which represented values
|
||||||
|
accumulated within the line, could be removed, since now, these
|
||||||
|
values can be calculated simply in a loop.
|
||||||
|
- On the other hand, this means that some words may not belong to any
|
||||||
|
line. For this reason, in some cases (e. g. in
|
||||||
|
dw::Textblock::sizeRequestImpl) dw::Textblock::showMissingLines is
|
||||||
|
called, which creates temporary lines, which must, under other
|
||||||
|
circumstances, be removed again by
|
||||||
|
dw::Textblock::removeTemporaryLines, since they have been created
|
||||||
|
based on limited information, and so possibly in a wrong way. (See
|
||||||
|
below for details.)
|
||||||
|
|
||||||
|
When a word can be hyphenated, an instance of dw::Textblock::Word is
|
||||||
|
used for each part. Notice that soft hyphens are evaluated
|
||||||
|
immediately, but automatic hyphenation is done in a lazy way (details
|
||||||
|
below), so the number of instances may change. There are some new
|
||||||
|
attributes: only when dw::Textblock::Word::canBeHyphenated is set to
|
||||||
|
*true*, automatic hyphenation is allowed; it is set to false when soft
|
||||||
|
hyphens are used for a word, and (of course) by the automatic
|
||||||
|
hyphenation itself. Furthermore, dw::Textblock::Word::hyphenWidth
|
||||||
|
(more details in the comment there) has to be included when
|
||||||
|
calculating line widths.
|
||||||
|
|
||||||
|
Some values should be configurable: dw::Textblock::HYPHEN_BREAK, the
|
||||||
|
penalty for hyphens. Also dw::Textblock::Word::stretchability,
|
||||||
|
dw::Textblock::Word::shrinkability, which are both set in
|
||||||
|
dw::Textblock::addSpace.
|
||||||
|
|
||||||
|
|
||||||
|
Criteria for Line-Breaking
|
||||||
|
==========================
|
||||||
|
|
||||||
|
Before these changes to line breaking, a word (represented by
|
||||||
|
dw::Textblock::Word) had the following attributes related to
|
||||||
|
line-breaking:
|
||||||
|
|
||||||
|
- the width of the word itself, represented by
|
||||||
|
dw::Textblock::Word::size;
|
||||||
|
- the width of the space following the word, represented by
|
||||||
|
dw::Textblock::Word::origSpace.
|
||||||
|
|
||||||
|
In a more mathematical notation, the \f$i\f$th word has a width
|
||||||
|
\f$w_i\f$ and a space \f$s_i\f$.
|
||||||
|
|
||||||
|
A break was possible, when there was a space between the two words,
|
||||||
|
and the first possible break was chosen.
|
||||||
|
|
||||||
|
With hyphenation, the criteria are refined. Hyphenation should only be
|
||||||
|
used when otherwise line breaking results in very large spaces. We
|
||||||
|
define:
|
||||||
|
|
||||||
|
- the badness \f$\beta\f$ of a line, which is greater the more the
|
||||||
|
spaces between the words differ from the ideal space;
|
||||||
|
- a penalty \f$p\f$ for any possible break point.
|
||||||
|
|
||||||
|
The goal is to find those break points, where \f$\beta + p\f$ is
|
||||||
|
minimal.
|
||||||
|
|
||||||
|
Examples for the penalty \f$p\f$:
|
||||||
|
|
||||||
|
- 0 for normal line breaks (between words);
|
||||||
|
- \f$\infty\f$ to prevent a line break at all costs;
|
||||||
|
- \f$-\infty\f$ to force a line
|
||||||
|
- a positive, but finite, value for hyphenation points.
|
||||||
|
|
||||||
|
So we need the following values:
|
||||||
|
|
||||||
|
- \f$w_i\f$ (the width of the word \f$i\f$ itself);
|
||||||
|
- \f$s_i\f$ (the width of the space following the word \f$i\f$);
|
||||||
|
- the stretchability \f$y_i\f$, a value denoting how much the space
|
||||||
|
after word\f$i\f$ can be stretched (typically \f${1\over 2} s_i\f$
|
||||||
|
for justified text; otherwise 0, since the spaces are not
|
||||||
|
stretched);
|
||||||
|
- the shrinkability \f$y_i\f$, a value denoting how much the space
|
||||||
|
after word\f$i\f$ can be shrunken (typically \f${1\over 3} s_i\f$
|
||||||
|
for justified text; otherwise 0, since the spaces are not shrunk);
|
||||||
|
- the penalty \f$p_i\f$, if the line is broken after word \f$i\f$;
|
||||||
|
- a width \f$h_i\f$, which is added, when the line is broken after
|
||||||
|
word \f$i\f$.
|
||||||
|
|
||||||
|
\f$h_i\f$ is the width of the hyphen, if the word \f$i\f$ is a part of
|
||||||
|
the hyphenated word (except the last part); otherwise 0.
|
||||||
|
|
||||||
|
Let \f$l\f$ be the (ideal) width (length) of the line, which is
|
||||||
|
e. at the top given by the browser window width. Furthermore, all words
|
||||||
|
from \f$a\f$ to \f$b\f$ are added to the line. \f$a\f$ is fixed: we do
|
||||||
|
not modify the previous lines anymore; but our task is to find a
|
||||||
|
suitable \f$b\f$.
|
||||||
|
|
||||||
|
We define:
|
||||||
|
|
||||||
|
\f[W_a^b = \sum_{i=a}^{b} w_i + \sum_{i=a}^{b-1} s_i + h_b\f]
|
||||||
|
|
||||||
|
\f[Y_a^b = {Y_0}_a^b + \sum_{i=a}^{b-1} y_i\f]
|
||||||
|
|
||||||
|
\f[Z_a^b = {Z_0}_a^b + \sum_{i=a}^{b-1} z_i\f]
|
||||||
|
|
||||||
|
|
||||||
|
\f$W_a^b\f$ is the total width, \f$Y_a^b\f$ the total stretchability,
|
||||||
|
and \f$Z_a^b\f$ the total shrinkability. \f${Y_0}_a^b\f$ and
|
||||||
|
\f${Z_0}_a^b\f$ are the stretchability and shrinkability defined per
|
||||||
|
line, and applied at the borders; they are 0 for justified text, but
|
||||||
|
\f${Y_0}_a^b\f$ has a positive value otherwise, see below for details.
|
||||||
|
|
||||||
|
Furthermore the *adjustment ratio* \f$r_a^b\f$:
|
||||||
|
|
||||||
|
- in the ideal case that \f$W_a^b = l\f$: \f$r_a^b = 0\f$;
|
||||||
|
- if \f$W_a^b < l\f$: \f$r_a^b = (l - W_a^b) / Y_a^b\f$
|
||||||
|
(\f$r_a^b < 0\f$ in this case);
|
||||||
|
- if \f$W_a^b > l\f$: \f$r_a^b = (l - W_a^b) / Z_a^b\f$
|
||||||
|
(\f$r_a^b < 0\f$ in this case).
|
||||||
|
|
||||||
|
The badness \f$\beta_a^b\f$ is defined as follows:
|
||||||
|
|
||||||
|
- if \f$r_a^b\f$ is undefined or \f$r_a^b < -1\f$: \f$\beta_a^b = \infty\f$;
|
||||||
|
- otherwise: \f$\beta_a^b = |r_a^b|^3\f$
|
||||||
|
|
||||||
|
The goal is to find the value of \f$b\f$ where \f$\beta_a^b + p_b\f$
|
||||||
|
is minimal. (\f$a\f$ is given, since we do not modify the previous
|
||||||
|
lines.)
|
||||||
|
|
||||||
|
After a couple of words, it is not predictable whether this minimum
|
||||||
|
has already been reached. There are two cases where this is possible
|
||||||
|
for a given \f$b'\f$:
|
||||||
|
|
||||||
|
- \f$\beta_{b'}^a = \infty\f$ (line gets too tight):
|
||||||
|
\f$a \le b < b'\f$, the minimum has to be searched between these two
|
||||||
|
values;
|
||||||
|
- \f$p_{b'} = -\infty\f$ (forced line break):
|
||||||
|
\f$a \le b \le b'\f$ (there may be another minimum of
|
||||||
|
\f$\beta_a^b\f$ before; note the \f$\le\f$ instead of \f$<\f$).
|
||||||
|
|
||||||
|
This leads to a problem that the last words of a text block are not
|
||||||
|
displayed this way, since they do not fulfill these rules for being
|
||||||
|
added to a line. For this reason, there are "temporary" lines already
|
||||||
|
described above.
|
||||||
|
|
||||||
|
(Note that the actual calculation differs from this description, since
|
||||||
|
integer arithmetic is used for performance, which make the actual
|
||||||
|
code more complicated. See dw::Textblock::BadnessAndPenalty for
|
||||||
|
details.)
|
||||||
|
|
||||||
|
Ragged Borders
|
||||||
|
--------------
|
||||||
|
|
||||||
|
For other than justified text (left-, right-aligned and centered), the
|
||||||
|
spaces between the words are not shrunk or stretched (so \f$y_i\f$
|
||||||
|
and \f$z_i\f$ are 0), but additional space is added to the left or
|
||||||
|
right border or to both. For this reason, an additional stretchability
|
||||||
|
\f${Y_0}_a^b\f$ is added (see definition above). Since this space at
|
||||||
|
the border is 0 in an ideal case (\f$W_a^b = l\f$), it cannot be
|
||||||
|
shrunken, so \f${Z_0}_a^b\f$ is 0.
|
||||||
|
|
||||||
|
This is not equivalent to the calculation of the total stretchability
|
||||||
|
as done for justified text, since in this case, the stretchability
|
||||||
|
depends on the number of words: consider the typical case that all
|
||||||
|
spaces and stretchabilities are equal (\f$y_a = y_{a + 1} = \ldots =
|
||||||
|
y_b\f$). With \f$n\f$ words, the total strechability would be \f$n
|
||||||
|
\cdot y_a\f$, so increase with an increasing number of words
|
||||||
|
(\f$y_a\f$ is constant). This is correct for justified text, but for
|
||||||
|
other alignments, where only one space (or two, for centered text) is
|
||||||
|
changed, this would mean that a line with many narrow words is more
|
||||||
|
stretchable than a line with few wide words.
|
||||||
|
|
||||||
|
It is obvious that left-aligned text can be handled in the same way as
|
||||||
|
right-aligned text. [... Centered text? ...]
|
||||||
|
|
||||||
|
The default value for the stretchability is the line height without
|
||||||
|
the space between the lines (more precisely: the maximum of all word
|
||||||
|
heights). The exact value not so important when comparing different
|
||||||
|
possible values for the badness \f$\beta_a^b\f$, when \f${Y_0}_a^b\f$
|
||||||
|
is nearly constant for different \f$b\f$ (which is the case for the
|
||||||
|
actual value), but it is important for the comparison with penalties,
|
||||||
|
which are constant. To be considered is also that for non-justified
|
||||||
|
text, hyphenation is differently (less) desirable; this effect can be
|
||||||
|
achieved by enlarging the stretchability, which will lead to a smaller
|
||||||
|
badness, and so make hyphenation less likely. The user can configure
|
||||||
|
the stretchability by changing the preference value
|
||||||
|
*stretchability_factor* (default: 1.0).
|
||||||
|
|
||||||
|
(Comparison to T<sub>E</sub>X: Knuth and Plass describe a method for
|
||||||
|
ragged borders, which is effectively the same as described here (Knuth
|
||||||
|
1999, pp. 93--94). The value for the stretchability of the line
|
||||||
|
is slightly less, 1 em (ibid., see also p. 72 for the
|
||||||
|
definition of the units). However, this article suggests a value for
|
||||||
|
the hyphenation penalty, which is ten times larger than the value for
|
||||||
|
justified text; this would suggest a larger value for
|
||||||
|
*stretchability_factor*.)
|
||||||
|
|
||||||
|
|
||||||
|
Hyphens
|
||||||
|
=======
|
||||||
|
|
||||||
|
Words (instances of dw::Textblock::Word), which are actually part of a
|
||||||
|
hyphenated word, are always drawn as a whole, not separately. This
|
||||||
|
way, the underlying platform is able to apply kerning, ligatures, etc.
|
||||||
|
|
||||||
|
Calculating the width of such words causes some problems, since it is
|
||||||
|
not required that the width of text "AB" is identical to the width of
|
||||||
|
"A" plus the width of "B", just for the reasons mentioned above. It
|
||||||
|
gets even a bit more complicated, since it is required that a word
|
||||||
|
part (instance of dw::Textblock::Word) has always the same length,
|
||||||
|
independent of whether hyphenation is applied or not. Furthermore, the
|
||||||
|
hyphen length is fixed for a word; for practical reasons, it is always
|
||||||
|
the width of a hyphen, in the given font.
|
||||||
|
|
||||||
|
For calculating the widths, consider a word of four syllables:
|
||||||
|
A-B-C-D. There are 3 hyphenation points, and so 2<sup>3</sup> = 8
|
||||||
|
possible ways of hyphenation: ABCD, ABC-D, AB-CD, AB-C-D, A-BCD,
|
||||||
|
A-BC-D, A-B-CD, A-B-C-D. (Some of them, like the last one, are only
|
||||||
|
probable for very narrow lines.)
|
||||||
|
|
||||||
|
Let w(A), w(B), w(C), w(D) be the word widths (part of
|
||||||
|
dw::Textblock::Word::size), which have to be calculated, and l be a
|
||||||
|
shorthand for dw::core::Platform::textWidth. Without considering
|
||||||
|
this problem, the calculation would be simple: w(A) = l(A)
|
||||||
|
etc. However, it gets a bit more complicated. Since all
|
||||||
|
non-hyphenations are drawn as a whole, the following conditions can be
|
||||||
|
concluded:
|
||||||
|
|
||||||
|
- from drawing "ABCD" (not hyphenated at all): w(A) + w(B) + w(C) +
|
||||||
|
w(D) = l(ABCD);
|
||||||
|
- from drawing "BCD", when hyphenated as "A-BCD" ("A-" is not
|
||||||
|
considered here): w(B) + w(C) + w(D) = l(BCD);
|
||||||
|
- likewise, from drawing "CD" (cases "AB-CD" and "A-B-CD"): w(C) +
|
||||||
|
w(D) = l(CD);
|
||||||
|
- finally, for the cases "ABC-D", "AB-C-D", "A-BC-D", and "A-B-C-D":
|
||||||
|
w(D) = l(D).
|
||||||
|
|
||||||
|
So, the calculation is simple:
|
||||||
|
|
||||||
|
- w(D) = l(D)
|
||||||
|
- w(C) = l(CD) - w(D)
|
||||||
|
- w(B) = l(BCD) - (w(C) + w(D))
|
||||||
|
- w(A) = l(ABCD) - (w(B) + w(C) + w(D))
|
||||||
|
|
||||||
|
For calculation the hyphen widths, the exact conditions would be
|
||||||
|
over-determined, even when the possibility for individual hyphen
|
||||||
|
widths (instead of simply the text width of a hyphen character) would
|
||||||
|
be used. However, a simple approach of fixed hyphen widths will have
|
||||||
|
near-perfect results, so this is kept simple.
|
||||||
|
|
||||||
|
|
||||||
|
Automatic Hyphenation
|
||||||
|
=====================
|
||||||
|
|
||||||
|
When soft hyphens are used, words are immediately divided into
|
||||||
|
different parts, and so different instances of
|
||||||
|
dw::Textblock::Word. Automatic hyphenation (using Liang's algorithm)
|
||||||
|
is, however, not applied always, but only when possibly needed, after
|
||||||
|
calculating a line without hyphenation:
|
||||||
|
|
||||||
|
- When the line is tight, the last word of the line is hyphenated;
|
||||||
|
possibly this will result in a line with less parts of this word,
|
||||||
|
and so a less tight line.
|
||||||
|
- When the line is loose, and there is another word (for the next
|
||||||
|
line) available, this word is hyphenated; possibly, some parts of
|
||||||
|
this word are taken into this line, making it less loose.
|
||||||
|
|
||||||
|
After this, the line is re-calculated.
|
||||||
|
|
||||||
|
A problem arises when the textblock is rewrapped, e. g. when the
|
||||||
|
user changes the window width. In this case, some new instances of
|
||||||
|
dw::Textblock::Word must be inserted into the word list,
|
||||||
|
dw::Textblock::words. This word list is implemented as an array, which
|
||||||
|
is dynamically increased; a simple approach would involve moving all
|
||||||
|
of the <i>n</i> elements after position <i>i</i>, so
|
||||||
|
<i>n</i> - <i>i</i> steps are necessary. This would not be a
|
||||||
|
problem, since O(n) steps are necessary; however, this will be
|
||||||
|
necessary again for the next hyphenated word (at the end of a
|
||||||
|
following line), and so on, so that
|
||||||
|
(<i>n</i> - <i>i</i><sub>1</sub>) +
|
||||||
|
(<i>n</i> - <i>i</i><sub>2</sub>) + ..., with
|
||||||
|
<i>i</i><sub>1</sub> < <i>i</i><sub>2</sub> < ...,
|
||||||
|
which results in O(n<sup>2</sup>) steps. For this reason, the word
|
||||||
|
list is managed by the class lout::misc::NotSoSimpleVector, which uses
|
||||||
|
a trick (a second array) to deal with exactly this problem. See there
|
||||||
|
for more details.
|
||||||
|
|
||||||
|
|
||||||
|
Tests
|
||||||
|
=====
|
||||||
|
|
||||||
|
There are test HTML files in the <i>test</i> directory. Also, there is
|
||||||
|
a program testing automatic hyphenation, <i>test/liang</i>, which can
|
||||||
|
be easily extended.
|
||||||
|
|
||||||
|
|
||||||
|
Bugs and Things Needing Improvement
|
||||||
|
===================================
|
||||||
|
|
||||||
|
High Priority
|
||||||
|
-------------
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
Medium Priority
|
||||||
|
---------------
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
Low Priority
|
||||||
|
------------
|
||||||
|
|
||||||
|
**Mark the end of a paragraph:** Should dw::core::Content::BREAK still
|
||||||
|
be used? Currently, this is redundant to
|
||||||
|
dw::Textblock::BadnessAndPenalty.
|
||||||
|
|
||||||
|
Solved (Must Be Documented)
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
These have been solved recently and should be documented above.
|
||||||
|
|
||||||
|
*Bugs in hyphenation:* There seem to be problems when breaking words
|
||||||
|
containing hyphens already. Example: "Abtei-Stadt", which is divided
|
||||||
|
into "Abtei-" and "Stadt", resulting possibly in
|
||||||
|
"Abtei-<span></span>-[new line]Stadt". See also below under
|
||||||
|
"Medium Priority", on how to deal with hyphens and dashes.
|
||||||
|
|
||||||
|
**Solution:** See next.
|
||||||
|
|
||||||
|
*Break hyphens and dashes:* The following rules seem to be relevant:
|
||||||
|
|
||||||
|
- In English, an em-dash is used with no spaces around. Breaking
|
||||||
|
before and after the dash should be possible, perhaps with a
|
||||||
|
penalty > 0. (In German, an en-dash (Halbgeviert) with spaces around
|
||||||
|
is used instead.)
|
||||||
|
- After a hyphen, which is part of a compound word, a break should be
|
||||||
|
possible. As described above ("Abtei-Stadt"), this collides with
|
||||||
|
hyphenation.
|
||||||
|
|
||||||
|
Where to implement? In the same dynamic, lazy way like hyphenation? As
|
||||||
|
part of hyphenation?
|
||||||
|
|
||||||
|
Notice that Liang's algorithm may behave different regarding hyphens:
|
||||||
|
"Abtei-Stadt" is (using the patterns from CTAN) divided into "Abtei-"
|
||||||
|
and "Stadt", but "Nordrhein-Westfalen" is divided into "Nord",
|
||||||
|
"rhein-West", "fa", "len": the part containing the hyphen
|
||||||
|
("rhein-West") is untouched. (Sorry for the German words; if you have
|
||||||
|
got English examples, send them me.)
|
||||||
|
|
||||||
|
**Solution for both:** This has been implemented in
|
||||||
|
dw::Textblock::addText, in a similar way to soft hyphens. Liang's
|
||||||
|
algorithm now only operates on the parts: "Abtei" and "Stadt";
|
||||||
|
"Nordrhein" and "Westfalen".
|
||||||
|
|
||||||
|
*Hyphens in adjacent lines:* It should be simple to assign a larger
|
||||||
|
penalty for hyphens, when the line before is already hyphenated. This
|
||||||
|
way, hyphens in adjacent lines are penalized further.
|
||||||
|
|
||||||
|
**Solved:** There are always two penalties. Must be documented in
|
||||||
|
detail.
|
||||||
|
|
||||||
|
*Incorrect calculation of extremes:* The minimal width of a text block
|
||||||
|
(as part of the width extremes, which are mainly used for tables) is
|
||||||
|
defined by everything between two possible breaks. A possible break
|
||||||
|
may also be a hyphenation point; however, hyphenation points are
|
||||||
|
calculated in a lazy way, when the lines are broken, and not when
|
||||||
|
extremes are calculated. So, it is a matter of chance whether the
|
||||||
|
calculation of the minimal width will take the two parts "dil-" and
|
||||||
|
"lo" into account (when "dillo" has already been hyphenated), or only
|
||||||
|
one part, "dillo" (when "dillo" has not yet been hyphenated),
|
||||||
|
resulting possibly in a different value for the minimal width.
|
||||||
|
|
||||||
|
Possible strategies to deal with this problem:
|
||||||
|
|
||||||
|
- Ignore. The implications should be minimal.
|
||||||
|
- Any solution will make it necessary to hyphenate at least some
|
||||||
|
words when calculating extremes. Since the minimal widths of all
|
||||||
|
words are used to calculate the minimal width of the text block, the
|
||||||
|
simplest approach will hyphenate all words. This would, of course,
|
||||||
|
eliminate the performance gains of the current lazy approach.
|
||||||
|
- The latter approach could be optimized in some ways. Examples: (i)
|
||||||
|
If a word is already narrower than the current accumulated value for
|
||||||
|
the minimal width, it makes no sense to hyphenate it. (ii) In other
|
||||||
|
cases, heuristics may be used to estimate the number of syllables,
|
||||||
|
the width of the widest of them etc.
|
||||||
|
|
||||||
|
**Solved:** Hyphenated parts of a word are not considered anymore for
|
||||||
|
width extremes, but only whole words. This is also one reason for the
|
||||||
|
introduction of the paragraphs list.
|
||||||
|
|
||||||
|
**Also:**
|
||||||
|
|
||||||
|
- Configuration of penalties.
|
||||||
|
|
||||||
|
*/
|
||||||
59
devdoc/dw-map.doc
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/** \page dw-map Dillo Widget Documentation Map
|
||||||
|
|
||||||
|
This maps includes special documentations as well as longer comments
|
||||||
|
in the sources. Arrows denote references between the documents.
|
||||||
|
|
||||||
|
\dot
|
||||||
|
digraph G {
|
||||||
|
rankdir=LR;
|
||||||
|
node [shape=record, fontname=Helvetica, fontsize=8];
|
||||||
|
fontname=Helvetica; fontsize=8;
|
||||||
|
|
||||||
|
dw_overview [label="Dillo Widget Overview", URL="\ref dw-overview"];
|
||||||
|
dw_usage [label="Dillo Widget Usage", URL="\ref dw-usage"];
|
||||||
|
dw_layout_views [label="Layout and Views", URL="\ref dw-layout-views"];
|
||||||
|
dw_layout_widgets [label="Layout and Widgets",
|
||||||
|
URL="\ref dw-layout-widgets"];
|
||||||
|
dw_widget_sizes [label="Sizes of Dillo Widgets",
|
||||||
|
URL="\ref dw-widget-sizes"];
|
||||||
|
dw_changes [label="Changes to the GTK+-based Release Version",
|
||||||
|
URL="\ref dw-changes"];
|
||||||
|
dw_images_and_backgrounds [label="Images and Backgrounds in Dw",
|
||||||
|
URL="\ref dw-images-and-backgrounds"];
|
||||||
|
dw_Image [label="dw::Image", URL="\ref dw::Image"];
|
||||||
|
dw_core_Imgbuf [label="dw::core::Imgbuf", URL="\ref dw::core::Imgbuf"];
|
||||||
|
dw_core_SelectionState [label="dw::core::SelectionState",
|
||||||
|
URL="\ref dw::core::SelectionState"];
|
||||||
|
dw_core_style [label="dw::core::style", URL="\ref dw::core::style"];
|
||||||
|
dw_Table [label="dw::Table", URL="\ref dw::Table"];
|
||||||
|
dw_Textblock [label="dw::Textblock", URL="\ref dw::Textblock"];
|
||||||
|
dw_core_ui [label="dw::core::ui", URL="\ref dw::core::ui"];
|
||||||
|
|
||||||
|
dw_overview -> dw_changes;
|
||||||
|
dw_overview -> dw_usage;
|
||||||
|
dw_overview -> dw_core_style;
|
||||||
|
dw_overview -> dw_core_ui;
|
||||||
|
dw_overview -> dw_images_and_backgrounds;
|
||||||
|
dw_overview -> dw_layout_widgets;
|
||||||
|
dw_overview -> dw_widget_sizes;
|
||||||
|
dw_overview -> dw_layout_views;
|
||||||
|
|
||||||
|
dw_usage -> dw_Table;
|
||||||
|
dw_usage -> dw_Textblock;
|
||||||
|
dw_usage -> dw_core_style;
|
||||||
|
dw_usage -> dw_core_ui;
|
||||||
|
dw_usage -> dw_images_and_backgrounds;
|
||||||
|
|
||||||
|
dw_layout_widgets -> dw_widget_sizes;
|
||||||
|
dw_layout_widgets -> dw_core_SelectionState;
|
||||||
|
|
||||||
|
dw_widget_sizes -> dw_Table;
|
||||||
|
dw_widget_sizes -> dw_Textblock;
|
||||||
|
|
||||||
|
dw_images_and_backgrounds -> dw_core_Imgbuf;
|
||||||
|
dw_images_and_backgrounds -> dw_Image;
|
||||||
|
|
||||||
|
dw_core_style -> dw_Textblock;
|
||||||
|
}
|
||||||
|
\enddot
|
||||||
|
*/
|
||||||
97
devdoc/dw-miscellaneous.doc
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
/** \page dw-miscellaneous Miscellaneous Notes on Dw
|
||||||
|
|
||||||
|
This is a barely sorted list of issues which I consider noteworthy,
|
||||||
|
but have yet to be moved to other parts of the documentation (which is
|
||||||
|
partly to be created).
|
||||||
|
|
||||||
|
General
|
||||||
|
=======
|
||||||
|
|
||||||
|
Widget allocation outside of parent allocation
|
||||||
|
----------------------------------------------
|
||||||
|
A widget allocation outside of the allocation of the parent is
|
||||||
|
allowed, but the part outside is not visible.
|
||||||
|
|
||||||
|
Which widgets may be drawn?
|
||||||
|
---------------------------
|
||||||
|
All drawing starts with the toplevel widget
|
||||||
|
(cf. dw::core::Widget::queueDrawArea, dw::core::Layout::queueDraw, and
|
||||||
|
dw::core::Layout::expose), and a widget has to draw its children, in a
|
||||||
|
way consistent with their stacking order.
|
||||||
|
|
||||||
|
There are two exceptions:
|
||||||
|
|
||||||
|
1. Direct descendants, which are not children, may be drawn, if the
|
||||||
|
parent can distinguish them and so omit drawing them a second
|
||||||
|
time. See dw::core::StackingContextMgr and \ref dw-stacking-context.
|
||||||
|
Parents should not draw children in flow for which
|
||||||
|
dw::core::StackingContextMgr::handledByStackingContextMgr returns
|
||||||
|
true.
|
||||||
|
2. Interrupted drawing: via dw::core::Widget::drawInterruption; see
|
||||||
|
\ref dw-interrupted-drawing.
|
||||||
|
|
||||||
|
Similar rules apply to handling mouse events
|
||||||
|
(dw::core::Widget::getWidgetAtPoint).
|
||||||
|
|
||||||
|
Interrupted drawing
|
||||||
|
-------------------
|
||||||
|
→ \ref dw-interrupted-drawing.
|
||||||
|
|
||||||
|
Similar rules apply to handling mouse events
|
||||||
|
(dw::core::Widget::getWidgetAtPoint).
|
||||||
|
|
||||||
|
Extra space
|
||||||
|
-----------
|
||||||
|
Should dw::core::Widget::calcExtraSpace be called from
|
||||||
|
dw::core::Widget::getExtremes?
|
||||||
|
|
||||||
|
|
||||||
|
Widgets out of flow
|
||||||
|
===================
|
||||||
|
|
||||||
|
dw::Textblock::getGeneratorWidth
|
||||||
|
--------------------------------
|
||||||
|
Re-evaluate dw::Textblock::getGeneratorWidth (especially the limitation on
|
||||||
|
instances of dw::Textblock) for positioned elements. Is this method really only
|
||||||
|
called for floats?
|
||||||
|
|
||||||
|
|
||||||
|
Widget sizes
|
||||||
|
============
|
||||||
|
|
||||||
|
Relation between dw::core::Widget::markSizeChange and dw::core::Widget::queueResize
|
||||||
|
------------------------------------------------------------------------------------
|
||||||
|
The following comment should be re-evaluated. Implementing incremental resizing
|
||||||
|
for dw::oof::OOFFloatsMgr seems to fix the performance problems, but this should
|
||||||
|
be examined further.
|
||||||
|
|
||||||
|
<div style="text-decoration: line-through; color: #606060">
|
||||||
|
dw::oof::OOFFloatsMgr::markSizeChange (called from
|
||||||
|
dw::Textblock::markSizeChange) calls dw::oof::OOFAwareWidget::updateReference,
|
||||||
|
whose implementation dw::Textblock::updateReference calls
|
||||||
|
dw::core::Widget::queueResize. This may result in a recursion,
|
||||||
|
|
||||||
|
- for which it is not clear whether it ends in all cases (although endless cases
|
||||||
|
are not known yet), and
|
||||||
|
- which nevertheless may take much time in cases where the number of calls
|
||||||
|
increases exponentially with the depth of the widget tree.
|
||||||
|
|
||||||
|
The recent change in dw::Textblock::updateReference (`if (lines->size () > 0)`)
|
||||||
|
seems to fix the performance problem, but the issue should be examined further,
|
||||||
|
albeit with lower priority. Especially, it has to be determined, under which
|
||||||
|
conditions it is allowed to (directly or indirectly) call
|
||||||
|
dw::core::Widget::queueResize within an implementation of
|
||||||
|
dw::core::Widget::markSizeChange.
|
||||||
|
|
||||||
|
Here is the original test case (slow, when `if (lines->size () > 0)` is removed
|
||||||
|
again):
|
||||||
|
|
||||||
|
(for i in $(seq 1 20); do echo '<div style="float:left"><div></div>'; done) > tmp.html; src/dillo tmp.html
|
||||||
|
|
||||||
|
You may change the number (20), or examine smaller cases with
|
||||||
|
<a href="http://home.gna.org/rtfl/">RTFL</a>:
|
||||||
|
|
||||||
|
(for i in $(seq 1 3); do echo '<div style="float:left"><div></div>'; done) > tmp.html; src/dillo tmp.html | rtfl-objview -OM -A "*" -a resize -a resize.oofm
|
||||||
|
</div>
|
||||||
|
|
||||||
|
*/
|
||||||
151
devdoc/dw-out-of-flow-floats.doc
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
/** \page dw-out-of-flow-floats Handling Elements Out Of Flow: Floats
|
||||||
|
|
||||||
|
TODO: Much missing.
|
||||||
|
|
||||||
|
(Historical) Note: Floats make use of \ref dw-size-request-pos, which
|
||||||
|
reduces the complexity of a previous design.
|
||||||
|
|
||||||
|
|
||||||
|
Sorting floats
|
||||||
|
==============
|
||||||
|
|
||||||
|
Floats are sorted, to make binary search possible, in these lists:
|
||||||
|
|
||||||
|
- for each generator: dw::OutOfFlowMgr::TBInfo::leftFloatsGB and
|
||||||
|
dw::OutOfFlowMgr::TBInfo::rightFloatsGB;
|
||||||
|
- for the container: dw::OutOfFlowMgr::leftFloatsCB and
|
||||||
|
dw::OutOfFlowMgr::rightFloatsCB.
|
||||||
|
|
||||||
|
The other two lists, dw::OutOfFlowMgr::leftFloatsAll and
|
||||||
|
dw::OutOfFlowMgr::rightFloatsAll are not sorted at all.
|
||||||
|
|
||||||
|
New floats are always added to the end of either list; this order is
|
||||||
|
called *generation order*. See also above: *GB lists and CB lists*.
|
||||||
|
|
||||||
|
On the other hand, there are different sorting criteria, implemented
|
||||||
|
by different comparators, so that different kinds of keys may be used
|
||||||
|
for searching. These sorting criteria are equivalent to the generation
|
||||||
|
order.
|
||||||
|
|
||||||
|
dw::OutOfFlowMgr::Float::CompareSideSpanningIndex compares
|
||||||
|
*sideSpanningIndex* (used to compare floats to those on the respective
|
||||||
|
other side); if you look at the definition
|
||||||
|
(dw::OutOfFlowMgr::addWidgetOOF) it becomes clear that this order is
|
||||||
|
equivalent to the generation order.
|
||||||
|
|
||||||
|
dw::OutOfFlowMgr::Float::CompareGBAndExtIndex compares *externalIndex*
|
||||||
|
for floats with same generators, otherwise: (i) if one generator (T1)
|
||||||
|
is a direct ancestor of the other generator (T2), the child of T1,
|
||||||
|
which is an ancestor of, or identical to, T2 is compared to the float
|
||||||
|
generated by T1, using *externalIndex*, as in this example:
|
||||||
|
|
||||||
|
T1 -+-> child --> ... -> T2 -> Float
|
||||||
|
`-> Float
|
||||||
|
|
||||||
|
Otherwise, the two blocks are compared, according to their position in
|
||||||
|
dw::OutOfFlowMgr::tbInfos:
|
||||||
|
|
||||||
|
common ancestor -+-> ... --> T1 -> Float
|
||||||
|
`-> ... --> T2 -> Float
|
||||||
|
|
||||||
|
This is equivalent to the generation order, as long it is ensured that
|
||||||
|
*externalIndex* reflects the generation order within a generating
|
||||||
|
block, for both floats and child blocks.
|
||||||
|
|
||||||
|
dw::OutOfFlowMgr::Float::ComparePosition ...
|
||||||
|
|
||||||
|
|
||||||
|
Miscellaneous notes
|
||||||
|
===================
|
||||||
|
|
||||||
|
Handling collisions
|
||||||
|
-------------------
|
||||||
|
The CSS specification allows two strategies to deal with colliding
|
||||||
|
floats: placing the second float beside or below the first one. Many
|
||||||
|
other browsers implement the first approach, while dillo implements
|
||||||
|
the second one, which may cause problems when the author assumes the
|
||||||
|
first. Example: the "tabs" at the top of every page at Wikipedia
|
||||||
|
("Article", "Talk", ...).
|
||||||
|
|
||||||
|
Float containers in flow
|
||||||
|
------------------------
|
||||||
|
Consider the following HTML snippet:
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<img src="....jpg" style="float:right">
|
||||||
|
<p style="overflow:hidden">Text</p>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
Interestingly, dillo shows "Text" always *below* the image, even if
|
||||||
|
there is enough space left of it. An investigation shows that the
|
||||||
|
paragraph (<p>) is regarded as own floats container (because of
|
||||||
|
*overflow:hidden*), so the floats container above (<body>)
|
||||||
|
regards this block as widget which must be fit between the floats
|
||||||
|
(dw::Textblock::mustBorderBeRegarded >
|
||||||
|
dw::Textblock::getWidgetRegardingBorderForLine). However, since a
|
||||||
|
textblock in flow always covers (at least) the whole available width,
|
||||||
|
which is defined *without* considering floats, the space left of the
|
||||||
|
float will always be to narrow, so that the paragraph is moved below
|
||||||
|
the float, by inserting an empty line before.
|
||||||
|
|
||||||
|
When searching for a solution, several difficulties show up:
|
||||||
|
|
||||||
|
1. The available width, which is used for the width of the textblock,
|
||||||
|
is defined independent of floats. Aside from problems when changing
|
||||||
|
this definition, a dependence on floats would be difficult to
|
||||||
|
implement, since *sizeRequest* is independent of a position. (See
|
||||||
|
also \ref dw-out-of-flow.)
|
||||||
|
2. I must admit that I do not remember the exact rationale and the
|
||||||
|
test case behind adding the exception in
|
||||||
|
dw::Textblock::getWidgetRegardingBorderForLine (see above), but
|
||||||
|
simply removing this exception will result in a possible
|
||||||
|
overlapping of floats from both containers, since no collisions are
|
||||||
|
tested for.
|
||||||
|
3. On the other hand, mixing the float containers (interaction of two
|
||||||
|
or more instances of dw::oof::OOFFloatsMgr), e. g. for
|
||||||
|
collision tests, would become too complex and possibly result in
|
||||||
|
performance problems.
|
||||||
|
|
||||||
|
Instead, this approach is focussed:
|
||||||
|
|
||||||
|
- Goal: the paragraph is narrowed so it fits, *as a whole*, between
|
||||||
|
the floats.
|
||||||
|
- The approach is to remove the exception in
|
||||||
|
dw::Textblock::getWidgetRegardingBorderForLine. A textblock, which
|
||||||
|
is a float container in flow (as this paragraph), is returned by
|
||||||
|
this method and so dw::Textblock::mustBorderBeRegarded returns
|
||||||
|
*true*. This will put this paragraph again at the correct position.
|
||||||
|
- To avoid overlappings, the linebreaking width of this paragraph
|
||||||
|
(which is also used for positioning of floats) is the available
|
||||||
|
width, minus the maximal float width on either side. (This is an
|
||||||
|
approach similar to the one dw::Ruler will use soon). Most likely,
|
||||||
|
some new methods will have to be added to calculate this.
|
||||||
|
- For paragraphs like this, dw::Textblock::borderChanged must rewrap
|
||||||
|
all lines; *y* is irrelevant in this case.
|
||||||
|
- Since the textblock will tend to become taller when getting
|
||||||
|
narrower, and so possibly cover more (wider) floats, and so become
|
||||||
|
narrower again etc., there may be multiple solutions for calculating
|
||||||
|
the size. Generally, a smaller height (and so larger width) is
|
||||||
|
preferred.
|
||||||
|
- There remains a problem: what if a word is too large? Should a
|
||||||
|
textblock of this kind then reard the floats in detail, to insert
|
||||||
|
empty lines when needed?
|
||||||
|
|
||||||
|
<b>Real-world cases:</b> *Overflow:hidden* is set for headings in
|
||||||
|
Wikipedia, and so this case occurs when there is a float (thumb image)
|
||||||
|
before a heading. See e. g.
|
||||||
|
<a href="http://de.wikipedia.org/wiki/Emmerich_am_Rhein#Ans.C3.A4ssige_Unternehmen">this page</a>
|
||||||
|
and scroll a bit up; the company logos should be right of this section.
|
||||||
|
|
||||||
|
<b>Priority:</b> Since this is not a regression, compared to not
|
||||||
|
supporting floats at all, a fix is not urgent for a new release.
|
||||||
|
|
||||||
|
Resizing
|
||||||
|
--------
|
||||||
|
Has the case that a float changes its position to be regarded? Probably yes, but
|
||||||
|
cases where no other mechanisms come into play are rather unlikely.
|
||||||
|
|
||||||
|
<b>Priority:</b> If this plays a role, this means a regression compared to not
|
||||||
|
supporting floats at all.
|
||||||
|
|
||||||
|
*/
|
||||||
66
devdoc/dw-out-of-flow-positioned.doc
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/** \page dw-out-of-flow-positioned Handling Elements Out Of Flow: Positioned Elements
|
||||||
|
|
||||||
|
<div style="border: 2px solid #ffff00; margin: 1em 0; padding: 0.5em
|
||||||
|
1em; background-color: #ffffe0">Positioned elements have been
|
||||||
|
deactivated, during the development of \ref dw-size-request-pos, to
|
||||||
|
focus on floats. See dw::core::IMPL_POS in file dw/core.hh.</div>
|
||||||
|
|
||||||
|
|
||||||
|
Miscellaneous notes
|
||||||
|
===================
|
||||||
|
|
||||||
|
General
|
||||||
|
-------
|
||||||
|
(See also *relative positions* below.)
|
||||||
|
|
||||||
|
What about negative positions?
|
||||||
|
|
||||||
|
dw::oof::OutOfFlowMgr::tellPosition1 and
|
||||||
|
dw::oof::OutOfFlowMgr::tellPosition2 could be combined again, and
|
||||||
|
called in the respective circumstance, depending on
|
||||||
|
dw::oof::OutOfFlowMgr::mayAffectBordersAtAll.
|
||||||
|
|
||||||
|
Relative positions
|
||||||
|
------------------
|
||||||
|
**General Overview:** At the original position, a space as large as
|
||||||
|
the positioned element is left. This is implemented by assigning a
|
||||||
|
size to the widget *reference*. For this there are two new methods:
|
||||||
|
dw::oof::OutOfFlowMgr::calcWidgetRefSize and
|
||||||
|
dw::oof::OOFAwareWidget::widgetRefSizeChanged.
|
||||||
|
|
||||||
|
**Bug:** Since the size of a relatively positioned element should be
|
||||||
|
calculated as if it was in flow, the available width should be
|
||||||
|
delegated to the *generator*; however, since
|
||||||
|
dw::oof::OOFPosRelMgr::dealingWithSizeOfChild returns *false* in all
|
||||||
|
cases, it is delegated to the *container*. **Idea for fix:**
|
||||||
|
dw::oof::OOFPosRelMgr::dealingWithSizeOfChild should return *false* if
|
||||||
|
and only if the generator of the child is the container (to prevent an
|
||||||
|
endless recursion). In other cases,
|
||||||
|
dw::oof::OOFPosRelMgr::getAvailWidthOfChild and
|
||||||
|
dw::oof::OOFPosRelMgr::getAvailHeightOfChild should directly call the
|
||||||
|
respective methods of the *generator*, which must be made public then.
|
||||||
|
|
||||||
|
**Performance:** In many cases, the first call of
|
||||||
|
dw::oof::OOFPosRelMgr::sizeAllocateEnd will queue again the resize
|
||||||
|
idle, since some elements are not considered in
|
||||||
|
dw::oof::OOFPosRelMgr::getSize. One case could be removed: if a
|
||||||
|
positioned element has *left* = *top* = 0, and its total size (the
|
||||||
|
requisition) is equal to the space left at the original position, the
|
||||||
|
size of the widget *reference*
|
||||||
|
(dw::oof::OOFAwareWidget::getRequisitionWithoutOOF, see
|
||||||
|
dw::oof::OOFPosRelMgr::calcWidgetRefSize).
|
||||||
|
|
||||||
|
**Documentation:** Describe why the latter is not covered by
|
||||||
|
dw::oof::OOFPositionedMgr::doChildrenExceedContainer. (Is this really
|
||||||
|
the case?)
|
||||||
|
|
||||||
|
**Open:** Stacking order? Furthermore: a relatively positioned element
|
||||||
|
does not always constitute a containing block (see CSS specification).
|
||||||
|
|
||||||
|
Fixed positions
|
||||||
|
---------------
|
||||||
|
Currently, fixedly positioned elements are positioned relative to the
|
||||||
|
canvas, not to the viewport. For a complete implementation, see \ref
|
||||||
|
dw-fixed-positions.
|
||||||
|
|
||||||
|
*/
|
||||||
102
devdoc/dw-out-of-flow.doc
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
/** \page dw-out-of-flow Handling Elements Out Of Flow
|
||||||
|
|
||||||
|
Introduction
|
||||||
|
============
|
||||||
|
|
||||||
|
This texts deals with both floats and positioned elements, which have
|
||||||
|
in common that there is a distinction between generating block and
|
||||||
|
containing block (we are here using the same notation as in the
|
||||||
|
CSS 2 specification). Consider this snippet (regarding floats):
|
||||||
|
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>Some text.</li>
|
||||||
|
<li>
|
||||||
|
<div style="float:right; width: 50%">Some longer text, so
|
||||||
|
that the effect described in this passage can be
|
||||||
|
demonstrated.
|
||||||
|
</div>
|
||||||
|
Some more and longer text.</li>
|
||||||
|
<li>Final text. Plus some more to demonstrate how text flows
|
||||||
|
around the float on the right side.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
which may be rendered like this
|
||||||
|
|
||||||
|
\image html dw-floats-01.png
|
||||||
|
|
||||||
|
The float (the DIV section, yellow in the image) is defined
|
||||||
|
("generated") within the list item (green), so, in CSS 2 terms, the
|
||||||
|
list item is the generating block of the float. However, as the image
|
||||||
|
shows, the float is not contained by the list item, but another block,
|
||||||
|
several levels above (not shown here). In terms of ::dw, this means
|
||||||
|
that the dw::Textblock representing the float cannot be a child of the
|
||||||
|
dw::Textblock representing the generating block, the list item, since
|
||||||
|
the allocation of a child widget must be within the allocation of the
|
||||||
|
parent widget. Instead, to each dw::Textblock, another dw::Textblock
|
||||||
|
is assigned as the containing box.
|
||||||
|
|
||||||
|
(Notice also that other text blocks must regard floats to calculate
|
||||||
|
their borders, and so their size. In this example, the following list
|
||||||
|
item (gray) must consider the position of the float. This is discussed
|
||||||
|
in detail in the following section, _Implementation overview_.)
|
||||||
|
|
||||||
|
|
||||||
|
Implementation overview
|
||||||
|
=======================
|
||||||
|
|
||||||
|
- dw::oof::OOFAwareWidget is the base for both generators and containers.
|
||||||
|
dw::Textblock and dw::Table are based on this, but dw::Table is only relevant
|
||||||
|
for positioned elements, so much simpler than dw::Textblock.
|
||||||
|
- For a given textblock (or, generally, generator), the float container is not
|
||||||
|
necessary the same container as for positioned elements. For this reason,
|
||||||
|
dw::oof::OOFAwareWidget::oofContainer is an array.
|
||||||
|
- The containers are set in dw::oof::OOFAwareWidget::notifySetAsTopLevel and
|
||||||
|
dw::oof::OOFAwareWidget::notifySetParent.
|
||||||
|
- If a widget is out of flow, the generating widget keeps a reference with the
|
||||||
|
type dw::core::Content::WIDGET_OOF_REF, while the containing block refers to
|
||||||
|
it as dw::core::Content::WIDGET_OOF_CONT. For widgets within flow,
|
||||||
|
dw::core::Content::WIDGET_IN_FLOW is used. Notice that in the first case,
|
||||||
|
there are two pieces of content referring to the same widget. An application
|
||||||
|
of this distinction is iterators. (For selection and searching, the generating
|
||||||
|
hierarchy is used, which is different from the widget hierarchy.)
|
||||||
|
|
||||||
|
Handling widgets out of flow is partly the task of class implementing
|
||||||
|
dw::oof::OutOfFlowMgr, which is stored by dw::oof::OOFAwareWidget::outOfFlowMgr,
|
||||||
|
but only for containing blocks. Generating blocks should refer
|
||||||
|
to *container->outOfFlowMgr[...]*.
|
||||||
|
|
||||||
|
Overview of the dw::oof::OutOfFlowMgr hierarchy;
|
||||||
|
|
||||||
|
\dot
|
||||||
|
digraph G {
|
||||||
|
node [shape=record, fontname=Helvetica, fontsize=10];
|
||||||
|
edge [arrowhead="none", arrowtail="empty", dir="both"];
|
||||||
|
fontname=Helvetica; fontsize=8;
|
||||||
|
|
||||||
|
OutOfFlowMgr [URL="\ref dw::oof::OutOfFlowMgr"; color="#a0a0a0"];
|
||||||
|
OOFFloatsMgr [URL="\ref dw::oof::OOFFloatsMgr"];
|
||||||
|
OOFPositionedMgr [URL="\ref dw::oof::OOFPositionedMgr"; color="#a0a0a0"];
|
||||||
|
OOFPosAbsLikeMgr [URL="\ref dw::oof::OOFPosAbsLikeMgr"; color="#a0a0a0"];
|
||||||
|
OOFPosAbsMgr [URL="\ref dw::oof::OOFPosAbsMgr"];
|
||||||
|
OOFPosFixedMgr [URL="\ref dw::oof::OOFPosFixedMgr"];
|
||||||
|
OOFPosRelMgr [URL="\ref dw::oof::OOFPosRelMgr"];
|
||||||
|
|
||||||
|
OutOfFlowMgr -> OOFFloatsMgr;
|
||||||
|
OutOfFlowMgr -> OOFPositionedMgr;
|
||||||
|
OOFPositionedMgr -> OOFPosAbsLikeMgr;
|
||||||
|
OOFPosAbsLikeMgr -> OOFPosAbsMgr;
|
||||||
|
OOFPosAbsLikeMgr -> OOFPosFixedMgr;
|
||||||
|
OOFPositionedMgr -> OOFPosRelMgr;
|
||||||
|
}
|
||||||
|
\enddot
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
Further details
|
||||||
|
===============
|
||||||
|
|
||||||
|
- \ref dw-out-of-flow-floats
|
||||||
|
- \ref dw-out-of-flow-positioned
|
||||||
|
|
||||||
|
*/
|
||||||
158
devdoc/dw-overview.doc
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
/** \page dw-overview Dillo Widget Overview
|
||||||
|
|
||||||
|
Note: If you are already familiar with the Gtk+-based version of Dw,
|
||||||
|
read \ref dw-changes.
|
||||||
|
|
||||||
|
|
||||||
|
The module Dw (Dillo Widget) is responsible for the low-level rendering of
|
||||||
|
all resources, e.g. images, plain text, and HTML pages (this is the
|
||||||
|
most complex type). Dw is \em not responsible for parsing HTML, or
|
||||||
|
decoding image data. Furthermore, the document tree, which is planned
|
||||||
|
for CSS, is neither a part of Dw, instead, it is a new module on top
|
||||||
|
of Dw.
|
||||||
|
|
||||||
|
The rendering, as done by Dw, is split into two phases:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li> the \em layouting, this means calculating the exact positions of
|
||||||
|
words, lines, etc. (in pixel position), and
|
||||||
|
<li> the \em drawing, i.e. making the result of the layouting visible
|
||||||
|
on the screen.
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
The result of the layouting allocates an area, which is called
|
||||||
|
\em canvas.
|
||||||
|
|
||||||
|
<h2>Structure</h2>
|
||||||
|
|
||||||
|
The whole Dw module can be split into the following parts:
|
||||||
|
|
||||||
|
\dot
|
||||||
|
digraph G {
|
||||||
|
node [shape=record, fontname=Helvetica, fontsize=10];
|
||||||
|
edge [arrowhead="open", fontname=Helvetica, fontsize=10,
|
||||||
|
labelfontname=Helvetica, labelfontsize=10,
|
||||||
|
color="#404040", labelfontcolor="#000080"];
|
||||||
|
|
||||||
|
subgraph cluster_core {
|
||||||
|
style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
|
||||||
|
label="Platform independent core";
|
||||||
|
|
||||||
|
Layout [URL="\ref dw::core::Layout"];
|
||||||
|
Platform [URL="\ref dw::core::Platform", color="#ff8080"];
|
||||||
|
View [URL="\ref dw::core::View", color="#ff8080"];
|
||||||
|
Widget [URL="\ref dw::core::Widget", color="#a0a0a0"];
|
||||||
|
}
|
||||||
|
|
||||||
|
subgraph cluster_fltk {
|
||||||
|
style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
|
||||||
|
label="FLTK specific part (as an\nexample for the platform specific\n\
|
||||||
|
implementations)";
|
||||||
|
|
||||||
|
subgraph cluster_fltkcore {
|
||||||
|
style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
|
||||||
|
label="FLTK core";
|
||||||
|
|
||||||
|
FltkPlatform [URL="\ref dw::fltk::FltkPlatform"];
|
||||||
|
FltkView [URL="\ref dw::fltk::FltkView", color="#ff8080"];
|
||||||
|
}
|
||||||
|
|
||||||
|
FltkViewport [URL="\ref dw::fltk::FltkViewport"];
|
||||||
|
FltkPreview [URL="\ref dw::fltk::FltkPreview"];
|
||||||
|
}
|
||||||
|
|
||||||
|
subgraph cluster_widgets {
|
||||||
|
style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
|
||||||
|
label="Platform independent widgets";
|
||||||
|
|
||||||
|
Textblock [URL="\ref dw::Textblock"];
|
||||||
|
AlignedTextblock [URL="\ref dw::AlignedTextblock", color="#a0a0a0"];
|
||||||
|
Table [URL="\ref dw::Table"];
|
||||||
|
Image [URL="\ref dw::Image"];
|
||||||
|
etc1 [label="..."];
|
||||||
|
etc2 [label="..."];
|
||||||
|
}
|
||||||
|
|
||||||
|
Layout -> Platform [headlabel="1", taillabel="1"];
|
||||||
|
Layout -> View [headlabel="*", taillabel="1"];
|
||||||
|
|
||||||
|
Layout -> Widget [headlabel="1", taillabel="1", label="topLevel"];
|
||||||
|
Widget -> Widget [headlabel="*", taillabel="1", label="children"];
|
||||||
|
|
||||||
|
Widget -> Textblock [arrowhead="none", arrowtail="empty", dir="both"];
|
||||||
|
Widget -> Table [arrowhead="none", arrowtail="empty", dir="both"];
|
||||||
|
Widget -> Image [arrowhead="none", arrowtail="empty", dir="both"];
|
||||||
|
Widget -> etc1 [arrowhead="none", arrowtail="empty", dir="both"];
|
||||||
|
Textblock -> AlignedTextblock [arrowhead="none", arrowtail="empty",
|
||||||
|
dir="both"];
|
||||||
|
AlignedTextblock -> etc2 [arrowhead="none", arrowtail="empty", dir="both"];
|
||||||
|
|
||||||
|
Platform -> FltkPlatform [arrowhead="none", arrowtail="empty", dir="both",
|
||||||
|
style="dashed"];
|
||||||
|
FltkPlatform -> FltkView [headlabel="*", taillabel="1"];
|
||||||
|
|
||||||
|
View -> FltkView [arrowhead="none", arrowtail="empty", dir="both"];
|
||||||
|
FltkView -> FltkViewport [arrowhead="none", arrowtail="empty", dir="both",
|
||||||
|
style="dashed"];
|
||||||
|
FltkView -> FltkPreview [arrowhead="none", arrowtail="empty", dir="both",
|
||||||
|
style="dashed"];
|
||||||
|
}
|
||||||
|
\enddot
|
||||||
|
|
||||||
|
<center>[\ref uml-legend "legend"]</center>
|
||||||
|
|
||||||
|
\em Platform means in most cases the underlying UI toolkit
|
||||||
|
(e.g. FLTK). A layout is bound to a specific platform, but multiple
|
||||||
|
platforms may be handled in one program.
|
||||||
|
|
||||||
|
A short overview:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li> dw::core::Layout is the central class, it manages the widgets and the
|
||||||
|
view, and provides delegation methods for the platform.
|
||||||
|
|
||||||
|
<li> The layouting is done by a tree of widgets (details are described in
|
||||||
|
\ref dw-layout-widgets), also the drawing, which is finally delegated
|
||||||
|
to the view.
|
||||||
|
|
||||||
|
<li> The view (implementation of dw::core::View) provides primitive methods
|
||||||
|
for drawing, but also have an influence on
|
||||||
|
the canvas size (via size hints). See \ref dw-layout-views for details.
|
||||||
|
|
||||||
|
<li> Some platform dependencies are handled by implementations
|
||||||
|
of dw::core::Platform.
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
<h3>Header Files</h3>
|
||||||
|
|
||||||
|
The structures mentioned above can be found in the following header
|
||||||
|
files:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li> Anything from the Dw core in core.hh. Do not include the single files.
|
||||||
|
|
||||||
|
<li> The single widgets can be found in the respective header files, e.g.
|
||||||
|
image.hh for dw::Image.
|
||||||
|
|
||||||
|
<li> The core of the FLTK implementation is defined in fltkcore.hh. This
|
||||||
|
includes dw::fltk::FltkPlatform, dw::fltk::FltkView, but not the concrete
|
||||||
|
view implementations.
|
||||||
|
|
||||||
|
<li> The views can be found in single header files, e.g fltkviewport.hh for
|
||||||
|
dw::fltk::FltkViewport.
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
<h2>Further Documentations</h2>
|
||||||
|
|
||||||
|
A complete map can be found at \ref dw-map.
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li> For learning, how to use Dw, read \ref dw-usage and related documents,
|
||||||
|
dw::core::style, dw::core::ui and \ref dw-images-and-backgrounds.
|
||||||
|
<li> Advanced topics are described in \ref dw-layout-widgets,
|
||||||
|
\ref dw-widget-sizes and \ref dw-layout-views.
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
*/
|
||||||
BIN
devdoc/dw-size-of-widget.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
35
devdoc/dw-size-request-pos-01.html
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<p style="margin-bottom: 5em"><i>This is the source of the image
|
||||||
|
<a href="dw-size-request-pos-01.png">dw-size-request-pos-01.png</a>.</i></p>
|
||||||
|
|
||||||
|
<table lang="en">
|
||||||
|
<td style="width: 220px">
|
||||||
|
<p>
|
||||||
|
<div style="float:right">
|
||||||
|
<img src="xhttps://www.gnu.org/graphics/heckert_gnu.small.png">
|
||||||
|
<p style="text-align: center"><i>A GNU</i></p>
|
||||||
|
</div>
|
||||||
|
Short.
|
||||||
|
</p>
|
||||||
|
<p>A longer paragraph, into which a float extends, which has been
|
||||||
|
defined in the previous paragraph. </p>
|
||||||
|
</td>
|
||||||
|
<td style="vertical-align: middle">
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<img src="http://upload.wikimedia.org/wikipedia/commons/8/80/CorrespMtl5.png" style="width: 80px"> </td>
|
||||||
|
<td style="width: 220px">
|
||||||
|
<p>
|
||||||
|
<div style="float:right">
|
||||||
|
<img src="https://www.gnu.org/graphics/heckert_gnu.small.png">
|
||||||
|
<p style="text-align: center"><i>A GNU</i></p>
|
||||||
|
</div>
|
||||||
|
Short.
|
||||||
|
</p>
|
||||||
|
<p>A longer paragraph, into which a float extends, which has been
|
||||||
|
defined in the previous paragraph. </p>
|
||||||
|
</td>
|
||||||
|
</table>
|
||||||
BIN
devdoc/dw-size-request-pos-01.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
172
devdoc/dw-size-request-pos.doc
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
/** \page dw-size-request-pos Size requisitions depending on positions
|
||||||
|
|
||||||
|
<div style="border: 2px solid #ffff00; margin: 1em 0;
|
||||||
|
padding: 0.5em 1em; background-color: #ffffe0">The complex "widget
|
||||||
|
sizes" is currently divided into three documents: \ref
|
||||||
|
dw-widget-sizes, \ref dw-grows, and **Size requisitions depending on
|
||||||
|
positions** (this document). Furthermore, there are some notes in
|
||||||
|
\ref dw-miscellaneous.</div>
|
||||||
|
|
||||||
|
|
||||||
|
Motivation
|
||||||
|
==========
|
||||||
|
|
||||||
|
As described in \ref dw-out-of-flow (*The sizeRequest/sizeAllocate
|
||||||
|
problem*), the principle that the size of a widget depends only on the
|
||||||
|
sizes of its children causes some problems with floats; the current
|
||||||
|
solution is a frequent correction by calling
|
||||||
|
dw::core::Widget::queueResize. In this document, an alternative
|
||||||
|
approach is presented.
|
||||||
|
|
||||||
|
<div style="border: 2px solid #ffff00; margin: 1em 0; padding: 0.5em 1em;
|
||||||
|
background-color: #ffffe0">This approach works very well for floats, but not
|
||||||
|
for positioned elements, which means that calling
|
||||||
|
dw::core::Widget::queueResize is still needed for the latter. On the other
|
||||||
|
hand, dw::oof::OOFFloatsMgr (which is much more complex than
|
||||||
|
dw::oof::OOFPositionedMgr) can be simplified quite much.</div>
|
||||||
|
|
||||||
|
|
||||||
|
General Idea
|
||||||
|
============
|
||||||
|
|
||||||
|
A widget size may depend on the position relative to an ancestor
|
||||||
|
widget. If a widget wants to get the size of a child widget, it should:
|
||||||
|
|
||||||
|
1. call the new methods dw::core::Widget::numSizeRequestReferences and
|
||||||
|
dw::core::Widget::sizeRequestReference, which return all widgets
|
||||||
|
relative to which the child's position must be calculated;
|
||||||
|
2. call dw::core::Widget::sizeRequest with the positions relative to
|
||||||
|
these widgets.
|
||||||
|
|
||||||
|
<div style="border: 2px solid #ffff00; margin: 1em 0;
|
||||||
|
padding: 0.5em 1em; background-color: #ffffe0">It is not sufficient
|
||||||
|
to work with *absolute* positions, since there may be an
|
||||||
|
interruption passing the positions so that absolute positions are
|
||||||
|
often not known.</div>
|
||||||
|
|
||||||
|
All positions passed to dw::core::Widget::sizeRequest must constitute
|
||||||
|
the position at which this child will be allocated.
|
||||||
|
|
||||||
|
There are situations where the parent widget is unable to determine
|
||||||
|
these positions before the size is known. An example: a textblock
|
||||||
|
widget cannot determine the positions of an inline widget (like an
|
||||||
|
image, or an inline block) before the line is finished; on the other
|
||||||
|
hand, finishing the line depends on knowing the sizes of the inline
|
||||||
|
widgets.
|
||||||
|
|
||||||
|
This may result in a conflict, when the size of an inline widget
|
||||||
|
depends on positions. Generally, the only widget whose size depends on
|
||||||
|
positions is dw::Textblock (the size will depend on the positions
|
||||||
|
within its oof container, see \ref dw-out-of-flow), so this conflict
|
||||||
|
occurs with inline blocks.
|
||||||
|
|
||||||
|
This conflict is handled in different ways:
|
||||||
|
|
||||||
|
1. Fortunately, this case is irrelevat for floats: an inline block
|
||||||
|
constitutes its own floats container, so that there is no dependence
|
||||||
|
on a position within another widget.
|
||||||
|
|
||||||
|
2. For positioned elements, this case is relevant, since an inline
|
||||||
|
block is in most cases not a container for positioned elements. In
|
||||||
|
this case, a generator will call the methods
|
||||||
|
dw::oof::OutOfFlowMgr::tellIncompletePosition1 and
|
||||||
|
dw::oof::OutOfFlowMgr::tellIncompletePosition2, instead of
|
||||||
|
dw::oof::OutOfFlowMgr::tellPosition and
|
||||||
|
dw::oof::OutOfFlowMgr::tellPosition2, respectively. (Since this
|
||||||
|
case is irrelevant for floats,
|
||||||
|
dw::oof::OOFFloatsMgr::tellIncompletePosition1 and
|
||||||
|
dw::oof::OOFFloatsMgr::tellIncompletePosition2 are not implemented but
|
||||||
|
simply abort.)
|
||||||
|
|
||||||
|
(This is not (yet) considered for borders: borders are only relevant
|
||||||
|
for floats, but conflicts do not occur for floats.)
|
||||||
|
|
||||||
|
|
||||||
|
Extremes
|
||||||
|
--------
|
||||||
|
Extremes may depend on the position in an analogue way, see:
|
||||||
|
|
||||||
|
- dw::core::Widget::numGetExtremesReferences,
|
||||||
|
- dw::core::Widget::getExtremesReference, and
|
||||||
|
- dw::core::Widget::getExtremes.
|
||||||
|
|
||||||
|
Resizing
|
||||||
|
--------
|
||||||
|
Currently, the size of a widget has to be recalculated when:
|
||||||
|
|
||||||
|
1. it has called dw::core::Widget::queueResize, or
|
||||||
|
2. the size of a child widget has to be recalculated.
|
||||||
|
|
||||||
|
Since for this new approach, the size does not only depend on the size of the
|
||||||
|
children, the second condition must be modified. See beginning of
|
||||||
|
dw::core::Widget::sizeRequest.
|
||||||
|
|
||||||
|
An implementation may have to consider, this too, especially when implementing
|
||||||
|
incremental resizing (see \ref dw-widget-sizes); see
|
||||||
|
dw::Textblock::sizeRequestImpl as an example.
|
||||||
|
|
||||||
|
Regarding changes of the position is not sufficient. Consider this example,
|
||||||
|
where a float size changes as soon as the image is loaded:
|
||||||
|
|
||||||
|
\image html dw-size-request-pos-01.png
|
||||||
|
|
||||||
|
The second paragraph ("A longer paragraph" ...) stays at the same position, both
|
||||||
|
absolute and relative to the float container, but has to be rewrapped because of
|
||||||
|
the float. Instead, this is handled by dw::oof::OutOfFlowMgr::markSizeChange
|
||||||
|
(and likewise dw::oof::OutOfFlowMgr::markExtremesChange), which is called by the
|
||||||
|
implementation of `markSizeChange` (or `markExtremesChange`, respectively) of
|
||||||
|
the OOF container. (See also the end of the comment of dw::oof::OOFAwareWidget.)
|
||||||
|
|
||||||
|
|
||||||
|
Plan
|
||||||
|
====
|
||||||
|
|
||||||
|
1. General design (dw::core::Widget::sizeRequestReference, changes to
|
||||||
|
dw::core::Widget::sizeRequest). Completed.
|
||||||
|
|
||||||
|
2. Implementation for dw::Textblock. Completed.
|
||||||
|
|
||||||
|
3. Change interface of dw::oof::OutOfFlowMgr (this affects mostly only
|
||||||
|
comments). Completed.
|
||||||
|
|
||||||
|
Affects methods dw::oof::OutOfFlowMgr::tellPosition1,
|
||||||
|
dw::oof::OutOfFlowMgr::tellPosition2,
|
||||||
|
dw::oof::OutOfFlowMgr::getLeftBorder,
|
||||||
|
dw::oof::OutOfFlowMgr::getRightBorder,
|
||||||
|
dw::oof::OutOfFlowMgr::hasFloatLeft,
|
||||||
|
dw::oof::OutOfFlowMgr::hasFloatRight,
|
||||||
|
dw::oof::OutOfFlowMgr::getLeftFloatHeight, and
|
||||||
|
dw::oof::OutOfFlowMgr::getRightFloatHeight.
|
||||||
|
|
||||||
|
4. Apply step 3 to calls within dw::Textblock. Completed.
|
||||||
|
|
||||||
|
<b>Attention:</b> After this step, and before completing the next steps, the
|
||||||
|
code is inconsistent and so incorrect.
|
||||||
|
|
||||||
|
5. Implement step 3 for floats (affects dw::oof::OOFFloatsMgr). **MOSTLY
|
||||||
|
COMPLETED.**
|
||||||
|
|
||||||
|
6. Implement step 3 for positioned elements (affects only
|
||||||
|
dw::oof::OOFPositionedMgr). **INCOMPLETE.** (But positioned elements are
|
||||||
|
currently deactivated.)
|
||||||
|
|
||||||
|
|
||||||
|
Issues
|
||||||
|
======
|
||||||
|
|
||||||
|
- Since the signature of dw::core::Widget::sizeRequestImpl changes quite often
|
||||||
|
during the development of *size requisitions depending on positions*, a
|
||||||
|
simpler option dw::core::Widget::sizeRequestSimpl has been added. May be
|
||||||
|
removed again, after the design is stable.
|
||||||
|
|
||||||
|
- As an alternative, passing the references may be done in a new method, which
|
||||||
|
is called *before* dw::core::Widget::sizeRequestImpl. This makes even more
|
||||||
|
sense, after dw::core::Widget::calcExtraSpace and
|
||||||
|
dw::core::Widget::calcExtraSpaceImpl have been extended by references.
|
||||||
|
|
||||||
|
- There may be inconsistencies for widget styles; see
|
||||||
|
[revision f797436687fe](http://flpsed.org/hgweb/dillo_grows/rev/f797436687fe)
|
||||||
|
as an example for a fix. Perhaps a different approach, where breaks are added,
|
||||||
|
_if `display` has the value `block`_ (or analogue), will work better.
|
||||||
|
|
||||||
|
*/
|
||||||
114
devdoc/dw-stacking-context.doc
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
/** \page dw-stacking-context Handling stacking contexts
|
||||||
|
|
||||||
|
Stacking Context and dw::core::StackingContextMgr
|
||||||
|
=================================================
|
||||||
|
|
||||||
|
For the definition of stacking contexts, see CSS 2.1 specification,
|
||||||
|
|
||||||
|
- <a href="http://www.w3.org/TR/CSS2/visuren.html#z-index">section
|
||||||
|
9.9.1: Specifying the stack level: the 'z-index' property</a> and
|
||||||
|
- <a href="http://www.w3.org/TR/CSS2/zindex.html">appendix E</a>.
|
||||||
|
|
||||||
|
A widget establishes a stacking context when it is positioned and its
|
||||||
|
style value of *z-index* is different from *auto* (see
|
||||||
|
dw::core::StackingContextMgr::isEstablishingStackingContext). In this
|
||||||
|
case, it is assigned an instance of dw::core::StackingContextMgr,
|
||||||
|
which has also access to the widgets establishing the child contexts.
|
||||||
|
|
||||||
|
|
||||||
|
Stacking Order
|
||||||
|
==============
|
||||||
|
|
||||||
|
The stacking order influences
|
||||||
|
|
||||||
|
1. the order in which child widgets are drawn (dw::core::Widget::draw),
|
||||||
|
and
|
||||||
|
2. the order in which mouse events are dispatched to child widgets
|
||||||
|
(dw::core::Widget::getWidgetAtPoint).
|
||||||
|
|
||||||
|
The first is done from bottom to top, the latter from top to bottom.
|
||||||
|
|
||||||
|
I'm here referring to the simplified description in
|
||||||
|
<a href="http://www.w3.org/TR/CSS2/visuren.html#z-index">section
|
||||||
|
9.9.1</a>. The table shows a recommended order for the implementations
|
||||||
|
of dw::core::Widget::draw and dw::core::Widget::getWidgetAtPoint
|
||||||
|
(for the latter, read from bottom to top):
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th> CSS specification <th> Drawing <th> Mouse events
|
||||||
|
<tr>
|
||||||
|
<td> *1. the background and borders of the element forming the
|
||||||
|
stacking context.*
|
||||||
|
<td> dw::core::Widget::drawBox
|
||||||
|
<td> Nothing necessary.
|
||||||
|
<tr>
|
||||||
|
<td> *2. the child stacking contexts with negative stack levels (most
|
||||||
|
negative first).*
|
||||||
|
<td> dw::core::StackingContextMgr::drawBottom (when defined)
|
||||||
|
<td> dw::core::StackingContextMgr::getBottomWidgetAtPoint (when defined)
|
||||||
|
<tr>
|
||||||
|
<td> *3. the in-flow, non-inline-level, non-positioned descendants.*
|
||||||
|
|
||||||
|
<td rowspan="4"> When (i) widget specific content is drawn, then (ii)
|
||||||
|
dw::oof::OOFAwareWidget::drawOOF is called, this will
|
||||||
|
have this effect:
|
||||||
|
|
||||||
|
1. all in-flow elements are drawn,
|
||||||
|
2. floats are drawn and
|
||||||
|
3. positioned elements with *z-index: auto* are drawn
|
||||||
|
(latter two done by
|
||||||
|
dw::oof::OOFAwareWidget::drawOOF, in this order).
|
||||||
|
|
||||||
|
This order differs from the specified order, but
|
||||||
|
since floats and in-flow elements do not overlap,
|
||||||
|
this difference has no effect.
|
||||||
|
|
||||||
|
Drawing in-line elements, floats and positioned
|
||||||
|
elements with *z-index: auto* and should avoid
|
||||||
|
duplicate calls: Widgets drawn by
|
||||||
|
dw::core::StackingContextMgr::drawBottom and by
|
||||||
|
dw::core::StackingContextMgr::drawTop should be
|
||||||
|
excluded here. This can be tested with
|
||||||
|
dw::core::StackingContextMgr::handledByStackingContextMgr.
|
||||||
|
|
||||||
|
<td rowspan="4"> Likewise, the implementation should (i) test
|
||||||
|
dw::oof::OOFAwareWidget::getWidgetOOFAtPoint, and
|
||||||
|
(ii) search through the children. Also, duplicate
|
||||||
|
calls should be avoided using
|
||||||
|
dw::core::StackingContextMgr::handledByStackingContextMgr.
|
||||||
|
|
||||||
|
There are already the implementations
|
||||||
|
dw::core::Widget::getWidgetAtPoint (ignoring
|
||||||
|
dw::oof::OutOfFlowMgr) and
|
||||||
|
dw::oof::OOFAwareWidget::getWidgetAtPoint (including
|
||||||
|
dw::oof::OutOfFlowMgr).
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td> *4. the non-positioned floats.*
|
||||||
|
<tr>
|
||||||
|
<td> *5. the in-flow, inline-level, non-positioned descendants,
|
||||||
|
including inline tables and inline blocks.*
|
||||||
|
<tr>
|
||||||
|
<td> (What about positioned elements with *z-index: auto*? Seems to be
|
||||||
|
missing in
|
||||||
|
<a href="http://www.w3.org/TR/CSS2/visuren.html#z-index">section
|
||||||
|
9.9.1</a>, but mentioned in
|
||||||
|
<a href="http://www.w3.org/TR/CSS2/zindex.html">appendix E</a>,
|
||||||
|
item 8.
|
||||||
|
<tr>
|
||||||
|
<td> *6. the child stacking contexts with stack level 0 and the
|
||||||
|
positioned descendants with stack level 0.*
|
||||||
|
<td rowspan="2"> dw::core::StackingContextMgr::drawTop (when defined)
|
||||||
|
<td rowspan="2"> dw::core::StackingContextMgr::getTopWidgetAtPoint
|
||||||
|
(when defined)
|
||||||
|
<tr>
|
||||||
|
<td> *7. the child stacking contexts with positive stack levels (least
|
||||||
|
positive first).*
|
||||||
|
</table>
|
||||||
|
|
||||||
|
Note: This is not quite in conformance with the specification: this
|
||||||
|
description refers to any widget, not only widgets establishing a
|
||||||
|
stacking context. Does this make a difference?
|
||||||
|
|
||||||
|
*/
|
||||||
BIN
devdoc/dw-style-box-model.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
devdoc/dw-style-length-absolute.png
Normal file
|
After Width: | Height: | Size: 575 B |
BIN
devdoc/dw-style-length-percentage.png
Normal file
|
After Width: | Height: | Size: 890 B |
BIN
devdoc/dw-style-length-relative.png
Normal file
|
After Width: | Height: | Size: 868 B |
BIN
devdoc/dw-textblock-collapsing-spaces-1-1.png
Normal file
|
After Width: | Height: | Size: 641 B |
BIN
devdoc/dw-textblock-collapsing-spaces-1-2.png
Normal file
|
After Width: | Height: | Size: 521 B |
BIN
devdoc/dw-textblock-collapsing-spaces-2-1.png
Normal file
|
After Width: | Height: | Size: 802 B |
BIN
devdoc/dw-textblock-collapsing-spaces-2-2.png
Normal file
|
After Width: | Height: | Size: 586 B |
375
devdoc/dw-usage.doc
Normal file
@ -0,0 +1,375 @@
|
|||||||
|
/** \page dw-usage Dillo Widget Usage
|
||||||
|
|
||||||
|
This document describes the usage of Dw, without going too much into
|
||||||
|
detail.
|
||||||
|
|
||||||
|
|
||||||
|
<h2>Getting Started</h2>
|
||||||
|
|
||||||
|
In this section, a small runnable example is described, based on the
|
||||||
|
FLTK implementation.
|
||||||
|
|
||||||
|
As described in \ref dw-overview, the following objects are needed:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li> dw::core::Layout,
|
||||||
|
<li> an implementation of dw::core::Platform (we will use
|
||||||
|
dw::fltk::FltkPlatform),
|
||||||
|
<li> at least one implementation of dw::core::View (dw::fltk::FltkViewport),
|
||||||
|
and
|
||||||
|
<li> some widgets (for this example, only a simple dw::Textblock).
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
First of all, the necessary \#include's:
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include <FL/Fl_Window.H>
|
||||||
|
#include <FL/Fl.H>
|
||||||
|
|
||||||
|
#include "dw/core.hh"
|
||||||
|
#include "dw/fltkcore.hh"
|
||||||
|
#include "dw/fltkviewport.hh"
|
||||||
|
#include "dw/textblock.hh"
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
Everything is put into one function:
|
||||||
|
|
||||||
|
\code
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
As the first object, the platform is instantiated:
|
||||||
|
|
||||||
|
\code
|
||||||
|
dw::fltk::FltkPlatform *platform = new dw::fltk::FltkPlatform ();
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
Then, the layout is created, with the platform attached:
|
||||||
|
|
||||||
|
\code
|
||||||
|
dw::core::Layout *layout = new dw::core::Layout (platform);
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
For the view, we first need a FLTK window:
|
||||||
|
|
||||||
|
\code
|
||||||
|
Fl_Window *window = new Fl_Window(200, 300, "Dw Example");
|
||||||
|
window->begin();
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
After this, we can create a viewport, and attach it to the layout:
|
||||||
|
|
||||||
|
\code
|
||||||
|
dw::fltk::FltkViewport *viewport =
|
||||||
|
new dw::fltk::FltkViewport (0, 0, 200, 300);
|
||||||
|
layout->attachView (viewport);
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
Each widget needs a style (dw::core::style::Style, see dw::core::style),
|
||||||
|
so we construct it here. For this, we need to fill a
|
||||||
|
dw::core::style::StyleAttrs structure with values, and call
|
||||||
|
dw::core::style::Style::create (latter is done further below):
|
||||||
|
|
||||||
|
\code
|
||||||
|
dw::core::style::StyleAttrs styleAttrs;
|
||||||
|
styleAttrs.initValues ();
|
||||||
|
styleAttrs.margin.setVal (5);
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
dw::core::style::StyleAttrs::initValues sets several default
|
||||||
|
values. The last line sets a margin of 5 pixels. Next, we need a
|
||||||
|
font. Fonts are created in a similar way, first, the attributes are
|
||||||
|
defined:
|
||||||
|
|
||||||
|
\code
|
||||||
|
dw::core::style::FontAttrs fontAttrs;
|
||||||
|
fontAttrs.name = "Bitstream Charter";
|
||||||
|
fontAttrs.size = 14;
|
||||||
|
fontAttrs.weight = 400;
|
||||||
|
fontAttrs.style = dw::core::style::FONT_STYLE_NORMAL;
|
||||||
|
fontAttrs.letterSpacing = 0;
|
||||||
|
fontAttrs.fontVariant = dw::core::style::FONT_VARIANT_NORMAL;
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
Now, the font can be created:
|
||||||
|
|
||||||
|
\code
|
||||||
|
styleAttrs.font = dw::core::style::Font::create (layout, &fontAttrs);
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
As the last attributes, the background and foreground colors are
|
||||||
|
defined, here dw::core::style::Color::createSimple must be called:
|
||||||
|
|
||||||
|
\code
|
||||||
|
styleAttrs.color =
|
||||||
|
dw::core::style::Color::create (layout, 0x000000);
|
||||||
|
styleAttrs.backgroundColor =
|
||||||
|
dw::core::style::Color::create (layout, 0xffffff);
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
Finally, the style for the widget is created:
|
||||||
|
|
||||||
|
\code
|
||||||
|
dw::core::style::Style *widgetStyle =
|
||||||
|
dw::core::style::Style::create (layout, &styleAttrs);
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
Now, we create a widget, assign a style to it, and set it as the
|
||||||
|
toplevel widget of the layout:
|
||||||
|
|
||||||
|
\code
|
||||||
|
dw::Textblock *textblock = new dw::Textblock (false);
|
||||||
|
textblock->setStyle (widgetStyle);
|
||||||
|
layout->setWidget (textblock);
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
The style is not needed anymore (a reference is added in
|
||||||
|
dw::core::Widget::setStyle), so it should be unreferred:
|
||||||
|
|
||||||
|
\code
|
||||||
|
widgetStyle->unref();
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
Now, some text should be added to the textblock. For this, we first
|
||||||
|
need another style. \em styleAttrs can still be used for this. We set
|
||||||
|
the margin to 0, and the background color to "transparent":
|
||||||
|
|
||||||
|
\code
|
||||||
|
styleAttrs.margin.setVal (0);
|
||||||
|
styleAttrs.backgroundColor = NULL;
|
||||||
|
|
||||||
|
dw::core::style::Style *wordStyle =
|
||||||
|
dw::core::style::Style::create (layout, &styleAttrs);
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
This loop adds some paragraphs:
|
||||||
|
|
||||||
|
\code
|
||||||
|
for(int i = 1; i <= 10; i++) {
|
||||||
|
char buf[4];
|
||||||
|
sprintf(buf, "%d.", i);
|
||||||
|
|
||||||
|
char *words[] = { "This", "is", "the", buf, "paragraph.",
|
||||||
|
"Here", "comes", "some", "more", "text",
|
||||||
|
"to", "demonstrate", "word", "wrapping.",
|
||||||
|
NULL };
|
||||||
|
|
||||||
|
for(int j = 0; words[j]; j++) {
|
||||||
|
textblock->addText(strdup(words[j]), wordStyle);
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
Notice the \em strdup, dw::Textblock::addText will feel responsible
|
||||||
|
for the string, and free the text at the end. (This has been done to
|
||||||
|
avoid some overhead in the HTML parser.)
|
||||||
|
|
||||||
|
The rest is simple, it also includes spaces (which also have styles):
|
||||||
|
|
||||||
|
\code
|
||||||
|
textblock->addSpace(wordStyle);
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
Finally, a paragraph break is added, which is 10 pixels high:
|
||||||
|
|
||||||
|
\code
|
||||||
|
textblock->addParbreak(10, wordStyle);
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
Again, this style should be unreferred:
|
||||||
|
|
||||||
|
\code
|
||||||
|
wordStyle->unref();
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
After adding text, this method should always be called (for faster
|
||||||
|
adding large text blocks):
|
||||||
|
|
||||||
|
\code
|
||||||
|
textblock->flush ();
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
Some FLTK stuff to finally show the window:
|
||||||
|
|
||||||
|
\code
|
||||||
|
window->resizable(viewport);
|
||||||
|
window->show();
|
||||||
|
int errorCode = Fl::run();
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
For cleaning up, it is sufficient to destroy the layout:
|
||||||
|
|
||||||
|
\code
|
||||||
|
delete layout;
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
And the rest
|
||||||
|
|
||||||
|
\code
|
||||||
|
return errorCode;
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
If you compile and start the program, you should see the following:
|
||||||
|
|
||||||
|
\image html dw-example-screenshot.png
|
||||||
|
|
||||||
|
Try to scroll, or to resize the window, you will see, that everything
|
||||||
|
is done automatically.
|
||||||
|
|
||||||
|
Of course, creating new widgets, adding text to widgets etc. can also
|
||||||
|
be done while the program is running, i.e. after fltk::run has been
|
||||||
|
called, within timeouts, idles, I/O functions etc. Notice that Dw is
|
||||||
|
not thread safe, so that everything should be done within one thread.
|
||||||
|
|
||||||
|
With the exception, that you have to call dw::Textblock::flush,
|
||||||
|
everything gets immediately visible, within reasonable times; Dw has
|
||||||
|
been optimized for frequent updates.
|
||||||
|
|
||||||
|
|
||||||
|
<h2>List of all Widgets</h2>
|
||||||
|
|
||||||
|
These widgets are used within dillo:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>dw::core::ui::Embed
|
||||||
|
<li>dw::AlignedTextblock
|
||||||
|
<li>dw::Bullet
|
||||||
|
<li>dw::Ruler
|
||||||
|
<li>dw::Image
|
||||||
|
<li>dw::ListItem
|
||||||
|
<li>dw::Table
|
||||||
|
<li>dw::TableCell
|
||||||
|
<li>dw::Textblock
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
If you want to create a new widget, refer to \ref dw-layout-widgets.
|
||||||
|
|
||||||
|
|
||||||
|
<h2>List of Views</h2>
|
||||||
|
|
||||||
|
There are three dw::core::View implementations for FLTK:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li> dw::fltk::FltkViewport implements a viewport, which is used in the
|
||||||
|
example above.
|
||||||
|
|
||||||
|
<li> dw::fltk::FltkPreview implements a preview window, together with
|
||||||
|
dw::fltk::FltkPreviewButton, it is possible to have a scaled down
|
||||||
|
overview of the whole canvas.
|
||||||
|
|
||||||
|
<li> dw::fltk::FltkFlatView is a "flat" view, i.e. it does not support
|
||||||
|
scrolling. It is used for HTML buttons, see
|
||||||
|
dw::fltk::ui::FltkComplexButtonResource and especially
|
||||||
|
dw::fltk::ui::FltkComplexButtonResource::createNewWidget for details.
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
More information about views in general can be found in \ref
|
||||||
|
dw-layout-views.
|
||||||
|
|
||||||
|
|
||||||
|
<h2>Iterators</h2>
|
||||||
|
|
||||||
|
For examining generally the contents of widgets, there are iterators
|
||||||
|
(dw::core::Iterator), created by the method
|
||||||
|
dw::core::Widget::iterator (see there for more details).
|
||||||
|
|
||||||
|
These simple iterators only iterate through one widget, and return
|
||||||
|
child widgets as dw::core::Content::WIDGET. The next call of
|
||||||
|
dw::core::Iterator::next will return the piece of contents \em after
|
||||||
|
(not within) this child widget.
|
||||||
|
|
||||||
|
If you want to iterate through the whole widget trees, there are two
|
||||||
|
possibilities:
|
||||||
|
|
||||||
|
<ol>
|
||||||
|
<li> Use a recursive function. Of course, with this approach, you are
|
||||||
|
limited by the program flow.
|
||||||
|
|
||||||
|
<li> Maintain a stack of iterators, so you can freely pass this stack
|
||||||
|
around. This is already implemented, as dw::core::DeepIterator.
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
As an example, dw::core::SelectionState represents the selected region
|
||||||
|
as two instances of dw::core::DeepIterator.
|
||||||
|
|
||||||
|
|
||||||
|
<h2>Finding Text</h2>
|
||||||
|
|
||||||
|
See dw::core::Layout::findtextState and dw::core::FindtextState
|
||||||
|
(details in the latter). There are delegation methods:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li> dw::core::Layout::search and
|
||||||
|
<li> dw::core::Layout::resetSearch.
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
<h2>Anchors and Scrolling</h2>
|
||||||
|
|
||||||
|
In some cases, it is necessary to scroll to a given position, or to
|
||||||
|
an anchor, programmatically.
|
||||||
|
|
||||||
|
<h3>Anchors</h3>
|
||||||
|
|
||||||
|
Anchors are defined by widgets, e.g. dw::Textblock defines them, when
|
||||||
|
dw::Textblock::addAnchor is called. To jump to a specific anchor
|
||||||
|
within the current widget tree, use dw::core::Layout::setAnchor.
|
||||||
|
|
||||||
|
This can be done immediately after assignig a toplevel widget, even
|
||||||
|
when the anchor has not yet been defined. The layout will remember the
|
||||||
|
anchor, and jump to the respective position, as soon as possible. Even
|
||||||
|
if the anchor position changes (e.g., when an anchor is moved
|
||||||
|
downwards, since some space is needed for an image in the text above),
|
||||||
|
the position is corrected.
|
||||||
|
|
||||||
|
As soon as the user scrolls the viewport, this correction is not done
|
||||||
|
anymore. If in dillo, the user request a page with an anchor, which is
|
||||||
|
quite at the bottom of the page, he may be get interested in the text
|
||||||
|
at the beginning of the page, and so scrolling down. If then, after
|
||||||
|
the anchor has been read and added to the dw::Textblock, this anchor
|
||||||
|
would be jumped at, the user would become confused.
|
||||||
|
|
||||||
|
The anchor is dismissed, too, when the toplevel widget is removed
|
||||||
|
again.
|
||||||
|
|
||||||
|
\todo Currently, anchors only define vertical positions.
|
||||||
|
|
||||||
|
<h3>Scrolling</h3>
|
||||||
|
|
||||||
|
To scroll to a given position, use the method
|
||||||
|
dw::core::Layout::scrollTo. It expects several parameters:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>a horizontal adjustment parameter, defined by dw::core::HPosition,
|
||||||
|
<li>a vertical adjustment parameter, defined by dw::core::VPosition, and
|
||||||
|
<li>a rectangle (\em x, \em y, \em width and \em height) of the region
|
||||||
|
to be adjusted.
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
If you just want to move the canvas coordinate (\em x, \em y) into the
|
||||||
|
upper left corner of the viewport, you can call:
|
||||||
|
|
||||||
|
\code
|
||||||
|
dw::core::Layout *layout;
|
||||||
|
// ...
|
||||||
|
layout->scrollTo(dw::core::HPOS_LEFT, dw::core::VPOS_TOP, 0, 0, 0, 0);
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
By using dw::core::HPOS_NO_CHANGE or dw::core::VPOS_NO_CHANGE, you can
|
||||||
|
change only one dimension. dw::core::HPOS_INTO_VIEW and
|
||||||
|
dw::core::VPOS_INTO_VIEW will cause the viewport to move as much as
|
||||||
|
necessary, that the region is visible in the viewport (this is
|
||||||
|
e.g. used for finding text).
|
||||||
|
|
||||||
|
|
||||||
|
<h2>Further Documentations</h2>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li> dw::core::style
|
||||||
|
<li> dw::core::ui
|
||||||
|
<li> \ref dw-images-and-backgrounds
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
*/
|
||||||
BIN
devdoc/dw-viewport-with-scrollbar.png
Normal file
|
After Width: | Height: | Size: 755 B |
BIN
devdoc/dw-viewport-without-scrollbar.png
Normal file
|
After Width: | Height: | Size: 542 B |
278
devdoc/dw-widget-sizes.doc
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
/** \page dw-widget-sizes Sizes of Dillo Widgets
|
||||||
|
|
||||||
|
<div style="border: 2px solid #ffff00; margin: 1em 0;
|
||||||
|
padding: 0.5em 1em; background-color: #ffffe0">The complex "widget
|
||||||
|
sizes" is currently divided into three documents: **Sizes of Dillo
|
||||||
|
Widgets** (this document), \ref dw-grows, and \ref
|
||||||
|
dw-size-request-pos. Furthermore, there are some notes in
|
||||||
|
\ref dw-miscellaneous.</div>
|
||||||
|
|
||||||
|
<div style="border: 2px solid #ff4040; margin: 1em 0;
|
||||||
|
padding: 0.5em 1em; background-color: #fff0f0"><b>Info:</b>
|
||||||
|
Not up to date, see other documents.</div>
|
||||||
|
|
||||||
|
Allocation
|
||||||
|
==========
|
||||||
|
|
||||||
|
Each widget has an \em allocation at a given time, this includes
|
||||||
|
|
||||||
|
- the position (\em x, \em y) relative to the upper left corner of the
|
||||||
|
canvas, and
|
||||||
|
- the size (\em width, \em ascent, \em descent).
|
||||||
|
|
||||||
|
The \em canvas is the whole area available for the widgets, in most
|
||||||
|
cases, only a part is seen in a viewport. The allocation of the
|
||||||
|
toplevel widget is exactly the allocation of the canvas, i.e.
|
||||||
|
|
||||||
|
- the position of the toplevel widget is always (0, 0), and
|
||||||
|
- the canvas size is defined by the size of the toplevel widget.
|
||||||
|
|
||||||
|
The size of a widget is not simply defined by the width and the
|
||||||
|
height, instead, widgets may have a base line, and so are vertically
|
||||||
|
divided into an ascender (which height is called \em ascent), and a
|
||||||
|
descender (which height is called \em descent). The total height is so
|
||||||
|
the sum of \em ascent and \em descent.
|
||||||
|
|
||||||
|
Sizes of zero are allowed. The upper limit for the size of a widget is
|
||||||
|
defined by the limits of the C++ type \em int.
|
||||||
|
|
||||||
|
\image html dw-size-of-widget.png Allocation of a Widget
|
||||||
|
|
||||||
|
In the example in the image, the widget has the following allocation:
|
||||||
|
|
||||||
|
- \em x = 50
|
||||||
|
- \em y = 50
|
||||||
|
- \em width = 150
|
||||||
|
- \em ascent = 150
|
||||||
|
- \em descent = 100
|
||||||
|
|
||||||
|
The current allocation of a widget is hold in
|
||||||
|
dw::core::Widget::allocation. It can be set from outside by
|
||||||
|
calling dw::core::Widget::sizeAllocate. This is a concrete method,
|
||||||
|
which will call dw::core::Widget::sizeAllocateImpl (see code of
|
||||||
|
dw::core::Widget::sizeAllocate for details).
|
||||||
|
|
||||||
|
For trivial widgets (like dw::Bullet),
|
||||||
|
dw::core::Widget::sizeAllocateImpl does not need to be
|
||||||
|
implemented. For more complex widgets, the implementation should call
|
||||||
|
dw::core::Widget::sizeAllocate (not
|
||||||
|
dw::core::Widget::sizeAllocateImpl) on all child widgets, with
|
||||||
|
appropriate child allocations. dw::core::Widget::allocation should not
|
||||||
|
be changed here, this is already done in
|
||||||
|
dw::core::Widget::sizeAllocate.
|
||||||
|
|
||||||
|
|
||||||
|
Requisitions
|
||||||
|
============
|
||||||
|
|
||||||
|
A widget may prefer a given size for the allocation. This size, the
|
||||||
|
\em requisition, should be returned by the method
|
||||||
|
dw::core::Widget::sizeRequestImpl. In the simplest case, this is
|
||||||
|
independent of the context, e.g. for an
|
||||||
|
image. dw::Image::sizeRequestImpl returns the following size:
|
||||||
|
|
||||||
|
- If no buffer has yet been assigned (see dw::Image for more details),
|
||||||
|
the size necessary for the alternative text is returned. If no
|
||||||
|
alternative text has been set, zero is returned.
|
||||||
|
|
||||||
|
- If a buffer has been assigned (by dw::Image::setBuffer), the root
|
||||||
|
size is returned (i.e. the original size of the image to display).
|
||||||
|
|
||||||
|
This is a bit simplified, dw::Image::sizeRequestImpl should also deal
|
||||||
|
with margins, borders and paddings, see dw::core::style.
|
||||||
|
|
||||||
|
From the outside, dw::Image::sizeRequest should be called, which does
|
||||||
|
a bit of optimization. Notice that in dw::Image::sizeRequestImpl, no
|
||||||
|
optimization like lazy evaluation is necessary, this is already done
|
||||||
|
in dw::Image::sizeRequest.
|
||||||
|
|
||||||
|
A widget, which has children, will likely call dw::Image::sizeRequest
|
||||||
|
on its children, to calculate the total requisition.
|
||||||
|
|
||||||
|
The caller (this is either the dw::core::Layout, or the parent
|
||||||
|
widget), may, but also may not consider the requisition. Instead, a
|
||||||
|
widget must deal with any allocation. (For example, dw::Image scales
|
||||||
|
the image buffer when allocated at another size.)
|
||||||
|
|
||||||
|
|
||||||
|
Size Hints
|
||||||
|
==========
|
||||||
|
|
||||||
|
<div style="border: 2px solid #ff4040; margin-bottom: 0.5em;
|
||||||
|
padding: 0.5em 1em; background-color: #fff0f0"><b>Info:</b>
|
||||||
|
Size hints have been removed, see \ref dw-grows.</div>
|
||||||
|
|
||||||
|
|
||||||
|
Width Extremes
|
||||||
|
==============
|
||||||
|
|
||||||
|
dw::Table uses width extremes for fast calculation of column
|
||||||
|
widths. The structure dw::core::Extremes represents the minimal and
|
||||||
|
maximal width of a widget, as defined by:
|
||||||
|
|
||||||
|
- the minimal width is the smallest width, at which a widget can still
|
||||||
|
display contents, and
|
||||||
|
- the maximal width is the largest width, above which increasing the
|
||||||
|
width- does not make any sense.
|
||||||
|
|
||||||
|
Especially the latter is vaguely defined, here are some examples:
|
||||||
|
|
||||||
|
- For those widgets, which do not depend on size hints, the minimal
|
||||||
|
and the maximal width is the inherent width (the one returned by
|
||||||
|
dw::core::Widget::sizeRequest).
|
||||||
|
|
||||||
|
- For a textblock, the minimal width is the width of the widest
|
||||||
|
(unbreakable) word, the maximal width is the width of the total
|
||||||
|
paragraph (stretching a paragraph further would only waste space).
|
||||||
|
Actually, the implementation of dw::Textblock::getExtremesImpl is a
|
||||||
|
bit more complex.
|
||||||
|
|
||||||
|
- dw::Table is an example, where the width extremes are calculated
|
||||||
|
from the width extremes of the children.
|
||||||
|
|
||||||
|
Handling width extremes is similar to handling requisitions, a widget
|
||||||
|
must implement dw::core::Widget::getExtremesImpl, but a caller will
|
||||||
|
use dw::core::Widget::getExtremes.
|
||||||
|
|
||||||
|
|
||||||
|
Resizing
|
||||||
|
========
|
||||||
|
|
||||||
|
When the widget changes its size (requisition), it should call
|
||||||
|
dw::core::Widget::queueResize. The next call of
|
||||||
|
dw::core::Widget::sizeRequestImpl should then return the new
|
||||||
|
size. See dw::Image::setBuffer as an example.
|
||||||
|
|
||||||
|
Interna are described in the code of dw::core::Widget::queueResize.
|
||||||
|
|
||||||
|
<h3>Incremental Resizing</h3>
|
||||||
|
|
||||||
|
A widget may calculate its size based on size calculations already
|
||||||
|
done before. In this case, a widget must exactly know the reasons, why
|
||||||
|
a call of dw::core::Widget::sizeRequestImpl is necessary. To make use
|
||||||
|
of this, a widget must implement the following:
|
||||||
|
|
||||||
|
1. There is a member dw::core::Widget::parentRef, which is totally
|
||||||
|
under control of the parent widget (and so sometimes not used at
|
||||||
|
all). It is necessary to define how parentRef is used by a specific
|
||||||
|
parent widget, and it has to be set to the correct value whenever
|
||||||
|
necessary.
|
||||||
|
2. The widget must implement dw::core::Widget::markSizeChange and
|
||||||
|
dw::core::Widget::markExtremesChange, these methods are called in
|
||||||
|
two cases:
|
||||||
|
1. directly after dw::core::Widget::queueResize, with the
|
||||||
|
argument ref was passed to dw::core::Widget::queueResize,
|
||||||
|
and
|
||||||
|
2. if a child widget has called dw::core::Widget::queueResize,
|
||||||
|
with the value of the parent_ref member of this child.
|
||||||
|
|
||||||
|
This way, a widget can exactly keep track on size changes, and so
|
||||||
|
implement resizing in a faster way. A good example on how to use this
|
||||||
|
is dw::Textblock.
|
||||||
|
|
||||||
|
|
||||||
|
Rules for Methods Related to Resizing
|
||||||
|
=====================================
|
||||||
|
|
||||||
|
Which method can be called, when the call of another method is not
|
||||||
|
finished? These rules are important in two circumstances:
|
||||||
|
|
||||||
|
1. To know which method can be called, and, especially, which methods
|
||||||
|
*must not* be called, within the implementation of
|
||||||
|
*sizeRequestImpl* (called by *sizeRequest*), *markSizeChange*, and
|
||||||
|
*markExtremesChange* (the latter two are called by *queueResize*).
|
||||||
|
2. On the other hand, to make sure that the calls, which are allowed,
|
||||||
|
are handled correctly, especially in implementations of
|
||||||
|
*sizeRequestImpl*, *markSizeChange*, *markExtremesChange*
|
||||||
|
|
||||||
|
Generally, the rules defined below are, in case of doubt, rather
|
||||||
|
strict; when changing the rules, loosening is simpler than to tighten
|
||||||
|
them, since this will make it necessary to review old code for calls
|
||||||
|
previously allowed but now forbidden.
|
||||||
|
|
||||||
|
Short recap:
|
||||||
|
|
||||||
|
- *QueueResize* directly calls *markSizeChange* and
|
||||||
|
*markExtremesChanges*, and queues an idle function for the actual
|
||||||
|
resizing (dw::core::Layout::resizeIdle). (The idle function is
|
||||||
|
called some time after *queueResize* is finished.)
|
||||||
|
- The resize idle function first calls *sizeRequest*, then
|
||||||
|
*sizeAllocate*, for the toplevel widget.
|
||||||
|
|
||||||
|
In the following table, the rules are defined in detail. "Within call
|
||||||
|
of ..." includes all methods called from the original method: the
|
||||||
|
first row (*queueResize*) defines also the rules for
|
||||||
|
*markExtremesChanges* and *markExtremesChanges*, and in the second row
|
||||||
|
(*sizeAllocate*), even *sizeRequest* has to be considered.
|
||||||
|
|
||||||
|
<div style="border: 2px solid #ff4040; margin-bottom: 0.5em;
|
||||||
|
padding: 0.5em 1em; background-color: #fff0f0"><b>Info:</b>
|
||||||
|
Not up to date: *queueResize* can now be called recursively (so to
|
||||||
|
speak). See code there.</div>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Within call of ... ↓
|
||||||
|
<th>... is call allowed of ... ? →
|
||||||
|
<th>queueResize
|
||||||
|
<th>sizeAllocate
|
||||||
|
<th>sizeRequest
|
||||||
|
<th>getExtremes
|
||||||
|
<tr>
|
||||||
|
<th colspan=2>queueResize
|
||||||
|
<td>No
|
||||||
|
<td>No<sup>1</sup>
|
||||||
|
<td>No<sup>1</sup>
|
||||||
|
<td>No<sup>1</sup>
|
||||||
|
<tr>
|
||||||
|
<th colspan=2>sizeAllocate
|
||||||
|
<td>Yes
|
||||||
|
<td>Only for children<sup>2</sup>
|
||||||
|
<td>Yes(?)
|
||||||
|
<td>Yes(?)
|
||||||
|
<tr>
|
||||||
|
<th colspan=2>sizeRequest
|
||||||
|
<td>Yes<sup>3</sup>
|
||||||
|
<td>No
|
||||||
|
<td>Limited<sup>4</sup>
|
||||||
|
<td>Limited<sup>4</sup>
|
||||||
|
<tr>
|
||||||
|
<th colspan=2>getExtremes
|
||||||
|
<td>Yes<sup>3</sup>
|
||||||
|
<td>No
|
||||||
|
<td>Limited<sup>4</sup>
|
||||||
|
<td>Limited<sup>4</sup>
|
||||||
|
<tr>
|
||||||
|
<td colspan=6><sup>1</sup>) Otherwise, since these other methods
|
||||||
|
may be call *queueResize*, the limitation that *queueResize* must not
|
||||||
|
call *queueResize* can be violated.
|
||||||
|
|
||||||
|
<sup>2</sup>) Could perhaps be loosened as for *sizeRequest* and
|
||||||
|
*getExtremes*, but there is probably no need.
|
||||||
|
|
||||||
|
<sup>3</sup>) Therefore the distinction between *RESIZE_QUEUED* and
|
||||||
|
*NEEDS_RESIZE*, and *EXTREMES_QUEUED* and *EXTREMES_CHANGED*,
|
||||||
|
respectively.
|
||||||
|
|
||||||
|
<sup>4</sup>) Calls only for children are safe. In other cases, you
|
||||||
|
take a large responsibility to prevent endless recursions by
|
||||||
|
(typically indirectly) calling *sizeRequest* / *getExtremes* for
|
||||||
|
direct ancestors.
|
||||||
|
</table>
|
||||||
|
|
||||||
|
Furthermore, *sizeAllocate* can only be called within a call of
|
||||||
|
dw::core::Layout::resizeIdleId, so (if you do not touch dw::core) do
|
||||||
|
not call it outside of *sizeAllocateImpl*. The other methods can be
|
||||||
|
called outsize; e. g. *sizeRequest* is called in
|
||||||
|
dw::Textblock::addWidget.
|
||||||
|
|
||||||
|
To avoid painful debugging, there are some tests for the cases that
|
||||||
|
one method call is strictly forbidden while another method is called.
|
||||||
|
|
||||||
|
This could be done furthermore:
|
||||||
|
|
||||||
|
- The tests could be refined.
|
||||||
|
- Is it possible to define exacter rules, along with a proof that no
|
||||||
|
problems (like endless recursion) can occur?
|
||||||
|
|
||||||
|
*/
|
||||||
180
devdoc/fltk-problems.doc
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
/** \page fltk-problems Problems with FLTK
|
||||||
|
|
||||||
|
<h2>dw::fltk::FltkViewport</h2>
|
||||||
|
|
||||||
|
Current problems:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li> How should dw::fltk::FltkViewport::cancelQueueDraw be implemented?
|
||||||
|
|
||||||
|
<li> If the value of a scrollbar is changed by the program, not the user,
|
||||||
|
the callback seems not to be called. Can this be assured?
|
||||||
|
|
||||||
|
<li> The same for dw::fltk::FltkViewport::layout?
|
||||||
|
|
||||||
|
<li> Also, the problems with the widgets seems to work. Also sure?
|
||||||
|
|
||||||
|
<li> When drawing, clipping of 32 bit values is not working properly.
|
||||||
|
|
||||||
|
<li> The item group within a selection widget (menu) should not be selectable.
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
<h2>dw::fltk::FltkPlatform</h2>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li> There is the problem, that fltk::font always returns a font, the
|
||||||
|
required one, or a replacements. The latter is not wanted in all
|
||||||
|
cases, e.g. when several fonts are tested. Perhaps, this could be
|
||||||
|
solved by searching in the font list. <i>[This was true of fltk2.
|
||||||
|
What is the state of font handling now with fltk-1.3?]</i>
|
||||||
|
|
||||||
|
<li> Distinction between italics and oblique would be nice
|
||||||
|
(dw::fltk::FltkFont::FltkFont).
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
<h2>dw::fltk::ui::FltkCheckButtonResource</h2>
|
||||||
|
|
||||||
|
Groups of Fl_Radio_Button must be added to one Fl_Group, which is
|
||||||
|
not possible in this context. There are two alternatives:
|
||||||
|
|
||||||
|
<ol>
|
||||||
|
<li>there is a more flexible way to group radio buttons, or
|
||||||
|
<li>radio buttons are not grouped, instead, grouping (especially
|
||||||
|
unchecking other buttons) is done by the application.
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
(This is mostly solved.)
|
||||||
|
|
||||||
|
<h2>dw::fltk::FltkImgbuf</h2>
|
||||||
|
|
||||||
|
Alpha transparency should be best abstracted by FLTK itself. If not,
|
||||||
|
perhaps different implementations for different window systems could
|
||||||
|
be used. Then, it is for X necessary to use GCs with clipping masks.
|
||||||
|
|
||||||
|
|
||||||
|
<h2>dw::fltk::ui::ComplexButton</h2>
|
||||||
|
|
||||||
|
Unfortunately, FLTK does not provide a button with Fl_Group as parent, so
|
||||||
|
that children may be added to the button. dw::fltk::ui::ComplexButton does
|
||||||
|
exactly this, and is, in an ugly way, a modified copy of the FLTK
|
||||||
|
button.
|
||||||
|
|
||||||
|
It would be nice, if this is merged with the standard FLTK
|
||||||
|
button. Furthermore, setting the type is strange.
|
||||||
|
|
||||||
|
If the files do not compile, it may be useful to create a new one from
|
||||||
|
the FLTK source:
|
||||||
|
|
||||||
|
<ol>
|
||||||
|
<li> Copy Fl_Button.H from FLTK to dw/fltkcomplexbutton.hh and
|
||||||
|
src/Button.cxx to dw/fltkcomplexbutton.cc.
|
||||||
|
|
||||||
|
<li> In both files, rename "Button" to "ComplexButton". Automatic replacing
|
||||||
|
should work.
|
||||||
|
|
||||||
|
<li> Apply the changes below.
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
The following changes should be applied manually.
|
||||||
|
|
||||||
|
<h3>Changes in fltkcomplexbutton.hh</h3>
|
||||||
|
|
||||||
|
First of all, the \#define's for avoiding multiple includes:
|
||||||
|
|
||||||
|
\code
|
||||||
|
-#ifndef fltk_ComplexButton_h // fltk_Button_h formerly
|
||||||
|
-#define fltk_ComplexButton_h
|
||||||
|
+#ifndef __FLTK_COMPLEX_BUTTON_HH__
|
||||||
|
+#define __FLTK_COMPLEX_BUTTON_HH__
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
at the beginning and
|
||||||
|
|
||||||
|
\code
|
||||||
|
-#endif
|
||||||
|
+#endif // __FLTK_COMPLEX_BUTTON_HH__
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
at the end. Then, the namespace is changed:
|
||||||
|
|
||||||
|
\code
|
||||||
|
-namespace fltk {
|
||||||
|
+namespace dw {
|
||||||
|
+namespace fltk {
|
||||||
|
+namespace ui {
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
at the beginning and
|
||||||
|
|
||||||
|
\code
|
||||||
|
-}
|
||||||
|
+} // namespace ui
|
||||||
|
+} // namespace fltk
|
||||||
|
+} // namespace dw
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
at the end. Most important, the base class is changed:
|
||||||
|
|
||||||
|
\code
|
||||||
|
-#include "FL/Fl_Widget.H"
|
||||||
|
+#include <FL/Fl_Group.H>
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
and
|
||||||
|
|
||||||
|
\code
|
||||||
|
-class FL_API ComplexButton : public Fl_Widget {
|
||||||
|
+class ComplexButton: public Fl_Group
|
||||||
|
+{
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
Finally, for dw::fltk::ui::ComplexButton::default_style, there is a
|
||||||
|
namespace conflict:
|
||||||
|
|
||||||
|
\code
|
||||||
|
- static NamedStyle* default_style;
|
||||||
|
+ static ::fltk::NamedStyle* default_style;
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
<h3>Changes in fltkcomplexbutton.cc</h3>
|
||||||
|
|
||||||
|
First, \#include's:
|
||||||
|
|
||||||
|
\code
|
||||||
|
|
||||||
|
#include <FL/Fl.H>
|
||||||
|
-#include <FL/ComplexButton.h> // <FL/Fl_Button.H> formerly
|
||||||
|
#include <FL/Fl_Group.H>
|
||||||
|
#include <FL/Fl_Window.H>
|
||||||
|
+
|
||||||
|
+#include "fltkcomplexbutton.hh"
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
Second, namespaces:
|
||||||
|
|
||||||
|
\code
|
||||||
|
+using namespace dw::fltk::ui;
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
Since the base class is now Fl_Group, the constructor must be changed:
|
||||||
|
|
||||||
|
\code
|
||||||
|
-ComplexButton::ComplexButton(int x,int y,int w,int h, const char *l) : Fl_Widget(x,y,w,h,l) {
|
||||||
|
+ComplexButton::ComplexButton(int x,int y,int w,int h, const char *l) :
|
||||||
|
+ Fl_Group(x,y,w,h,l)
|
||||||
|
+{
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
Finally, the button must draw its children (end of
|
||||||
|
dw::fltk::ui::ComplexButton::draw()):
|
||||||
|
|
||||||
|
\code
|
||||||
|
+
|
||||||
|
+ for (int i = children () - 1; i >= 0; i--)
|
||||||
|
+ draw_child (*child (i));
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
*/
|
||||||
48
devdoc/index.doc
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/** \mainpage
|
||||||
|
|
||||||
|
<h2>Overview</h2>
|
||||||
|
|
||||||
|
This is a list of documents to start with:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li> \ref lout
|
||||||
|
<li> \ref dw-overview (map at \ref dw-map)
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
Currently, a document \ref fltk-problems is maintained, ideally, it
|
||||||
|
will be removed soon.
|
||||||
|
|
||||||
|
<h2>Historical</h2>
|
||||||
|
|
||||||
|
<h3>Replacements for GTK+ and GLib</h3>
|
||||||
|
|
||||||
|
There are several classes etc., which are used for tasks formerly (in the GTK+
|
||||||
|
version of dillo) achieved by GtkObject (in 1.2.x, this is part of Gtk+) and
|
||||||
|
GLib. For an overview on all this, take a look at \ref lout.
|
||||||
|
|
||||||
|
GtkObject is replaced by the following:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li> lout::object::Object is a common base class for many classes used
|
||||||
|
dillo. In the namespace lout::object, there are also some more common
|
||||||
|
classes and interfaces.
|
||||||
|
|
||||||
|
<li> A sub class of lout::object::Object is
|
||||||
|
lout::identity::IdentifiableObject, which allows to determine the
|
||||||
|
class at run-time (equivalent to GTK_CHECK_CAST in GtkObject).
|
||||||
|
|
||||||
|
<li> For signals, there is the namespace lout::signal.
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
Hash tables, linked lists etc. can be found in the lout::container namespace,
|
||||||
|
several useful macros from GLib have been implemented as inline functions
|
||||||
|
in the lout::misc namespace.
|
||||||
|
|
||||||
|
As an alternative to the macros defined in list.h, there is also a template
|
||||||
|
class, lout::misc::SimpleVector, which does the same.
|
||||||
|
|
||||||
|
<h3>Changes in Dw</h3>
|
||||||
|
|
||||||
|
If you have been familiar with Dw before, take a look at \ref dw-changes.
|
||||||
|
|
||||||
|
*/
|
||||||
95
devdoc/lout.doc
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
/** \page lout Lots of Useful Tools
|
||||||
|
|
||||||
|
In the "lout" directory, there are some common base functionality for
|
||||||
|
C++. Most is described as doxygen comments, this text gives an
|
||||||
|
overview.
|
||||||
|
|
||||||
|
<h2>Common Base Class</h2>
|
||||||
|
|
||||||
|
Many classes are derived from lout::object::Object, which defines some
|
||||||
|
general methods. See there for more information.
|
||||||
|
|
||||||
|
For the case, that you need primitive C++ types, there are some
|
||||||
|
wrappers:
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr><th>C++ Type <th>Wrapper Class
|
||||||
|
<tr><td>void* <td>lout::object::Pointer
|
||||||
|
<tr><td>specific pointer <td>lout::object::TypedPointer (template class)
|
||||||
|
<tr><td>int <td>lout::object::Integer
|
||||||
|
<tr><td>const char* <td>lout::object::ConstString
|
||||||
|
<tr><td>char* <td>lout::object::String
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
<h2>Containers</h2>
|
||||||
|
|
||||||
|
In the namespace lout::container, several container classes are defined,
|
||||||
|
which all deal with instances of lout::object::Object.
|
||||||
|
|
||||||
|
<h3>Untyped Containers</h3>
|
||||||
|
|
||||||
|
In lout::container::untyped, there are the following containers:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>lout::container::untyped::Vector, a dynamically increases array,
|
||||||
|
<li>lout::container::untyped::List, a linked list,
|
||||||
|
<li>lout::container::untyped::HashTable, a hash table, and
|
||||||
|
<li>lout::container::untyped::Stack, a stack.
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
All provide specific methods, but since they have a common base class,
|
||||||
|
lout::container::untyped::Collection, they all provide iterators, by the
|
||||||
|
method lout::container::untyped::Collection::iterator.
|
||||||
|
|
||||||
|
<h3>Typed Containers</h3>
|
||||||
|
|
||||||
|
lout::container::typed provides wrappers for the container classes defined
|
||||||
|
in lout::container::untyped, which are more type safe, by using C++
|
||||||
|
templates.
|
||||||
|
|
||||||
|
|
||||||
|
<h2>Signals</h2>
|
||||||
|
|
||||||
|
For how to connect objects at run-time (to reduce dependencies), take a
|
||||||
|
look at the lout::signal namespace.
|
||||||
|
|
||||||
|
There is also a base class lout::signal::ObservedObject, which implements
|
||||||
|
signals for deletion.
|
||||||
|
|
||||||
|
|
||||||
|
<h2>Debugging</h2>
|
||||||
|
|
||||||
|
In debug.hh, there are some some useful macros for debugging messages,
|
||||||
|
see the file for mor information.
|
||||||
|
|
||||||
|
|
||||||
|
<h2>Identifying Classes at Runtime</h2>
|
||||||
|
|
||||||
|
If the class of an object must be identified at runtime,
|
||||||
|
lout::identity::IdentifiableObject should be used as the base class,
|
||||||
|
see there for more details.
|
||||||
|
|
||||||
|
|
||||||
|
<h2>Miscellaneous</h2>
|
||||||
|
|
||||||
|
The lout::misc namespace provides several miscellaneous stuff:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li> In some contexts, it is necessary to compare objects
|
||||||
|
(less/greater), for this, also lout::misc::Comparable must be
|
||||||
|
implemented. For example., lout::container::untyped::Vector::sort and
|
||||||
|
lout::container::typed::Vector::sort cast the elements to
|
||||||
|
lout::misc::Comparable. This can be mixed with lout::object::Object.
|
||||||
|
<li> lout::misc::SimpleVector, a simple, template based vector class
|
||||||
|
(not depending on lout::object::Object) (a variant for handling a
|
||||||
|
special case in an efficient way is lout::misc::NotSoSimpleVector),
|
||||||
|
<li> lout::misc::StringBuffer, class for fast concatenation of a large number
|
||||||
|
of strings,
|
||||||
|
<li> lout::misc::BitSet implements a bitset.
|
||||||
|
<li> useful (template) functions (lout::misc::min, lout::misc::max), and
|
||||||
|
<li> some functions useful for runtime checks (lout::misc::assert,
|
||||||
|
lout::misc::assertNotReached).
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
*/
|
||||||
BIN
devdoc/not-so-simple-container.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
785
devdoc/not-so-simple-container.svg
Normal file
@ -0,0 +1,785 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="210mm"
|
||||||
|
height="297mm"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.3.1 r9886"
|
||||||
|
sodipodi:docname="not-so-simple-container.svg"
|
||||||
|
inkscape:export-filename="/home/sg/dev/dillo/dillo/doc/not-so-simple-container.png"
|
||||||
|
inkscape:export-xdpi="69"
|
||||||
|
inkscape:export-ydpi="69">
|
||||||
|
<defs
|
||||||
|
id="defs4">
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="Arrow1Mend"
|
||||||
|
orient="auto"
|
||||||
|
refY="0.0"
|
||||||
|
refX="0.0"
|
||||||
|
id="Arrow1Mend"
|
||||||
|
style="overflow:visible;">
|
||||||
|
<path
|
||||||
|
id="path3998"
|
||||||
|
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||||
|
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;"
|
||||||
|
transform="scale(0.4) rotate(180) translate(10,0)" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="Arrow1Send"
|
||||||
|
orient="auto"
|
||||||
|
refY="0.0"
|
||||||
|
refX="0.0"
|
||||||
|
id="Arrow1Send"
|
||||||
|
style="overflow:visible;">
|
||||||
|
<path
|
||||||
|
id="path4004"
|
||||||
|
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||||
|
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;"
|
||||||
|
transform="scale(0.2) rotate(180) translate(6,0)" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="Arrow1Lend"
|
||||||
|
orient="auto"
|
||||||
|
refY="0.0"
|
||||||
|
refX="0.0"
|
||||||
|
id="Arrow1Lend"
|
||||||
|
style="overflow:visible;">
|
||||||
|
<path
|
||||||
|
id="path3992"
|
||||||
|
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
|
||||||
|
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;"
|
||||||
|
transform="scale(0.8) rotate(180) translate(12.5,0)" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="Arrow1Mend"
|
||||||
|
orient="auto"
|
||||||
|
refY="0"
|
||||||
|
refX="0"
|
||||||
|
id="Arrow1Mend-5"
|
||||||
|
style="overflow:visible">
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path3998-5"
|
||||||
|
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||||
|
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||||
|
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="Arrow1Mend"
|
||||||
|
orient="auto"
|
||||||
|
refY="0"
|
||||||
|
refX="0"
|
||||||
|
id="Arrow1Mend-3"
|
||||||
|
style="overflow:visible">
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path3998-2"
|
||||||
|
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||||
|
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||||
|
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="Arrow1Mend"
|
||||||
|
orient="auto"
|
||||||
|
refY="0"
|
||||||
|
refX="0"
|
||||||
|
id="Arrow1Mend-2"
|
||||||
|
style="overflow:visible">
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path3998-22"
|
||||||
|
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||||
|
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||||
|
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="Arrow1Mend"
|
||||||
|
orient="auto"
|
||||||
|
refY="0"
|
||||||
|
refX="0"
|
||||||
|
id="marker4917"
|
||||||
|
style="overflow:visible">
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path4919"
|
||||||
|
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||||
|
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||||
|
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="Arrow1Mend"
|
||||||
|
orient="auto"
|
||||||
|
refY="0"
|
||||||
|
refX="0"
|
||||||
|
id="marker4921"
|
||||||
|
style="overflow:visible">
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path4923"
|
||||||
|
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||||
|
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||||
|
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="Arrow1Mend"
|
||||||
|
orient="auto"
|
||||||
|
refY="0"
|
||||||
|
refX="0"
|
||||||
|
id="Arrow1Mend-9"
|
||||||
|
style="overflow:visible">
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path3998-7"
|
||||||
|
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||||
|
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||||
|
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="Arrow1Mend"
|
||||||
|
orient="auto"
|
||||||
|
refY="0"
|
||||||
|
refX="0"
|
||||||
|
id="Arrow1Mend-6"
|
||||||
|
style="overflow:visible">
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path3998-6"
|
||||||
|
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||||
|
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||||
|
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
|
||||||
|
</marker>
|
||||||
|
<marker
|
||||||
|
inkscape:stockid="Arrow1Mend"
|
||||||
|
orient="auto"
|
||||||
|
refY="0"
|
||||||
|
refX="0"
|
||||||
|
id="Arrow1Mend-51"
|
||||||
|
style="overflow:visible">
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path3998-3"
|
||||||
|
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||||
|
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||||
|
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
|
||||||
|
</marker>
|
||||||
|
</defs>
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="1"
|
||||||
|
inkscape:cx="226.09436"
|
||||||
|
inkscape:cy="741.31258"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="true"
|
||||||
|
inkscape:window-width="1598"
|
||||||
|
inkscape:window-height="876"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="22"
|
||||||
|
inkscape:window-maximized="1">
|
||||||
|
<inkscape:grid
|
||||||
|
type="xygrid"
|
||||||
|
id="grid2985" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<metadata
|
||||||
|
id="metadata7">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Ebene 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1">
|
||||||
|
<rect
|
||||||
|
style="fill:#e0e0e0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||||
|
id="rect3010"
|
||||||
|
width="35.433071"
|
||||||
|
height="17.716536"
|
||||||
|
x="442.91339"
|
||||||
|
y="25.611128" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L"
|
||||||
|
x="496.06299"
|
||||||
|
y="38.581394"
|
||||||
|
id="text3012"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3014"
|
||||||
|
x="496.06299"
|
||||||
|
y="38.581394"
|
||||||
|
style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L">unaffected (in main array)</tspan></text>
|
||||||
|
<flowRoot
|
||||||
|
xml:space="preserve"
|
||||||
|
id="flowRoot3800"
|
||||||
|
style="fill:black;stroke:none;stroke-opacity:1;stroke-width:1px;stroke-linejoin:miter;stroke-linecap:butt;fill-opacity:1;font-family:Nimbus Mono L;font-style:normal;font-weight:normal;font-size:20px;line-height:125%;letter-spacing:0px;word-spacing:0px;-inkscape-font-specification:Nimbus Mono L;font-stretch:normal;font-variant:normal"><flowRegion
|
||||||
|
id="flowRegion3802"><rect
|
||||||
|
id="rect3804"
|
||||||
|
width="276.7818"
|
||||||
|
height="34.345188"
|
||||||
|
x="424.26407"
|
||||||
|
y="20.996433" /></flowRegion><flowPara
|
||||||
|
id="flowPara3806" /></flowRoot> <rect
|
||||||
|
style="fill:#e0e0e0;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||||
|
id="rect3812"
|
||||||
|
width="70.866142"
|
||||||
|
height="17.716536"
|
||||||
|
x="17.716558"
|
||||||
|
y="60.236198" />
|
||||||
|
<rect
|
||||||
|
style="fill:#b0b0ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||||
|
id="rect3814"
|
||||||
|
width="35.433071"
|
||||||
|
height="17.716536"
|
||||||
|
x="442.91339"
|
||||||
|
y="61.480198" />
|
||||||
|
<rect
|
||||||
|
style="fill:#80ff00;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||||
|
id="rect3816"
|
||||||
|
width="35.433071"
|
||||||
|
height="17.716536"
|
||||||
|
x="442.91339"
|
||||||
|
y="96.913269" />
|
||||||
|
<rect
|
||||||
|
style="fill:#ffff40;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||||
|
id="rect3818"
|
||||||
|
width="35.433071"
|
||||||
|
height="17.716536"
|
||||||
|
x="442.91339"
|
||||||
|
y="131.10234" />
|
||||||
|
<rect
|
||||||
|
style="fill:#b0b0ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||||
|
id="rect3816-2"
|
||||||
|
width="88.58268"
|
||||||
|
height="17.716536"
|
||||||
|
x="124.01577"
|
||||||
|
y="60.236198" />
|
||||||
|
<rect
|
||||||
|
style="fill:#e0e0e0;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||||
|
id="rect3812-5"
|
||||||
|
width="88.582687"
|
||||||
|
height="17.716536"
|
||||||
|
x="301.18112"
|
||||||
|
y="60.236198" />
|
||||||
|
<rect
|
||||||
|
style="fill:#80ff00;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||||
|
id="rect3816-4"
|
||||||
|
width="35.433071"
|
||||||
|
height="17.716536"
|
||||||
|
x="442.91339"
|
||||||
|
y="96.913269" />
|
||||||
|
<rect
|
||||||
|
style="fill:#80ff00;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||||
|
id="rect3816-4-2"
|
||||||
|
width="35.433071"
|
||||||
|
height="17.716536"
|
||||||
|
x="88.582703"
|
||||||
|
y="95.669266" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||||
|
d="m 17.716558,60.236198 70.866142,0 0,17.716536 -70.866142,0"
|
||||||
|
id="path3939"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||||
|
d="m 389.7638,60.236198 -88.58268,0 0,17.716536 88.58268,0"
|
||||||
|
id="path3941"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cccc" />
|
||||||
|
<rect
|
||||||
|
style="fill:#e0e0e0;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||||
|
id="rect3812-3"
|
||||||
|
width="70.866142"
|
||||||
|
height="17.716536"
|
||||||
|
x="17.71656"
|
||||||
|
y="148.81888" />
|
||||||
|
<rect
|
||||||
|
style="fill:#e0e0e0;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||||
|
id="rect3812-5-7"
|
||||||
|
width="88.582687"
|
||||||
|
height="17.716537"
|
||||||
|
x="301.18112"
|
||||||
|
y="148.81888" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||||
|
d="m 17.716558,148.81888 70.86614,0 0,17.71653 -70.86614,0"
|
||||||
|
id="path3939-4"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||||
|
d="m 389.7638,148.81887 -88.58268,1e-5 0,17.71653 88.58268,0"
|
||||||
|
id="path3941-8"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cccc" />
|
||||||
|
<rect
|
||||||
|
style="fill:#80ff00;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||||
|
id="rect3816-4-2-6"
|
||||||
|
width="35.433071"
|
||||||
|
height="17.716536"
|
||||||
|
x="124.01577"
|
||||||
|
y="148.81888" />
|
||||||
|
<rect
|
||||||
|
style="fill:#b0b0ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||||
|
id="rect3816-2-5"
|
||||||
|
width="53.149605"
|
||||||
|
height="17.716537"
|
||||||
|
x="159.44885"
|
||||||
|
y="148.81888" />
|
||||||
|
<rect
|
||||||
|
style="fill:#b0b0ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||||
|
id="rect3816-2-50"
|
||||||
|
width="35.433067"
|
||||||
|
height="17.716537"
|
||||||
|
x="212.59845"
|
||||||
|
y="184.25195" />
|
||||||
|
<rect
|
||||||
|
style="fill:#ffff40;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||||
|
id="rect3818-4"
|
||||||
|
width="53.149609"
|
||||||
|
height="17.716534"
|
||||||
|
x="248.03152"
|
||||||
|
y="184.25195" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
|
||||||
|
d="m 88.5827,148.81887 35.43307,0"
|
||||||
|
id="path4078-6"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
|
||||||
|
d="m 88.5827,166.53541 35.43307,0"
|
||||||
|
id="path4078-4-7"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
|
||||||
|
d="m 212.59845,60.236198 88.58267,0"
|
||||||
|
id="path4078-66"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
|
||||||
|
d="m 212.59845,77.952734 88.58267,0"
|
||||||
|
id="path4078-4-0"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#8080ff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:2, 4;stroke-dashoffset:0"
|
||||||
|
d="m 88.5827,77.952734 0,17.716535"
|
||||||
|
id="path4156"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#808000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
|
||||||
|
d="m 88.5827,77.952734 0,17.716535"
|
||||||
|
id="path4176"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#808000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
|
||||||
|
d="m 124.01577,95.669269 0,-17.716535"
|
||||||
|
id="path4178"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#808000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
|
||||||
|
d="m 212.59845,166.53541 0,17.71654"
|
||||||
|
id="path4180"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#808000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
|
||||||
|
d="m 301.18112,184.25195 0,-17.71654"
|
||||||
|
id="path4182"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#8080ff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
|
||||||
|
d="m 88.5827,77.952734 0,17.716535"
|
||||||
|
id="path4186"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
inkscape:export-filename="/home/sg/dev/dillo/dillo/doc/path4188.png"
|
||||||
|
inkscape:export-xdpi="69"
|
||||||
|
inkscape:export-ydpi="69" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#8080ff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
|
||||||
|
d="m 124.01577,95.669269 0,-17.716535"
|
||||||
|
id="path4188"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
inkscape:export-xdpi="69"
|
||||||
|
inkscape:export-ydpi="69" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#8080ff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
|
||||||
|
d="m 212.59845,166.53541 0,17.71654"
|
||||||
|
id="path4190"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
inkscape:export-filename="/home/sg/dev/dillo/dillo/doc/path4188.png"
|
||||||
|
inkscape:export-xdpi="69"
|
||||||
|
inkscape:export-ydpi="69" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#8080ff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
|
||||||
|
d="m 301.18112,184.25195 0,-17.71654"
|
||||||
|
id="path4192"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
inkscape:export-filename="/home/sg/dev/dillo/dillo/doc/path4188.png"
|
||||||
|
inkscape:export-xdpi="69"
|
||||||
|
inkscape:export-ydpi="69" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
|
||||||
|
d="m 88.5827,77.952734 0,17.716535"
|
||||||
|
id="path4078-6-0"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
|
||||||
|
d="m 124.01577,77.952734 0,17.716535"
|
||||||
|
id="path4078-6-0-9"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
|
||||||
|
d="m 212.59845,166.53541 0,17.71654"
|
||||||
|
id="path4078-6-0-0"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
|
||||||
|
d="m 301.18112,166.53541 0,17.71654"
|
||||||
|
id="path4078-6-0-6"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:none;marker-end:url(#Arrow1Mend)"
|
||||||
|
d="m 106.29923,116.92911 c 35.43307,28.34646 0,0 35.43307,28.34646"
|
||||||
|
id="path3803"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:none;marker-end:url(#Arrow1Mend)"
|
||||||
|
d="m 148.81892,81.496041 c 35.43307,63.779529 0,0 35.43307,63.779529"
|
||||||
|
id="path3803-7"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
|
||||||
|
d="m 177.16537,60.236198 0,17.716535"
|
||||||
|
id="path4078-6-0-9-5"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:none;marker-end:url(#Arrow1Mend)"
|
||||||
|
d="m 194.88191,81.496038 c 35.43307,99.212602 0,3e-6 35.43307,99.212602"
|
||||||
|
id="path3803-7-0"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L"
|
||||||
|
x="496.06299"
|
||||||
|
y="74.426468"
|
||||||
|
id="text3012-5"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3014-0"
|
||||||
|
x="496.06299"
|
||||||
|
y="74.426468"
|
||||||
|
style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L">main array (moved)</tspan></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
|
||||||
|
x="496.06299"
|
||||||
|
y="109.85954"
|
||||||
|
id="text4897"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan4899"
|
||||||
|
x="496.06299"
|
||||||
|
y="109.85954"
|
||||||
|
style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L">original extra array</tspan></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L"
|
||||||
|
x="496.06299"
|
||||||
|
y="145.60861"
|
||||||
|
id="text4901"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan4903"
|
||||||
|
x="496.06299"
|
||||||
|
y="145.60861"
|
||||||
|
style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L">new inserted area</tspan></text>
|
||||||
|
<rect
|
||||||
|
style="fill:#e0e0e0;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||||
|
id="rect3812-37"
|
||||||
|
width="53.149609"
|
||||||
|
height="17.716536"
|
||||||
|
x="17.716558"
|
||||||
|
y="272.83463" />
|
||||||
|
<rect
|
||||||
|
style="fill:#b0b0ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||||
|
id="rect3816-2-1"
|
||||||
|
width="35.433067"
|
||||||
|
height="17.716537"
|
||||||
|
x="159.44884"
|
||||||
|
y="272.83463" />
|
||||||
|
<rect
|
||||||
|
style="fill:#e0e0e0;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||||
|
id="rect3812-5-5"
|
||||||
|
width="53.149582"
|
||||||
|
height="17.716537"
|
||||||
|
x="336.61423"
|
||||||
|
y="272.83463" />
|
||||||
|
<rect
|
||||||
|
style="fill:#80ff00;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||||
|
id="rect3816-4-2-3"
|
||||||
|
width="88.582664"
|
||||||
|
height="17.716534"
|
||||||
|
x="70.866173"
|
||||||
|
y="308.2677" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||||
|
d="m 17.716558,272.83462 53.149615,0 0,17.71654 -53.149615,0"
|
||||||
|
id="path3939-43"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cccc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||||
|
d="m 389.7638,272.83462 -53.14961,0 0,17.71654 53.14961,0"
|
||||||
|
id="path3941-6"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cccc" />
|
||||||
|
<rect
|
||||||
|
style="fill:#e0e0e0;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||||
|
id="rect3812-3-3"
|
||||||
|
width="53.149624"
|
||||||
|
height="17.716515"
|
||||||
|
x="17.716558"
|
||||||
|
y="361.4173" />
|
||||||
|
<rect
|
||||||
|
style="fill:#e0e0e0;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||||
|
id="rect3812-5-7-9"
|
||||||
|
width="53.149582"
|
||||||
|
height="17.716543"
|
||||||
|
x="336.61423"
|
||||||
|
y="361.4173" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||||
|
d="m 17.716559,361.4173 53.149604,0 0,17.71653 -53.149604,1e-5"
|
||||||
|
id="path3939-4-2"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cccc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||||
|
d="m 389.7638,361.4173 -53.14961,0 0,17.71653 53.14961,1e-5"
|
||||||
|
id="path3941-8-1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cccc" />
|
||||||
|
<rect
|
||||||
|
style="fill:#80ff00;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||||
|
id="rect3816-4-2-6-7"
|
||||||
|
width="35.433071"
|
||||||
|
height="17.716536"
|
||||||
|
x="159.44884"
|
||||||
|
y="361.4173" />
|
||||||
|
<rect
|
||||||
|
style="fill:#b0b0ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||||
|
id="rect3816-2-50-6"
|
||||||
|
width="35.433067"
|
||||||
|
height="17.716537"
|
||||||
|
x="248.03151"
|
||||||
|
y="396.85037" />
|
||||||
|
<rect
|
||||||
|
style="fill:#ffff40;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||||
|
id="rect3818-4-9"
|
||||||
|
width="53.149609"
|
||||||
|
height="17.716534"
|
||||||
|
x="283.4646"
|
||||||
|
y="396.85037" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
|
||||||
|
d="m 70.866173,361.41729 88.582667,1e-5"
|
||||||
|
id="path4078-6-8"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
|
||||||
|
d="m 70.866173,379.13383 88.582667,1e-5"
|
||||||
|
id="path4078-4-7-4"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
|
||||||
|
d="m 194.88192,272.83462 141.73227,0"
|
||||||
|
id="path4078-66-9"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
|
||||||
|
d="m 194.88192,290.55116 141.73227,0"
|
||||||
|
id="path4078-4-0-6"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#8080ff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:2, 4;stroke-dashoffset:0"
|
||||||
|
d="m 70.866173,290.55116 0,17.71653"
|
||||||
|
id="path4156-9"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#808000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
|
||||||
|
d="m 70.866173,290.55116 0,17.71653"
|
||||||
|
id="path4176-4"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#808000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
|
||||||
|
d="m 106.29924,308.26769 0,-17.71653"
|
||||||
|
id="path4178-9"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#808000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
|
||||||
|
d="m 194.88192,379.13383 0,17.71654"
|
||||||
|
id="path4180-3"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#808000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
|
||||||
|
d="m 336.61419,396.85037 0,-17.71654"
|
||||||
|
id="path4182-8"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#8080ff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
|
||||||
|
d="m 70.866173,290.55116 0,17.71653"
|
||||||
|
id="path4186-1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
inkscape:export-filename="/home/sg/dev/dillo/dillo/doc/path4188.png"
|
||||||
|
inkscape:export-xdpi="69"
|
||||||
|
inkscape:export-ydpi="69"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#8080ff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
|
||||||
|
d="m 106.29924,308.26769 0,-17.71653"
|
||||||
|
id="path4188-2"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
inkscape:export-xdpi="69"
|
||||||
|
inkscape:export-ydpi="69" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#8080ff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
|
||||||
|
d="m 194.88192,379.13383 0,17.71654"
|
||||||
|
id="path4190-8"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
inkscape:export-filename="/home/sg/dev/dillo/dillo/doc/path4188.png"
|
||||||
|
inkscape:export-xdpi="69"
|
||||||
|
inkscape:export-ydpi="69" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#8080ff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
|
||||||
|
d="m 336.61419,396.85037 0,-17.71654"
|
||||||
|
id="path4192-4"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
inkscape:export-filename="/home/sg/dev/dillo/dillo/doc/path4188.png"
|
||||||
|
inkscape:export-xdpi="69"
|
||||||
|
inkscape:export-ydpi="69" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
|
||||||
|
d="m 70.866173,290.55116 0,17.71653"
|
||||||
|
id="path4078-6-0-4"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
|
||||||
|
d="m 159.44883,290.55116 0,17.71653"
|
||||||
|
id="path4078-6-0-9-0"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
|
||||||
|
d="m 194.88192,379.13383 0,17.71654"
|
||||||
|
id="path4078-6-0-0-7"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
|
||||||
|
d="m 336.61419,379.13383 0,17.71654"
|
||||||
|
id="path4078-6-0-6-1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<rect
|
||||||
|
style="fill:#80ff00;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||||
|
id="rect3816-4-2-6-7-8"
|
||||||
|
width="53.149616"
|
||||||
|
height="17.716539"
|
||||||
|
x="194.88191"
|
||||||
|
y="396.85037" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
|
||||||
|
d="m 106.29924,308.26769 0,17.71654"
|
||||||
|
id="path4078-6-0-9-5-1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:none;marker-end:url(#Arrow1Mend)"
|
||||||
|
d="m 88.5827,329.52754 c 88.58267,28.34645 0,0 88.58267,28.34645"
|
||||||
|
id="path3803-4"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:none;marker-end:url(#Arrow1Mend)"
|
||||||
|
d="m 131.10238,329.52754 c 70.86614,24.80315 53.14961,3.5433 88.58268,63.77952"
|
||||||
|
id="path3803-4-0"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:none;marker-end:url(#Arrow1Mend)"
|
||||||
|
d="m 177.16537,294.09447 c 88.58268,99.21259 0,0 88.58268,99.21259"
|
||||||
|
id="path3803-4-01"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L"
|
||||||
|
x="17.716536"
|
||||||
|
y="42.519665"
|
||||||
|
id="text3012-5-7"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3014-0-2"
|
||||||
|
x="17.716536"
|
||||||
|
y="42.519665"
|
||||||
|
style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L">Example 1:</tspan></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L"
|
||||||
|
x="528"
|
||||||
|
y="389.36218"
|
||||||
|
id="text5205"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan5207"
|
||||||
|
x="528"
|
||||||
|
y="389.36218" /></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L"
|
||||||
|
x="17.716536"
|
||||||
|
y="255.11809"
|
||||||
|
id="text3012-5-7-9"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
x="17.716536"
|
||||||
|
y="255.11809"
|
||||||
|
style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
|
||||||
|
id="tspan5230">Example 2:</tspan></text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 35 KiB |
35
devdoc/rounding-errors.doc
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/** \page rounding-errors How to Avoid Rounding Errors
|
||||||
|
|
||||||
|
(Probably, this is a standard algorithm, so if someone knows the name,
|
||||||
|
drop me a note.)
|
||||||
|
|
||||||
|
If something like
|
||||||
|
|
||||||
|
\f[y_i = {x_i a \over b}\f]
|
||||||
|
|
||||||
|
is to be calculated, and all numbers are integers, a naive
|
||||||
|
implementation would result in something, for which
|
||||||
|
|
||||||
|
\f[\sum y_i \ne {(\sum x_i) a \over b}\f]
|
||||||
|
|
||||||
|
because of rounding errors, due to the integer division. This can be
|
||||||
|
avoided by transforming the formula into
|
||||||
|
|
||||||
|
\f[y_i = {(\sum_{j=0}^{j=i} x_j) a \over b} - \sum_{j=0}^{j=i-1} y_j\f]
|
||||||
|
|
||||||
|
Of course, when all \f$y_i\f$ are calculated in a sequence,
|
||||||
|
\f$\sum_{j=0}^{j=i} x_j\f$ and \f$\sum_{j=0}^{j=i-1} y_j\f$ can be
|
||||||
|
accumulated in the same loop. Regard this as sample:
|
||||||
|
|
||||||
|
\code
|
||||||
|
int n, x[n], a, b; // Should all be initialized.
|
||||||
|
int y[n], cumX = 0, cumY = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
cumX += x[i]
|
||||||
|
y[i] = (cumX * a) / b - cumY;
|
||||||
|
cumY += y[i];
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
*/
|
||||||
195
devdoc/uml-legend.doc
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
/** \page uml-legend UML Legend
|
||||||
|
|
||||||
|
This page describes the notation for several diagrams used in the
|
||||||
|
documentation, which is a slight variation of UML.
|
||||||
|
|
||||||
|
|
||||||
|
<h2>Classes</h2>
|
||||||
|
|
||||||
|
Classes are represented by boxes, containing there names:
|
||||||
|
|
||||||
|
\dot
|
||||||
|
digraph G {
|
||||||
|
node [shape=record, fontname=Helvetica, fontsize=10];
|
||||||
|
fontname=Helvetica; fontsize=8;
|
||||||
|
"Concrete Class";
|
||||||
|
"Abstract Class" [color="#a0a0a0"];
|
||||||
|
Interface [color="#ff8080"];
|
||||||
|
}
|
||||||
|
\enddot
|
||||||
|
|
||||||
|
(In most cases, the attributes and operations are left away, for
|
||||||
|
better readability. Just click on it, to get to the detailed
|
||||||
|
description.)
|
||||||
|
|
||||||
|
Of course, in C++, there are no interfaces, but here, we call a class,
|
||||||
|
which has only virtual abstract methods, and so does not provide any
|
||||||
|
functionality, an interface.
|
||||||
|
|
||||||
|
Templates get a yellow background color:
|
||||||
|
|
||||||
|
\dot
|
||||||
|
digraph G {
|
||||||
|
node [shape=record, fontname=Helvetica, fontsize=10,
|
||||||
|
fillcolor="#ffffc0", style="filled"];
|
||||||
|
fontname=Helvetica; fontsize=8;
|
||||||
|
"Concrete Class Template";
|
||||||
|
"Abstract Class Template" [color="#a0a0a0"];
|
||||||
|
"Interface Template" [color="#ff8080"];
|
||||||
|
}
|
||||||
|
\enddot
|
||||||
|
|
||||||
|
|
||||||
|
<h2>Objects</h2>
|
||||||
|
|
||||||
|
In some cases, an example for a concrete constellation of objects is
|
||||||
|
shown. An object is represented by a box containing a name and the
|
||||||
|
class, separated by a colon.
|
||||||
|
|
||||||
|
\dot
|
||||||
|
digraph G {
|
||||||
|
node [shape=record, fontname=Helvetica, fontsize=10];
|
||||||
|
edge [arrowhead="open", labelfontname=Helvetica, labelfontsize=10,
|
||||||
|
color="#404040", labelfontcolor="#000080"];
|
||||||
|
fontname=Helvetica; fontsize=10;
|
||||||
|
|
||||||
|
"x: A" -> "y1: B";
|
||||||
|
"x: A" -> "y2: B";
|
||||||
|
}
|
||||||
|
\enddot
|
||||||
|
|
||||||
|
The names (\em x, \em y, and \em z) are only meant within the context
|
||||||
|
of the diagram, there needs not to be a relation to the actual names
|
||||||
|
in the program. They should be unique within the diagram.
|
||||||
|
|
||||||
|
Classes and objects may be mixed in one diagram.
|
||||||
|
|
||||||
|
|
||||||
|
<h2>Associations</h2>
|
||||||
|
|
||||||
|
\dot
|
||||||
|
digraph G {
|
||||||
|
node [shape=record, fontname=Helvetica, fontsize=10];
|
||||||
|
edge [arrowhead="open", labelfontname=Helvetica, labelfontsize=10,
|
||||||
|
color="#404040", labelfontcolor="#000080",
|
||||||
|
fontname=Helvetica, fontsize=10, fontcolor="#000080"];
|
||||||
|
fontname=Helvetica; fontsize=10;
|
||||||
|
A -> B [headlabel="*", taillabel="1", label="x"];
|
||||||
|
}
|
||||||
|
\enddot
|
||||||
|
|
||||||
|
In this example, one instance of A refers to an arbitrary number of B
|
||||||
|
instances (denoted by the "*"), and each instance of B is referred by
|
||||||
|
exactly one ("1") A. The label \em x is the name of the association,
|
||||||
|
in most cases the name of the field, e.g. A::x.
|
||||||
|
|
||||||
|
Possible other values for the \em multiplicity:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li> a concrete number, in most cases "1",
|
||||||
|
<li> a range, e.g. "0..1",
|
||||||
|
<li> "*", denoting an arbitrary number.
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
<h2>Implementations and Inheritance</h2>
|
||||||
|
|
||||||
|
\dot
|
||||||
|
digraph G {
|
||||||
|
node [shape=record, fontname=Helvetica, fontsize=10];
|
||||||
|
edge [arrowhead="none", dir="both", arrowtail="empty",
|
||||||
|
labelfontname=Helvetica, labelfontsize=10, color="#404040",
|
||||||
|
labelfontcolor="#000080"];
|
||||||
|
fontname=Helvetica; fontsize=10;
|
||||||
|
A[color="#ff8080"];
|
||||||
|
B[color="#ff8080"];
|
||||||
|
C;
|
||||||
|
D;
|
||||||
|
A -> B;
|
||||||
|
A -> C [style="dashed"];
|
||||||
|
C -> D;
|
||||||
|
}
|
||||||
|
\enddot
|
||||||
|
|
||||||
|
In this example,
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li> the interface B extends the interface A,
|
||||||
|
<li> the class C implements the interface A, and
|
||||||
|
<li> the class D extends the class C.
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
<h2>Template Instantiations</h2>
|
||||||
|
|
||||||
|
Template instantiations are shown as own classes/interfaces, the
|
||||||
|
instantiation by the template is shown by a yellow dashed arrow:
|
||||||
|
|
||||||
|
\dot
|
||||||
|
digraph G {
|
||||||
|
node [shape=record, fontname=Helvetica, fontsize=10];
|
||||||
|
edge [arrowhead="none", arrowtail="empty", dir="both",
|
||||||
|
labelfontname=Helvetica, labelfontsize=10, color="#404040",
|
||||||
|
labelfontcolor="#000080"];
|
||||||
|
fontname=Helvetica; fontsize=10;
|
||||||
|
|
||||||
|
A[color="#ff8080"];
|
||||||
|
B[color="#ff8080"];
|
||||||
|
C[color="#ff8080", fillcolor="#ffffc0", style="filled"];
|
||||||
|
C_A[color="#ff8080", label="C \<A\>"];
|
||||||
|
C_B[color="#ff8080", label="C \<A\>"];
|
||||||
|
D;
|
||||||
|
|
||||||
|
C -> C_A [arrowhead="open", arrowtail="none", style="dashed",
|
||||||
|
color="#808000"];
|
||||||
|
C -> C_B [arrowhead="open", arrowtail="none", style="dashed",
|
||||||
|
color="#808000"];
|
||||||
|
A -> C_A;
|
||||||
|
B -> C_B;
|
||||||
|
C_A -> D [style="dashed"];
|
||||||
|
}
|
||||||
|
\enddot
|
||||||
|
|
||||||
|
In this example, the interface template C uses the template argument
|
||||||
|
as super interface.
|
||||||
|
|
||||||
|
|
||||||
|
<h2>Packages</h2>
|
||||||
|
|
||||||
|
Packages are presented by dashed rectangles:
|
||||||
|
|
||||||
|
\dot
|
||||||
|
digraph G {
|
||||||
|
node [shape=record, fontname=Helvetica, fontsize=10];
|
||||||
|
edge [arrowhead="none", arrowtail="empty", dir="both",
|
||||||
|
labelfontname=Helvetica, labelfontsize=10, color="#404040",
|
||||||
|
labelfontcolor="#000080"];
|
||||||
|
fontname=Helvetica; fontsize=10;
|
||||||
|
|
||||||
|
subgraph cluster_1 {
|
||||||
|
style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
|
||||||
|
label="package 1";
|
||||||
|
|
||||||
|
A;
|
||||||
|
B [color="#a0a0a0"];
|
||||||
|
}
|
||||||
|
|
||||||
|
subgraph cluster_2 {
|
||||||
|
style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
|
||||||
|
label="package 2";
|
||||||
|
|
||||||
|
C;
|
||||||
|
D [color="#a0a0a0"];
|
||||||
|
E
|
||||||
|
}
|
||||||
|
|
||||||
|
A -> C;
|
||||||
|
B -> D;
|
||||||
|
D -> E;
|
||||||
|
E -> A [arrowhead="open", arrowtail="none"];
|
||||||
|
}
|
||||||
|
\enddot
|
||||||
|
|
||||||
|
Packages may be nested.
|
||||||
|
|
||||||
|
*/
|
||||||
175
dillo-install-hyphenation
Executable file
@ -0,0 +1,175 @@
|
|||||||
|
#!/usr/bin/env perl
|
||||||
|
use POSIX;
|
||||||
|
use File::Basename;
|
||||||
|
use Net::FTP;
|
||||||
|
use Getopt::Long;
|
||||||
|
|
||||||
|
$host = "mirrors.dotsrc.org";
|
||||||
|
$basesourcedir = "/ctan";
|
||||||
|
$sourcedir = "";
|
||||||
|
|
||||||
|
sub ftpmessage {
|
||||||
|
# I'm not sure whether $ftp->message is supposed to end with "\n"
|
||||||
|
# or not. To be sure (and have nicer output on the screen), it is
|
||||||
|
# removed here.
|
||||||
|
my $x = $ftp->message;
|
||||||
|
chomp $x;
|
||||||
|
return $x;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Determine ${prefix}, for the default target directory for pattern
|
||||||
|
# files. Two different strategies ...
|
||||||
|
$makefile = (dirname $0) . "/Makefile";
|
||||||
|
if (-e $makefile) {
|
||||||
|
# 1. Makefile exists in the same directory, so it can be assumed
|
||||||
|
# that this script is not yet installed, but called from the source
|
||||||
|
# directory. Search Makefile for prexix.
|
||||||
|
open MF, $makefile or die "Cannot open $makefile: $!";
|
||||||
|
while (<MF>) {
|
||||||
|
if (/^\s*prefix\s*=\s*(.*)\s*$/) {
|
||||||
|
$prefix = $1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close MF;
|
||||||
|
|
||||||
|
if ($prefix eq "") {
|
||||||
|
die "Prefix not defined in $makefile.";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
# 2. Assume that this is script is installed, so its path contains
|
||||||
|
# the prefix.
|
||||||
|
if ($0 =~ /(.*)\/bin\/[^\/]+$/) {
|
||||||
|
$prefix = $1;
|
||||||
|
} else {
|
||||||
|
die "Failed to determine prefix from $0.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$targetdir = "$prefix/lib/dillo/hyphenation";
|
||||||
|
|
||||||
|
if (!GetOptions ("host=s" => \$host,
|
||||||
|
"basesourcedir=s" => \$basesourcedir,
|
||||||
|
"sourcedir=s" => \$sourcedir,
|
||||||
|
"targetdir=s" => \$targetdir)
|
||||||
|
|| @ARGV == 0) {
|
||||||
|
print "Usage: $0 [OPTIONS] LANG [LANG ...]\n\n";
|
||||||
|
print <<EOT;
|
||||||
|
Download and install hyphenation patterns from CTAN for different languages,
|
||||||
|
via FTP. Languages are specified as defined by ISO 639-1 ("en" for English
|
||||||
|
etc.).
|
||||||
|
|
||||||
|
If there are multiple pattern files for a language (and so the filename is not
|
||||||
|
"hyph-LANG.pat.txt"), you must specify the respective part of the filename,
|
||||||
|
e. g. "en-gb" or "en-us". In this case, the extra part ("-gb" or "-us") is
|
||||||
|
automatically removed.
|
||||||
|
|
||||||
|
If you are not sure, simply specify the language code ("en" in this example);
|
||||||
|
you will then given a list of existing pattern files.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-h, --host=HOST the host to connect to (default:
|
||||||
|
mirrors.dotrc.org)
|
||||||
|
-s, --sourcedir=DIR the directory on the FTP server
|
||||||
|
-b, --basesourcedir=DIR alternatively, the base directory, after which
|
||||||
|
/language/hyph-utf8/tex/generic/hyph-utf8...
|
||||||
|
.../patterns/txt is added (default: /ctan/)
|
||||||
|
-t, --targetdir=DIR where to install the hyphenation patterns
|
||||||
|
(default: where they are read by dillo)
|
||||||
|
EOT
|
||||||
|
} else {
|
||||||
|
if ($sourcedir eq "") {
|
||||||
|
$sourcedir =
|
||||||
|
"$basesourcedir/language/hyph-utf8/tex/generic/hyph-utf8/patterns/txt";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!-e $targetdir) {
|
||||||
|
mkdir $targetdir or die "Cannot create directory $targetdir: $!";
|
||||||
|
print "Created $targetdir.\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Connect to CTAN FTP server, change to the directory where the
|
||||||
|
# patterns lie, and read files list (which may be useful later).
|
||||||
|
$ftp = Net::FTP->new($host,Timeout=>240)
|
||||||
|
or die "Cannot connect to $host: $@";
|
||||||
|
$ftp->login() or die "Cannot login: ", ftpmessage;
|
||||||
|
$ftp->cwd($sourcedir)
|
||||||
|
or die "Cannot change to directory $sourcedir: ", ftpmessage;
|
||||||
|
@files = $ftp->ls or die "Cannot read directory: ", ftpmessage;
|
||||||
|
|
||||||
|
# Finally, read pattern files.
|
||||||
|
foreach $arg (@ARGV) {
|
||||||
|
if ($arg =~ /^([a-z]+)-.*$/) {
|
||||||
|
# More files per language, e. g. "en-gb".
|
||||||
|
$lang = $1;
|
||||||
|
} else {
|
||||||
|
# One file per language, e. g. "ru".
|
||||||
|
$lang = $arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
# First, download the pattern file to a temporary file.
|
||||||
|
$tmppat = tmpnam();
|
||||||
|
if ($ftp->get ("hyph-$arg.pat.txt", $tmppat)) {
|
||||||
|
printf ("Successfully downloaded pattern file for \"$arg\".\n");
|
||||||
|
|
||||||
|
# Search for a licence file. (Only a warning, when it does
|
||||||
|
# not exist.)
|
||||||
|
$tmplic = tmpnam();
|
||||||
|
$licfound = 0;
|
||||||
|
if ($ftp->get ("hyph-$arg.lic.txt", $tmplic)) {
|
||||||
|
$licfound = 1;
|
||||||
|
} else {
|
||||||
|
print "Warning: Cannot download license file for \"$arg\": ",
|
||||||
|
ftpmessage, "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Combine both, licence and pattern, to the final pattern
|
||||||
|
# file.
|
||||||
|
$outfile = "$targetdir/$lang.pat";
|
||||||
|
open OUT, "> $outfile" or die "Cannot open $outfile: $!";
|
||||||
|
|
||||||
|
if ($licfound) {
|
||||||
|
print OUT
|
||||||
|
"% Licence from ftp://$host$sourcedir/hyph-$arg.lic.txt\n";
|
||||||
|
print OUT "%\n";
|
||||||
|
open IN, $tmplic or die "Cannot open $tmplic: $!";
|
||||||
|
while (<IN>) {
|
||||||
|
# Each line from the licence file must be a comment.
|
||||||
|
if (!/^%/) {
|
||||||
|
print OUT "% ";
|
||||||
|
}
|
||||||
|
print OUT;
|
||||||
|
}
|
||||||
|
close IN;
|
||||||
|
unlink $tmplic;
|
||||||
|
|
||||||
|
print OUT "%\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
print OUT "% Patterns from ftp://$host$sourcedir/hyph-$arg.pat.txt\n";
|
||||||
|
print OUT "%\n";
|
||||||
|
open IN, $tmppat or die "Cannot open $tmppat: $!";
|
||||||
|
while (<IN>) {
|
||||||
|
print OUT;
|
||||||
|
}
|
||||||
|
close IN;
|
||||||
|
unlink $tmppat;
|
||||||
|
|
||||||
|
close OUT;
|
||||||
|
} else {
|
||||||
|
# Not found. If a single language was specified (e. g. "en"),
|
||||||
|
# search for possibilities.
|
||||||
|
print "Error: Cannot download pattern file for \"$arg\": ",
|
||||||
|
ftpmessage, "\n";
|
||||||
|
if ($lang eq $arg) {
|
||||||
|
print "Try one of these:\n";
|
||||||
|
foreach(@files) {
|
||||||
|
if (/^hyph-($lang-.*)\.pat\.txt$/) {
|
||||||
|
print " $1\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$ftp->quit;
|
||||||
|
}
|
||||||
10
dillo.desktop
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[Desktop Entry]
|
||||||
|
GenericName=Web Browser
|
||||||
|
Name=Dillo
|
||||||
|
Comment=Fast and small graphical web browser
|
||||||
|
MimeType=text/html;text/xml;application/xhtml+xml;x-scheme-handler/http;x-scheme-handler/https;
|
||||||
|
Exec=dillo %U
|
||||||
|
Icon=dillo
|
||||||
|
Terminal=false
|
||||||
|
Type=Application
|
||||||
|
Categories=Network;WebBrowser;
|
||||||
469
dillorc
Normal file
@ -0,0 +1,469 @@
|
|||||||
|
# dillorc
|
||||||
|
# Sample dillo initialization file.
|
||||||
|
#
|
||||||
|
# Lines that start with a '#' are comments.
|
||||||
|
# "#option=..." shows the built-in default.
|
||||||
|
# "# option=..." is an additional example.
|
||||||
|
# "option=..." overrides the built-in value.
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
# FIRST SECTION :)
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Set the desired initial browser size
|
||||||
|
# geometry=650x545+0+20
|
||||||
|
#geometry=780x580
|
||||||
|
|
||||||
|
# Change this (and the following option) if you want to have text-only browsing
|
||||||
|
# from the start. (While browsing, this can be changed from the tools/settings
|
||||||
|
# menu.)
|
||||||
|
#load_images=YES
|
||||||
|
|
||||||
|
# A space separated list of image formats that will be ignored (not viewed).
|
||||||
|
# An option to download the image will be offered instead.
|
||||||
|
# Available formats: gif, png, webp, jpeg and svg.
|
||||||
|
# ignore_image_formats="webp svg"
|
||||||
|
#ignore_image_formats=""
|
||||||
|
|
||||||
|
# Change this if you want background images to be loaded initially.
|
||||||
|
# (While browsing, this can be changed from the tools/settings menu.)
|
||||||
|
#load_background_images=NO
|
||||||
|
|
||||||
|
# Change this if you want to disable loading of CSS stylesheets initially.
|
||||||
|
# (While browsing, this can be changed from the tools/settings menu.)
|
||||||
|
#load_stylesheets=YES
|
||||||
|
|
||||||
|
# Change this if you want to disable parsing of embedded CSS initially.
|
||||||
|
# (While browsing, this can be changed from the tools/settings menu.)
|
||||||
|
#parse_embedded_css=YES
|
||||||
|
|
||||||
|
# Change the buffering scheme for drawing
|
||||||
|
# 0 no double buffering - useful for debugging
|
||||||
|
# 1 light buffering using a single back buffer for all windows
|
||||||
|
# 2 full fltk-based double buffering for all windows
|
||||||
|
#buffered_drawing=1
|
||||||
|
|
||||||
|
# Set your default directory for download/save operations
|
||||||
|
#save_dir=/tmp
|
||||||
|
|
||||||
|
# Set the increment in pixels that the page is moved up/down with each input
|
||||||
|
# from the mouse wheel, keyboard arrow keys, or scrollbar arrow buttons.
|
||||||
|
#scroll_step=100
|
||||||
|
|
||||||
|
# Controls the overlap in pixels when scrolling to the next or previous page.
|
||||||
|
# It is used to repeat the last line(s) to avoid losing the reading flow. It
|
||||||
|
# controls the overlap in both the vertical and horizontal directions.
|
||||||
|
#scroll_page_overlap=50
|
||||||
|
|
||||||
|
# Place the vertical scrollbar on the left side (default right).
|
||||||
|
#scrollbar_on_left=NO
|
||||||
|
|
||||||
|
# Enable the page mode for the vertical scrollbar. When this mode is enabled,
|
||||||
|
# clicking anywhere on the vertical scrollbar with the left button scrolls one
|
||||||
|
# page down. With the right button, one page up. Click with the middle button to
|
||||||
|
# jump to a given position.
|
||||||
|
#scrollbar_page_mode=NO
|
||||||
|
|
||||||
|
# Define custom actions for the link menu. The format is <label>:<cmd>. The
|
||||||
|
# command will be executed in the system shell using the system() call. You can
|
||||||
|
# implement your own handling logic in a script or program. The following
|
||||||
|
# environment variables are set:
|
||||||
|
# $url: URL being opened
|
||||||
|
# $origin: URL of the current document
|
||||||
|
# Examples:
|
||||||
|
# link_action="Debug variables:echo url=$url origin=$origin"
|
||||||
|
# link_action="Open in MPV:mpv $url"
|
||||||
|
# link_action="Open in Firefox:firefox $url"
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
# RENDERING SECTION
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Default fonts:
|
||||||
|
#
|
||||||
|
# If FLTK has been configured with Xft enabled (the default), you can use
|
||||||
|
# scalable fonts such as DejaVu or Liberation (try running
|
||||||
|
# "fc-list : family | cut -d ',' -f 2 | sort").
|
||||||
|
#font_serif="DejaVu Serif"
|
||||||
|
#font_sans_serif="DejaVu Sans"
|
||||||
|
#font_cursive="DejaVu Sans"
|
||||||
|
#font_fantasy="DejaVu Sans"
|
||||||
|
#font_monospace="DejaVu Sans Mono"
|
||||||
|
#
|
||||||
|
# Otherwise, use bitmapped fonts like the following (for a list, try running
|
||||||
|
# "xlsfonts -fn *-iso10646-1 | grep -v -e -0-0 | cut -d - -f 3 | sort | uniq").
|
||||||
|
# font_serif="times"
|
||||||
|
# font_sans_serif="helvetica"
|
||||||
|
# font_cursive="helvetica"
|
||||||
|
# font_fantasy="helvetica"
|
||||||
|
# font_monospace="courier"
|
||||||
|
|
||||||
|
# All font sizes are scaled by this value
|
||||||
|
# font_factor=1.5
|
||||||
|
#font_factor=1.0
|
||||||
|
|
||||||
|
# Maximum font size in pixels
|
||||||
|
#font_max_size=100
|
||||||
|
|
||||||
|
# Minimum font size in pixels
|
||||||
|
#font_min_size=6
|
||||||
|
|
||||||
|
# Show tooltip popups for HTML title attributes
|
||||||
|
#show_tooltip=YES
|
||||||
|
|
||||||
|
# Set this to YES to limit the word wrap width to the viewport width
|
||||||
|
#limit_text_width=NO
|
||||||
|
|
||||||
|
# If this is set to YES, all CSS size specifications are adjusted so that
|
||||||
|
# all contents can be displayed. (Except for tables, see below.)
|
||||||
|
#adjust_min_width=YES
|
||||||
|
|
||||||
|
# If this is set to YES, all CSS size specifications for tables are
|
||||||
|
# adjusted so that all contents can be displayed. This is separated
|
||||||
|
# from "adjust_min_width" so that it is able to mimic Firefox, which
|
||||||
|
# differentiates between tables and, say, textblocks (in some cases).
|
||||||
|
#adjust_table_min_width=YES
|
||||||
|
|
||||||
|
# Sets the initial zoom factor, which scales the size of all HTML elements.
|
||||||
|
# zoom_factor=1.5
|
||||||
|
#zoom_factor=1.0
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
# PENALTIES
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Penalties are used to control good and bad break points. The bigger
|
||||||
|
# the penalty for a given break point, the less likely the line is
|
||||||
|
# broken here. "inf" means that breaking is prohibited, "-inf" means
|
||||||
|
# that a line *must* be broken here. (The latter should not be used
|
||||||
|
# here, however.) Normal spaces get a penalty of 0. The exact
|
||||||
|
# definition can be found in doc/dw-line-breaking.doc.
|
||||||
|
|
||||||
|
# Penalties for hyphenation breaks; this covers automatic hyphenation,
|
||||||
|
# soft hyphens, and unconditional hyphens. Since hyphenation should
|
||||||
|
# rather be avoided, the default values are larger than 0.
|
||||||
|
|
||||||
|
# This is used for hyphenation points, when there is no hyphen or dash
|
||||||
|
# before:
|
||||||
|
#penalty_hyphen = 1
|
||||||
|
|
||||||
|
# This is used for hyphenation points, when the line before ends
|
||||||
|
# already with a hyphen or a dash. Consequent lines ending with
|
||||||
|
# hyphens or dashes should be avoided, so this value is bigger than
|
||||||
|
# "penalty_hyphen":
|
||||||
|
#penalty_hyphen_2 = 8
|
||||||
|
|
||||||
|
# The same for a break right of an em-dash, when there are no spaces
|
||||||
|
# surrounding it (as in English). The default values are the same as
|
||||||
|
# for hyphens:
|
||||||
|
#penalty_em_dash_right = 1
|
||||||
|
#penalty_em_dash_right_2 = 8
|
||||||
|
|
||||||
|
# Penalty for a break *left* of an em-dash. Since a line ending with
|
||||||
|
# an em-dash (and so breaking right of the em-dash) looks better than
|
||||||
|
# a line beginning with an em-dash (breaking left of an em-dash), the
|
||||||
|
# default value is bigger than "penalty_em_dash_right":
|
||||||
|
#penalty_em_dash_left = 8
|
||||||
|
|
||||||
|
# Notice that there is no "penalty_em_dash_left_2", since breaking
|
||||||
|
# left of an em-dash makes the line *begin*, not *end* with a dash.
|
||||||
|
|
||||||
|
# This factor is multiplied with the line height to get the
|
||||||
|
# stretchability of a non-justified line. The larger this factor (and
|
||||||
|
# thus, the stretchability), the less likely the words are hyphenated;
|
||||||
|
# so you can use this value to control hyphenation of non-justified
|
||||||
|
# text.
|
||||||
|
#stretchability_factor=1
|
||||||
|
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
# NETWORK SECTION
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Set the start page.
|
||||||
|
# start_page="about:blank"
|
||||||
|
# start_page="https://dillo-browser.github.io/"
|
||||||
|
# start_page="file:/home/jcid/custom_page.html"
|
||||||
|
#start_page="about:splash"
|
||||||
|
|
||||||
|
# Set the home location
|
||||||
|
# home="file:/home/jcid/HomePage/Home.html"
|
||||||
|
#home="https://dillo-browser.github.io/"
|
||||||
|
|
||||||
|
# Set the new tab page.
|
||||||
|
# new_tab_page="dpi:/bm/"
|
||||||
|
# new_tab_page="https://example.com/"
|
||||||
|
#new_tab_page="about:blank"
|
||||||
|
|
||||||
|
# Set the URLs used by the web search dialog.
|
||||||
|
# "%s" is replaced with the search keywords separated by '+'.
|
||||||
|
# Format: search_url="[prefix ][<label> ]<url>"
|
||||||
|
# You can enable multiple search_url strings at once and select from among
|
||||||
|
# them at runtime, with the first being the default.
|
||||||
|
# (the prefix serves to search from the Location Bar. e.g. "dd dillo image")
|
||||||
|
search_url="dd DuckDuckGo (https) https://duckduckgo.com/lite/?kp=-1&kd=-1&q=%s"
|
||||||
|
search_url="Wikipedia http://www.wikipedia.org/w/index.php?search=%s&go=Go"
|
||||||
|
search_url="Free Dictionary http://www.thefreedictionary.com/%s"
|
||||||
|
search_url="Startpage (https) https://www.startpage.com/do/search?query=%s"
|
||||||
|
search_url="Google https://www.google.com/search?ie=UTF-8&oe=UTF-8&gbv=1&q=%s"
|
||||||
|
|
||||||
|
# If set, dillo will ask web servers to send pages in this language.
|
||||||
|
# This setting does NOT change dillo's user interface.
|
||||||
|
# Format explained: www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
|
||||||
|
# Language-REGION values: www.iana.org/assignments/language-subtag-registry
|
||||||
|
# (by default, no Accept-Language header is sent)
|
||||||
|
# http_language="de"
|
||||||
|
# http_language="pt-BR"
|
||||||
|
# http_language="en-US,en;q=0.5"
|
||||||
|
|
||||||
|
# Maximum number of simultaneous TCP connections to a single server or proxy.
|
||||||
|
# http_max_conns=6
|
||||||
|
|
||||||
|
# If enabled, Dillo will reuse HTTP connections to a server or proxy when
|
||||||
|
# possible rather than making a new connection for every request for a new
|
||||||
|
# page/image/stylesheet.
|
||||||
|
#http_persistent_conns=YES
|
||||||
|
|
||||||
|
# This mechanism allows servers to specify that they are only to be contacted
|
||||||
|
# through HTTPS and not HTTP.
|
||||||
|
#
|
||||||
|
# Overall, this is a valuable security measure against TLS stripping
|
||||||
|
# attacks, etc., but in principle a site could contrive to use this as a
|
||||||
|
# tracking mechanism. The term is "HSTS super cookie", although note that these
|
||||||
|
# HSTS directives are not saved between browser sessions.
|
||||||
|
#http_strict_transport_security=YES
|
||||||
|
|
||||||
|
# If enabled, Dillo will force all HTTP connections to be upgraded to
|
||||||
|
# a more secure HTTPS connection. This will prevent sites from loading
|
||||||
|
# if they only support HTTP.
|
||||||
|
#http_force_https=NO
|
||||||
|
|
||||||
|
# Set the proxy information for http/https.
|
||||||
|
# Note that the http_proxy environment variable overrides this setting.
|
||||||
|
# WARNING: FTP and downloads plugins use wget. To use a proxy with them,
|
||||||
|
# you will need to configure wget accordingly. See
|
||||||
|
# http://www.gnu.org/software/wget/manual/html_node/Proxies.html
|
||||||
|
# http_proxy="http://localhost:8080/"
|
||||||
|
#(by default, no proxy is used)
|
||||||
|
|
||||||
|
# If you need to provide a user/password pair for the proxy,
|
||||||
|
# set the proxy user name here and Dillo will ask for the password later.
|
||||||
|
# http_proxyuser="joe"
|
||||||
|
#(by default, no proxy is used)
|
||||||
|
|
||||||
|
# Set the domains to access without proxy
|
||||||
|
# no_proxy = ".hola.com .mynet.cl .hi.de"
|
||||||
|
#no_proxy="localhost 127.0.0.1"
|
||||||
|
|
||||||
|
# Set the HTTP Referer (sic) header.
|
||||||
|
# Note that there is no option to reveal the page that you came from because it
|
||||||
|
# would endanger your privacy. 'host' and 'path' allow you to pretend that the
|
||||||
|
# link you followed was on the same site that you're going to.
|
||||||
|
# none : Don't send any Referer header at all.
|
||||||
|
# host : Send the requested URI's hostname.
|
||||||
|
# path : Send the requested URI's host and path.
|
||||||
|
#http_referer=host
|
||||||
|
|
||||||
|
# Set the HTTP User-Agent header.
|
||||||
|
# This can be useful for privacy and for working around servers who think
|
||||||
|
# Dillo is less capable than it really is. However, if you pretend to use a
|
||||||
|
# different browser, servers may send you pages that work with the features
|
||||||
|
# and bugs of that other browser -- or even disallow access in cases like
|
||||||
|
# wget or googlebot. Remember this before submitting bug reports.
|
||||||
|
#
|
||||||
|
# See http://zytrax.com/tech/web/browser_ids.htm for a compilation of strings.
|
||||||
|
#
|
||||||
|
# http_user_agent="Mozilla/5.0 (Windows NT 6.1; WOW64; rv:28.0) Gecko/20100101 Firefox/28.0"
|
||||||
|
# http_user_agent="Wget/1.13.4 (linux-gnu)"
|
||||||
|
#The default is "Dillo/"+current_version_number
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
# COLORS SECTION
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Set the page background color
|
||||||
|
# bg_color=gray
|
||||||
|
# bg_color=0xd6d6c0
|
||||||
|
#bg_color=0xdcd1ba
|
||||||
|
|
||||||
|
# If your eyes suffer with white backgrounds, change this.
|
||||||
|
#allow_white_bg=YES
|
||||||
|
|
||||||
|
# If allow_white_bg is set to NO, white backgrounds are replaced by
|
||||||
|
# this color.
|
||||||
|
#white_bg_replacement=0xe0e0a3
|
||||||
|
|
||||||
|
# When set to YES, the page author's visited link color may be overridden
|
||||||
|
# to allow better contrast with text/links/background
|
||||||
|
#contrast_visited_color=YES
|
||||||
|
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
# USER INTERFACE SECTION
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# UI theme
|
||||||
|
# "none" is the default FLTK appearance, which "resembles old Windows...and
|
||||||
|
# old GTK/KDE".
|
||||||
|
# "plastic" "is inspired by the Aqua user interface on Mac OS X".
|
||||||
|
# "gtk+" "is inspired by the Red Hat Bluecurve theme".
|
||||||
|
#
|
||||||
|
# If you have fltk-1.3.3 or newer, you can specify "gleam", which
|
||||||
|
# is "a sort of Clearlooks Glossy scheme". ("fltk-config --version")
|
||||||
|
#theme=none
|
||||||
|
# theme=gtk+
|
||||||
|
# theme=plastic
|
||||||
|
|
||||||
|
# UI colors
|
||||||
|
# Note that FLTK may sometimes override colors, generally for contrast and
|
||||||
|
# readability.
|
||||||
|
#
|
||||||
|
# ui_fg_color, ui_main_bg_color, ui_text_bg_color, and ui_selection_color
|
||||||
|
# map to concepts in the underlying FLTK toolkit which are described as:
|
||||||
|
# "the default foreground color...used for labels and text", "default
|
||||||
|
# background color", "the default background color for text, list, and
|
||||||
|
# valuator widgets", and "the default selection/highlight color". They
|
||||||
|
# sometimes have other uses in the more complex FLTK widgets.
|
||||||
|
#
|
||||||
|
# ui_button_highlight_color is the background used when the mouse cursor is
|
||||||
|
# over a button. By default, this is a lightened version of the main
|
||||||
|
# background color.
|
||||||
|
#
|
||||||
|
# ui_tab_active_fg_color and ui_tab_active_bg_color are used for the current
|
||||||
|
# tab. By default, they are the main foreground color and the text background
|
||||||
|
# color, respectively.
|
||||||
|
#
|
||||||
|
# ui_tab_fg_color and ui_tab_bg_color are used for the other tabs. By default,
|
||||||
|
# they are the main foreground color and the main background color,
|
||||||
|
# respectively.
|
||||||
|
#
|
||||||
|
# ui_tab_height controls the height of the tabs (default 20).
|
||||||
|
#
|
||||||
|
# Note to packagers: leaving these variables for the system to guess
|
||||||
|
# gives different results in different environments, so we played it safe
|
||||||
|
# by defining the traditional colors. Please choose the color theme that
|
||||||
|
# better fits your distro.
|
||||||
|
|
||||||
|
#
|
||||||
|
# Gray theme (traditional)
|
||||||
|
#
|
||||||
|
ui_fg_color=black
|
||||||
|
ui_main_bg_color=#c6c6c6
|
||||||
|
ui_text_bg_color=#bfdabf
|
||||||
|
ui_selection_color=#191970
|
||||||
|
ui_button_highlight_color=#a9a9a9
|
||||||
|
ui_tab_active_bg_color=#87aca7
|
||||||
|
ui_tab_active_fg_color=black
|
||||||
|
ui_tab_bg_color=#b7beb7
|
||||||
|
|
||||||
|
#
|
||||||
|
# Earthly theme:
|
||||||
|
#
|
||||||
|
#ui_fg_color=#100404
|
||||||
|
#ui_main_bg_color=#c2a47b
|
||||||
|
#ui_text_bg_color=#cdc9a5
|
||||||
|
#ui_selection_color=#763024
|
||||||
|
#ui_tab_active_bg_color=#af4b3f
|
||||||
|
#ui_tab_active_fg_color=white
|
||||||
|
#ui_tab_bg_color=#d2b48c
|
||||||
|
|
||||||
|
#
|
||||||
|
# Greenish theme:
|
||||||
|
#
|
||||||
|
#ui_fg_color=#100404
|
||||||
|
#ui_main_bg_color=#c8d394
|
||||||
|
#ui_text_bg_color=#bdd8b6
|
||||||
|
#ui_selection_color=#7c5f42
|
||||||
|
#ui_button_highlight_color=#adad70
|
||||||
|
#ui_tab_active_bg_color=#b5b679
|
||||||
|
#ui_tab_active_fg_color=#b60907
|
||||||
|
#ui_tab_bg_color=#cac682
|
||||||
|
|
||||||
|
|
||||||
|
# Size of dillo panel
|
||||||
|
# tiny : buttons, location, and progress boxes in one row
|
||||||
|
# small : location in one row, buttons + progress boxes in another
|
||||||
|
# medium : adds text labels to buttons and boxes
|
||||||
|
# panel_size=tiny
|
||||||
|
# panel_size=small
|
||||||
|
#panel_size=medium
|
||||||
|
|
||||||
|
#small_icons=NO
|
||||||
|
|
||||||
|
# Here you can choose to hide some widgets of the dillo panel...
|
||||||
|
#show_back=YES
|
||||||
|
#show_forw=YES
|
||||||
|
#show_home=YES
|
||||||
|
#show_reload=YES
|
||||||
|
#show_save=YES
|
||||||
|
#show_stop=YES
|
||||||
|
#show_bookmarks=YES
|
||||||
|
#show_tools=YES
|
||||||
|
#show_filemenu=YES
|
||||||
|
#show_clear_url=YES
|
||||||
|
#show_url=YES
|
||||||
|
#show_search=YES
|
||||||
|
#show_help=YES
|
||||||
|
#show_progress_box=YES
|
||||||
|
|
||||||
|
# Show tooltip popups for the UI
|
||||||
|
#show_ui_tooltip=YES
|
||||||
|
|
||||||
|
# Start dillo with the panels hidden?
|
||||||
|
#fullwindow_start=NO
|
||||||
|
|
||||||
|
# When filling out forms, our default behaviour is to submit on enterpress,
|
||||||
|
# but only when there's a single text entry (to avoid incomplete submits).
|
||||||
|
# OTOH, if you have to fill out the same form repeatedly, you may find it
|
||||||
|
# useful to keep away from the mouse by forcing enter to submit.
|
||||||
|
#enterpress_forces_submit=NO
|
||||||
|
|
||||||
|
# A mouse's middle click over a link opens a new Tab.
|
||||||
|
# If you prefer to open a new Window instead, set it to NO.
|
||||||
|
#middle_click_opens_new_tab=YES
|
||||||
|
|
||||||
|
# A mouse's middle click over a tab closes the Tab.
|
||||||
|
# With mousewheel mouses, right click feels way better (set to YES).
|
||||||
|
#right_click_closes_tab=YES
|
||||||
|
|
||||||
|
# Scroll over tabs (using the mouse wheel) to switch among tabs.
|
||||||
|
# If set to NO, the page will be scrolled instead.
|
||||||
|
#scroll_switches_tabs=YES
|
||||||
|
|
||||||
|
# Reverse the direction of tab scrolling with mouse wheel.
|
||||||
|
#scroll_switches_tabs_reverse=NO
|
||||||
|
|
||||||
|
# Mouse middle click by default drives drag-scrolling.
|
||||||
|
# To paste an URL into the window instead of scrolling, set it to NO.
|
||||||
|
# Note: You could always paste the URL onto the URL box clear button.
|
||||||
|
#middle_click_drags_page=YES
|
||||||
|
|
||||||
|
# Focus follows new Tabs.
|
||||||
|
# You can hold SHIFT to temporarily revert this behaviour.
|
||||||
|
#focus_new_tab=NO
|
||||||
|
|
||||||
|
# Ask before quitting Dillo with more than one window or tab open.
|
||||||
|
#show_quit_dialog=NO
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
# DEBUG MESSAGES SECTION
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Soon we should add the "show_debug_messages=NO" option...
|
||||||
|
|
||||||
|
# Generic messages (mainly for debugging specific parts)
|
||||||
|
# Change this to disable them.
|
||||||
|
#show_msg=YES
|
||||||
|
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
# HTML BUG MESSAGES SECTION
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Accepted by the W3C validator but "strongly discouraged" by the SPEC.
|
||||||
|
# (Such as "TAB character inside <PRE>").
|
||||||
|
#show_extra_warnings=NO
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
# dillorc ends here.
|
||||||
8
dlib/Makefile.am
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
AM_CPPFLAGS = \
|
||||||
|
-I$(top_srcdir)
|
||||||
|
|
||||||
|
noinst_LIBRARIES = libDlib.a
|
||||||
|
|
||||||
|
libDlib_a_SOURCES = \
|
||||||
|
dlib.h \
|
||||||
|
dlib.c
|
||||||
980
dlib/dlib.c
Normal file
@ -0,0 +1,980 @@
|
|||||||
|
/*
|
||||||
|
* File: dlib.c
|
||||||
|
*
|
||||||
|
* Copyright (C) 2006-2007 Jorge Arellano Cid <jcid@dillo.org>
|
||||||
|
* Copyright (C) 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Memory allocation, Simple dynamic strings, Lists (simple and sorted),
|
||||||
|
* and a few utility functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: vsnprintf() is in C99, maybe a simple replacement if necessary.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "dlib.h"
|
||||||
|
|
||||||
|
static bool_t dLib_show_msg = TRUE;
|
||||||
|
|
||||||
|
/* dlib msgs go to stderr to avoid problems with filter dpis */
|
||||||
|
#define DLIB_MSG(...) \
|
||||||
|
D_STMT_START { \
|
||||||
|
if (dLib_show_msg) \
|
||||||
|
fprintf(stderr, __VA_ARGS__); \
|
||||||
|
} D_STMT_END
|
||||||
|
|
||||||
|
/*
|
||||||
|
*- Memory --------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
void *dMalloc (size_t size)
|
||||||
|
{
|
||||||
|
void *value = malloc (size);
|
||||||
|
if (value == 0)
|
||||||
|
exit(1);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *dRealloc (void *mem, size_t size)
|
||||||
|
{
|
||||||
|
void *value = realloc (mem, size);
|
||||||
|
if (value == 0)
|
||||||
|
exit(1);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *dMalloc0 (size_t size)
|
||||||
|
{
|
||||||
|
void *value = dMalloc (size);
|
||||||
|
memset (value, 0, size);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dFree (void *mem)
|
||||||
|
{
|
||||||
|
free(mem);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*- strings (char *) ----------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
char *dStrdup(const char *s)
|
||||||
|
{
|
||||||
|
if (s) {
|
||||||
|
int len = strlen(s)+1;
|
||||||
|
char *ns = dNew(char, len);
|
||||||
|
memcpy(ns, s, len);
|
||||||
|
return ns;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *dStrndup(const char *s, size_t sz)
|
||||||
|
{
|
||||||
|
if (s) {
|
||||||
|
char *ns = dNew(char, sz+1);
|
||||||
|
memcpy(ns, s, sz);
|
||||||
|
ns[sz] = 0;
|
||||||
|
return ns;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Concatenate a NULL-terminated list of strings
|
||||||
|
*/
|
||||||
|
char *dStrconcat(const char *s1, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
char *s, *ns = NULL;
|
||||||
|
|
||||||
|
if (s1) {
|
||||||
|
Dstr *dstr = dStr_sized_new(64);
|
||||||
|
va_start(args, s1);
|
||||||
|
for (s = (char*)s1; s; s = va_arg(args, char*))
|
||||||
|
dStr_append(dstr, s);
|
||||||
|
va_end(args);
|
||||||
|
ns = dstr->str;
|
||||||
|
dStr_free(dstr, 0);
|
||||||
|
}
|
||||||
|
return ns;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove leading and trailing whitespace
|
||||||
|
*/
|
||||||
|
char *dStrstrip(char *s)
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
if (s && *s) {
|
||||||
|
for (p = s; dIsspace(*p); ++p);
|
||||||
|
for (len = strlen(p); len && dIsspace(p[len-1]); --len);
|
||||||
|
if (p > s)
|
||||||
|
memmove(s, p, len);
|
||||||
|
s[len] = 0;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the contents of the string
|
||||||
|
*/
|
||||||
|
void dStrshred(char *s)
|
||||||
|
{
|
||||||
|
if (s)
|
||||||
|
memset(s, 0, strlen(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a new string of length 'len' filled with 'c' characters
|
||||||
|
*/
|
||||||
|
char *dStrnfill(size_t len, char c)
|
||||||
|
{
|
||||||
|
char *ret = dNew(char, len+1);
|
||||||
|
for (ret[len] = 0; len > 0; ret[--len] = c);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* strsep() implementation
|
||||||
|
*/
|
||||||
|
char *dStrsep(char **orig, const char *delim)
|
||||||
|
{
|
||||||
|
char *str, *p;
|
||||||
|
|
||||||
|
if (!(str = *orig))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
p = strpbrk(str, delim);
|
||||||
|
if (p) {
|
||||||
|
*p++ = 0;
|
||||||
|
*orig = p;
|
||||||
|
} else {
|
||||||
|
*orig = NULL;
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ASCII functions to avoid the case difficulties introduced by I/i in
|
||||||
|
* Turkic locales.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Case insensitive strstr
|
||||||
|
*/
|
||||||
|
char *dStriAsciiStr(const char *haystack, const char *needle)
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
char *ret = NULL;
|
||||||
|
|
||||||
|
if (haystack && needle) {
|
||||||
|
for (i = 0, j = 0; haystack[i] && needle[j]; ++i)
|
||||||
|
if (D_ASCII_TOLOWER(haystack[i]) == D_ASCII_TOLOWER(needle[j])) {
|
||||||
|
++j;
|
||||||
|
} else if (j) {
|
||||||
|
i -= j;
|
||||||
|
j = 0;
|
||||||
|
}
|
||||||
|
if (!needle[j]) /* Got all */
|
||||||
|
ret = (char *)(haystack + i - j);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dStrAsciiCasecmp(const char *s1, const char *s2)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
while ((*s1 || *s2) &&
|
||||||
|
!(ret = D_ASCII_TOLOWER(*s1) - D_ASCII_TOLOWER(*s2))) {
|
||||||
|
s1++;
|
||||||
|
s2++;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dStrnAsciiCasecmp(const char *s1, const char *s2, size_t n)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
while (n-- && (*s1 || *s2) &&
|
||||||
|
!(ret = D_ASCII_TOLOWER(*s1) - D_ASCII_TOLOWER(*s2))) {
|
||||||
|
s1++;
|
||||||
|
s2++;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*- dStr ----------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private allocator
|
||||||
|
*/
|
||||||
|
static void dStr_resize(Dstr *ds, int n_sz, int keep)
|
||||||
|
{
|
||||||
|
if (n_sz >= 0) {
|
||||||
|
if (keep && n_sz > ds->len) {
|
||||||
|
ds->str = (Dstr_char_t*) dRealloc (ds->str, n_sz*sizeof(Dstr_char_t));
|
||||||
|
ds->sz = n_sz;
|
||||||
|
} else {
|
||||||
|
dFree(ds->str);
|
||||||
|
ds->str = dNew(Dstr_char_t, n_sz);
|
||||||
|
ds->sz = n_sz;
|
||||||
|
ds->len = 0;
|
||||||
|
ds->str[0] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new string with a given size.
|
||||||
|
* Initialized to ""
|
||||||
|
*/
|
||||||
|
Dstr *dStr_sized_new (int sz)
|
||||||
|
{
|
||||||
|
Dstr *ds;
|
||||||
|
if (sz < 2)
|
||||||
|
sz = 2;
|
||||||
|
|
||||||
|
ds = dNew(Dstr, 1);
|
||||||
|
ds->str = NULL;
|
||||||
|
dStr_resize(ds, sz + 1, 0); /* (sz + 1) for the extra '\0' */
|
||||||
|
return ds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return memory if there's too much allocated.
|
||||||
|
*/
|
||||||
|
void dStr_fit (Dstr *ds)
|
||||||
|
{
|
||||||
|
dStr_resize(ds, ds->len + 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert a C string, at a given position, into a Dstr (providing length).
|
||||||
|
* Note: It also works with embedded nil characters.
|
||||||
|
*/
|
||||||
|
void dStr_insert_l (Dstr *ds, int pos_0, const char *s, int l)
|
||||||
|
{
|
||||||
|
int n_sz;
|
||||||
|
|
||||||
|
if (ds && s && l && pos_0 >= 0 && pos_0 <= ds->len) {
|
||||||
|
for (n_sz = ds->sz; ds->len + l >= n_sz; n_sz *= 2);
|
||||||
|
if (n_sz > ds->sz) {
|
||||||
|
dStr_resize(ds, n_sz, (ds->len > 0) ? 1 : 0);
|
||||||
|
}
|
||||||
|
if (pos_0 < ds->len)
|
||||||
|
memmove(ds->str+pos_0+l, ds->str+pos_0, ds->len-pos_0);
|
||||||
|
memcpy(ds->str+pos_0, s, l);
|
||||||
|
ds->len += l;
|
||||||
|
ds->str[ds->len] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert a C string, at a given position, into a Dstr.
|
||||||
|
*/
|
||||||
|
void dStr_insert (Dstr *ds, int pos_0, const char *s)
|
||||||
|
{
|
||||||
|
if (s)
|
||||||
|
dStr_insert_l(ds, pos_0, s, strlen(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append a C string to a Dstr (providing length).
|
||||||
|
* Note: It also works with embedded nil characters.
|
||||||
|
*/
|
||||||
|
void dStr_append_l (Dstr *ds, const char *s, int l)
|
||||||
|
{
|
||||||
|
dStr_insert_l(ds, ds->len, s, l);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append a C string to a Dstr.
|
||||||
|
*/
|
||||||
|
void dStr_append (Dstr *ds, const char *s)
|
||||||
|
{
|
||||||
|
dStr_append_l(ds, s, strlen(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new string.
|
||||||
|
* Initialized to 's' or empty if 's == NULL'
|
||||||
|
*/
|
||||||
|
Dstr *dStr_new (const char *s)
|
||||||
|
{
|
||||||
|
Dstr *ds = dStr_sized_new(0);
|
||||||
|
if (s && *s)
|
||||||
|
dStr_append(ds, s);
|
||||||
|
return ds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free a dillo string.
|
||||||
|
* if 'all' free everything, else free the structure only.
|
||||||
|
*/
|
||||||
|
void dStr_free (Dstr *ds, int all)
|
||||||
|
{
|
||||||
|
if (ds) {
|
||||||
|
if (all)
|
||||||
|
dFree(ds->str);
|
||||||
|
dFree(ds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append one character.
|
||||||
|
*/
|
||||||
|
void dStr_append_c (Dstr *ds, int c)
|
||||||
|
{
|
||||||
|
char cs[2];
|
||||||
|
|
||||||
|
if (ds) {
|
||||||
|
if (ds->sz > ds->len + 1) {
|
||||||
|
ds->str[ds->len++] = (Dstr_char_t)c;
|
||||||
|
ds->str[ds->len] = 0;
|
||||||
|
} else {
|
||||||
|
cs[0] = (Dstr_char_t)c;
|
||||||
|
cs[1] = 0;
|
||||||
|
dStr_append_l (ds, cs, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Truncate a Dstr to be 'len' bytes long.
|
||||||
|
*/
|
||||||
|
void dStr_truncate (Dstr *ds, int len)
|
||||||
|
{
|
||||||
|
if (ds && len < ds->len) {
|
||||||
|
ds->str[len] = 0;
|
||||||
|
ds->len = len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear a Dstr.
|
||||||
|
*/
|
||||||
|
void dStr_shred (Dstr *ds)
|
||||||
|
{
|
||||||
|
if (ds && ds->sz > 0)
|
||||||
|
memset(ds->str, '\0', ds->sz);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erase a substring.
|
||||||
|
*/
|
||||||
|
void dStr_erase (Dstr *ds, int pos_0, int len)
|
||||||
|
{
|
||||||
|
if (ds && pos_0 >= 0 && len > 0 && pos_0 + len <= ds->len) {
|
||||||
|
memmove(ds->str + pos_0, ds->str + pos_0 + len, ds->len - pos_0 - len);
|
||||||
|
ds->len -= len;
|
||||||
|
ds->str[ds->len] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vsprintf-like function that appends.
|
||||||
|
* Used by: dStr_vsprintf(), dStr_sprintf() and dStr_sprintfa().
|
||||||
|
*/
|
||||||
|
void dStr_vsprintfa (Dstr *ds, const char *format, va_list argp)
|
||||||
|
{
|
||||||
|
int n, n_sz;
|
||||||
|
|
||||||
|
if (ds && format) {
|
||||||
|
va_list argp2; /* Needed in case of looping on non-32bit arch */
|
||||||
|
while (1) {
|
||||||
|
va_copy(argp2, argp);
|
||||||
|
n = vsnprintf(ds->str + ds->len, ds->sz - ds->len, format, argp2);
|
||||||
|
va_end(argp2);
|
||||||
|
#if defined(__sgi)
|
||||||
|
/* IRIX does not conform to C99; if the entire argument did not fit
|
||||||
|
* into the buffer, n = buffer space used (minus 1 for terminator)
|
||||||
|
*/
|
||||||
|
if (n > -1 && n + 1 < ds->sz - ds->len) {
|
||||||
|
ds->len += n; /* Success! */
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
n_sz = ds->sz * 2;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (n > -1 && n < ds->sz - ds->len) {
|
||||||
|
ds->len += n; /* Success! */
|
||||||
|
break;
|
||||||
|
} else if (n > -1) { /* glibc >= 2.1 */
|
||||||
|
n_sz = ds->len + n + 1;
|
||||||
|
} else { /* old glibc */
|
||||||
|
n_sz = ds->sz * 2;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
dStr_resize(ds, n_sz, (ds->len > 0) ? 1 : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vsprintf-like function.
|
||||||
|
*/
|
||||||
|
void dStr_vsprintf (Dstr *ds, const char *format, va_list argp)
|
||||||
|
{
|
||||||
|
if (ds) {
|
||||||
|
dStr_truncate(ds, 0);
|
||||||
|
dStr_vsprintfa(ds, format, argp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Printf-like function
|
||||||
|
*/
|
||||||
|
void dStr_sprintf (Dstr *ds, const char *format, ...)
|
||||||
|
{
|
||||||
|
va_list argp;
|
||||||
|
|
||||||
|
if (ds && format) {
|
||||||
|
va_start(argp, format);
|
||||||
|
dStr_vsprintf(ds, format, argp);
|
||||||
|
va_end(argp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Printf-like function that appends.
|
||||||
|
*/
|
||||||
|
void dStr_sprintfa (Dstr *ds, const char *format, ...)
|
||||||
|
{
|
||||||
|
va_list argp;
|
||||||
|
|
||||||
|
if (ds && format) {
|
||||||
|
va_start(argp, format);
|
||||||
|
dStr_vsprintfa(ds, format, argp);
|
||||||
|
va_end(argp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare two dStrs.
|
||||||
|
*/
|
||||||
|
int dStr_cmp(Dstr *ds1, Dstr *ds2)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (ds1 && ds2)
|
||||||
|
ret = memcmp(ds1->str, ds2->str, MIN(ds1->len+1, ds2->len+1));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a pointer to the first occurrence of needle in haystack.
|
||||||
|
*/
|
||||||
|
char *dStr_memmem(Dstr *haystack, Dstr *needle)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (needle && haystack) {
|
||||||
|
if (needle->len == 0)
|
||||||
|
return haystack->str;
|
||||||
|
|
||||||
|
for (i = 0; i <= (haystack->len - needle->len); i++) {
|
||||||
|
if (haystack->str[i] == needle->str[0] &&
|
||||||
|
!memcmp(haystack->str + i, needle->str, needle->len))
|
||||||
|
return haystack->str + i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a printable representation of the provided Dstr, limited to a length
|
||||||
|
* of roughly maxlen.
|
||||||
|
*
|
||||||
|
* This is NOT threadsafe.
|
||||||
|
*/
|
||||||
|
const char *dStr_printable(Dstr *in, int maxlen)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
static const char *const HEX = "0123456789ABCDEF";
|
||||||
|
static Dstr *out = NULL;
|
||||||
|
|
||||||
|
if (in == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (out)
|
||||||
|
dStr_truncate(out, 0);
|
||||||
|
else
|
||||||
|
out = dStr_sized_new(in->len);
|
||||||
|
|
||||||
|
for (i = 0; (i < in->len) && (out->len < maxlen); ++i) {
|
||||||
|
if (isprint(in->str[i]) || (in->str[i] == '\n')) {
|
||||||
|
dStr_append_c(out, in->str[i]);
|
||||||
|
} else {
|
||||||
|
dStr_append_l(out, "\\x", 2);
|
||||||
|
dStr_append_c(out, HEX[(in->str[i] >> 4) & 15]);
|
||||||
|
dStr_append_c(out, HEX[in->str[i] & 15]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (out->len >= maxlen)
|
||||||
|
dStr_append(out, "...");
|
||||||
|
return out->str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*- dList ---------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new empty list
|
||||||
|
*/
|
||||||
|
Dlist *dList_new(int size)
|
||||||
|
{
|
||||||
|
Dlist *l;
|
||||||
|
if (size <= 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
l = dNew(Dlist, 1);
|
||||||
|
l->len = 0;
|
||||||
|
l->sz = size;
|
||||||
|
l->list = dNew(void*, l->sz);
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free a list (not its elements)
|
||||||
|
*/
|
||||||
|
void dList_free (Dlist *lp)
|
||||||
|
{
|
||||||
|
if (!lp)
|
||||||
|
return;
|
||||||
|
|
||||||
|
dFree(lp->list);
|
||||||
|
dFree(lp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert an element at a given position [0 based]
|
||||||
|
*/
|
||||||
|
void dList_insert_pos (Dlist *lp, void *data, int pos0)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!lp || pos0 < 0 || pos0 > lp->len)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (lp->sz == lp->len) {
|
||||||
|
lp->sz *= 2;
|
||||||
|
lp->list = (void**) dRealloc (lp->list, lp->sz*sizeof(void*));
|
||||||
|
}
|
||||||
|
++lp->len;
|
||||||
|
|
||||||
|
for (i = lp->len - 1; i > pos0; --i)
|
||||||
|
lp->list[i] = lp->list[i - 1];
|
||||||
|
lp->list[pos0] = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append a data item to the list
|
||||||
|
*/
|
||||||
|
void dList_append (Dlist *lp, void *data)
|
||||||
|
{
|
||||||
|
dList_insert_pos(lp, data, lp->len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepend a data item to the list
|
||||||
|
*/
|
||||||
|
void dList_prepend (Dlist *lp, void *data)
|
||||||
|
{
|
||||||
|
dList_insert_pos(lp, data, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For completing the ADT.
|
||||||
|
*/
|
||||||
|
int dList_length (Dlist *lp)
|
||||||
|
{
|
||||||
|
if (!lp)
|
||||||
|
return 0;
|
||||||
|
return lp->len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a data item without preserving order.
|
||||||
|
*/
|
||||||
|
void dList_remove_fast (Dlist *lp, const void *data)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!lp)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i = 0; i < lp->len; ++i) {
|
||||||
|
if (lp->list[i] == data) {
|
||||||
|
lp->list[i] = lp->list[--lp->len];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove a data item preserving order.
|
||||||
|
*/
|
||||||
|
void dList_remove (Dlist *lp, const void *data)
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
if (!lp)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i = 0; i < lp->len; ++i) {
|
||||||
|
if (lp->list[i] == data) {
|
||||||
|
--lp->len;
|
||||||
|
for (j = i; j < lp->len; ++j)
|
||||||
|
lp->list[j] = lp->list[j + 1];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the nth data item,
|
||||||
|
* NULL when not found or 'n0' is out of range
|
||||||
|
*/
|
||||||
|
void *dList_nth_data (Dlist *lp, int n0)
|
||||||
|
{
|
||||||
|
if (!lp || n0 < 0 || n0 >= lp->len)
|
||||||
|
return NULL;
|
||||||
|
return lp->list[n0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the found data item, or NULL if not present.
|
||||||
|
*/
|
||||||
|
void *dList_find (Dlist *lp, const void *data)
|
||||||
|
{
|
||||||
|
int i = dList_find_idx(lp, data);
|
||||||
|
return (i >= 0) ? lp->list[i] : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search a data item.
|
||||||
|
* Return value: its index if found, -1 if not present.
|
||||||
|
* (this is useful for a list of integers, for finding number zero).
|
||||||
|
*/
|
||||||
|
int dList_find_idx (Dlist *lp, const void *data)
|
||||||
|
{
|
||||||
|
int i, ret = -1;
|
||||||
|
|
||||||
|
if (!lp)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
for (i = 0; i < lp->len; ++i) {
|
||||||
|
if (lp->list[i] == data) {
|
||||||
|
ret = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search a data item using a custom function.
|
||||||
|
* func() is given the list item and the user data as parameters.
|
||||||
|
* Return: data item when found, NULL otherwise.
|
||||||
|
*/
|
||||||
|
void *dList_find_custom (Dlist *lp, const void *data, dCompareFunc func)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
void *ret = NULL;
|
||||||
|
|
||||||
|
if (!lp)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
for (i = 0; i < lp->len; ++i) {
|
||||||
|
if (func(lp->list[i], data) == 0) {
|
||||||
|
ret = lp->list[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* QuickSort implementation.
|
||||||
|
* This allows for a simple compare function for all the ADT.
|
||||||
|
*/
|
||||||
|
static void QuickSort(void **left, void **right, dCompareFunc compare)
|
||||||
|
{
|
||||||
|
void **p = left, **q = right, **t = left;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
while (p != t && compare(*p, *t) < 0)
|
||||||
|
++p;
|
||||||
|
while (q != t && compare(*q, *t) > 0)
|
||||||
|
--q;
|
||||||
|
if (p > q)
|
||||||
|
break;
|
||||||
|
if (p < q) {
|
||||||
|
void *tmp = *p;
|
||||||
|
*p = *q;
|
||||||
|
*q = tmp;
|
||||||
|
if (t == p)
|
||||||
|
t = q;
|
||||||
|
else if (t == q)
|
||||||
|
t = p;
|
||||||
|
}
|
||||||
|
if (++p > --q)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (left < q)
|
||||||
|
QuickSort(left, q, compare);
|
||||||
|
if (p < right)
|
||||||
|
QuickSort(p, right, compare);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort the list using a custom function
|
||||||
|
*/
|
||||||
|
void dList_sort (Dlist *lp, dCompareFunc func)
|
||||||
|
{
|
||||||
|
if (lp && lp->len > 1) {
|
||||||
|
QuickSort(lp->list, lp->list + lp->len - 1, func);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert an element into a sorted list.
|
||||||
|
* The comparison function receives two list elements.
|
||||||
|
*/
|
||||||
|
void dList_insert_sorted (Dlist *lp, void *data, dCompareFunc func)
|
||||||
|
{
|
||||||
|
int i, st, min, max;
|
||||||
|
|
||||||
|
if (lp) {
|
||||||
|
min = st = i = 0;
|
||||||
|
max = lp->len - 1;
|
||||||
|
while (min <= max) {
|
||||||
|
i = (min + max) / 2;
|
||||||
|
st = func(lp->list[i], data);
|
||||||
|
if (st < 0) {
|
||||||
|
min = i + 1;
|
||||||
|
} else if (st > 0) {
|
||||||
|
max = i - 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dList_insert_pos(lp, data, (st >= 0) ? i : i+1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search a sorted list.
|
||||||
|
* Return the found data item, or NULL if not present.
|
||||||
|
* func() is given the list item and the user data as parameters.
|
||||||
|
*/
|
||||||
|
void *dList_find_sorted (Dlist *lp, const void *data, dCompareFunc func)
|
||||||
|
{
|
||||||
|
int i, st, min, max;
|
||||||
|
void *ret = NULL;
|
||||||
|
|
||||||
|
if (lp && lp->len) {
|
||||||
|
min = 0;
|
||||||
|
max = lp->len - 1;
|
||||||
|
while (min <= max) {
|
||||||
|
i = (min + max) / 2;
|
||||||
|
st = func(lp->list[i], data);
|
||||||
|
if (st < 0) {
|
||||||
|
min = i + 1;
|
||||||
|
} else if (st > 0) {
|
||||||
|
max = i - 1;
|
||||||
|
} else {
|
||||||
|
ret = lp->list[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*- Parse function ------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Take a dillo rc line and return 'name' and 'value' pointers to it.
|
||||||
|
* Notes:
|
||||||
|
* - line is modified!
|
||||||
|
* - it skips blank lines and lines starting with '#'
|
||||||
|
*
|
||||||
|
* Return value: 1 on blank line or comment, 0 on successful value/pair,
|
||||||
|
* -1 otherwise.
|
||||||
|
*/
|
||||||
|
int dParser_parse_rc_line(char **line, char **name, char **value)
|
||||||
|
{
|
||||||
|
char *eq, *p;
|
||||||
|
int len, ret = -1;
|
||||||
|
|
||||||
|
dReturn_val_if_fail(*line, ret);
|
||||||
|
|
||||||
|
*name = NULL;
|
||||||
|
*value = NULL;
|
||||||
|
dStrstrip(*line);
|
||||||
|
if (!*line[0] || *line[0] == '#') {
|
||||||
|
/* blank line or comment */
|
||||||
|
ret = 1;
|
||||||
|
} else if ((eq = strchr(*line, '='))) {
|
||||||
|
/* get name */
|
||||||
|
for (p = *line; *p && *p != '=' && !dIsspace(*p); ++p);
|
||||||
|
*p = 0;
|
||||||
|
*name = *line;
|
||||||
|
|
||||||
|
/* skip whitespace */
|
||||||
|
if (p < eq)
|
||||||
|
for (++p; dIsspace(*p); ++p);
|
||||||
|
|
||||||
|
/* get value */
|
||||||
|
if (p == eq) {
|
||||||
|
for (++p; dIsspace(*p); ++p);
|
||||||
|
len = strlen(p);
|
||||||
|
if (len >= 2 && *p == '"' && p[len-1] == '"') {
|
||||||
|
p[len-1] = 0;
|
||||||
|
++p;
|
||||||
|
}
|
||||||
|
*value = p;
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*- Dlib messages -------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
void dLib_show_messages(bool_t show)
|
||||||
|
{
|
||||||
|
dLib_show_msg = show;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*- Misc utility functions ----------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the current working directory in a new string
|
||||||
|
*/
|
||||||
|
char *dGetcwd (void)
|
||||||
|
{
|
||||||
|
size_t size = 128;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
char *buffer = dNew(char, size);
|
||||||
|
if (getcwd (buffer, size) == buffer)
|
||||||
|
return buffer;
|
||||||
|
dFree (buffer);
|
||||||
|
if (errno != ERANGE)
|
||||||
|
return 0;
|
||||||
|
size *= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the home directory in a static string (don't free)
|
||||||
|
*/
|
||||||
|
char *dGethomedir (void)
|
||||||
|
{
|
||||||
|
static char *homedir = NULL;
|
||||||
|
|
||||||
|
if (!homedir) {
|
||||||
|
if (getenv("HOME")) {
|
||||||
|
homedir = dStrdup(getenv("HOME"));
|
||||||
|
|
||||||
|
} else if (getenv("HOMEDRIVE") && getenv("HOMEPATH")) {
|
||||||
|
homedir = dStrconcat(getenv("HOMEDRIVE"), getenv("HOMEPATH"), NULL);
|
||||||
|
} else {
|
||||||
|
DLIB_MSG("dGethomedir: $HOME not set, using '/'.\n");
|
||||||
|
homedir = dStrdup("/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return homedir;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a line from a FILE stream.
|
||||||
|
* Return value: read line on success, NULL on EOF.
|
||||||
|
*/
|
||||||
|
char *dGetline (FILE *stream)
|
||||||
|
{
|
||||||
|
int ch;
|
||||||
|
Dstr *dstr;
|
||||||
|
char *line;
|
||||||
|
|
||||||
|
dReturn_val_if_fail (stream, 0);
|
||||||
|
|
||||||
|
dstr = dStr_sized_new(64);
|
||||||
|
while ((ch = fgetc(stream)) != EOF) {
|
||||||
|
dStr_append_c(dstr, ch);
|
||||||
|
if (ch == '\n')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
line = (dstr->len) ? dstr->str : NULL;
|
||||||
|
dStr_free(dstr, (line) ? 0 : 1);
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close a FD handling EINTR.
|
||||||
|
*/
|
||||||
|
int dClose(int fd)
|
||||||
|
{
|
||||||
|
int st;
|
||||||
|
|
||||||
|
do
|
||||||
|
st = close(fd);
|
||||||
|
while (st == -1 && errno == EINTR);
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Portable usleep() function.
|
||||||
|
*
|
||||||
|
* The usleep() function is deprecated in POSIX.1-2001 and removed in
|
||||||
|
* POSIX.1-2008, see usleep(3).
|
||||||
|
*/
|
||||||
|
int dUsleep(unsigned long usec)
|
||||||
|
{
|
||||||
|
struct timespec ts;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
ts.tv_sec = usec / 1000000UL;
|
||||||
|
ts.tv_nsec = (usec % 1000000UL) * 1000UL;
|
||||||
|
|
||||||
|
do {
|
||||||
|
res = nanosleep(&ts, &ts);
|
||||||
|
} while (res && errno == EINTR);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
187
dlib/dlib.h
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
#ifndef __DLIB_H__
|
||||||
|
#define __DLIB_H__
|
||||||
|
|
||||||
|
#include <stdio.h> /* for FILE* */
|
||||||
|
#include <stddef.h> /* for size_t */
|
||||||
|
#include <stdarg.h> /* for va_list */
|
||||||
|
#include <string.h> /* for strerror */
|
||||||
|
|
||||||
|
#include "d_size.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
/*
|
||||||
|
*-- Common macros -----------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#ifndef FALSE
|
||||||
|
#define FALSE (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TRUE
|
||||||
|
#define TRUE (!FALSE)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#undef MAX
|
||||||
|
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||||
|
|
||||||
|
#undef MIN
|
||||||
|
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||||
|
|
||||||
|
/* Handle signed char */
|
||||||
|
#define dIsspace(c) isspace((uchar_t)(c))
|
||||||
|
#define dIsalnum(c) isalnum((uchar_t)(c))
|
||||||
|
|
||||||
|
#define D_ASCII_TOUPPER(c) (((c) >= 'a' && (c) <= 'z') ? (c) - 0x20 : (c))
|
||||||
|
#define D_ASCII_TOLOWER(c) (((c) >= 'A' && (c) <= 'Z') ? (c) + 0x20 : (c))
|
||||||
|
/*
|
||||||
|
*-- Casts -------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
/* TODO: include a void* size test in configure.in */
|
||||||
|
/* (long) works for both 32bit and 64bit */
|
||||||
|
#define VOIDP2INT(p) ((long)(p))
|
||||||
|
#define INT2VOIDP(i) ((void*)((long)(i)))
|
||||||
|
|
||||||
|
/*
|
||||||
|
*-- Memory -------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#define dNew(type, count) \
|
||||||
|
((type *) dMalloc ((unsigned) sizeof (type) * (count)))
|
||||||
|
#define dNew0(type, count) \
|
||||||
|
((type *) dMalloc0 ((unsigned) sizeof (type) * (count)))
|
||||||
|
|
||||||
|
void *dMalloc (size_t size);
|
||||||
|
void *dRealloc (void *mem, size_t size);
|
||||||
|
void *dMalloc0 (size_t size);
|
||||||
|
void dFree (void *mem);
|
||||||
|
|
||||||
|
/*
|
||||||
|
*- Debug macros --------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#define D_STMT_START do
|
||||||
|
#define D_STMT_END while (0)
|
||||||
|
#define dReturn_if(expr) \
|
||||||
|
D_STMT_START{ \
|
||||||
|
if (expr) { return; }; \
|
||||||
|
}D_STMT_END
|
||||||
|
#define dReturn_val_if(expr,val) \
|
||||||
|
D_STMT_START{ \
|
||||||
|
if (expr) { return val; }; \
|
||||||
|
}D_STMT_END
|
||||||
|
#define dReturn_if_fail(expr) \
|
||||||
|
D_STMT_START{ \
|
||||||
|
if (!(expr)) { return; }; \
|
||||||
|
}D_STMT_END
|
||||||
|
#define dReturn_val_if_fail(expr,val) \
|
||||||
|
D_STMT_START{ \
|
||||||
|
if (!(expr)) { return val; }; \
|
||||||
|
}D_STMT_END
|
||||||
|
|
||||||
|
/*
|
||||||
|
*- C strings -----------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
char *dStrdup(const char *s);
|
||||||
|
char *dStrndup(const char *s, size_t sz);
|
||||||
|
char *dStrconcat(const char *s1, ...);
|
||||||
|
char *dStrstrip(char *s);
|
||||||
|
char *dStrnfill(size_t len, char c);
|
||||||
|
char *dStrsep(char **orig, const char *delim);
|
||||||
|
void dStrshred(char *s);
|
||||||
|
char *dStriAsciiStr(const char *haystack, const char *needle);
|
||||||
|
int dStrAsciiCasecmp(const char *s1, const char *s2);
|
||||||
|
int dStrnAsciiCasecmp(const char *s1, const char *s2, size_t n);
|
||||||
|
|
||||||
|
#define dStrerror strerror
|
||||||
|
|
||||||
|
/*
|
||||||
|
*-- dStr ---------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#define Dstr_char_t char
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int sz; /* allocated size (private) */
|
||||||
|
int len;
|
||||||
|
Dstr_char_t *str;
|
||||||
|
} Dstr;
|
||||||
|
|
||||||
|
Dstr *dStr_new (const char *s);
|
||||||
|
Dstr *dStr_sized_new (int sz);
|
||||||
|
void dStr_fit (Dstr *ds);
|
||||||
|
void dStr_free (Dstr *ds, int all);
|
||||||
|
void dStr_append_c (Dstr *ds, int c);
|
||||||
|
void dStr_append (Dstr *ds, const char *s);
|
||||||
|
void dStr_append_l (Dstr *ds, const char *s, int l);
|
||||||
|
void dStr_insert (Dstr *ds, int pos_0, const char *s);
|
||||||
|
void dStr_insert_l (Dstr *ds, int pos_0, const char *s, int l);
|
||||||
|
void dStr_truncate (Dstr *ds, int len);
|
||||||
|
void dStr_shred (Dstr *ds);
|
||||||
|
void dStr_erase (Dstr *ds, int pos_0, int len);
|
||||||
|
void dStr_vsprintfa (Dstr *ds, const char *format, va_list argp);
|
||||||
|
void dStr_vsprintf (Dstr *ds, const char *format, va_list argp);
|
||||||
|
void dStr_sprintf (Dstr *ds, const char *format, ...);
|
||||||
|
void dStr_sprintfa (Dstr *ds, const char *format, ...);
|
||||||
|
int dStr_cmp(Dstr *ds1, Dstr *ds2);
|
||||||
|
char *dStr_memmem(Dstr *haystack, Dstr *needle);
|
||||||
|
const char *dStr_printable(Dstr *in, int maxlen);
|
||||||
|
|
||||||
|
/*
|
||||||
|
*-- dList --------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
int sz; /* allocated size (private) */
|
||||||
|
int len;
|
||||||
|
void **list;
|
||||||
|
} Dlist;
|
||||||
|
|
||||||
|
/* dCompareFunc:
|
||||||
|
* Return: 0 if parameters are equal (for dList_find_custom).
|
||||||
|
* Return: 0 if equal, < 0 if (a < b), > 0 if (b < a) --for insert sorted.
|
||||||
|
*
|
||||||
|
* For finding a data node with an external key, the comparison function
|
||||||
|
* parameters are: first the data node, and then the key.
|
||||||
|
*/
|
||||||
|
typedef int (*dCompareFunc) (const void *a, const void *b);
|
||||||
|
|
||||||
|
|
||||||
|
Dlist *dList_new(int size);
|
||||||
|
void dList_free (Dlist *lp);
|
||||||
|
void dList_append (Dlist *lp, void *data);
|
||||||
|
void dList_prepend (Dlist *lp, void *data);
|
||||||
|
void dList_insert_pos (Dlist *lp, void *data, int pos0);
|
||||||
|
int dList_length (Dlist *lp);
|
||||||
|
void dList_remove (Dlist *lp, const void *data);
|
||||||
|
void dList_remove_fast (Dlist *lp, const void *data);
|
||||||
|
void *dList_nth_data (Dlist *lp, int n0);
|
||||||
|
void *dList_find (Dlist *lp, const void *data);
|
||||||
|
int dList_find_idx (Dlist *lp, const void *data);
|
||||||
|
void *dList_find_custom (Dlist *lp, const void *data, dCompareFunc func);
|
||||||
|
void dList_sort (Dlist *lp, dCompareFunc func);
|
||||||
|
void dList_insert_sorted (Dlist *lp, void *data, dCompareFunc func);
|
||||||
|
void *dList_find_sorted (Dlist *lp, const void *data, dCompareFunc func);
|
||||||
|
|
||||||
|
/*
|
||||||
|
*- Parse function ------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
int dParser_parse_rc_line(char **line, char **name, char **value);
|
||||||
|
|
||||||
|
/*
|
||||||
|
*- Dlib messages -------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
void dLib_show_messages(bool_t show);
|
||||||
|
|
||||||
|
/*
|
||||||
|
*- Misc utility functions ----------------------------------------------------
|
||||||
|
*/
|
||||||
|
char *dGetcwd(void);
|
||||||
|
char *dGethomedir(void);
|
||||||
|
char *dGetline(FILE *stream);
|
||||||
|
int dClose(int fd);
|
||||||
|
int dUsleep(unsigned long us);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
#endif /* __DLIB_H__ */
|
||||||
|
|
||||||
74
doc/Cookies.txt
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
Jan 2002, Jörgen Viksell - jorgen.viksell@telia.com,
|
||||||
|
Jorge Arellano Cid --
|
||||||
|
Last update: March 2010
|
||||||
|
|
||||||
|
|
||||||
|
==================
|
||||||
|
Cookies in Dillo
|
||||||
|
==================
|
||||||
|
|
||||||
|
The current specification for cookies is RFC 6265
|
||||||
|
( http://tools.ietf.org/html/rfc6265 ).
|
||||||
|
|
||||||
|
Cookies are handled by a dpi (plugin) which shares them between your
|
||||||
|
instances of Dillo.
|
||||||
|
|
||||||
|
Current cookie limits are: 20 per domain, and 1200 in total.
|
||||||
|
|
||||||
|
When the dpi exits, cookies that you have ACCEPTed are saved to
|
||||||
|
~/.dillo/cookies.txt, and ACCEPT_SESSION cookies are forgotten.
|
||||||
|
The dpi normally exits after a period of inactivity, but you can force it to
|
||||||
|
exit with the command "dpidc stop".
|
||||||
|
|
||||||
|
|
||||||
|
=====================
|
||||||
|
Controlling cookies
|
||||||
|
=====================
|
||||||
|
|
||||||
|
Out of the box, dillo rejects all cookies.
|
||||||
|
|
||||||
|
|
||||||
|
If you want to accept certain cookies, you can specify rules for different
|
||||||
|
domains in the file ~/.dillo/cookiesrc. The syntax looks like:
|
||||||
|
|
||||||
|
#host action
|
||||||
|
DEFAULT DENY
|
||||||
|
fltk.org ACCEPT
|
||||||
|
.host.com ACCEPT_SESSION
|
||||||
|
|
||||||
|
Line 0: Comment line begins with '#'.
|
||||||
|
Line 1: Deny all cookies from all domains not otherwise specified.
|
||||||
|
Line 2: Accept all cookies from fltk.org, and save them to
|
||||||
|
~/.dillo/cookies.txt when the cookies dpi exits.
|
||||||
|
Line 3: Accept all cookies from all subdomains of host.com, but
|
||||||
|
do not save them when the dpi exits.
|
||||||
|
|
||||||
|
|
||||||
|
If you are positive that you will never want any cookies, you can
|
||||||
|
configure/compile Dillo without cookie support. The option is:
|
||||||
|
./configure --disable-cookies
|
||||||
|
|
||||||
|
|
||||||
|
===================
|
||||||
|
Cookies & Privacy
|
||||||
|
===================
|
||||||
|
|
||||||
|
Cookies can be a severe threat to personal privacy. The pages you
|
||||||
|
visit can be tracked, logged, and associated to a personal data-record,
|
||||||
|
allowing the possibility of building a detailed profile of your
|
||||||
|
browsing habits.
|
||||||
|
|
||||||
|
This data is sold to companies that profit from direct use of such
|
||||||
|
information (SPAM, Spying, etc).
|
||||||
|
|
||||||
|
If this data is cross-referenced with other databases, they can end up
|
||||||
|
with more information than you have about yourself.
|
||||||
|
|
||||||
|
Some people may tell you this is "paranoid". But please, take my words
|
||||||
|
as those of someone who has written a web browser, a cookies implementation,
|
||||||
|
and who has deep understanding of HTTP and cookies.
|
||||||
|
|
||||||
|
The dillo project is especially concerned about privacy and security
|
||||||
|
issues. Our advice is to avoid cookies whenever possible and at most set
|
||||||
|
ACCEPT_SESSION to specific, trusted sites. -- You have been warned.
|
||||||
|
|
||||||
17
doc/Makefile.am
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
doc_DATA = user_help.html
|
||||||
|
man_MANS = dillo.1
|
||||||
|
EXTRA_DIST = \
|
||||||
|
README \
|
||||||
|
Cookies.txt \
|
||||||
|
dillo.1.in \
|
||||||
|
user_help.in.html \
|
||||||
|
install.md
|
||||||
|
|
||||||
|
dillo.1: $(srcdir)/dillo.1.in Makefile
|
||||||
|
sed 's%/usr/local%${prefix}%g' < $(srcdir)/dillo.1.in > dillo.1
|
||||||
|
|
||||||
|
# Use .in.html instead of .html.in so it is recognized as HTML.
|
||||||
|
user_help.html: $(srcdir)/user_help.in.html Makefile
|
||||||
|
sed 's/__VERSION__/${VERSION}/g' $(srcdir)/user_help.in.html > $@
|
||||||
|
|
||||||
|
DISTCLEANFILES = dillo.1 user_help.html
|
||||||
5
doc/README
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
Last update: June 2015
|
||||||
|
|
||||||
|
This directory contains user documentation. Developer documentation is
|
||||||
|
only stored in the Git repository at <https://github.com/dillo-browser/dillo>, in
|
||||||
|
the directory "devdoc", but not part of the tarball.
|
||||||
117
doc/dillo.1.in
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
.TH dillo 1 "May 28, 2015" "" "USER COMMANDS"
|
||||||
|
.SH NAME
|
||||||
|
dillo \- web browser
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B dillo
|
||||||
|
.RI [ OPTION ]...
|
||||||
|
.RB [ \-\- ]
|
||||||
|
.RI [ URL | FILE ]...
|
||||||
|
.SH DESCRIPTION
|
||||||
|
.PP
|
||||||
|
Dillo is a lightweight graphical web browser that aims to be secure.
|
||||||
|
It handles HTTP internally, and FILE, FTP, and
|
||||||
|
DATA URIs are handled through a plugin system (dpi). In addition,
|
||||||
|
.I EXPERIMENTAL
|
||||||
|
HTTPS support can be enabled. Both FTP and Dillo's download manager use the
|
||||||
|
.BR wget (1)
|
||||||
|
downloader.
|
||||||
|
.PP
|
||||||
|
Dillo displays HTML, text, PNG, JPEG, GIF, SVG and WebP files.
|
||||||
|
It handles cookies, HTTP authentication (basic and digest), proxying (basic),
|
||||||
|
and some CSS.
|
||||||
|
.PP
|
||||||
|
Framesets are displayed as links to frames, and there is currently
|
||||||
|
no support for javascript or video.
|
||||||
|
.PP
|
||||||
|
In order to use the hyphenation feature, pattern files from CTAN need to
|
||||||
|
be installed to
|
||||||
|
.IR /usr/local/lib/dillo/hyphenation/ .
|
||||||
|
This can be done with the script
|
||||||
|
.IR dillo-install-hyphenation .
|
||||||
|
Call it with ISO-639-1 language codes as arguments, or without arguments
|
||||||
|
to get more help.
|
||||||
|
.SH OPTIONS
|
||||||
|
.TP
|
||||||
|
\fB\-f\fR, \fB\-\-fullwindow\fR
|
||||||
|
Start in full window mode: hide address bar, navigation buttons, menu, and
|
||||||
|
status bar.
|
||||||
|
.TP
|
||||||
|
\fB\-g\fR, \fB\-geometry \fIGEO\fR
|
||||||
|
Set initial window position where \fIGEO\fR is
|
||||||
|
\fIW\fBx\fIH\fR[{\fB+\-\fR}\fIX\fR{\fB+\-\fR}\fIY\fR].
|
||||||
|
.TP
|
||||||
|
\fB\-h\fR, \fB\-\-help\fR
|
||||||
|
Display this help text and exit.
|
||||||
|
.TP
|
||||||
|
\fB\-l\fR, \fB\-\-local\fR
|
||||||
|
Don't load images or stylesheets, or follow redirections, for these FILEs or
|
||||||
|
URLs. This is intended for use with HTML email.
|
||||||
|
.TP
|
||||||
|
\fB\-v\fR, \fB\-\-version\fR
|
||||||
|
Display version info and exit.
|
||||||
|
.TP
|
||||||
|
\fB\-x\fR, \fB\-\-xid \fIXID\fR
|
||||||
|
Open first Dillo window in an existing window whose window ID is \fIXID\fR.
|
||||||
|
.SH EXIT STATUS
|
||||||
|
.TP
|
||||||
|
.B 0
|
||||||
|
No error.
|
||||||
|
.TP
|
||||||
|
.B 1
|
||||||
|
Internal error.
|
||||||
|
.TP
|
||||||
|
.B 2
|
||||||
|
Error in command line arguments.
|
||||||
|
.SH ENVIRONMENT
|
||||||
|
.TP
|
||||||
|
.BR "HOME " "(or " "HOMEDRIVE " "and " "HOMEPATH " "on Cygwin)"
|
||||||
|
User's home directory.
|
||||||
|
.TP
|
||||||
|
.B http_proxy
|
||||||
|
URL of proxy to send HTTP/HTTPS traffic through.
|
||||||
|
.SH FILES
|
||||||
|
.TP
|
||||||
|
.I dpid
|
||||||
|
Dillo plugin daemon
|
||||||
|
.TP
|
||||||
|
.I dpidc
|
||||||
|
Control program for dpid.
|
||||||
|
.TP
|
||||||
|
.I ~/.dillo/bm.txt
|
||||||
|
User bookmarks
|
||||||
|
.TP
|
||||||
|
.I ~/.dillo/certs/
|
||||||
|
Saved certificates for HTTPS.
|
||||||
|
.TP
|
||||||
|
.I ~/.dillo/cookies.txt
|
||||||
|
Stored cookies
|
||||||
|
.TP
|
||||||
|
.I ~/.dillo/cookiesrc
|
||||||
|
Cookie settings
|
||||||
|
.TP
|
||||||
|
.I ~/.dillo/dillorc
|
||||||
|
Configuration file.
|
||||||
|
.TP
|
||||||
|
.I ~/.dillo/domainrc
|
||||||
|
Rules for cross-domain requests.
|
||||||
|
.TP
|
||||||
|
.I ~/.dillo/dpid_comm_keys
|
||||||
|
Keys used in dpi daemon communication.
|
||||||
|
.TP
|
||||||
|
.I ~/.dillo/dpidrc
|
||||||
|
Contains name of directory containing dpis, and associates
|
||||||
|
dpi files with protocols.
|
||||||
|
.TP
|
||||||
|
.I ~/.dillo/keysrc
|
||||||
|
Keybindings.
|
||||||
|
.TP
|
||||||
|
.I ~/.dillo/style.css
|
||||||
|
User style sheet.
|
||||||
|
.TP
|
||||||
|
.I /usr/local/lib/dillo/hyphenation/
|
||||||
|
Hyphenation pattern files.
|
||||||
|
.SH SEE ALSO
|
||||||
|
.BR wget (1)
|
||||||
|
.PP
|
||||||
|
Dillo website:
|
||||||
|
.B https://dillo-browser.github.io/
|
||||||
BIN
doc/dillo.png
Normal file
|
After Width: | Height: | Size: 136 KiB |
220
doc/install.md
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
# Installing Dillo on Linux
|
||||||
|
|
||||||
|
Dillo is already packaged in many Linux distributions. To use the binary
|
||||||
|
from your distribution check your package manager. Example in Arch
|
||||||
|
Linux:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ sudo pacman -S dillo
|
||||||
|
```
|
||||||
|
|
||||||
|
## Building from source
|
||||||
|
|
||||||
|
Dillo requires FLTK-1.3, if you don't have it (try `fltk-config
|
||||||
|
--version` to check), follow the steps in the FLTK documentation to
|
||||||
|
install it:
|
||||||
|
|
||||||
|
https://www.fltk.org/doc-1.3/intro.html
|
||||||
|
|
||||||
|
Additionally, it is **strongly recommended** that you install a TLS
|
||||||
|
library to browse HTTPS pages. Currently, Dillo supports any of the
|
||||||
|
following libraries:
|
||||||
|
|
||||||
|
- OpenSSL 1.1 or 3
|
||||||
|
- LibreSSL
|
||||||
|
- mbedTLS 2 or 3 (TLSv1.3 is not supported yet)
|
||||||
|
|
||||||
|
If you don't want to use a TLS library, use the configure option
|
||||||
|
`--disable-tls` to disable TLS support. You can use `--disable-openssl`
|
||||||
|
and `--disable-mbedtls` to control the search. By default OpenSSL or
|
||||||
|
LibreSSL is search first, then mbedTLS.
|
||||||
|
|
||||||
|
For Debian, you can use the following command to install the required
|
||||||
|
packages to build Dillo:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ sudo apt install gcc g++ autoconf automake make zlib1g-dev \
|
||||||
|
libfltk1.3-dev libssl-dev libc6-dev \
|
||||||
|
libpng-dev libjpeg-dev libwebp-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
If you prefer to use mbedTLS, replace `libssl-dev` with
|
||||||
|
`libmbedtls-dev`.
|
||||||
|
|
||||||
|
To build and install Dillo follow the steps below.
|
||||||
|
|
||||||
|
### From a release
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ tar jxvf dillo-3.0.5.tar.bz2
|
||||||
|
$ cd dillo-3.0.5
|
||||||
|
$ mkdir build
|
||||||
|
$ cd build
|
||||||
|
$ ../configure --prefix=/usr/local
|
||||||
|
$ make
|
||||||
|
$ sudo make install
|
||||||
|
```
|
||||||
|
|
||||||
|
### From git
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ git clone https://github.com/dillo-browser/dillo.git
|
||||||
|
$ cd dillo
|
||||||
|
$ ./autogen.sh
|
||||||
|
$ mkdir build
|
||||||
|
$ cd build
|
||||||
|
$ ../configure --prefix=/usr/local
|
||||||
|
$ make
|
||||||
|
$ sudo make install
|
||||||
|
```
|
||||||
|
|
||||||
|
## Hyphenation database
|
||||||
|
|
||||||
|
In order to use the hyphenation feature, pattern files from CTAN need to
|
||||||
|
be installed. This can be done with the script
|
||||||
|
`dillo-install-hyphenation`. Call it with ISO-639-1 language codes
|
||||||
|
("en", "es", "de"...) as arguments, or without arguments to get more
|
||||||
|
help.
|
||||||
|
|
||||||
|
## Dpi programs
|
||||||
|
|
||||||
|
These are installed by `make install`. If you don't have root access,
|
||||||
|
copy "dillo" and "dpid" to some directory in your path and install
|
||||||
|
the dpis by running `./install-dpi-local` from the top directory (they
|
||||||
|
will be installed under ~/.dillo).
|
||||||
|
|
||||||
|
# Other systems
|
||||||
|
|
||||||
|
## BSD
|
||||||
|
|
||||||
|
On FreeBSD 14.0 you can install the required dependencies from ports as:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ pkg install -y automake fltk
|
||||||
|
```
|
||||||
|
|
||||||
|
Notice that on BSD systems, some required dependencies like FLTK are
|
||||||
|
available as ports and are installed in /usr/local (instead of /usr).
|
||||||
|
Additionally, the compiler won't look for headers or libraries in
|
||||||
|
/usr/local by default (you can check with $CC -v), however the `$PATH`
|
||||||
|
may include /usr/local/bin, so the binaries may be available but not the
|
||||||
|
headers. Some dependencies like FLTK are searched invoking the
|
||||||
|
`fltk-config` command that inform where to find the headers and
|
||||||
|
libraries for that package.
|
||||||
|
|
||||||
|
Other libraries are searched by attempting to locate the headers and
|
||||||
|
libraries directly. By default, the configure script won't look in
|
||||||
|
non-default paths of the compiler, so to add /usr/local to the search
|
||||||
|
path, invoke the configure script with the following options:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ ./configure CPPFLAGS='-I/usr/local/include' LDFLAGS='-L/usr/local/lib'
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that you'll need GNU make to build Dillo.
|
||||||
|
|
||||||
|
If it crashes or locks at times, use the `--disable-threaded-dns`
|
||||||
|
option, so Dillo uses a single thread for name resolution.
|
||||||
|
|
||||||
|
## Solaris
|
||||||
|
|
||||||
|
Dillo may compile and run OK on Solaris but (please report).
|
||||||
|
Use gmake (a symbolic link make -> gmake works OK).
|
||||||
|
|
||||||
|
Solaris is very inconsistent so you may need to add/remove:
|
||||||
|
|
||||||
|
```
|
||||||
|
-lrt -lposix4
|
||||||
|
```
|
||||||
|
|
||||||
|
at link time.
|
||||||
|
|
||||||
|
## MacOS
|
||||||
|
|
||||||
|
Dillo is now packaged in [Homebrew](https://brew.sh/).
|
||||||
|
|
||||||
|
```
|
||||||
|
$ brew install dillo
|
||||||
|
```
|
||||||
|
|
||||||
|
### Building from source
|
||||||
|
|
||||||
|
To build Dillo on MacOS you would need to get FLTK as well as
|
||||||
|
autoconf and automake if you are building Dillo from the git repository.
|
||||||
|
They are available in the Homebrew package manager:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ brew install autoconf automake fltk@1.3
|
||||||
|
```
|
||||||
|
|
||||||
|
For OpenSSL you can use either 1.1 or 3.X (recommended):
|
||||||
|
|
||||||
|
```
|
||||||
|
$ brew install openssl@1.1
|
||||||
|
$ brew install openssl@3
|
||||||
|
```
|
||||||
|
|
||||||
|
Homebrew installs libraries and headers in different folders depending on the
|
||||||
|
architecture (Intel vs ARM), so you will need to invoke the configure script
|
||||||
|
with the following options so it knows where to search:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ ./configure LDFLAGS="-L`brew --prefix openssl`/lib" CPPFLAGS="-I`brew --prefix openssl`/include"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Windows via Cygwin
|
||||||
|
|
||||||
|
Dillo can be built for Windows (tested on Windows 11) by using the
|
||||||
|
[Cygwin](https://www.cygwin.com/) POSIX portability layer and run with Xorg. You
|
||||||
|
will need the following dependencies to build Dillo with mbedTLS:
|
||||||
|
|
||||||
|
```
|
||||||
|
gcc-core gcc-g++ autoconf automake make zlib-devel mbedtls-devel libfltk-devel
|
||||||
|
libiconv-devel libpng-devel libjpeg-devel libwebp-devel
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note**: Dillo can also be built with OpenSSL (libssl-devel) but there is a
|
||||||
|
[known problem with detached threads](https://github.com/dillo-browser/dillo/issues/172)
|
||||||
|
used by the DNS resolver and OpenSSL that causes a crash. If you use OpenSSL,
|
||||||
|
disable the threaded resolver with `--disable-threaded-dns`.
|
||||||
|
|
||||||
|
You will also need [Xorg](https://x.cygwin.com/docs/ug/cygwin-x-ug.html) to run
|
||||||
|
Dillo graphically:
|
||||||
|
|
||||||
|
```
|
||||||
|
xorg-server xinit
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also install all the dependencies from the command line with:
|
||||||
|
```
|
||||||
|
setup-x86_64.exe -q -P gcc-core,gcc-g++,autoconf,automake,make,zlib-devel,mbedtls-devel,libfltk-devel,libiconv-devel,libpng-devel,libjpeg-devel,libwebp-devel,xorg-server,xinit
|
||||||
|
```
|
||||||
|
|
||||||
|
To build Dillo, follow the usual steps from a Cygwin shell:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ git clone https://github.com/dillo-browser/dillo.git
|
||||||
|
$ cd dillo
|
||||||
|
$ ./autogen.sh
|
||||||
|
$ mkdir build
|
||||||
|
$ cd build
|
||||||
|
$ ../configure --prefix=/usr/local
|
||||||
|
$ make
|
||||||
|
$ make install
|
||||||
|
```
|
||||||
|
|
||||||
|
You should be able to find Dillo in the `$PATH` as it should be installed in
|
||||||
|
`/usr/local/bin/dillo`. To run it, you first need to have an [Xorg server
|
||||||
|
running](https://x.cygwin.com/docs/ug/using.html#using-starting), which you can
|
||||||
|
do from a shell:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ startxwin
|
||||||
|
```
|
||||||
|
|
||||||
|
And then, from another shell:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ export DISPLAY=:0
|
||||||
|
$ dillo
|
||||||
|
```
|
||||||
835
doc/user_help.in.html
Normal file
@ -0,0 +1,835 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
|
<title>Dillo User Manual</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background: white;
|
||||||
|
color: black;
|
||||||
|
line-height: 1.5;
|
||||||
|
margin: 3em;
|
||||||
|
font-family: sans-serif;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
.main {
|
||||||
|
margin: 0 auto; /* won't work on Dillo, yet */
|
||||||
|
max-width: 40em;
|
||||||
|
min-width: 20em;
|
||||||
|
}
|
||||||
|
ul.toc {
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
ul.toc li {
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
margin-top: 0.25em;
|
||||||
|
margin-bottom: 0.25em
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
padding: 0.5em;
|
||||||
|
margin-top: 1em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
padding-top: 1em;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
border-left: 4px solid #ccc;
|
||||||
|
padding: 0.5em;
|
||||||
|
background-color: #f8f8f8;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
margin-left: 0px;
|
||||||
|
padding-left: 1.5em;
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
padding: 0.2em;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
th,td {
|
||||||
|
padding: 0.25em;
|
||||||
|
border-spacing: 0px;
|
||||||
|
border-top: solid 1px #ccc;
|
||||||
|
border-bottom: solid 1px #ccc;
|
||||||
|
}
|
||||||
|
th {
|
||||||
|
background-color: #eee;
|
||||||
|
}
|
||||||
|
footer {
|
||||||
|
color: #777;
|
||||||
|
margin: 2em;
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="main">
|
||||||
|
<h1>Dillo User Manual</h1>
|
||||||
|
|
||||||
|
<p>Welcome to the user manual of the Dillo browser. The manual is divided into
|
||||||
|
<em>sections</em> but is written in a <em>single page</em> to allow search by
|
||||||
|
keywords. Generated for version __VERSION__.</p>
|
||||||
|
|
||||||
|
<details><!-- Not supported in Dillo, but decays ok -->
|
||||||
|
<summary class="toc">Table of contents:</summary>
|
||||||
|
<ul class="toc">
|
||||||
|
<li><a href="#introduction">Introduction</a></li>
|
||||||
|
<li><a href="#reading">Reading</a>
|
||||||
|
<ul>
|
||||||
|
<li><a href="#basics">Basics</a></li>
|
||||||
|
<li><a href="#scrolling">Scrolling</a></li>
|
||||||
|
<li><a href="#find-text">Find text</a></li>
|
||||||
|
<li><a href="#copy-and-paste">Copy and paste</a></li>
|
||||||
|
<li><a href="#zoom">Zoom</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li><a href="#navigation">Navigation</a>
|
||||||
|
<ul>
|
||||||
|
<li><a href="#hyperlinks">Hyperlinks</a></li>
|
||||||
|
<li><a href="#history">History</a></li>
|
||||||
|
<li><a href="#location-bar">Location bar</a></li>
|
||||||
|
<li><a href="#web-search">Web search</a></li>
|
||||||
|
<li><a href="#tabs">Tabs</a></li>
|
||||||
|
<li><a href="#bookmarks">Bookmarks</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li><a href="#privacy-and-network">Privacy and Network</a>
|
||||||
|
<ul>
|
||||||
|
<li><a href="#https">HTTPS</a></li>
|
||||||
|
<li><a href="#cookies">Cookies</a></li>
|
||||||
|
<li><a href="#proxy">Proxy</a></li>
|
||||||
|
<li><a href="#ad-blocking">Ad-blocking</a></li>
|
||||||
|
<li><a href="#downloads">Downloads</a></li>
|
||||||
|
<li><a href="#images-off-mode">Images-off mode</a></li>
|
||||||
|
</ul>
|
||||||
|
<li><a href="#configuration">Configuration</a>
|
||||||
|
<ul>
|
||||||
|
<li><a href="#dillorc">Dillorc</a></li>
|
||||||
|
<li><a href="#cookiesrc">Cookiesrc</a></li>
|
||||||
|
<li><a href="#domainrc">Domainrc</a></li>
|
||||||
|
<li><a href="#style-css">Style.css</a></li>
|
||||||
|
<li><a href="#dpidrc">Dpidrc</a></li>
|
||||||
|
<li><a href="#bm-txt">bm.txt</a></li>
|
||||||
|
<li><a href="#keysrc">Keysrc</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li><a href="#advanced-usage">Advanced usage</a>
|
||||||
|
<ul>
|
||||||
|
<li><a href="#plugins">Plugins</a></li>
|
||||||
|
<li><a href="#bug-meter">Bug meter</a></li>
|
||||||
|
<li><a href="#keyboard-shortcuts">Keyboard shortcuts</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<h2 id="introduction">Introduction</h2>
|
||||||
|
<p>Dillo is a web browser designed to be fast, use few resources and support
|
||||||
|
slow and unreliable networks on resource-constrained machines. It can load local
|
||||||
|
and remote files via HTTP, HTTPS and FTP. Other protocols like Gemini, Gopher,
|
||||||
|
IPFS and others are available as
|
||||||
|
<a href="#plugins">plugins</a>.</p>
|
||||||
|
|
||||||
|
<p>Dillo supports a subset of HTML 4.01 and CSS 2.1 but it <b>doesn't support
|
||||||
|
JavaScript</b> and only implements some elements of HTML 5 and CSS 3. It also
|
||||||
|
renders plain text documents and images in PNG, JPG, GIF, SVG and WebP
|
||||||
|
formats.</p>
|
||||||
|
|
||||||
|
<h2 id="reading">Reading</h2>
|
||||||
|
<p>In this section we cover the basics to read a web page, scrolling and finding
|
||||||
|
or copying text.</p>
|
||||||
|
<h3 id="basics">Basics</h3>
|
||||||
|
<p>
|
||||||
|
The graphical interface is designed to be used with a mouse or pointing
|
||||||
|
device. At the top of the browser window you have the location bar and the main
|
||||||
|
buttons to control the browser. You can leave the mouse for a brief moment over
|
||||||
|
any part of the menu to show a tooltip with more information.
|
||||||
|
<p>
|
||||||
|
Dillo has <em>context sensitive menus</em>, which are opened with the right mouse
|
||||||
|
button, available on pages, links, images, forms, the Back and Forward buttons,
|
||||||
|
and the bug meter. They show different actions to be performed specific to the
|
||||||
|
element. For example, to save a page you can right-click on the page and select
|
||||||
|
"Save page as...". Or to copy a link URL you can right-click on a link and
|
||||||
|
select "Copy link location".
|
||||||
|
<p>
|
||||||
|
Dillo can hide all panels and use the whole window area to display the page. To
|
||||||
|
switch between modes use the ESC key. You can also choose the control panel size
|
||||||
|
by going to the Tools button and selecting a different one under "Panel size".
|
||||||
|
<p>
|
||||||
|
You can open this manual from Dillo by clicking on the top right "?" button. It
|
||||||
|
doesn't require a network connection.
|
||||||
|
|
||||||
|
<h3 id="scrolling">Scrolling</h3>
|
||||||
|
|
||||||
|
<p>There are several methods to move or scroll the view of the current page.</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>Using the <em>keyboard</em>:
|
||||||
|
<ul>
|
||||||
|
<li>Use the <code>Home</code> or <code>End</code> keys to jump to the top
|
||||||
|
or to the end of a page.</li>
|
||||||
|
<li>Use <code>PgUp</code> or <code>PgDn</code> to advance a whole
|
||||||
|
<em>page</em> (window size) minus one <em>step</em>.</li>
|
||||||
|
<li>Use the arrow keys to move the view a <em>step</em> in that
|
||||||
|
direction.</li>
|
||||||
|
<li>You can also use the <code>Space</code> key as <code>PgDn</code> and
|
||||||
|
<code>b</code> as PgUp.</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>Using the <em>mouse</em> or <em>pointing device</em>:
|
||||||
|
<ul>
|
||||||
|
<li>Rotate the mouse wheel to move one <em>step</em> in that direction.
|
||||||
|
Hold Shift to move one <em>page</em> instead.
|
||||||
|
<li>Keep the middle button (or mouse wheel button) pressed while
|
||||||
|
dragging to scroll the page precisely.
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>Using the <em>scrollbar</em>:
|
||||||
|
<ul>
|
||||||
|
<li>Drag the thumb in the scrollbar on the side up or down to scroll the
|
||||||
|
page.
|
||||||
|
<li>Left-click or right-click on the scrollbar above or below the thumb to
|
||||||
|
move one <em>page</em> in that direction.
|
||||||
|
<li>Middle-click on the scrollbar above or below the thumb to jump to that
|
||||||
|
specific position of the page.
|
||||||
|
<li>Click the scrollbar arrows to move the view one <em>step</em> in
|
||||||
|
that direction.
|
||||||
|
<li>Rotate the mouse wheel over the vertical scrollbar to move one
|
||||||
|
<em>page</em> in that direction.
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p>You can control the <b>size of a <em>step</em></b> by setting the
|
||||||
|
<code>scroll_step</code> option in the <a href="#dillorc">dillorc</a>
|
||||||
|
configuration file. By default it will scroll 100 pixels per step. The vertical
|
||||||
|
scrollbar can be positioned on the left side setting the
|
||||||
|
<code>scrollbar_on_left</code> option to <code>YES</code>, by default it is on
|
||||||
|
the right side.</p>
|
||||||
|
|
||||||
|
The vertical scrollbar has another mode of operation to navigate full
|
||||||
|
<em>pages</em> that can be enabled by setting the
|
||||||
|
<code>scrollbar_page_mode</code> option to <code>YES</code> or temporarily by
|
||||||
|
holding the Shift key. When this mode is active, left-clicking anywhere on the
|
||||||
|
scrollbar will scroll down one page and right-clicking will scroll up one page.
|
||||||
|
Middle clicking on the scrollbar will move the thumb to that position, which can
|
||||||
|
also be dragged. The Shift key can also be used to temporarily disable this
|
||||||
|
mode if it was enabled in the configuration.
|
||||||
|
|
||||||
|
<h3 id="find-text">Find text</h3>
|
||||||
|
<p>
|
||||||
|
To find text in a document right-click to open the <em>Page menu</em> and select
|
||||||
|
<em>Find text</em> or press <code>Ctrl+F</code> on the keyboard. Then type the
|
||||||
|
substring that you want to find and click Next (or the <code>Enter</code> key).
|
||||||
|
</p>
|
||||||
|
Dillo will scroll the page and highlight found text starting from the top. To
|
||||||
|
find a word (not a substring) use spaces around it to limit the search to
|
||||||
|
matching words only.
|
||||||
|
<p>
|
||||||
|
To close the find bar you can click on the red X on the bottom right or press
|
||||||
|
ESC.
|
||||||
|
|
||||||
|
<h3 id="copy-and-paste">Copy and paste</h3>
|
||||||
|
<p>
|
||||||
|
To copy some text just hold down the left mouse button and move to select the
|
||||||
|
area to copy. To paste, go to the target application and press the middle mouse
|
||||||
|
button.
|
||||||
|
<p>
|
||||||
|
If you want to select more than one screen, hold the mouse left button down and
|
||||||
|
scroll with PgUp, PgDn or the arrow keys.
|
||||||
|
<P>
|
||||||
|
If you want to paste an URL into Dillo, do it on the "clear-URL"
|
||||||
|
button (the "X" next to the <a href="#location-bar">location bar</a>).
|
||||||
|
<p>
|
||||||
|
|
||||||
|
<h3 id="zoom">Zoom</h3>
|
||||||
|
<p>
|
||||||
|
You can increase or decrease the size of the elements of a page by changing the
|
||||||
|
zoom factor. Use <code>Ctrl +</code> to increase the size, <code>Ctrl -</code>
|
||||||
|
to decrease it and <code>Ctrl 0</code> to reset the value to 100%.
|
||||||
|
<p>
|
||||||
|
The initial zoom factor is specified by the <code>zoom_factor</code> option in
|
||||||
|
the <a href="#dillorc">dillorc</a> configuration file. When a new tab or window
|
||||||
|
is opened, the current zoom factor value is inherited.
|
||||||
|
|
||||||
|
<h2 id="navigation">Navigation</h2>
|
||||||
|
This section focuses on how to navigate to other pages by following hyperlinks,
|
||||||
|
using bookmarks, typing or pasting a new URL or using the history.
|
||||||
|
|
||||||
|
<h3 id="hyperlinks">Hyperlinks</h3>
|
||||||
|
|
||||||
|
<p>Hyperlinks (or just links) allow you to navigate to other pages by clicking
|
||||||
|
them. They change the cursor to a hand to indicate that an element can be
|
||||||
|
clicked with the left button:
|
||||||
|
<p style="text-align: center">
|
||||||
|
<img
|
||||||
|
style="border: solid 1px #ddd;"
|
||||||
|
alt="hand shaped cursor"
|
||||||
|
src="data:image/gif;base64,R0lGODlhGAAYAPYAAAAAAAEBAQICAgQEBAcHBx4eHiwsLC8vLzo6Oj09PVZWVldXV1xcXF1dXWlpaWxsbG5uboODg52dnZ6enqCgoKKioqWlpaampqioqKqqqq+vr7S0tLW1tbe3t7u7u7y8vL6+vr+/v8HBwcTExMfHx8jIyMzMzNLS0tPT09jY2Nra2tvb29zc3N3d3d7e3t/f3+Dg4OLi4uPj4+Tk5OXl5efn5+jo6Onp6erq6uvr6+zs7O7u7u/v7/Hx8fLy8vT09Pb29vf39/j4+Pn5+fr6+vv7+/z8/P39/f7+/v///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAAAAAAALAAAAAAYABgAAAfMgEmCg4SFhoeFQUGIjINEIDQ0IESNiDoxCQg0OZWHMUkAAEkunYakoUkqpYWnoidBRIqrrUmRHJEePYuNtJlJmTqRk4y0qKiZm4ZEy6qhLsaiqKSOJioqHCNEodsNzqiqgz0iDgYSOdrb6duphDkgD6EGEer07IM6GTkL9Pyi4IJEYlCg0a1fOnuOXJjbZxAAASLTCEFc2NCAiogSFdJ4QIBfAREDEUGkMMyQwBK7DhGhMUKDhZcwM3A40aNTEB05curMoYPSqp9ADQUCADs=">
|
||||||
|
<p>
|
||||||
|
Links to other pages usually appear in
|
||||||
|
<span style="color:blue; text-decoration: underline">blue and underlined</span>
|
||||||
|
by default and once they are visited they change to
|
||||||
|
<span style="color:purple; text-decoration: underline">purple</span>.
|
||||||
|
However, this is not always the case, so using the mouse cursor shape is the
|
||||||
|
best indicator of a
|
||||||
|
<em><a style="color:black; text-decoration: none" href="#">link</a></em>.
|
||||||
|
<p>
|
||||||
|
When you follow a link by clicking on it, the previous pages will be remembered
|
||||||
|
in case you want to go back to them. Use the Back and Forward buttons on the top
|
||||||
|
bar to navigate among history pages.
|
||||||
|
<p>
|
||||||
|
|
||||||
|
<h3 id="history">History</h3>
|
||||||
|
<p>
|
||||||
|
When you follow several links across different web pages, they are remembered in
|
||||||
|
case you can to go back. Use the "Back" and "Forward" buttons of the panel to go
|
||||||
|
to the previous or next page.
|
||||||
|
<p>
|
||||||
|
You can also right-click on the Back or Forward buttons to open a menu with the
|
||||||
|
list of previous or next pages available, so you can jump directly to them. Use
|
||||||
|
the left button to open them in the current tab or the middle button to open
|
||||||
|
them in a new tab.
|
||||||
|
<p>
|
||||||
|
The <code>,</code> (comma) and <code>.</code> (dot) keys can be used to jump
|
||||||
|
backwards or forward (mnemonic: those keys are usually labeled "<" and
|
||||||
|
">").
|
||||||
|
|
||||||
|
<h3 id="location-bar">Location bar</h3>
|
||||||
|
<p>
|
||||||
|
The location bar on the top displays the URL of the current page loaded and can
|
||||||
|
be used to access other pages by typing the new URL or by pasting it and
|
||||||
|
pressing Enter.
|
||||||
|
<p>
|
||||||
|
The red <b style="color: darkred">x</b>
|
||||||
|
on the left clears the location bar content when left clicked. Then you can type
|
||||||
|
or paste a new address from the clipboard by middle clicking on the location
|
||||||
|
bar.
|
||||||
|
As these two actions are usually performed together, you can simply middle-click
|
||||||
|
on the
|
||||||
|
<b style="color: darkred">x</b>
|
||||||
|
button to go to the URL in the clipboard.
|
||||||
|
<p>
|
||||||
|
You can also select the location bar content from the keyboard by pressing
|
||||||
|
<code>Ctrl+L</code>.
|
||||||
|
|
||||||
|
<h3 id="web-search">Web search</h3>
|
||||||
|
<p>
|
||||||
|
Several search engines are available by pressing the magnifying glass icon next
|
||||||
|
to the location bar or by pressing <code>Ctrl+S</code>. The search engines are
|
||||||
|
configured in <a href="#dillorc">Dillorc</a> with the option
|
||||||
|
<code>search_url</code>. Use it multiple times to define multiple search
|
||||||
|
engines.
|
||||||
|
<p>
|
||||||
|
The location bar can also be used to search the web by using a special prefix
|
||||||
|
for each search engine. For example, the following line:<br>
|
||||||
|
<pre>
|
||||||
|
search_url="dd DuckDuckGo http://duckduckgo.com/lite/?kp=-1&kd=-1&q=%s"
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
Defines the "DuckDuckGo" search engine with the prefix "<code>dd</code>", so you
|
||||||
|
can type in the location bar "<code>dd dillo browser</code>" to search with
|
||||||
|
DuckDuckGo for the keywords "dillo browser".
|
||||||
|
|
||||||
|
<h3 id="tabs">Tabs</h3>
|
||||||
|
<p>
|
||||||
|
Dillo can open different web pages into tabs. To open a link in a new tab click
|
||||||
|
the middle button instead of the left button. The same applies to buttons to
|
||||||
|
submit a form. A new tab can also be opened from the <code>File</code> menu or
|
||||||
|
using the shortcut <code>Ctrl+T</code>.
|
||||||
|
<p>
|
||||||
|
Press <code>Shift</code> while middle-clicking a link to automatically focus the
|
||||||
|
new tab. If you want this behaviour to be the default, set the following option
|
||||||
|
in the <a href="#dillorc">dillorc</a> configuration file:
|
||||||
|
<pre>
|
||||||
|
focus_new_tab=YES
|
||||||
|
</pre>
|
||||||
|
If this option is set, pressing <code>Shift</code> middle-clicking will instead
|
||||||
|
avoid focusing the new tab.
|
||||||
|
<p>
|
||||||
|
It is also possible to have the middle-click open a window instead of a tab. For
|
||||||
|
this set the option:
|
||||||
|
<pre>
|
||||||
|
middle_click_opens_new_tab=NO
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
To close a tab press the X button on the top right (only visible with multiple
|
||||||
|
tabs). You can also close the tab by clicking with the right button on the tab
|
||||||
|
label. The middle button can be used instead by setting the option:
|
||||||
|
<pre>
|
||||||
|
right_click_closes_tab=NO
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
Use the <code>new_tab_page</code> option to control which page is loaded in a
|
||||||
|
newly opened tab, by default is an empty page. To open the
|
||||||
|
bookmarks page set the following line in <a href="#dillorc">dillorc</a>:
|
||||||
|
<pre>
|
||||||
|
new_tab_page="dpi:/bm/"
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<h3 id="bookmarks">Bookmarks</h3>
|
||||||
|
<p>
|
||||||
|
Dillo can save URLs of web pages as bookmarks so you can open them later. To
|
||||||
|
bookmark the current page, open the <em>Page menu</em> by right-clicking on the
|
||||||
|
page and select "Bookmark this page" (it also works over a link).
|
||||||
|
<p>
|
||||||
|
To see or edit the bookmarks, click on the Bookmarks button on the panel or
|
||||||
|
press <code>Ctrl+B</code>.
|
||||||
|
<p>
|
||||||
|
Bookmarks are handled by a built-in Dillo plugin named <code>bm</code> (read more
|
||||||
|
about plugins in the <a href="#plugins">Plugins section</a>) and are
|
||||||
|
synchronized across all instances of the browser. The list of bookmarks can also
|
||||||
|
be shown by opening the URL <a href="dpi:/bm/"><code>dpi:/bm/</code></a>.
|
||||||
|
<p>
|
||||||
|
The bookmarks are stored in a simple plain text file named <code>bm.txt</code>
|
||||||
|
(see the details of this file in the <a href="#bm-txt">bm.txt section</a>). You
|
||||||
|
can edit the file directly to change the bookmarks.
|
||||||
|
|
||||||
|
<h2 id="privacy-and-network">Privacy and network</h2>
|
||||||
|
|
||||||
|
<p>The default privacy policy in Dillo attempts to keep the user safe from
|
||||||
|
tracking, <em>even if this policy breaks a lot of website functionality</em>. In
|
||||||
|
this section you can add exceptions or relax the configuration at your own risk.
|
||||||
|
|
||||||
|
<h3 id="https">HTTPS</h3>
|
||||||
|
|
||||||
|
<p>Dillo has support for
|
||||||
|
<a href="https://en.wikipedia.org/wiki/HTTPS">HTTPS</a>,
|
||||||
|
allowing secure connections to remote websites. Use the protocol
|
||||||
|
"<code>https:</code>" to specify the use of HTTPS. When a problem is encountered
|
||||||
|
with the remote certificate, a warning dialog explains the details and allows
|
||||||
|
you to choose what to do: continue loading the website or cancel.
|
||||||
|
<p>
|
||||||
|
By default Dillo looks for a TLS library at build time, which can be OpenSSL or
|
||||||
|
mbedTLS. You can see which library is being used by looking at the console when
|
||||||
|
it starts:
|
||||||
|
<pre>
|
||||||
|
$ dillo
|
||||||
|
dillo_dns_init: Here we go! (threaded)
|
||||||
|
TLS library: <u>OpenSSL 3.2.1 30 Jan 2024</u>
|
||||||
|
...
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<h3 id="cookies">Cookies</h3>
|
||||||
|
<p>
|
||||||
|
Due to privacy concerns, <b>cookies are disabled by default</b> unless
|
||||||
|
explicitly enabled in the configuration.
|
||||||
|
<p>
|
||||||
|
Support for cookies is implemented using a built-in plugin that shares them
|
||||||
|
between several instances of Dillo and follows the
|
||||||
|
<a href="http://tools.ietf.org/html/rfc6265">RFC 6265</a>
|
||||||
|
specification. Current cookie limits are 20 per domain and
|
||||||
|
1200 in total.
|
||||||
|
|
||||||
|
<p>Cookies are configured by the <code>cookiesrc</code> configuration file. See
|
||||||
|
the <a href="#cookiesrc">Cookiesrc section</a> to see how to enable cookies for
|
||||||
|
some domains or accept them by default.</p>
|
||||||
|
|
||||||
|
<h3 id="proxy">Proxy</h3>
|
||||||
|
<p>Dillo can use a HTTP proxy by setting the environment variable
|
||||||
|
<code>http_proxy</code> or the
|
||||||
|
<a href="#dillorc">configuration option</a> with the same name. All HTTP and
|
||||||
|
HTTPS traffic will be sent through the proxy.
|
||||||
|
|
||||||
|
<p><b>Note</b>: Plugins may not implement proxy support.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 id="ad-blocking">Ad blocking</h3>
|
||||||
|
<p>Dillo has the ability to block content when loading a page based on the domain
|
||||||
|
the embedded content is being loaded from. You can control how Dillo handles
|
||||||
|
automatic requests for resources (like images and style sheets) that aren't at
|
||||||
|
the same domain as the original page.</p>
|
||||||
|
|
||||||
|
<p>See the <a href="#domainrc">domainrc</a> configuration file to find out how to
|
||||||
|
specify which domains are blocked or allowed.</p>
|
||||||
|
|
||||||
|
<h3 id="downloads">Downloads</h3>
|
||||||
|
<p>Downloads are made using
|
||||||
|
<a href="http://www.gnu.org/software/wget/">wget</a>
|
||||||
|
with a <a href="http://www.fltk.org">FLTK</a>-based GUI wrapper, through
|
||||||
|
the <a href="#plugins">Dillo plugin (dpi) framework</a>.
|
||||||
|
If you close the browser window, downloads will continue.</p>
|
||||||
|
|
||||||
|
<h3 id="images-off-mode">Images-off mode</h3>
|
||||||
|
<p>
|
||||||
|
You can browse without images now:
|
||||||
|
<ul>
|
||||||
|
<li>There is an option in the Tools menu to disable automatic image loading.
|
||||||
|
An image's alt text (or <code>[IMG]</code> placeholder) will appear in the
|
||||||
|
page.
|
||||||
|
<li>If you want to load an individual image, left click on its text.
|
||||||
|
<li>You can set "no images" as the default mode in dillorc.
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2 id="configuration">Configuration</h2>
|
||||||
|
|
||||||
|
<p>Dillo has <b>several configuration files</b> that control the behavior of the
|
||||||
|
browser. Each configuration file is searched <em>first</em> in the
|
||||||
|
<a href="file:~/.dillo"><code>~/.dillo</code></a> directory and,
|
||||||
|
<em>if not found</em>, in the default system configuration directory (typically
|
||||||
|
<code>/etc/dillo</code>).</p>
|
||||||
|
|
||||||
|
<p>Most configuration files can include comments by <em>starting</em> a line
|
||||||
|
with the "<code>#</code>" symbol. Their specific syntax is described in the
|
||||||
|
following sections.</p>
|
||||||
|
|
||||||
|
<h3 id="dillorc">Dillorc</h3>
|
||||||
|
|
||||||
|
<p>The main configuration of Dillo is controlled by the
|
||||||
|
<a href="file:~/.dillo/dillorc"><code>dillorc</code></a> file.
|
||||||
|
The list of all available options can be found in the system
|
||||||
|
configuration file, typically
|
||||||
|
<a href="file:/etc/dillo/dillorc"><code>/etc/dillo/dillorc</code></a>. You may
|
||||||
|
want to copy it into
|
||||||
|
<a href="file:~/.dillo/dillorc"><code>~/.dillo/dillorc</code></a>
|
||||||
|
and edit it to suit your needs.</p>
|
||||||
|
|
||||||
|
The file is commented to describe what each option does. The default value for
|
||||||
|
each option as well as other interesting values are also available as comments:
|
||||||
|
<ul>
|
||||||
|
<li>"<code>#option=...</code>" shows the built-in default.</li>
|
||||||
|
<li>"<code># option=...</code>" is an additional example.</li>
|
||||||
|
<li>"<code>option=...</code>" overrides the built-in value.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p>The <code>search_url</code> option can be specified multiple times:
|
||||||
|
<pre>
|
||||||
|
search_url="dd DuckDuckGo (https) https://duckduckgo.com/lite/?kp=-1&q=%s"
|
||||||
|
search_url="Wikipedia http://www.wikipedia.org/w/index.php?search=%s&go=Go"
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<h3 id="cookiesrc">Cookiesrc</h3>
|
||||||
|
|
||||||
|
<p>Cookies are configured in the
|
||||||
|
<a href="file:~/.dillo/cookiesrc"><code>~/.dillo/cookiesrc</code></a>
|
||||||
|
file by using rules, with one rule per line. The rule syntax is very simple,
|
||||||
|
first specify the <em>host</em> and then the <em>action</em>, separated by
|
||||||
|
white spaces. Comment lines start with <code>#</code> as the first character and
|
||||||
|
the whole line is ignored.
|
||||||
|
<p>
|
||||||
|
The host can be in the form <code>example.com</code> to match only that
|
||||||
|
domain or <code>.example.com</code> to match all subdomains of example.com. The
|
||||||
|
special word <code>DEFAULT</code> specifies the default policy, when no other
|
||||||
|
match occurs.
|
||||||
|
<p>
|
||||||
|
The action can be: <code>DENY</code> to ignore cookies,
|
||||||
|
<code>ACCEPT_SESSION</code> to only accept session cookies but don't save them
|
||||||
|
to disk and <code>ACCEPT</code> to accept and store cookies in disk (this will
|
||||||
|
allow sites to track you over time).
|
||||||
|
<p>
|
||||||
|
When the cookies plugin exits, only the accepted cookies by <code>ACCEPT</code>
|
||||||
|
are saved to
|
||||||
|
<a href="file:~/.dillo/cookies.txt"><code>~/.dillo/cookies.txt</code></a>,
|
||||||
|
and ACCEPT_SESSION cookies are
|
||||||
|
forgotten. The cookies plugin normally exits after a period of inactivity, but
|
||||||
|
you can force it to exit with the command <code>dpidc stop</code>.
|
||||||
|
<p>
|
||||||
|
Here is an example <code>cookiesrc</code> file:
|
||||||
|
<pre>
|
||||||
|
# host action
|
||||||
|
DEFAULT DENY
|
||||||
|
fltk.org ACCEPT
|
||||||
|
.example.com ACCEPT_SESSION
|
||||||
|
</pre>
|
||||||
|
<p> Which is parsed as follows:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Line 1: Comment line begins with <code>#</code>.</li>
|
||||||
|
<li>Line 2: Deny all cookies from all domains not otherwise specified (this is
|
||||||
|
the default).
|
||||||
|
<li>Line 3: Accept all cookies from fltk.org, and save them to disk when the
|
||||||
|
cookie plugin exits.</li>
|
||||||
|
<li>Line 4: Accept all cookies from all subdomains of example.com, but do not
|
||||||
|
save them when the cookie plugin exits.</li>
|
||||||
|
</ul>
|
||||||
|
<p>
|
||||||
|
Dillo is especially concerned about privacy and security issues. Our advice is
|
||||||
|
to <em>avoid cookies whenever possible</em> and at most set ACCEPT_SESSION to
|
||||||
|
<em>specific trusted sites</em>.
|
||||||
|
|
||||||
|
<h3 id="domainrc">Domainrc</h3>
|
||||||
|
|
||||||
|
<p>With the
|
||||||
|
<code>~/.dillo/domainrc</code>
|
||||||
|
file, you can control how Dillo handles automatic requests for resources
|
||||||
|
(like images and style sheets) that aren't at the same domain as the
|
||||||
|
original page.
|
||||||
|
<p>
|
||||||
|
The file contains one rule per line. Comments are specified by starting the line
|
||||||
|
with the <code>#</code> symbol. The default rule is either
|
||||||
|
<code>default accept</code> or <code>default deny</code> and will cause Dillo
|
||||||
|
to, respectively, accept all requests by default or deny all requests by
|
||||||
|
default.
|
||||||
|
<p>
|
||||||
|
Depending on the default rule, the next rules behave as exceptions by denying
|
||||||
|
specific connections or allowing specific connections, respectively.
|
||||||
|
<p>
|
||||||
|
Exceptions to the default rule are written in the format
|
||||||
|
<code>source destination</code>, and match request from the source domain to the
|
||||||
|
destination domain. The source and destination domains can be specified in three
|
||||||
|
ways:
|
||||||
|
<ul>
|
||||||
|
<li>"<code>*</code>" to match any domain</li>
|
||||||
|
<li>"<code>example.com</code>" to match the specific host example.com</li>
|
||||||
|
<li>"<code>.example.com</code>" to match example.com and any of its
|
||||||
|
subdomains</li>
|
||||||
|
</ul>
|
||||||
|
<p>
|
||||||
|
Here is an example:
|
||||||
|
<pre>
|
||||||
|
# Accept all requests by default
|
||||||
|
default accept
|
||||||
|
# But block some ad-sites and trackers from any domain
|
||||||
|
* .doubleclick.net
|
||||||
|
* .googleadservices.com
|
||||||
|
* .quantserve.com
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<h3 id="style-css">Style.css</h3>
|
||||||
|
|
||||||
|
<p>Custom
|
||||||
|
<a href="https://en.wikipedia.org/wiki/CSS">CSS styles</a> can be placed in the
|
||||||
|
<a href="file:~/.dillo/style.css"><code>~/.dillo/style.css</code></a>
|
||||||
|
file to set default web page styles. To override page styles add the
|
||||||
|
"<code>!important</code>" flag.
|
||||||
|
|
||||||
|
<h3 id="dpidrc">Dpidrc</h3>
|
||||||
|
|
||||||
|
<p>The configuration for <a href="#plugins">plugins</a> is placed in the
|
||||||
|
<a href="file:~/.dillo/dpidrc"><code>~/.dillo/dpidrc</code></a> file.</p>
|
||||||
|
|
||||||
|
<p>Plugins are searched in the <code>~/.dillo/dpi/</code> directory first, and
|
||||||
|
then in the system directory if not found. The <code>dpi_dir</code> option
|
||||||
|
sets the system <code>dpi</code> directory and must be specified once. Here is
|
||||||
|
an example:</p>
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
dpi_dir=/usr/lib/dillo/dpi
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p>By default, plugins will receive requests at the URL
|
||||||
|
<code>dpi:/<em>name</em>/</code>, for example bookmarks are available at
|
||||||
|
<a href="dpi:/bm/"><code>dpi:/bm/</code></a>.
|
||||||
|
|
||||||
|
<p>Plugins can also be associated with a protocol (like "<code>file:</code>").
|
||||||
|
When a request is made using the given protocol the associated plugin is used to
|
||||||
|
process the request. The rest of the file assigns a plugin to a protocol, one
|
||||||
|
per line. The syntax is
|
||||||
|
<code>proto.<em>name</em>=<em>path-to-plugin.dpi</em></code>. The path of the
|
||||||
|
plugin is relative to the <code>dpi</code> directory. Here are protocols
|
||||||
|
used by the built-in plugins:</p>
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
proto.file=file/file.dpi
|
||||||
|
proto.ftp=ftp/ftp.filter.dpi
|
||||||
|
proto.data=datauri/datauri.filter.dpi
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p>Here (
|
||||||
|
<img alt="red dot"
|
||||||
|
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==">
|
||||||
|
) is an example image using the <code>data:</code> protocol used to define a PNG
|
||||||
|
image inline in a HTML document.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 id="bm-txt">bm.txt</h3>
|
||||||
|
|
||||||
|
<p>Web page URLs can be saved as bookmarks so they can be accessed in the future.
|
||||||
|
Bookmarks are stored in a plain text file located at
|
||||||
|
<a href="file:~/.dillo/bm.txt"><code>~/.dillo/bm.txt</code></a>. The file can be
|
||||||
|
modified by using the
|
||||||
|
<a href="dpi:/bm/">Book button</a>
|
||||||
|
in the toolbar and the context menu on a page or link, but they can also be
|
||||||
|
edited manually. Bookmarks are organized into sections and are given a
|
||||||
|
description (usually the page title).</p>
|
||||||
|
|
||||||
|
<p>The syntax of the <code>bm.txt</code> file is described as follows:</p>
|
||||||
|
|
||||||
|
<p>First, the sections are defined at the beginning, prefixed with a
|
||||||
|
<code>:s<em>N</em>:</code> identifier, along with the description of the
|
||||||
|
section:</p>
|
||||||
|
<pre>
|
||||||
|
:s0: News
|
||||||
|
:s1: Blog
|
||||||
|
</pre>
|
||||||
|
<p>Then every bookmark is added in a single line, by writing the section
|
||||||
|
identifier, the URL and a description:</p>
|
||||||
|
<pre>
|
||||||
|
s0 https://slashdot.org Slashdot
|
||||||
|
s0 https://news.ycombinator.com/ Hacker News
|
||||||
|
s1 https://100r.co/site/log.html 100 Rabbits Log
|
||||||
|
</pre>
|
||||||
|
<p>The <code>bm.txt</code> file is designed to be <em>easily</em> read by humans
|
||||||
|
and is suitable to be kept updated among machines by using a version control
|
||||||
|
tool.
|
||||||
|
|
||||||
|
<h3 id="keysrc">Keysrc</h3>
|
||||||
|
|
||||||
|
<p>The mapping of keys in Dillo is controlled by the
|
||||||
|
<a href="file:~/.dillo/keysrc"><code>~/.dillo/keysrc</code></a> file.
|
||||||
|
The format is "<code>key = action</code>" or
|
||||||
|
"<code><modifier>key = action</code>". Lines that begin with a "#" are comments.
|
||||||
|
The commented-out bindings below show the defaults built into Dillo.
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>Modifiers recognized: Shift, Ctrl, Alt, Meta (on Mac OS X use "Meta" for
|
||||||
|
Command).
|
||||||
|
|
||||||
|
<li>Key names recognized: Backspace, Delete, Down, End, Esc, F1 through
|
||||||
|
F12, Home, Insert, Left, Menu, PageDown, PageUp, Print, Return, Right, Space,
|
||||||
|
Tab, Up.
|
||||||
|
|
||||||
|
<li>Multimedia keys: Back, Favorites, Forward, HomePage, Mail, MediaNext,
|
||||||
|
MediaPlay, MediaPrev, MediaStop, Refresh, Search, Sleep, Stop, VolumeDown,
|
||||||
|
VolumeMute, VolumeUp.
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p>If Dillo is running under X11, keys whose names are not recognized can be
|
||||||
|
specified using their keysym value in hexadecimal. Use <code>xev</code> to get
|
||||||
|
the keysym, for example:
|
||||||
|
<pre>
|
||||||
|
0x1008ff27 = forward
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p>The action "nop" (no operation) can be used to remove a binding.
|
||||||
|
|
||||||
|
<p>Example file:</p>
|
||||||
|
<pre>
|
||||||
|
# "close-all" closes all tabs/windows and exits.
|
||||||
|
<ctrl>q = close-all
|
||||||
|
|
||||||
|
# "left-tab" and "right-tab" switch to the left/right of the current tab.
|
||||||
|
<ctrl><shift>tab = left-tab
|
||||||
|
|
||||||
|
# Use HJKL to move around
|
||||||
|
k = line-up
|
||||||
|
j = line-down
|
||||||
|
h = left
|
||||||
|
l = right
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<h2 id="advanced-usage">Advanced usage</h2>
|
||||||
|
|
||||||
|
These sections focus on advanced topics and are recommended for experienced
|
||||||
|
users of Dillo.
|
||||||
|
|
||||||
|
<h3 id="plugins">Plugins</h3>
|
||||||
|
<p>The functionality of Dillo can be extended by using plugins, which can
|
||||||
|
translate other formats to HTML, implement new protocols or provide a custom
|
||||||
|
service.</p>
|
||||||
|
|
||||||
|
<p>Plugins can be written in <em>any programming language</em> and they
|
||||||
|
interact with the browser using the
|
||||||
|
<a href="https://dillo-browser.github.io/old/dpi1.html">DPI protocol</a>. A
|
||||||
|
<a href="https://dillo-browser.github.io/#plugins">list of plugins</a>
|
||||||
|
is available on the Dillo website. Some plugins are just a
|
||||||
|
<a href="https://raw.githubusercontent.com/dillo-browser/dillo-plugin-man/master/man.filter.dpi">few
|
||||||
|
lines of shell script</a>, so you are encouraged to read them to learn how to
|
||||||
|
write your own plugins. Plugins are searched by looking for files that end with
|
||||||
|
the <code>.dpi</code> extension (or <code>.dpi.exe</code> in Windows) in
|
||||||
|
<code>~/.dillo/dpi</code> and <code>dpi_dir</code> (see the
|
||||||
|
<a href="#dpidrc">Dpidrc</a> configuration section).</p>
|
||||||
|
|
||||||
|
<p>There are two types of plugins: <em>filters</em> and <em>servers</em>.
|
||||||
|
Filters are executed for each request like a
|
||||||
|
<a href="https://en.wikipedia.org/wiki/Pipeline_(Unix)">UNIX pipe</a>,
|
||||||
|
they read from the
|
||||||
|
standard input and write to the standard output. The name of filter plugin
|
||||||
|
programs must end in <code>.filter.dpi</code>. On the other hand, servers listen
|
||||||
|
on a socket for new requests. They can process several requests at the same
|
||||||
|
time, preventing the overhead of spawning multiple processes and they can easily
|
||||||
|
share information among requests.</p>
|
||||||
|
|
||||||
|
<p>You can install plugins from any third party, but you should always
|
||||||
|
review the source before running code written by others. To install a new
|
||||||
|
plugin, copy the files to <code>~/.dillo/dpi/<em>name</em>/</code> and
|
||||||
|
then associate the <em>name</em> protocol to the program that must run in
|
||||||
|
the
|
||||||
|
<a href="file:~/.dillo/dpidrc"><code>~/.dillo/dpidrc</code></a> file.
|
||||||
|
Plugins may have other software dependencies required for it to work.</p>
|
||||||
|
|
||||||
|
<p>Here is an example of how to manually install the
|
||||||
|
<a href="https://github.com/dillo-browser/dillo-plugin-gemini">Gemini protocol plugin</a>
|
||||||
|
(it comes with a Makefile that automates the process, so this is not necessary),
|
||||||
|
which is a filter plugin written in shell script:</p>
|
||||||
|
<pre>
|
||||||
|
$ mkdir -p ~/.dillo/dpi/gemini
|
||||||
|
$ cp gemini.filter.dpi ~/.dillo/dpi/gemini/
|
||||||
|
$ chmod +x ~/.dillo/dpi/gemini/gemini.filter.dpi
|
||||||
|
$ echo "proto.gemini=gemini/gemini.filter.dpi" >> ~/.dillo/dpidrc
|
||||||
|
$ dpidc stop
|
||||||
|
</pre>
|
||||||
|
<p>Now, when a request is made to an URL that begins with the
|
||||||
|
<code>gemini:</code> protocol, it will be processed by the
|
||||||
|
<code>gemini/gemini.filter.dpi</code> program, and the output will be displayed
|
||||||
|
by Dillo.</p>
|
||||||
|
|
||||||
|
<h3 id="bug-meter">Bug Meter</h3>
|
||||||
|
<p>
|
||||||
|
Dillo includes a
|
||||||
|
<a href='https://dillo-browser.github.io/old/help/bug_meter.html'>bug meter</a>
|
||||||
|
which shows the number of detected bugs inside the page. The bugs are caught at
|
||||||
|
parsing time, so the error messages also show the line where they occur and
|
||||||
|
provide a hint of what was expected instead.
|
||||||
|
<p>
|
||||||
|
The primary purpose of the bug meter is to help webmasters and page authors to
|
||||||
|
polish the contents of their sites with a view to making them
|
||||||
|
compliant with HTML standards.
|
||||||
|
<p>
|
||||||
|
The bug meter is located at the lower right corner of Dillo. Use the left-click
|
||||||
|
to see the messages, right-click for a menu to open other HTML validators.
|
||||||
|
|
||||||
|
<h3 id="keyboard-shortcuts">Keyboard shortcuts</h3>
|
||||||
|
<p>
|
||||||
|
Most actions can be issued by using a keyboard shortcut. The key bindings can be
|
||||||
|
changed in the
|
||||||
|
<code><a href="file:~/.dillo/keysrc">~/.dillo/keysrc</a></code> file (see the
|
||||||
|
<a href="#keysrc">keysrc</a> section).
|
||||||
|
The list of default bindings is given in the following table.
|
||||||
|
<p>
|
||||||
|
<table>
|
||||||
|
<tr><th>Shortcut <th>Mnemonic <th>Function
|
||||||
|
<tr><td>Ctrl-L <td>Location <td>Enter a new URL
|
||||||
|
<tr><td>Ctrl-F <td>Find <td>Find text
|
||||||
|
<tr><td>Ctrl-S <td>Search <td>Search the web
|
||||||
|
<tr><td>Ctrl-R <td>Reload <td>Reload current page
|
||||||
|
<tr><td>Ctrl-N <td>New <td>New browser window
|
||||||
|
<tr><td>Ctrl-T <td>Tab <td>New tab
|
||||||
|
<tr><td>Ctrl-W <td>Window <td>Quit tab/window
|
||||||
|
<tr><td>Ctrl-O <td>Open <td>Open file
|
||||||
|
<tr><td>Ctrl-U <td> <td>View source
|
||||||
|
<tr><td>Ctrl-B <td>Bookmarks <td>View bookmarks
|
||||||
|
<tr><td>Ctrl-Q <td>Quit <td>Quit dillo
|
||||||
|
<tr><td>Ctrl-+ or Ctrl-= <td>Bigger <td>Zoom in
|
||||||
|
<tr><td>Ctrl-- <td>Smaller <td>Zoom out
|
||||||
|
<tr><td>Ctrl-0 <td>100% <td>Reset zoom to 100%
|
||||||
|
<tr><td>Back or "<b>,</b>" <td>< <td>Previous page
|
||||||
|
<tr><td>Shift-Back or "<b>.</b>" <td>> <td>Next page
|
||||||
|
<tr><td>Alt-F <td>File <td>File menu
|
||||||
|
<tr><td>Ctrl-Tab or<br>
|
||||||
|
Ctrl-PageDown <td>Tab <td>Next tab
|
||||||
|
<tr><td>Ctrl-Shift-Tab or<br>
|
||||||
|
Ctrl-PageUp <td>Tab <td>Previous tab
|
||||||
|
<tr><td>Esc <td>escape <td>Close dialog,
|
||||||
|
Close findbar,<br>
|
||||||
|
Hide/show control panels
|
||||||
|
</table>
|
||||||
|
<footer>
|
||||||
|
<p>This manual has been written in HTML <em>by hand</em> using Vim.<br>
|
||||||
|
If you find any issue, please report it via
|
||||||
|
<a href="https://github.com/dillo-browser/dillo/issues/new">GitHub</a> or
|
||||||
|
<a href="mailto:dillo-dev@mailman3.com">email</a>.
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
56
dpi/Makefile.am
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
AM_CPPFLAGS = \
|
||||||
|
-I$(top_srcdir)
|
||||||
|
|
||||||
|
bookmarksdir = $(libdir)/dillo/dpi/bookmarks
|
||||||
|
downloadsdir = $(libdir)/dillo/dpi/downloads
|
||||||
|
ftpdir = $(libdir)/dillo/dpi/ftp
|
||||||
|
hellodir = $(libdir)/dillo/dpi/hello
|
||||||
|
vsourcedir = $(libdir)/dillo/dpi/vsource
|
||||||
|
filedir = $(libdir)/dillo/dpi/file
|
||||||
|
cookiesdir = $(libdir)/dillo/dpi/cookies
|
||||||
|
datauridir = $(libdir)/dillo/dpi/datauri
|
||||||
|
bookmarks_PROGRAMS = bookmarks.dpi
|
||||||
|
downloads_PROGRAMS = downloads.dpi
|
||||||
|
ftp_PROGRAMS = ftp.filter.dpi
|
||||||
|
hello_PROGRAMS = hello.filter.dpi
|
||||||
|
vsource_PROGRAMS = vsource.filter.dpi
|
||||||
|
file_PROGRAMS = file.dpi
|
||||||
|
cookies_PROGRAMS = cookies.dpi
|
||||||
|
datauri_PROGRAMS = datauri.filter.dpi
|
||||||
|
|
||||||
|
bookmarks_dpi_LDADD = \
|
||||||
|
$(top_builddir)/dpip/libDpip.a \
|
||||||
|
$(top_builddir)/dlib/libDlib.a
|
||||||
|
downloads_dpi_LDADD = @LIBFLTK_LIBS@ \
|
||||||
|
$(top_builddir)/dpip/libDpip.a \
|
||||||
|
$(top_builddir)/dlib/libDlib.a
|
||||||
|
ftp_filter_dpi_LDADD = \
|
||||||
|
$(top_builddir)/dpip/libDpip.a \
|
||||||
|
$(top_builddir)/dlib/libDlib.a
|
||||||
|
hello_filter_dpi_LDADD = \
|
||||||
|
$(top_builddir)/dpip/libDpip.a \
|
||||||
|
$(top_builddir)/dlib/libDlib.a
|
||||||
|
vsource_filter_dpi_LDADD = \
|
||||||
|
$(top_builddir)/dpip/libDpip.a \
|
||||||
|
$(top_builddir)/dlib/libDlib.a
|
||||||
|
file_dpi_LDADD = \
|
||||||
|
$(top_builddir)/dpip/libDpip.a \
|
||||||
|
$(top_builddir)/dlib/libDlib.a
|
||||||
|
cookies_dpi_LDADD = \
|
||||||
|
$(top_builddir)/dpip/libDpip.a \
|
||||||
|
$(top_builddir)/dlib/libDlib.a
|
||||||
|
datauri_filter_dpi_LDADD = \
|
||||||
|
$(top_builddir)/dpip/libDpip.a \
|
||||||
|
$(top_builddir)/dlib/libDlib.a
|
||||||
|
|
||||||
|
downloads_dpi_CXXFLAGS = @LIBFLTK_CXXFLAGS@
|
||||||
|
|
||||||
|
bookmarks_dpi_SOURCES = bookmarks.c dpiutil.c dpiutil.h
|
||||||
|
downloads_dpi_SOURCES = downloads.cc dpiutil.c dpiutil.h
|
||||||
|
ftp_filter_dpi_SOURCES = ftp.c dpiutil.c dpiutil.h
|
||||||
|
hello_filter_dpi_SOURCES = hello.c dpiutil.c dpiutil.h
|
||||||
|
vsource_filter_dpi_SOURCES = vsource.c dpiutil.c dpiutil.h
|
||||||
|
file_dpi_SOURCES = file.c dpiutil.c dpiutil.h
|
||||||
|
cookies_dpi_SOURCES = cookies.c dpiutil.c dpiutil.h
|
||||||
|
datauri_filter_dpi_SOURCES = datauri.c dpiutil.c dpiutil.h
|
||||||
|
|
||||||
1680
dpi/bookmarks.c
Normal file
1707
dpi/cookies.c
Normal file
351
dpi/datauri.c
Normal file
@ -0,0 +1,351 @@
|
|||||||
|
/*
|
||||||
|
* File: datauri.c
|
||||||
|
*
|
||||||
|
* Copyright (C) 2006-2007 Jorge Arellano Cid <jcid@dillo.org>
|
||||||
|
*
|
||||||
|
* Filter dpi for the "data:" URI scheme (RFC 2397).
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "../dpip/dpip.h"
|
||||||
|
#include "dpiutil.h"
|
||||||
|
#include "../src/misc.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Debugging macros
|
||||||
|
*/
|
||||||
|
#define SILENT 1
|
||||||
|
#define _MSG(...)
|
||||||
|
#if SILENT
|
||||||
|
#define MSG(...)
|
||||||
|
#else
|
||||||
|
#define MSG(...) fprintf(stderr, "[datauri dpi]: " __VA_ARGS__)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Global variables
|
||||||
|
*/
|
||||||
|
static Dsh *sh = NULL;
|
||||||
|
|
||||||
|
static void b64strip_illegal_chars(unsigned char* str)
|
||||||
|
{
|
||||||
|
unsigned char *p, *s = str;
|
||||||
|
|
||||||
|
MSG("len=%d{%s}\n", strlen((char*)str), str);
|
||||||
|
|
||||||
|
for (p = s; (*p = *s); ++s) {
|
||||||
|
if (d_isascii(*p) && (isalnum(*p) || strchr("+/=", *p)))
|
||||||
|
++p;
|
||||||
|
}
|
||||||
|
|
||||||
|
MSG("len=%d{%s}\n", strlen((char *)str), str);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int b64decode(unsigned char* str)
|
||||||
|
{
|
||||||
|
unsigned char *cur, *start;
|
||||||
|
int d, dlast, phase;
|
||||||
|
unsigned char c;
|
||||||
|
static int table[256] = {
|
||||||
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */
|
||||||
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */
|
||||||
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */
|
||||||
|
52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */
|
||||||
|
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */
|
||||||
|
15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */
|
||||||
|
-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */
|
||||||
|
41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */
|
||||||
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */
|
||||||
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */
|
||||||
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */
|
||||||
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */
|
||||||
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */
|
||||||
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */
|
||||||
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */
|
||||||
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */
|
||||||
|
};
|
||||||
|
|
||||||
|
d = dlast = phase = 0;
|
||||||
|
start = str;
|
||||||
|
for (cur = str; *cur != '\0'; ++cur ) {
|
||||||
|
// jer: treat line endings as physical breaks.
|
||||||
|
//if (*cur == '\n' || *cur == '\r'){phase = dlast = 0; continue;}
|
||||||
|
d = table[(int)*cur];
|
||||||
|
if (d != -1) {
|
||||||
|
switch(phase) {
|
||||||
|
case 0:
|
||||||
|
++phase;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
c = ((dlast << 2) | ((d & 0x30) >> 4));
|
||||||
|
*str++ = c;
|
||||||
|
++phase;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
c = (((dlast & 0xf) << 4) | ((d & 0x3c) >> 2));
|
||||||
|
*str++ = c;
|
||||||
|
++phase;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
c = (((dlast & 0x03 ) << 6) | d);
|
||||||
|
*str++ = c;
|
||||||
|
phase = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dlast = d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*str = '\0';
|
||||||
|
return str - start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modified from src/url.c --------------------------------------------------*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Given an hex octet (e.g., e3, 2F, 20), return the corresponding
|
||||||
|
* character if the octet is valid, and -1 otherwise
|
||||||
|
*/
|
||||||
|
static int Url_decode_hex_octet(const char *s)
|
||||||
|
{
|
||||||
|
int hex_value;
|
||||||
|
char *tail, hex[3];
|
||||||
|
|
||||||
|
if (s && (hex[0] = s[0]) && (hex[1] = s[1])) {
|
||||||
|
hex[2] = 0;
|
||||||
|
hex_value = strtol(hex, &tail, 16);
|
||||||
|
if (tail - hex == 2)
|
||||||
|
return hex_value;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse possible hexadecimal octets in the URI path.
|
||||||
|
* Returns a new allocated string.
|
||||||
|
*/
|
||||||
|
char *a_Url_decode_hex_str(const char *str, size_t *p_sz)
|
||||||
|
{
|
||||||
|
char *new_str, *dest;
|
||||||
|
int i, val;
|
||||||
|
|
||||||
|
if (!str) {
|
||||||
|
*p_sz = 0;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
dest = new_str = dNew(char, strlen(str) + 1);
|
||||||
|
for (i = 0; str[i]; i++) {
|
||||||
|
*dest++ = (str[i] == '%' && (val = Url_decode_hex_octet(str+i+1)) >= 0) ?
|
||||||
|
i+=2, val : str[i];
|
||||||
|
}
|
||||||
|
*dest = 0;
|
||||||
|
|
||||||
|
*p_sz = (size_t)(dest - new_str);
|
||||||
|
new_str = dRealloc(new_str, sizeof(char) * (dest - new_str + 1));
|
||||||
|
return new_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* end ----------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send decoded data to dillo in an HTTP envelope.
|
||||||
|
*/
|
||||||
|
static void send_decoded_data(const char *url, const char *mime_type,
|
||||||
|
unsigned char *data, size_t data_sz)
|
||||||
|
{
|
||||||
|
char *d_cmd;
|
||||||
|
|
||||||
|
/* Send dpip tag */
|
||||||
|
d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
|
||||||
|
a_Dpip_dsh_write_str(sh, 1, d_cmd);
|
||||||
|
dFree(d_cmd);
|
||||||
|
|
||||||
|
/* Send HTTP header. */
|
||||||
|
a_Dpip_dsh_write_str(sh, 0, "Content-type: ");
|
||||||
|
a_Dpip_dsh_write_str(sh, 0, mime_type);
|
||||||
|
a_Dpip_dsh_write_str(sh, 1, "\n\n");
|
||||||
|
|
||||||
|
/* Send message */
|
||||||
|
a_Dpip_dsh_write(sh, 0, (char *)data, data_sz);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void send_failure_message(const char *url, const char *mime_type,
|
||||||
|
unsigned char *data, size_t data_sz)
|
||||||
|
{
|
||||||
|
char *d_cmd;
|
||||||
|
char buf[1024];
|
||||||
|
|
||||||
|
const char *msg =
|
||||||
|
"<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
|
||||||
|
"<html><body>\n"
|
||||||
|
"<hr><h1>Datauri dpi</h1><hr>\n"
|
||||||
|
"<p><b>Can't parse datauri:</b><br>\n";
|
||||||
|
const char *msg_mime_type="text/html";
|
||||||
|
|
||||||
|
/* Send dpip tag */
|
||||||
|
d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
|
||||||
|
a_Dpip_dsh_write_str(sh, 1, d_cmd);
|
||||||
|
dFree(d_cmd);
|
||||||
|
|
||||||
|
/* Send HTTP header. */
|
||||||
|
a_Dpip_dsh_write_str(sh, 0, "Content-type: ");
|
||||||
|
a_Dpip_dsh_write_str(sh, 0, msg_mime_type);
|
||||||
|
a_Dpip_dsh_write_str(sh, 1, "\n\n");
|
||||||
|
|
||||||
|
/* Send message */
|
||||||
|
a_Dpip_dsh_write_str(sh, 0, msg);
|
||||||
|
|
||||||
|
/* send some debug info */
|
||||||
|
snprintf(buf, 1024, "mime_type: %s<br>data size: %d<br>data: %s<br>",
|
||||||
|
mime_type, (int)data_sz, data);
|
||||||
|
a_Dpip_dsh_write_str(sh, 0, buf);
|
||||||
|
|
||||||
|
/* close page */
|
||||||
|
a_Dpip_dsh_write_str(sh, 0, "</body></html>");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get mime type from the data URI.
|
||||||
|
*/
|
||||||
|
static char *datauri_get_mime(char *url)
|
||||||
|
{
|
||||||
|
char buf[256];
|
||||||
|
char *mime_type = NULL, *p;
|
||||||
|
size_t len = 0;
|
||||||
|
|
||||||
|
if (dStrnAsciiCasecmp(url, "data:", 5) == 0) {
|
||||||
|
if ((p = strchr(url, ',')) && p - url < 256) {
|
||||||
|
url += 5;
|
||||||
|
len = p - url;
|
||||||
|
strncpy(buf, url, len);
|
||||||
|
buf[len] = 0;
|
||||||
|
/* strip ";base64" */
|
||||||
|
if (len >= 7 && dStrAsciiCasecmp(buf + len - 7, ";base64") == 0) {
|
||||||
|
len -= 7;
|
||||||
|
buf[len] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* that's it, now handle omitted types */
|
||||||
|
if (len == 0) {
|
||||||
|
mime_type = dStrdup("text/plain;charset=US-ASCII");
|
||||||
|
} else if (!dStrnAsciiCasecmp(buf, "charset", 7)) {
|
||||||
|
mime_type = dStrconcat("text/plain;", buf, NULL);
|
||||||
|
} else {
|
||||||
|
mime_type = dStrdup(buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mime_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return a decoded data string.
|
||||||
|
*/
|
||||||
|
static unsigned char *datauri_get_data(char *url, size_t *p_sz)
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
int is_base64 = 0;
|
||||||
|
unsigned char *data = NULL;
|
||||||
|
|
||||||
|
if ((p = strchr(url, ',')) && p - url >= 12 && /* "data:;base64" */
|
||||||
|
dStrnAsciiCasecmp(p - 7, ";base64", 7) == 0) {
|
||||||
|
is_base64 = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p) {
|
||||||
|
++p;
|
||||||
|
if (is_base64) {
|
||||||
|
data = (unsigned char *)Unescape_uri_str(p);
|
||||||
|
b64strip_illegal_chars(data);
|
||||||
|
*p_sz = (size_t) b64decode(data);
|
||||||
|
} else {
|
||||||
|
data = (unsigned char *)a_Url_decode_hex_str(p, p_sz);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
data = (unsigned char *)dStrdup("");
|
||||||
|
*p_sz = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
char *dpip_tag = NULL, *cmd = NULL, *url = NULL, *mime_type;
|
||||||
|
unsigned char *data;
|
||||||
|
int rc;
|
||||||
|
size_t data_size = 0;
|
||||||
|
|
||||||
|
/* Initialize the SockHandler */
|
||||||
|
sh = a_Dpip_dsh_new(STDIN_FILENO, STDOUT_FILENO, 8*1024);
|
||||||
|
|
||||||
|
rc = chdir("/tmp");
|
||||||
|
if (rc == -1) {
|
||||||
|
MSG("paths: error changing directory to /tmp: %s\n",
|
||||||
|
dStrerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Authenticate our client... */
|
||||||
|
if (!(dpip_tag = a_Dpip_dsh_read_token(sh, 1)) ||
|
||||||
|
a_Dpip_check_auth(dpip_tag) < 0) {
|
||||||
|
MSG("can't authenticate request: %s\n", dStrerror(errno));
|
||||||
|
a_Dpip_dsh_close(sh);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
dFree(dpip_tag);
|
||||||
|
|
||||||
|
/* Read the dpi command from STDIN */
|
||||||
|
dpip_tag = a_Dpip_dsh_read_token(sh, 1);
|
||||||
|
MSG("[%s]\n", dpip_tag);
|
||||||
|
|
||||||
|
cmd = a_Dpip_get_attr(dpip_tag, "cmd");
|
||||||
|
url = a_Dpip_get_attr(dpip_tag, "url");
|
||||||
|
if (!cmd || !url) {
|
||||||
|
MSG("Error, cmd=%s, url=%s\n", cmd, url);
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse the data URI */
|
||||||
|
mime_type = datauri_get_mime(url);
|
||||||
|
data = datauri_get_data(url, &data_size);
|
||||||
|
|
||||||
|
MSG("mime_type: %s\n", mime_type);
|
||||||
|
MSG("data_size: %d\n", (int)data_size);
|
||||||
|
MSG("data: {%s}\n", data);
|
||||||
|
|
||||||
|
if (mime_type && data) {
|
||||||
|
/* good URI */
|
||||||
|
send_decoded_data(url, mime_type, data, data_size);
|
||||||
|
} else {
|
||||||
|
/* malformed URI */
|
||||||
|
send_failure_message(url, mime_type, data, data_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
dFree(data);
|
||||||
|
dFree(mime_type);
|
||||||
|
dFree(url);
|
||||||
|
dFree(cmd);
|
||||||
|
dFree(dpip_tag);
|
||||||
|
|
||||||
|
/* Finish the SockHandler */
|
||||||
|
a_Dpip_dsh_close(sh);
|
||||||
|
a_Dpip_dsh_free(sh);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
1128
dpi/downloads.cc
Normal file
168
dpi/dpiutil.c
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
/*
|
||||||
|
* File: dpiutil.c
|
||||||
|
*
|
||||||
|
* Copyright 2004-2007 Jorge Arellano Cid <jcid@dillo.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
#include "dpiutil.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Debugging macros
|
||||||
|
*/
|
||||||
|
#define _MSG(...)
|
||||||
|
#define MSG(...) printf("[dpiutil.c]: " __VA_ARGS__)
|
||||||
|
|
||||||
|
|
||||||
|
/* Escaping/De-escaping ---------------------------------------------------*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Escape URI characters in 'esc_set' as %XX sequences.
|
||||||
|
* Return value: New escaped string.
|
||||||
|
*/
|
||||||
|
char *Escape_uri_str(const char *str, const char *p_esc_set)
|
||||||
|
{
|
||||||
|
static const char *esc_set, *hex = "0123456789ABCDEF";
|
||||||
|
char *p;
|
||||||
|
Dstr *dstr;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
esc_set = (p_esc_set) ? p_esc_set : "%#:' ";
|
||||||
|
dstr = dStr_sized_new(64);
|
||||||
|
for (i = 0; str[i]; ++i) {
|
||||||
|
if (str[i] <= 0x1F || str[i] == 0x7F || strchr(esc_set, str[i])) {
|
||||||
|
dStr_append_c(dstr, '%');
|
||||||
|
dStr_append_c(dstr, hex[(str[i] >> 4) & 15]);
|
||||||
|
dStr_append_c(dstr, hex[str[i] & 15]);
|
||||||
|
} else {
|
||||||
|
dStr_append_c(dstr, str[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p = dstr->str;
|
||||||
|
dStr_free(dstr, FALSE);
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unescape %XX sequences in a string.
|
||||||
|
* Return value: a new unescaped string
|
||||||
|
*/
|
||||||
|
char *Unescape_uri_str(const char *s)
|
||||||
|
{
|
||||||
|
char *p, *buf = dStrdup(s);
|
||||||
|
|
||||||
|
if (strchr(s, '%')) {
|
||||||
|
for (p = buf; (*p = *s); ++s, ++p) {
|
||||||
|
if (*p == '%' && isxdigit(s[1]) && isxdigit(s[2])) {
|
||||||
|
*p = (isdigit(s[1]) ? (s[1] - '0')
|
||||||
|
: D_ASCII_TOUPPER(s[1]) - 'A' + 10) * 16;
|
||||||
|
*p += isdigit(s[2]) ? (s[2] - '0')
|
||||||
|
: D_ASCII_TOUPPER(s[2]) - 'A' + 10;
|
||||||
|
s += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const char *unsafe_chars = "&<>\"'";
|
||||||
|
static const char *unsafe_rep[] =
|
||||||
|
{ "&", "<", ">", """, "'" };
|
||||||
|
static const int unsafe_rep_len[] = { 5, 4, 4, 6, 5 };
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Escape unsafe characters as html entities.
|
||||||
|
* Return value: New escaped string.
|
||||||
|
*/
|
||||||
|
char *Escape_html_str(const char *str)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
char *p;
|
||||||
|
Dstr *dstr = dStr_sized_new(64);
|
||||||
|
|
||||||
|
for (i = 0; str[i]; ++i) {
|
||||||
|
if ((p = strchr(unsafe_chars, str[i])))
|
||||||
|
dStr_append(dstr, unsafe_rep[p - unsafe_chars]);
|
||||||
|
else
|
||||||
|
dStr_append_c(dstr, str[i]);
|
||||||
|
}
|
||||||
|
p = dstr->str;
|
||||||
|
dStr_free(dstr, FALSE);
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unescape a few HTML entities (inverse of Escape_html_str)
|
||||||
|
* Return value: New unescaped string.
|
||||||
|
*/
|
||||||
|
char *Unescape_html_str(const char *str)
|
||||||
|
{
|
||||||
|
int i, j, k;
|
||||||
|
char *u_str = dStrdup(str);
|
||||||
|
|
||||||
|
if (!strchr(str, '&'))
|
||||||
|
return u_str;
|
||||||
|
|
||||||
|
for (i = 0, j = 0; str[i]; ++i) {
|
||||||
|
if (str[i] == '&') {
|
||||||
|
for (k = 0; k < 5; ++k) {
|
||||||
|
if (!dStrnAsciiCasecmp(str + i, unsafe_rep[k], unsafe_rep_len[k])) {
|
||||||
|
i += unsafe_rep_len[k] - 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
u_str[j++] = (k < 5) ? unsafe_chars[k] : str[i];
|
||||||
|
} else {
|
||||||
|
u_str[j++] = str[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
u_str[j] = 0;
|
||||||
|
|
||||||
|
return u_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Filter '\n', '\r', "%0D" and "%0A" from the authority part of an FTP url.
|
||||||
|
* This helps to avoid a SMTP relaying hack. This filtering could be done
|
||||||
|
* only when port == 25, but if the mail server is listening on another
|
||||||
|
* port it wouldn't work.
|
||||||
|
* Note: AFAIS this should be done by wget.
|
||||||
|
*/
|
||||||
|
char *Filter_smtp_hack(char *url)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
char c;
|
||||||
|
|
||||||
|
if (strlen(url) > 6) { /* ftp:// */
|
||||||
|
for (i = 6; (c = url[i]) && c != '/'; ++i) {
|
||||||
|
if (c == '\n' || c == '\r') {
|
||||||
|
memmove(url + i, url + i + 1, strlen(url + i));
|
||||||
|
--i;
|
||||||
|
} else if (c == '%' && url[i+1] == '0' &&
|
||||||
|
(D_ASCII_TOLOWER(url[i+2]) == 'a' ||
|
||||||
|
D_ASCII_TOLOWER(url[i+2]) == 'd')) {
|
||||||
|
memmove(url + i, url + i + 3, strlen(url + i + 2));
|
||||||
|
--i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
66
dpi/dpiutil.h
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* File: dpiutil.h
|
||||||
|
*
|
||||||
|
* Copyright 2004-2005 Jorge Arellano Cid <jcid@dillo.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file contains common functions used by dpi programs.
|
||||||
|
* (i.e. a convenience library).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __DPIUTIL_H__
|
||||||
|
#define __DPIUTIL_H__
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "d_size.h"
|
||||||
|
#include "../dlib/dlib.h"
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Escape URI characters in 'esc_set' as %XX sequences.
|
||||||
|
* Return value: New escaped string.
|
||||||
|
*/
|
||||||
|
char *Escape_uri_str(const char *str, const char *p_esc_set);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unescape %XX sequences in a string.
|
||||||
|
* Return value: a new unescaped string
|
||||||
|
*/
|
||||||
|
char *Unescape_uri_str(const char *str);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Escape unsafe characters as html entities.
|
||||||
|
* Return value: New escaped string.
|
||||||
|
*/
|
||||||
|
char *Escape_html_str(const char *str);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unescape a few HTML entities (inverse of Escape_html_str)
|
||||||
|
* Return value: New unescaped string.
|
||||||
|
*/
|
||||||
|
char *Unescape_html_str(const char *str);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Filter an SMTP hack with a FTP URI
|
||||||
|
*/
|
||||||
|
char *Filter_smtp_hack(char *url);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
#endif /* __DPIUTIL_H__ */
|
||||||
|
|
||||||
1138
dpi/file.c
Normal file
354
dpi/ftp.c
Normal file
@ -0,0 +1,354 @@
|
|||||||
|
/*
|
||||||
|
* Dpi for FTP.
|
||||||
|
*
|
||||||
|
* This server checks the ftp-URL to be a directory (requires wget).
|
||||||
|
* If true, it sends back an html representation of it, and if not
|
||||||
|
* a dpip message (which is caught by dillo who redirects the ftp URL
|
||||||
|
* to the downloads server).
|
||||||
|
*
|
||||||
|
* Feel free to polish!
|
||||||
|
*
|
||||||
|
* Copyright 2003-2007 Jorge Arellano Cid <jcid@dillo.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO:
|
||||||
|
* - Send feedback about the FTP login process from wget's stderr.
|
||||||
|
* i.e. capture our child's stderr, process it, and report back.
|
||||||
|
* - Handle simultaneous connections.
|
||||||
|
* If ftp.dpi is implemented with a low level ftp library, it becomes
|
||||||
|
* possible to keep the connection alive, and thus make browsing of ftp
|
||||||
|
* directories faster (this avoids one login per page, and forks). Perhaps
|
||||||
|
* it's not worth, but can be done.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include "../dpip/dpip.h"
|
||||||
|
#include "dpiutil.h"
|
||||||
|
#include "d_size.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Debugging macros
|
||||||
|
* (Set debugging messages to stderr, to see them)
|
||||||
|
*/
|
||||||
|
#define _MSG(...)
|
||||||
|
//#define MSG(...) fprintf(stderr, "[ftp dpi]: " __VA_ARGS__)
|
||||||
|
#define MSG(...) printf("[ftp dpi]: " __VA_ARGS__)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Global variables
|
||||||
|
*/
|
||||||
|
static Dsh *sh = NULL;
|
||||||
|
static char **dl_argv = NULL;
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
/* TODO: could use dStr ADT! */
|
||||||
|
typedef struct {
|
||||||
|
const char *str;
|
||||||
|
int len;
|
||||||
|
} ContentType_t;
|
||||||
|
|
||||||
|
static const ContentType_t MimeTypes[] = {
|
||||||
|
{ "application/octet-stream", 24 },
|
||||||
|
{ "text/html", 9 },
|
||||||
|
{ "text/plain", 10 },
|
||||||
|
{ "image/gif", 9 },
|
||||||
|
{ "image/png", 9 },
|
||||||
|
{ "image/jpeg", 10 },
|
||||||
|
{ NULL, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Detects 'Content-Type' from a data stream sample.
|
||||||
|
*
|
||||||
|
* It uses the magic(5) logic from file(1). Currently, it
|
||||||
|
* only checks the few mime types that Dillo supports.
|
||||||
|
*
|
||||||
|
* 'Data' is a pointer to the first bytes of the raw data.
|
||||||
|
*
|
||||||
|
* Return value: (0 on success, 1 on doubt, 2 on lack of data).
|
||||||
|
*/
|
||||||
|
static int a_Misc_get_content_type_from_data2(void *Data, size_t Size,
|
||||||
|
const char **PT)
|
||||||
|
{
|
||||||
|
int st = 1; /* default to "doubt' */
|
||||||
|
int Type = 0; /* default to "application/octet-stream" */
|
||||||
|
char *p = Data;
|
||||||
|
uchar_t ch;
|
||||||
|
size_t i, non_ascci;
|
||||||
|
|
||||||
|
/* HTML try */
|
||||||
|
for (i = 0; i < Size && dIsspace(p[i]); ++i);
|
||||||
|
if ((Size - i >= 5 && !dStrnAsciiCasecmp(p+i, "<html", 5)) ||
|
||||||
|
(Size - i >= 5 && !dStrnAsciiCasecmp(p+i, "<head", 5)) ||
|
||||||
|
(Size - i >= 6 && !dStrnAsciiCasecmp(p+i, "<title", 6)) ||
|
||||||
|
(Size - i >= 14 && !dStrnAsciiCasecmp(p+i, "<!doctype html", 14)) ||
|
||||||
|
/* this line is workaround for FTP through the Squid proxy */
|
||||||
|
(Size - i >= 17 && !dStrnAsciiCasecmp(p+i, "<!-- HTML listing", 17))) {
|
||||||
|
|
||||||
|
Type = 1;
|
||||||
|
st = 0;
|
||||||
|
/* Images */
|
||||||
|
} else if (Size >= 4 && !strncmp(p, "GIF8", 4)) {
|
||||||
|
Type = 3;
|
||||||
|
st = 0;
|
||||||
|
} else if (Size >= 4 && !strncmp(p, "\x89PNG", 4)) {
|
||||||
|
Type = 4;
|
||||||
|
st = 0;
|
||||||
|
} else if (Size >= 2 && !strncmp(p, "\xff\xd8", 2)) {
|
||||||
|
/* JPEG has the first 2 bytes set to 0xffd8 in BigEndian - looking
|
||||||
|
* at the character representation should be machine independent. */
|
||||||
|
Type = 5;
|
||||||
|
st = 0;
|
||||||
|
|
||||||
|
/* Text */
|
||||||
|
} else {
|
||||||
|
/* We'll assume "text/plain" if the set of chars above 127 is <= 10%
|
||||||
|
* of the sample. This helps to catch ASCII, LATIN1 and UTF-8 as text.
|
||||||
|
* Better heuristics are welcomed! :-) */
|
||||||
|
non_ascci = 0;
|
||||||
|
Size = MIN (Size, 256);
|
||||||
|
for (i = 0; i < Size; i++) {
|
||||||
|
ch = (uchar_t) p[i];
|
||||||
|
if ((ch < 32 || ch > 126) && !dIsspace(ch))
|
||||||
|
++non_ascci;
|
||||||
|
}
|
||||||
|
if (Size == 256) {
|
||||||
|
Type = (non_ascci > Size/10) ? 0 : 2;
|
||||||
|
st = 0;
|
||||||
|
} else {
|
||||||
|
Type = (non_ascci > Size/10) ? 0 : 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*PT = MimeTypes[Type].str;
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Build a shell command using wget for this URL.
|
||||||
|
*/
|
||||||
|
static void make_wget_argv(char *url)
|
||||||
|
{
|
||||||
|
char *esc_url;
|
||||||
|
|
||||||
|
if (dl_argv) {
|
||||||
|
dFree(dl_argv[3]);
|
||||||
|
dFree(dl_argv);
|
||||||
|
}
|
||||||
|
dl_argv = dNew(char*, 10);
|
||||||
|
|
||||||
|
esc_url = Escape_uri_str(url, "'");
|
||||||
|
/* avoid malicious SMTP relaying with FTP urls */
|
||||||
|
Filter_smtp_hack(esc_url);
|
||||||
|
|
||||||
|
dl_argv[0] = "wget";
|
||||||
|
dl_argv[1] = "-t1"; /* try once, default is 20 */
|
||||||
|
dl_argv[2] = "-O-";
|
||||||
|
dl_argv[3] = esc_url;
|
||||||
|
dl_argv[4] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fork, exec command, get its output and send via stdout.
|
||||||
|
* Return: Number of bytes transferred, -1 if file-not_found, -2 if aborted.
|
||||||
|
*/
|
||||||
|
static int try_ftp_transfer(char *url)
|
||||||
|
{
|
||||||
|
#define MIN_SZ 256
|
||||||
|
#define READ_SZ 16*1024
|
||||||
|
|
||||||
|
ssize_t n;
|
||||||
|
int nb, has_mime_type, has_html_header, no_such_file, offer_download;
|
||||||
|
const char *mime_type = "application/octet-stream";
|
||||||
|
char buf[READ_SZ], *d_cmd;
|
||||||
|
Dstr *dbuf = dStr_sized_new(READ_SZ);
|
||||||
|
pid_t ch_pid;
|
||||||
|
int aborted = 0;
|
||||||
|
int DataPipe[2];
|
||||||
|
|
||||||
|
MSG("try_ftp_transfer: url=%s\n", url);
|
||||||
|
|
||||||
|
if (pipe(DataPipe) < 0) {
|
||||||
|
MSG("pipe, %s\n", dStrerror(errno));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prepare args for execvp() */
|
||||||
|
make_wget_argv(url);
|
||||||
|
|
||||||
|
/* Start the child process */
|
||||||
|
if ((ch_pid = fork()) == 0) {
|
||||||
|
/* child */
|
||||||
|
/* start wget */
|
||||||
|
close(DataPipe[0]);
|
||||||
|
dup2(DataPipe[1], 1); /* stdout */
|
||||||
|
execvp(dl_argv[0], dl_argv);
|
||||||
|
_exit(1);
|
||||||
|
} else if (ch_pid < 0) {
|
||||||
|
perror("fork, ");
|
||||||
|
exit(1);
|
||||||
|
} else {
|
||||||
|
/* father continues below */
|
||||||
|
close(DataPipe[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read/Write the real data */
|
||||||
|
nb = 0;
|
||||||
|
has_mime_type = 0;
|
||||||
|
has_html_header = 0;
|
||||||
|
no_such_file = 0;
|
||||||
|
offer_download = 0;
|
||||||
|
do {
|
||||||
|
while ((n = read(DataPipe[0], buf, READ_SZ)) < 0 && errno == EINTR);
|
||||||
|
if (n > 0) {
|
||||||
|
dStr_append_l(dbuf, buf, n);
|
||||||
|
if (!has_mime_type && dbuf->len < MIN_SZ)
|
||||||
|
continue;
|
||||||
|
} else if (n < 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!has_mime_type) {
|
||||||
|
if (dbuf->len == 0) {
|
||||||
|
/* When the file doesn't exist, the transfer size is zero */
|
||||||
|
no_such_file = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
a_Misc_get_content_type_from_data2(dbuf->str, dbuf->len, &mime_type);
|
||||||
|
has_mime_type = 1;
|
||||||
|
|
||||||
|
if (strcmp(mime_type, "application/octet-stream") == 0) {
|
||||||
|
/* abort transfer */
|
||||||
|
kill(ch_pid, SIGTERM);
|
||||||
|
/* The "application/octet-stream" MIME type will be sent and
|
||||||
|
* Dillo will offer a download dialog */
|
||||||
|
offer_download = 1;
|
||||||
|
aborted = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offer_download || (!aborted && !has_html_header && dbuf->len)) {
|
||||||
|
/* Send dpip tag */
|
||||||
|
d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
|
||||||
|
a_Dpip_dsh_write_str(sh, 1, d_cmd);
|
||||||
|
dFree(d_cmd);
|
||||||
|
|
||||||
|
/* Send HTTP header. */
|
||||||
|
a_Dpip_dsh_write_str(sh, 0, "Content-type: ");
|
||||||
|
a_Dpip_dsh_write_str(sh, 0, mime_type);
|
||||||
|
a_Dpip_dsh_write_str(sh, 1, "\r\n\r\n");
|
||||||
|
has_html_header = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!aborted && dbuf->len) {
|
||||||
|
a_Dpip_dsh_write(sh, 1, dbuf->str, dbuf->len);
|
||||||
|
nb += dbuf->len;
|
||||||
|
dStr_truncate(dbuf, 0);
|
||||||
|
}
|
||||||
|
} while (n > 0 && !aborted);
|
||||||
|
|
||||||
|
dStr_free(dbuf, 1);
|
||||||
|
return (no_such_file ? -1 : (aborted ? -2 : nb));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
const char *err_msg = "404 Not Found\nNo such file or directory";
|
||||||
|
char *dpip_tag = NULL, *cmd = NULL, *url = NULL, *url2 = NULL;
|
||||||
|
int st, rc;
|
||||||
|
char *p, *d_cmd;
|
||||||
|
|
||||||
|
/* wget may need to write a temporary file... */
|
||||||
|
rc = chdir("/tmp");
|
||||||
|
if (rc == -1) {
|
||||||
|
MSG("paths: error changing directory to /tmp: %s\n",
|
||||||
|
dStrerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize the SockHandler */
|
||||||
|
sh = a_Dpip_dsh_new(STDIN_FILENO, STDOUT_FILENO, 8*1024);
|
||||||
|
|
||||||
|
if (argc == 2) {
|
||||||
|
/* Debugging with a command line argument */
|
||||||
|
dpip_tag = dStrdup(argv[1]);
|
||||||
|
} else {
|
||||||
|
/* Authenticate our client... */
|
||||||
|
if (!(dpip_tag = a_Dpip_dsh_read_token(sh, 1)) ||
|
||||||
|
a_Dpip_check_auth(dpip_tag) < 0) {
|
||||||
|
MSG("can't authenticate request: %s\n", dStrerror(errno));
|
||||||
|
a_Dpip_dsh_close(sh);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
dFree(dpip_tag);
|
||||||
|
/* Read the dpi command from STDIN */
|
||||||
|
dpip_tag = a_Dpip_dsh_read_token(sh, 1);
|
||||||
|
}
|
||||||
|
MSG("tag=[%s]\n", dpip_tag);
|
||||||
|
|
||||||
|
cmd = a_Dpip_get_attr(dpip_tag, "cmd");
|
||||||
|
url = a_Dpip_get_attr(dpip_tag, "url");
|
||||||
|
if (!cmd || !url) {
|
||||||
|
MSG("ERROR, cmd=%s, url=%s\n", cmd, url);
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((st = try_ftp_transfer(url)) == -1) {
|
||||||
|
/* Transfer failed, the requested file may not exist or be a symlink
|
||||||
|
* to a directory. Try again... */
|
||||||
|
if ((p = strrchr(url, '/')) && p[1] &&
|
||||||
|
p > url && p[-1] != '/') {
|
||||||
|
url2 = dStrconcat(url, "/", NULL);
|
||||||
|
st = try_ftp_transfer(url2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (st == -1) {
|
||||||
|
/* The transfer failed, let dillo know... */
|
||||||
|
d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
|
||||||
|
a_Dpip_dsh_write_str(sh, 0, d_cmd);
|
||||||
|
dFree(d_cmd);
|
||||||
|
a_Dpip_dsh_printf(sh, 1,
|
||||||
|
"HTTP/1.1 404 Not Found\r\n"
|
||||||
|
"Content-Type: text/plain\r\n"
|
||||||
|
"Content-Length: %d\r\n"
|
||||||
|
"\r\n"
|
||||||
|
"%s",
|
||||||
|
strlen(err_msg), err_msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
dFree(cmd);
|
||||||
|
dFree(url);
|
||||||
|
dFree(url2);
|
||||||
|
dFree(dpip_tag);
|
||||||
|
|
||||||
|
/* Finish the SockHandler */
|
||||||
|
a_Dpip_dsh_close(sh);
|
||||||
|
a_Dpip_dsh_free(sh);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
190
dpi/hello.c
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
/*
|
||||||
|
* Dpi for "Hello World".
|
||||||
|
*
|
||||||
|
* This server is an example. Play with it and modify to your taste.
|
||||||
|
*
|
||||||
|
* Copyright 2003-2007 Jorge Arellano Cid <jcid@dillo.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include "../dpip/dpip.h"
|
||||||
|
#include "dpiutil.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Debugging macros
|
||||||
|
*/
|
||||||
|
#define _MSG(...)
|
||||||
|
#define MSG(...) printf("[hello dpi]: " __VA_ARGS__)
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
FILE *in_stream;
|
||||||
|
Dsh *sh;
|
||||||
|
char *dpip_tag, *cmd = NULL, *url = NULL, *child_cmd = NULL;
|
||||||
|
char *esc_tag, *d_cmd;
|
||||||
|
size_t n;
|
||||||
|
int ret;
|
||||||
|
char buf[4096];
|
||||||
|
char *choice[] = {"Window was closed", "Yes", "No",
|
||||||
|
"Could be", "It's OK", "Cancel"};
|
||||||
|
/* "Could>be", ">It's OK", "Can'>cel"}; --for testing */
|
||||||
|
int choice_num = -1;
|
||||||
|
|
||||||
|
MSG("starting...\n");
|
||||||
|
/* sleep(20) */
|
||||||
|
|
||||||
|
/* Initialize the SockHandler.
|
||||||
|
* This means we'll use stdin for input and stdout for output.
|
||||||
|
* In case of a server dpi, we'd use a socket and pass its file descriptor
|
||||||
|
* twice (e.g. a_Dpip_dsh_new(sock_fd, sock_fd, 1024).
|
||||||
|
* (Note: by now the last parameter is not used) */
|
||||||
|
sh = a_Dpip_dsh_new(STDIN_FILENO, STDOUT_FILENO, 2*1024);
|
||||||
|
|
||||||
|
/* Authenticate our client...
|
||||||
|
* As we're using Internet domain sockets, DPIP checks whether the client
|
||||||
|
* runs with the user's ID, by means of a shared secret. The DPIP API does
|
||||||
|
* the work for us. */
|
||||||
|
if (!(dpip_tag = a_Dpip_dsh_read_token(sh, 1)) ||
|
||||||
|
a_Dpip_check_auth(dpip_tag) < 0) {
|
||||||
|
MSG("can't authenticate request: %s\n", dStrerror(errno));
|
||||||
|
a_Dpip_dsh_close(sh);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
dFree(dpip_tag);
|
||||||
|
|
||||||
|
/* Read the dpi command from STDIN
|
||||||
|
* Now we're past the authentication phase, let's see what's dillo
|
||||||
|
* asking from us. a_Dpip_dsh_read_token() will block and return
|
||||||
|
* a full dpip token or null on error (it's commented in dpip.c) */
|
||||||
|
dpip_tag = a_Dpip_dsh_read_token(sh, 1);
|
||||||
|
MSG("tag = [%s]\n", dpip_tag);
|
||||||
|
|
||||||
|
/* Now that we have the dpip_tag, let's isolate the command and url */
|
||||||
|
cmd = a_Dpip_get_attr(dpip_tag, "cmd");
|
||||||
|
url = a_Dpip_get_attr(dpip_tag, "url");
|
||||||
|
|
||||||
|
/*-- Dialog part */
|
||||||
|
/* This is the dialog window. This is an example of interaction with
|
||||||
|
* the user. If you're starting to understand dpis, comment this out
|
||||||
|
* by switching to "#if 0" and the dialog will be disabled. */
|
||||||
|
#if 1
|
||||||
|
{
|
||||||
|
char *dpip_tag2, *dialog_msg;
|
||||||
|
|
||||||
|
/* Let's confirm the request */
|
||||||
|
/* NOTE: you can send less alternatives (e.g. only alt1 and alt2) */
|
||||||
|
d_cmd = a_Dpip_build_cmd(
|
||||||
|
"cmd=%s title=%s msg=%s alt1=%s alt2=%s alt3=%s alt4=%s alt5=%s",
|
||||||
|
"dialog", "Dillo: Hello", "Do you want to see the hello page?",
|
||||||
|
choice[1], choice[2], choice[3], choice[4], choice[5]);
|
||||||
|
a_Dpip_dsh_write_str(sh, 1, d_cmd);
|
||||||
|
dFree(d_cmd);
|
||||||
|
|
||||||
|
/* Get the answer */
|
||||||
|
dpip_tag2 = a_Dpip_dsh_read_token(sh, 1);
|
||||||
|
MSG("tag = [%s]\n", dpip_tag2);
|
||||||
|
|
||||||
|
/* Get "msg" value */
|
||||||
|
dialog_msg = a_Dpip_get_attr(dpip_tag2, "msg");
|
||||||
|
choice_num = 0;
|
||||||
|
if (dialog_msg)
|
||||||
|
choice_num = *dialog_msg - '0';
|
||||||
|
|
||||||
|
dFree(dialog_msg);
|
||||||
|
dFree(dpip_tag2);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
/*-- EOD part */
|
||||||
|
|
||||||
|
/* Start sending our answer.
|
||||||
|
* (You can read the comments for DPIP API functions in dpip/dpip.c) */
|
||||||
|
d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
|
||||||
|
a_Dpip_dsh_write_str(sh, 0, d_cmd);
|
||||||
|
dFree(d_cmd);
|
||||||
|
|
||||||
|
a_Dpip_dsh_printf(sh, 0,
|
||||||
|
"Content-type: text/html\n\n"
|
||||||
|
"<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
|
||||||
|
"<html>\n"
|
||||||
|
"<head><title>Simple dpi test page (hello.dpi)</title></head>\n"
|
||||||
|
"<body><hr><h1>Hello world!</h1><hr>\n<br><br>\n");
|
||||||
|
|
||||||
|
/* Show the choice received with the dialog */
|
||||||
|
a_Dpip_dsh_printf(sh, 0,
|
||||||
|
"<hr>\n"
|
||||||
|
"<table width='100%%' border='1' bgcolor='burlywood'><tr><td>\n"
|
||||||
|
"<big><em>Dialog question:</em> Do you want to see the hello page?<br>\n"
|
||||||
|
"<em>Answer received:</em> <b>%s</b></big> </table>\n"
|
||||||
|
"<hr>\n",
|
||||||
|
choice_num < 0 ? "There was NO dialog!" : choice[choice_num]);
|
||||||
|
|
||||||
|
/* Show the dpip tag we received */
|
||||||
|
esc_tag = Escape_html_str(dpip_tag);
|
||||||
|
a_Dpip_dsh_printf(sh, 0,
|
||||||
|
"<h3>dpip tag received:</h3>\n"
|
||||||
|
"<pre>\n%s</pre>\n"
|
||||||
|
"<br><small>(<b>dpip:</b> dpi protocol)</small><br><br><br>\n",
|
||||||
|
esc_tag);
|
||||||
|
dFree(esc_tag);
|
||||||
|
|
||||||
|
|
||||||
|
/* Now something more interesting,
|
||||||
|
* fork a command and show its feedback.
|
||||||
|
* (An example of generating dynamic content with an external
|
||||||
|
* program). */
|
||||||
|
if (cmd && url) {
|
||||||
|
child_cmd = dStrdup("date -R");
|
||||||
|
MSG("[%s]\n", child_cmd);
|
||||||
|
|
||||||
|
/* Fork, exec command, get its output and answer */
|
||||||
|
if ((in_stream = popen(child_cmd, "r")) == NULL) {
|
||||||
|
perror("popen");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
a_Dpip_dsh_write_str(sh, 0, "<h3>date:</h3>\n");
|
||||||
|
a_Dpip_dsh_write_str(sh, 0, "<pre>\n");
|
||||||
|
|
||||||
|
/* Read/Write */
|
||||||
|
while ((n = fread (buf, 1, 4096, in_stream)) > 0) {
|
||||||
|
a_Dpip_dsh_write(sh, 0, buf, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
a_Dpip_dsh_write_str(sh, 0, "</pre>\n");
|
||||||
|
|
||||||
|
if ((ret = pclose(in_stream)) != 0)
|
||||||
|
MSG("popen: [%d]\n", ret);
|
||||||
|
|
||||||
|
dFree(child_cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
a_Dpip_dsh_write_str(sh, 1, "</body></html>\n");
|
||||||
|
|
||||||
|
dFree(cmd);
|
||||||
|
dFree(url);
|
||||||
|
dFree(dpip_tag);
|
||||||
|
|
||||||
|
/* Finish the SockHandler */
|
||||||
|
a_Dpip_dsh_close(sh);
|
||||||
|
a_Dpip_dsh_free(sh);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
261
dpi/vsource.c
Normal file
@ -0,0 +1,261 @@
|
|||||||
|
/*
|
||||||
|
* Dpi for "View source".
|
||||||
|
*
|
||||||
|
* This server is an example. Play with it and modify to your taste.
|
||||||
|
*
|
||||||
|
* Copyright 2010-2015 Jorge Arellano Cid <jcid@dillo.org>
|
||||||
|
* Copyright 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include "../dpip/dpip.h"
|
||||||
|
#include "dpiutil.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Debugging macros
|
||||||
|
*/
|
||||||
|
#define _MSG(...)
|
||||||
|
#define MSG(...) fprintf(stderr, "[vsource dpi]: " __VA_ARGS__)
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
const char *DOCTYPE=
|
||||||
|
"<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>";
|
||||||
|
|
||||||
|
|
||||||
|
void send_dpip_tag(Dsh *sh, char *dpip_tag)
|
||||||
|
{
|
||||||
|
a_Dpip_dsh_write_str(sh, 0, "\nDpip tag received: ");
|
||||||
|
a_Dpip_dsh_write_str(sh, 0, dpip_tag ? dpip_tag : "None");
|
||||||
|
a_Dpip_dsh_write_str(sh, 1, "\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send source as plain text
|
||||||
|
* (handles embedded null chars correctly).
|
||||||
|
*/
|
||||||
|
void send_plain_text(Dsh *sh, int data_size)
|
||||||
|
{
|
||||||
|
char *token;
|
||||||
|
int bytes_read = 0, token_size;
|
||||||
|
|
||||||
|
/* Send HTTP header for plain text MIME type */
|
||||||
|
a_Dpip_dsh_write_str(sh, 0, "Content-type: text/plain\n\n");
|
||||||
|
|
||||||
|
while (bytes_read < data_size &&
|
||||||
|
(token = a_Dpip_dsh_read_token2(sh, 1, &token_size))) {
|
||||||
|
bytes_read += token_size;
|
||||||
|
_MSG("data_size=%d bytes_read=%d\n", data_size, bytes_read);
|
||||||
|
a_Dpip_dsh_write(sh, 1, token, token_size);
|
||||||
|
dFree(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send source as plain text with line numbers
|
||||||
|
* (handles embedded null chars correctly).
|
||||||
|
*/
|
||||||
|
void send_numbered_text(Dsh *sh, int data_size)
|
||||||
|
{
|
||||||
|
int bytes_read = 0, line = 1, token_size = 0;
|
||||||
|
char *p, *q, *token, line_str[32];
|
||||||
|
|
||||||
|
/* Send HTTP header for plain text MIME type */
|
||||||
|
a_Dpip_dsh_write_str(sh, 0, "Content-type: text/plain\n\n");
|
||||||
|
|
||||||
|
while (bytes_read < data_size &&
|
||||||
|
(token = a_Dpip_dsh_read_token2(sh, 1, &token_size))) {
|
||||||
|
bytes_read += token_size;
|
||||||
|
p = q = token;
|
||||||
|
|
||||||
|
while (*p) {
|
||||||
|
snprintf(line_str, 32, "%2d: ", line);
|
||||||
|
a_Dpip_dsh_write_str(sh, 0, line_str);
|
||||||
|
if ((p = strpbrk(q, "\r\n"))) {
|
||||||
|
a_Dpip_dsh_write(sh, 0, q, p - q + 1);
|
||||||
|
if (*p == '\r' && p[1] == '\n')
|
||||||
|
++p;
|
||||||
|
++line;
|
||||||
|
} else {
|
||||||
|
/* send all the rest */
|
||||||
|
a_Dpip_dsh_write(sh, 1, q, token_size - (q - token));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
q = ++p;
|
||||||
|
}
|
||||||
|
dFree(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send source as html text with line numbers
|
||||||
|
* (handles embedded null chars correctly).
|
||||||
|
*/
|
||||||
|
void send_html_text(Dsh *sh, const char *url, int data_size)
|
||||||
|
{
|
||||||
|
int bytes_read = 0, old_line = 0, line = 1, token_size = 0;
|
||||||
|
char *p, *q, *token, line_str[128];
|
||||||
|
|
||||||
|
if (dStrnAsciiCasecmp(url, "dpi:", 4) == 0 &&
|
||||||
|
strncmp(url+4, "/vsource/:", 10) == 0)
|
||||||
|
url += 14;
|
||||||
|
|
||||||
|
/* Send HTTP header for html text MIME type */
|
||||||
|
a_Dpip_dsh_write_str(sh, 0, "Content-type: text/html\n\n");
|
||||||
|
|
||||||
|
a_Dpip_dsh_write_str(sh, 0, DOCTYPE);
|
||||||
|
a_Dpip_dsh_printf(sh, 0,
|
||||||
|
"\n"
|
||||||
|
"<html><head>\n"
|
||||||
|
"<title>Source for %s</title>\n"
|
||||||
|
"<style type=\"text/css\">\n"
|
||||||
|
" body {\n"
|
||||||
|
" white-space: pre-wrap;\n"
|
||||||
|
" font-family: monospace;\n"
|
||||||
|
" margin: 0;\n"
|
||||||
|
" width: 100%;\n"
|
||||||
|
" }\n"
|
||||||
|
" table { border:0 }\n"
|
||||||
|
" td.num {\n"
|
||||||
|
" padding-top: 1px;\n"
|
||||||
|
" padding-bottom: 1px;\n"
|
||||||
|
" padding-left: 0.5em;\n"
|
||||||
|
" padding-right: 0.5em;\n"
|
||||||
|
" text-align: right;\n"
|
||||||
|
" border-right: 1px solid #999999;\n"
|
||||||
|
" background-color: #c6c6c6;\n"
|
||||||
|
" }"
|
||||||
|
" td.src { padding-left:0.25em; }\n"
|
||||||
|
" a { color: black; text-decoration:none; }\n"
|
||||||
|
"</style>\n"
|
||||||
|
"</head>\n"
|
||||||
|
"<body id=\"dillo_vs\">\n<table cellspacing='0' cellpadding='0'>\n", url);
|
||||||
|
|
||||||
|
while (bytes_read < data_size &&
|
||||||
|
(token = a_Dpip_dsh_read_token2(sh, 1, &token_size))) {
|
||||||
|
bytes_read += token_size;
|
||||||
|
p = q = token;
|
||||||
|
|
||||||
|
while (*p) {
|
||||||
|
if (line > old_line) {
|
||||||
|
snprintf(line_str, 128,
|
||||||
|
"<tr><td class='num' id='L%d'><a href='#L%d'>%d</a><td class='src'>",
|
||||||
|
line, line, line);
|
||||||
|
a_Dpip_dsh_write_str(sh, 0, line_str);
|
||||||
|
old_line = line;
|
||||||
|
}
|
||||||
|
if ((p = strpbrk(q, "\r\n<&"))) {
|
||||||
|
if (*p == '\r' || *p == '\n') {
|
||||||
|
a_Dpip_dsh_write(sh, 0, q, p - q + 1);
|
||||||
|
if (*p == '\r' && p[1] == '\n')
|
||||||
|
p++;
|
||||||
|
++line;
|
||||||
|
} else {
|
||||||
|
a_Dpip_dsh_write(sh, 0, q, p - q);
|
||||||
|
a_Dpip_dsh_write_str(sh, 0, (*p == '<') ? "<" : "&");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* send all the rest */
|
||||||
|
a_Dpip_dsh_write(sh, 1, q, token_size - (q - token));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
q = ++p;
|
||||||
|
}
|
||||||
|
dFree(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
a_Dpip_dsh_write_str(sh, 1, "</table></body></html>");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
Dsh *sh;
|
||||||
|
int data_size;
|
||||||
|
char *dpip_tag, *cmd = NULL, *cmd2 = NULL, *url = NULL, *size_str = NULL;
|
||||||
|
char *d_cmd;
|
||||||
|
|
||||||
|
_MSG("starting...\n");
|
||||||
|
//sleep(20);
|
||||||
|
|
||||||
|
/* Initialize the SockHandler.
|
||||||
|
* This means we'll use stdin for input and stdout for output.
|
||||||
|
* In case of a server dpi, we'd use a socket and pass its file descriptor
|
||||||
|
* twice (e.g. a_Dpip_dsh_new(sock_fd, sock_fd, 1024).
|
||||||
|
* (Note: by now the last parameter is not used) */
|
||||||
|
sh = a_Dpip_dsh_new(STDIN_FILENO, STDOUT_FILENO, 2*1024);
|
||||||
|
|
||||||
|
/* Authenticate our client...
|
||||||
|
* As we're using Internet domain sockets, DPIP checks whether the client
|
||||||
|
* runs with the user's ID, by means of a shared secret. The DPIP API does
|
||||||
|
* the work for us. */
|
||||||
|
if (!(dpip_tag = a_Dpip_dsh_read_token(sh, 1)) ||
|
||||||
|
a_Dpip_check_auth(dpip_tag) < 0) {
|
||||||
|
MSG("can't authenticate request: %s\n", dStrerror(errno));
|
||||||
|
a_Dpip_dsh_close(sh);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
dFree(dpip_tag);
|
||||||
|
|
||||||
|
/* Read the dpi command from STDIN
|
||||||
|
* Now we're past the authentication phase, let's see what's dillo
|
||||||
|
* asking from us. a_Dpip_dsh_read_token() will block and return
|
||||||
|
* a full dpip token or null on error (it's commented in dpip.c) */
|
||||||
|
dpip_tag = a_Dpip_dsh_read_token(sh, 1);
|
||||||
|
_MSG("tag = [%s]\n", dpip_tag);
|
||||||
|
|
||||||
|
/* Now that we have the dpip_tag, let's isolate the command and url */
|
||||||
|
cmd = a_Dpip_get_attr(dpip_tag, "cmd");
|
||||||
|
url = a_Dpip_get_attr(dpip_tag, "url");
|
||||||
|
|
||||||
|
/* Start sending our answer.
|
||||||
|
* (You can read the comments for DPIP API functions in dpip/dpip.c) */
|
||||||
|
d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
|
||||||
|
a_Dpip_dsh_write_str(sh, 0, d_cmd);
|
||||||
|
dFree(d_cmd);
|
||||||
|
dFree(dpip_tag);
|
||||||
|
|
||||||
|
dpip_tag = a_Dpip_dsh_read_token(sh, 1);
|
||||||
|
cmd2 = a_Dpip_get_attr(dpip_tag, "cmd");
|
||||||
|
if (cmd2) {
|
||||||
|
if (strcmp(cmd2, "start_send_page") == 0 &&
|
||||||
|
(size_str = a_Dpip_get_attr(dpip_tag, "data_size"))) {
|
||||||
|
data_size = strtol(size_str, NULL, 10);
|
||||||
|
/* Choose your flavour */
|
||||||
|
//send_plain_text(sh, data_size);
|
||||||
|
//send_numbered_text(sh, data_size);
|
||||||
|
send_html_text(sh, url, data_size);
|
||||||
|
} else if (strcmp(cmd2, "DpiError") == 0) {
|
||||||
|
/* Dillo detected an error (other failures just close the socket) */
|
||||||
|
a_Dpip_dsh_write_str(sh, 0, "Content-type: text/plain\n\n");
|
||||||
|
a_Dpip_dsh_write_str(sh, 1, "[vsource dpi]: "
|
||||||
|
"ERROR: Page not cached.\n");
|
||||||
|
}
|
||||||
|
dFree(cmd2);
|
||||||
|
}
|
||||||
|
|
||||||
|
dFree(cmd);
|
||||||
|
dFree(url);
|
||||||
|
dFree(size_str);
|
||||||
|
dFree(dpip_tag);
|
||||||
|
|
||||||
|
/* Finish the SockHandler */
|
||||||
|
a_Dpip_dsh_close(sh);
|
||||||
|
a_Dpip_dsh_free(sh);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
36
dpid/Makefile.am
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
AM_CPPFLAGS = \
|
||||||
|
-I$(top_srcdir) \
|
||||||
|
-DEXEEXT='"$(EXEEXT)"' \
|
||||||
|
-DDPIDRC_SYS='"$(sysconfdir)/dpidrc"'
|
||||||
|
|
||||||
|
bin_PROGRAMS = dpid dpidc
|
||||||
|
dpid_LDADD = \
|
||||||
|
$(top_builddir)/dpip/libDpip.a \
|
||||||
|
$(top_builddir)/dlib/libDlib.a
|
||||||
|
dpidc_LDADD = \
|
||||||
|
$(top_builddir)/dpip/libDpip.a \
|
||||||
|
$(top_builddir)/dlib/libDlib.a
|
||||||
|
|
||||||
|
EXTRA_DIST = dpidrc.in
|
||||||
|
|
||||||
|
dpid_SOURCES = \
|
||||||
|
dpi.h \
|
||||||
|
dpi_socket_dir.h \
|
||||||
|
dpid.h \
|
||||||
|
dpid_common.h \
|
||||||
|
misc_new.h \
|
||||||
|
dpi.c \
|
||||||
|
dpi_socket_dir.c \
|
||||||
|
dpid.c \
|
||||||
|
dpid_common.c \
|
||||||
|
main.c \
|
||||||
|
misc_new.c
|
||||||
|
|
||||||
|
dpidc_SOURCES = dpidc.c
|
||||||
|
|
||||||
|
sysconf_DATA = dpidrc
|
||||||
|
CLEANFILES = $(sysconf_DATA)
|
||||||
|
|
||||||
|
dpidrc: $(srcdir)/dpidrc.in Makefile
|
||||||
|
sed -e 's|[@]libdir[@]|$(libdir)|;s|[@]EXEEXT[@]|$(EXEEXT)|g' $(srcdir)/dpidrc.in > dpidrc
|
||||||
|
|
||||||
32
dpid/TODO
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
Todo List
|
||||||
|
|
||||||
|
File dpi_service.c
|
||||||
|
This module should be removed because its original functions
|
||||||
|
have been removed or modified. Put these functions in dpid.c
|
||||||
|
|
||||||
|
File dpi_service.h
|
||||||
|
This module should be removed because its original functions
|
||||||
|
have been removed or modified. Put these functions in dpid.c
|
||||||
|
|
||||||
|
Add other file types, but first we need to add files associated
|
||||||
|
with a dpi to the design.
|
||||||
|
|
||||||
|
Global SRS_NAME
|
||||||
|
Should read this from dillorc
|
||||||
|
|
||||||
|
File dpid_common.h
|
||||||
|
The dpid error codes will be used in the next patch
|
||||||
|
|
||||||
|
Global main()
|
||||||
|
+ add new plugins when they become available
|
||||||
|
__________________________________________________________
|
||||||
|
|
||||||
|
Remove global variables.
|
||||||
|
|
||||||
|
Use a singly linked list for dpi_attr_list
|
||||||
|
|
||||||
|
Allow dpis to be registered one at a time
|
||||||
|
|
||||||
|
Only run dpis listed in users dillorc
|
||||||
|
|
||||||
|
MAYBE Have dpid accept all connections to dpis (fixes inf loop if dpi crashes before accept)
|
||||||
97
dpid/dpi.c
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2003 Ferdi Franceschini <ferdif@optusnet.com.au>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*! \file
|
||||||
|
* Access functions for ~/.dillo/dpi_socket_dir.
|
||||||
|
* The most useful function for dillo is a_Dpi_srs, it returns
|
||||||
|
* the full path to the dpid service request socket.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h> /* for exit */
|
||||||
|
#include "dpid_common.h"
|
||||||
|
#include "dpi.h"
|
||||||
|
#include "misc_new.h"
|
||||||
|
|
||||||
|
/*! \Return
|
||||||
|
* Returns path to the dpi_socket_dir file
|
||||||
|
* Use dFree to free memory
|
||||||
|
*/
|
||||||
|
char *a_Dpi_sockdir_file(void)
|
||||||
|
{
|
||||||
|
char *dpi_socket_dir, *dirfile_path = "/.dillo/dpi_socket_dir";
|
||||||
|
|
||||||
|
dpi_socket_dir = dStrconcat(dGethomedir(), dirfile_path, NULL);
|
||||||
|
return dpi_socket_dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Read socket directory path from ~/.dillo/dpi_socket_dir
|
||||||
|
* \Return
|
||||||
|
* socket directory path or NULL if the dpi_socket_dir file does not exist.
|
||||||
|
* \Note
|
||||||
|
* This function exits if ~/.dillo does not exist or
|
||||||
|
* if the dpi_socket_dir file cannot be opened for a
|
||||||
|
* reason other than it does not exist.
|
||||||
|
*/
|
||||||
|
|
||||||
|
char *a_Dpi_rd_dpi_socket_dir(char *dirname)
|
||||||
|
{
|
||||||
|
FILE *dir;
|
||||||
|
char *sockdir = NULL, *rcpath;
|
||||||
|
|
||||||
|
rcpath = dStrconcat(dGethomedir(), "/.dillo", NULL);
|
||||||
|
|
||||||
|
/* If .dillo does not exist it is an unrecoverable error */
|
||||||
|
if (access(rcpath, F_OK) == -1) {
|
||||||
|
ERRMSG("a_Dpi_rd_dpi_socket_dir", "access", errno);
|
||||||
|
MSG_ERR(" - %s\n", rcpath);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
dFree(rcpath);
|
||||||
|
|
||||||
|
if ((dir = fopen(dirname, "r")) != NULL) {
|
||||||
|
sockdir = dGetline(dir);
|
||||||
|
fclose(dir);
|
||||||
|
} else if (errno == ENOENT) {
|
||||||
|
ERRMSG("a_Dpi_rd_dpi_socket_dir", "fopen", errno);
|
||||||
|
MSG_ERR(" - %s\n", dirname);
|
||||||
|
} else if (errno != ENOENT) {
|
||||||
|
ERRMSG("a_Dpi_rd_dpi_socket_dir", "fopen", errno);
|
||||||
|
MSG_ERR(" - %s\n", dirname);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sockdir;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \Modifies
|
||||||
|
* srs_name
|
||||||
|
* \Return
|
||||||
|
* The service request socket name with its complete path.
|
||||||
|
*/
|
||||||
|
char *a_Dpi_srs(void)
|
||||||
|
{
|
||||||
|
char *dirfile_path, *sockdir, *srs_name;
|
||||||
|
|
||||||
|
dirfile_path = a_Dpi_sockdir_file();
|
||||||
|
sockdir = dStrstrip(a_Dpi_rd_dpi_socket_dir(dirfile_path));
|
||||||
|
srs_name = dStrconcat(sockdir, "/", "dpid.srs", NULL);
|
||||||
|
dFree(sockdir);
|
||||||
|
dFree(dirfile_path);
|
||||||
|
return (srs_name);
|
||||||
|
}
|
||||||
45
dpid/dpi.h
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/*! \file
|
||||||
|
* Access functions for ~/.dillo/dpi_socket_dir.
|
||||||
|
* The most useful function for dillo is a_Dpi_srs, it returns
|
||||||
|
* the full path to the dpid service request socket.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DPI_H
|
||||||
|
#define DPI_H
|
||||||
|
|
||||||
|
#include <unistd.h> /* for socklen_t */
|
||||||
|
#include <sys/socket.h> /* for socklen_t and AF_LOCAL */
|
||||||
|
|
||||||
|
/* Some systems may not have this one... */
|
||||||
|
#ifndef AF_LOCAL
|
||||||
|
#define AF_LOCAL AF_UNIX
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* This one is tricky, some sources state it should include the byte
|
||||||
|
* for the terminating NULL, and others say it shouldn't.
|
||||||
|
* The other way is to only use this one when a native SUN_LEN is not present,
|
||||||
|
* but as dillo has used this for a long time successfully, here it goes.
|
||||||
|
*/
|
||||||
|
# define D_SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \
|
||||||
|
+ strlen ((ptr)->sun_path))
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* dpi commands
|
||||||
|
*/
|
||||||
|
enum {
|
||||||
|
UNKNOWN_CMD,
|
||||||
|
AUTH_CMD, /* authentication */
|
||||||
|
BYE_CMD, /* "DpiBye" */
|
||||||
|
CHECK_SERVER_CMD, /* "check_server" */
|
||||||
|
REGISTER_ALL_CMD, /* "register_all" */
|
||||||
|
REGISTER_SERVICE_CMD /* "register_service" */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
char *a_Dpi_sockdir_file(void);
|
||||||
|
|
||||||
|
char *a_Dpi_rd_dpi_socket_dir(char *dirname);
|
||||||
|
|
||||||
|
char *a_Dpi_srs(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
129
dpid/dpi_socket_dir.c
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2003 Ferdi Franceschini <ferdif@optusnet.com.au>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*! \file
|
||||||
|
* Create a per user temporary directory for dpi sockets
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "dpid_common.h"
|
||||||
|
#include "dpi.h"
|
||||||
|
#include "misc_new.h"
|
||||||
|
#include "dpi_socket_dir.h" /* for function prototypes */
|
||||||
|
|
||||||
|
/*! Save socket directory name in ~/.dillo/dpi_socket_dir
|
||||||
|
* \Return
|
||||||
|
* \li 1 on success
|
||||||
|
* \li -1 on failure
|
||||||
|
*/
|
||||||
|
int w_dpi_socket_dir(char *dirname, char *sockdir)
|
||||||
|
{
|
||||||
|
FILE *dir;
|
||||||
|
|
||||||
|
if ((dir = fopen(dirname, "w")) == NULL) {
|
||||||
|
ERRMSG("w_dpi_socket_dir", "fopen", errno);
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
fprintf(dir, "%s", sockdir);
|
||||||
|
fclose(dir);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Test that socket directory exists and is a directory
|
||||||
|
* \Return
|
||||||
|
* \li 1 on success
|
||||||
|
* \li 0 on failure
|
||||||
|
* \bug Does not check permissions or that it's a symbolic link
|
||||||
|
* to another directory.
|
||||||
|
*/
|
||||||
|
int tst_dir(char *dir)
|
||||||
|
{
|
||||||
|
char *dirtest;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
/* test for a directory */
|
||||||
|
dirtest = dStrconcat(dir, "/", NULL);
|
||||||
|
if (access(dirtest, F_OK) == -1) {
|
||||||
|
ERRMSG("tst_dir", "access", errno);
|
||||||
|
MSG_ERR(" - %s\n", dirtest);
|
||||||
|
} else {
|
||||||
|
ret = 1;
|
||||||
|
}
|
||||||
|
dFree(dirtest);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Create socket directory
|
||||||
|
* \Return
|
||||||
|
* \li Socket directory path on success
|
||||||
|
* \li NULL on failure
|
||||||
|
*/
|
||||||
|
char *mk_sockdir(void)
|
||||||
|
{
|
||||||
|
char *template, *logname;
|
||||||
|
|
||||||
|
logname = getenv("LOGNAME") ? getenv("LOGNAME") : "dillo";
|
||||||
|
template = dStrconcat("/tmp/", logname, "-", "XXXXXX", NULL);
|
||||||
|
if (a_Misc_mkdtemp(template) == NULL) {
|
||||||
|
ERRMSG("mk_sockdir", "a_Misc_mkdtemp", 0);
|
||||||
|
MSG_ERR(" - %s\n", template);
|
||||||
|
dFree(template);
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Create socket directory if it does not exist and save its name in
|
||||||
|
* ~/.dillo/dpi_socket_dir.
|
||||||
|
* \Return
|
||||||
|
* \li Socket directory name on success
|
||||||
|
* \li NULL on failure.
|
||||||
|
*/
|
||||||
|
char *init_sockdir(char *dpi_socket_dir)
|
||||||
|
{
|
||||||
|
char *sockdir = NULL;
|
||||||
|
int dir_ok = 0;
|
||||||
|
|
||||||
|
if ((sockdir = a_Dpi_rd_dpi_socket_dir(dpi_socket_dir)) == NULL) {
|
||||||
|
MSG_ERR("init_sockdir: The dpi_socket_dir file %s does not exist\n",
|
||||||
|
dpi_socket_dir);
|
||||||
|
} else {
|
||||||
|
if ((dir_ok = tst_dir(sockdir)) == 1) {
|
||||||
|
MSG_ERR("init_sockdir: The socket directory %s exists and is OK\n",
|
||||||
|
sockdir);
|
||||||
|
} else {
|
||||||
|
MSG_ERR("init_sockdir: The socket directory %s does not exist "
|
||||||
|
"or is not a directory\n", sockdir);
|
||||||
|
dFree(sockdir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!dir_ok) {
|
||||||
|
sockdir = mk_sockdir();
|
||||||
|
if (sockdir == NULL) {
|
||||||
|
ERRMSG("init_sockdir", "mk_sockdir", 0);
|
||||||
|
MSG_ERR(" - Failed to create dpi socket directory\n");
|
||||||
|
} else if ((w_dpi_socket_dir(dpi_socket_dir, sockdir)) == -1) {
|
||||||
|
ERRMSG("init_sockdir", "w_dpi_socket_dir", 0);
|
||||||
|
MSG_ERR(" - failed to save %s\n", sockdir);
|
||||||
|
dFree(sockdir);
|
||||||
|
sockdir = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (sockdir);
|
||||||
|
}
|
||||||
17
dpid/dpi_socket_dir.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/*! \file
|
||||||
|
* Create a per user temporary directory for dpi sockets
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DPI_SOCKET_DIR_H
|
||||||
|
#define DPI_SOCKET_DIR_H
|
||||||
|
|
||||||
|
|
||||||
|
int w_dpi_socket_dir(char *dirname, char *sockdir);
|
||||||
|
|
||||||
|
int tst_dir(char *dir);
|
||||||
|
|
||||||
|
char *mk_sockdir(void);
|
||||||
|
|
||||||
|
char *init_sockdir(char *dpi_socket_dir);
|
||||||
|
|
||||||
|
#endif
|
||||||
908
dpid/dpid.c
Normal file
@ -0,0 +1,908 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2003 Ferdi Franceschini <ferdif@optusnet.com.au>
|
||||||
|
Copyright (C) 2025 Rodrigo Arias Mallo <rodarima@gmail.com>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*! \file
|
||||||
|
* Main functions to set-up dpi information and to initialise sockets
|
||||||
|
*/
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h> /* for exit */
|
||||||
|
#include <fcntl.h> /* for F_SETFD, F_GETFD, FD_CLOEXEC */
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <netinet/tcp.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include "dpid_common.h"
|
||||||
|
#include "dpid.h"
|
||||||
|
#include "dpi.h"
|
||||||
|
#include "dpi_socket_dir.h"
|
||||||
|
#include "misc_new.h"
|
||||||
|
|
||||||
|
#include "../dpip/dpip.h"
|
||||||
|
|
||||||
|
#define DPI_EXT (".dpi" EXEEXT)
|
||||||
|
|
||||||
|
#define QUEUE 5
|
||||||
|
|
||||||
|
volatile sig_atomic_t caught_sigchld = 0;
|
||||||
|
char *SharedKey = NULL;
|
||||||
|
|
||||||
|
/*! Remove dpid_comm_keys file.
|
||||||
|
* This avoids that dillo instances connect to a stale port after dpid
|
||||||
|
* has exited (e.g. after a reboot).
|
||||||
|
*/
|
||||||
|
void cleanup(void)
|
||||||
|
{
|
||||||
|
char *fname;
|
||||||
|
fname = dStrconcat(dGethomedir(), "/", dotDILLO_DPID_COMM_KEYS, NULL);
|
||||||
|
unlink(fname);
|
||||||
|
dFree(fname);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Free memory used to describe
|
||||||
|
* a set of dpi attributes
|
||||||
|
*/
|
||||||
|
void free_dpi_attr(struct dp *dpi_attr)
|
||||||
|
{
|
||||||
|
if (dpi_attr->id != NULL) {
|
||||||
|
dFree(dpi_attr->id);
|
||||||
|
dpi_attr->id = NULL;
|
||||||
|
}
|
||||||
|
if (dpi_attr->path != NULL) {
|
||||||
|
dFree(dpi_attr->path);
|
||||||
|
dpi_attr->path = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Free memory used by the plugin list
|
||||||
|
*/
|
||||||
|
void free_plugin_list(struct dp **dpi_attr_list_ptr, int numdpis)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct dp *dpi_attr_list = *dpi_attr_list_ptr;
|
||||||
|
|
||||||
|
if (dpi_attr_list == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i = 0; i < numdpis; i++)
|
||||||
|
free_dpi_attr(dpi_attr_list + i);
|
||||||
|
|
||||||
|
dFree(dpi_attr_list);
|
||||||
|
*dpi_attr_list_ptr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Free memory used by the services list
|
||||||
|
*/
|
||||||
|
void free_services_list(Dlist *s_list)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
struct service *s;
|
||||||
|
|
||||||
|
for (i=0; i < dList_length(s_list) ; i++) {
|
||||||
|
s = dList_nth_data(s_list, i);
|
||||||
|
dFree(s->name);
|
||||||
|
}
|
||||||
|
dList_free(s_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Signal handler for SIGINT, SIGQUIT, and SIGTERM. Calls cleanup
|
||||||
|
*/
|
||||||
|
static void terminator(int sig)
|
||||||
|
{
|
||||||
|
(void) sig; /* suppress unused parameter warning */
|
||||||
|
cleanup();
|
||||||
|
_exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Establish handler for termination signals
|
||||||
|
* and register cleanup with atexit */
|
||||||
|
void est_dpi_terminator(void)
|
||||||
|
{
|
||||||
|
struct sigaction act;
|
||||||
|
sigset_t block;
|
||||||
|
|
||||||
|
sigemptyset(&block);
|
||||||
|
sigaddset(&block, SIGHUP);
|
||||||
|
sigaddset(&block, SIGINT);
|
||||||
|
sigaddset(&block, SIGQUIT);
|
||||||
|
sigaddset(&block, SIGTERM);
|
||||||
|
|
||||||
|
act.sa_handler = terminator;
|
||||||
|
act.sa_mask = block;
|
||||||
|
act.sa_flags = 0;
|
||||||
|
|
||||||
|
if (sigaction(SIGHUP, &act, NULL) ||
|
||||||
|
sigaction(SIGINT, &act, NULL) ||
|
||||||
|
sigaction(SIGQUIT, &act, NULL) ||
|
||||||
|
sigaction(SIGTERM, &act, NULL)) {
|
||||||
|
ERRMSG("est_dpi_terminator", "sigaction", errno);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (atexit(cleanup) != 0) {
|
||||||
|
ERRMSG("est_dpi_terminator", "atexit", 0);
|
||||||
|
MSG_ERR("Hey! atexit failed, how did that happen?\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ends_with(const char *str, const char *suffix)
|
||||||
|
{
|
||||||
|
if (!str || !suffix)
|
||||||
|
return 0;
|
||||||
|
size_t lenstr = strlen(str);
|
||||||
|
size_t lensuffix = strlen(suffix);
|
||||||
|
if (lensuffix > lenstr)
|
||||||
|
return 0;
|
||||||
|
return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Identify a given file
|
||||||
|
* Currently there is only one file type associated with dpis.
|
||||||
|
* More file types will be added as needed
|
||||||
|
*/
|
||||||
|
enum file_type get_file_type(char *file_name)
|
||||||
|
{
|
||||||
|
if (ends_with(file_name, DPI_EXT))
|
||||||
|
/* Any filename ending in ".dpi" (or ".dpi.exe" on Windows) */
|
||||||
|
return DPI_FILE;
|
||||||
|
else {
|
||||||
|
MSG_ERR("get_file_type: Unknown file type for %s\n", file_name);
|
||||||
|
return UNKNOWN_FILE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Get dpi directory path from dpidrc
|
||||||
|
* \Return
|
||||||
|
* dpi directory on success, NULL on failure
|
||||||
|
* \Important
|
||||||
|
* The dpi_dir definition in dpidrc must have no leading white space.
|
||||||
|
*/
|
||||||
|
char *get_dpi_dir(char *dpidrc)
|
||||||
|
{
|
||||||
|
FILE *In;
|
||||||
|
int len;
|
||||||
|
char *rcline = NULL, *value = NULL, *p;
|
||||||
|
|
||||||
|
if ((In = fopen(dpidrc, "r")) == NULL) {
|
||||||
|
ERRMSG("dpi_dir", "fopen", errno);
|
||||||
|
MSG_ERR(" - %s\n", dpidrc);
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((rcline = dGetline(In)) != NULL) {
|
||||||
|
if (strncmp(rcline, "dpi_dir", 7) == 0)
|
||||||
|
break;
|
||||||
|
dFree(rcline);
|
||||||
|
}
|
||||||
|
fclose(In);
|
||||||
|
|
||||||
|
if (!rcline) {
|
||||||
|
ERRMSG("dpi_dir", "Failed to find a dpi_dir entry in dpidrc", 0);
|
||||||
|
MSG_ERR("Put your dillo plugins path in %s\n", dpidrc);
|
||||||
|
MSG_ERR("e.g. dpi_dir=/usr/local/lib/dillo/dpi\n");
|
||||||
|
MSG_ERR("with no leading spaces.\n");
|
||||||
|
value = NULL;
|
||||||
|
} else {
|
||||||
|
len = (int) strlen(rcline);
|
||||||
|
if (len && rcline[len - 1] == '\n')
|
||||||
|
rcline[len - 1] = 0;
|
||||||
|
|
||||||
|
if ((p = strchr(rcline, '='))) {
|
||||||
|
while (*++p == ' ');
|
||||||
|
value = dStrdup(p);
|
||||||
|
} else {
|
||||||
|
ERRMSG("dpi_dir", "strchr", 0);
|
||||||
|
MSG_ERR(" - '=' not found in %s\n", rcline);
|
||||||
|
value = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dFree(rcline);
|
||||||
|
return (value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Scans a service directory in dpi_dir and fills dpi_attr
|
||||||
|
* \Note
|
||||||
|
* Caller must allocate memory for dpi_attr.
|
||||||
|
* \Return
|
||||||
|
* \li 0 on success
|
||||||
|
* \li -1 on failure
|
||||||
|
* \todo
|
||||||
|
* Add other file types, but first we need to add files associated with a dpi
|
||||||
|
* to the design.
|
||||||
|
*/
|
||||||
|
int get_dpi_attr(char *dpi_dir, char *service, struct dp *dpi_attr)
|
||||||
|
{
|
||||||
|
char *service_dir = NULL;
|
||||||
|
struct stat statinfo;
|
||||||
|
enum file_type ftype;
|
||||||
|
int ret = -1;
|
||||||
|
DIR *dir_stream;
|
||||||
|
struct dirent *dir_entry = NULL;
|
||||||
|
|
||||||
|
service_dir = dStrconcat(dpi_dir, "/", service, NULL);
|
||||||
|
if (stat(service_dir, &statinfo) == -1) {
|
||||||
|
ERRMSG("get_dpi_attr", "stat", errno);
|
||||||
|
MSG_ERR("file=%s\n", service_dir);
|
||||||
|
} else if ((dir_stream = opendir(service_dir)) == NULL) {
|
||||||
|
ERRMSG("get_dpi_attr", "opendir", errno);
|
||||||
|
} else {
|
||||||
|
/* Scan the directory looking for dpi files.
|
||||||
|
* (currently there's only the dpi program, but in the future
|
||||||
|
* there may also be helper scripts.) */
|
||||||
|
while ( (dir_entry = readdir(dir_stream)) != NULL) {
|
||||||
|
if (dir_entry->d_name[0] == '.')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ftype = get_file_type(dir_entry->d_name);
|
||||||
|
switch (ftype) {
|
||||||
|
case DPI_FILE:
|
||||||
|
dpi_attr->path =
|
||||||
|
dStrconcat(service_dir, "/", dir_entry->d_name, NULL);
|
||||||
|
dpi_attr->id = dStrdup(service);
|
||||||
|
dpi_attr->port = 0;
|
||||||
|
dpi_attr->pid = 1;
|
||||||
|
if (strstr(dpi_attr->path, ".filter") != NULL)
|
||||||
|
dpi_attr->filter = 1;
|
||||||
|
else
|
||||||
|
dpi_attr->filter = 0;
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir(dir_stream);
|
||||||
|
|
||||||
|
if (ret != 0)
|
||||||
|
MSG_ERR("get_dpi_attr: No dpi plug-in in %s/%s\n",
|
||||||
|
dpi_dir, service);
|
||||||
|
}
|
||||||
|
dFree(service_dir);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Register a service
|
||||||
|
* Retrieves attributes for "service" and stores them
|
||||||
|
* in dpi_attr. It looks for "service" in ~/.dillo/dpi
|
||||||
|
* first, and then in the system wide dpi directory.
|
||||||
|
* Caller must allocate memory for dpi_attr.
|
||||||
|
* \Return
|
||||||
|
* \li 0 on success
|
||||||
|
* \li -1 on failure
|
||||||
|
*/
|
||||||
|
int register_service(struct dp *dpi_attr, char *service)
|
||||||
|
{
|
||||||
|
char *user_dpi_dir, *dpidrc, *user_service_dir, *dir = NULL;
|
||||||
|
int ret = -1;
|
||||||
|
|
||||||
|
user_dpi_dir = dStrconcat(dGethomedir(), "/", dotDILLO_DPI, NULL);
|
||||||
|
user_service_dir =
|
||||||
|
dStrconcat(dGethomedir(), "/", dotDILLO_DPI, "/", service, NULL);
|
||||||
|
|
||||||
|
dpidrc = dStrconcat(dGethomedir(), "/", dotDILLO_DPIDRC, NULL);
|
||||||
|
if (access(dpidrc, F_OK) == -1) {
|
||||||
|
if (access(DPIDRC_SYS, F_OK) == -1) {
|
||||||
|
ERRMSG("register_service", "Error ", 0);
|
||||||
|
MSG_ERR("\n - There is no %s or %s file\n", dpidrc,
|
||||||
|
DPIDRC_SYS);
|
||||||
|
dFree(user_dpi_dir);
|
||||||
|
dFree(user_service_dir);
|
||||||
|
dFree(dpidrc);
|
||||||
|
return(-1);
|
||||||
|
}
|
||||||
|
dFree(dpidrc);
|
||||||
|
dpidrc = dStrdup(DPIDRC_SYS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check home dir for dpis */
|
||||||
|
if (access(user_service_dir, F_OK) == 0) {
|
||||||
|
get_dpi_attr(user_dpi_dir, service, dpi_attr);
|
||||||
|
ret = 0;
|
||||||
|
} else { /* Check system wide dpis */
|
||||||
|
if ((dir = get_dpi_dir(dpidrc)) != NULL) {
|
||||||
|
if (access(dir, F_OK) == 0) {
|
||||||
|
get_dpi_attr(dir, service, dpi_attr);
|
||||||
|
ret = 0;
|
||||||
|
} else {
|
||||||
|
ERRMSG("register_service", "get_dpi_attr failed", 0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ERRMSG("register_service", "dpi_dir: Error getting dpi dir.", 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dFree(user_dpi_dir);
|
||||||
|
dFree(user_service_dir);
|
||||||
|
dFree(dpidrc);
|
||||||
|
dFree(dir);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Create dpi directory for available
|
||||||
|
* plugins and create plugin list.
|
||||||
|
* \Return
|
||||||
|
* \li Returns number of available plugins on success
|
||||||
|
* \li -1 on failure
|
||||||
|
*/
|
||||||
|
int register_all(struct dp **attlist)
|
||||||
|
{
|
||||||
|
char *user_dpidir = NULL, *sys_dpidir = NULL, *dpidrc = NULL;
|
||||||
|
struct dirent *user_dirent, *sys_dirent;
|
||||||
|
int st;
|
||||||
|
int snum;
|
||||||
|
size_t dp_sz = sizeof(struct dp);
|
||||||
|
|
||||||
|
if (*attlist != NULL) {
|
||||||
|
ERRMSG("register_all", "attlist parameter should be NULL", 0);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
user_dpidir = dStrconcat(dGethomedir(), "/", dotDILLO_DPI, NULL);
|
||||||
|
if (access(user_dpidir, F_OK) == -1) {
|
||||||
|
/* no dpis in user's space */
|
||||||
|
dFree(user_dpidir);
|
||||||
|
user_dpidir = NULL;
|
||||||
|
}
|
||||||
|
dpidrc = dStrconcat(dGethomedir(), "/", dotDILLO_DPIDRC, NULL);
|
||||||
|
if (access(dpidrc, F_OK) == -1) {
|
||||||
|
dFree(dpidrc);
|
||||||
|
dpidrc = dStrdup(DPIDRC_SYS);
|
||||||
|
if (access(dpidrc, F_OK) == -1) {
|
||||||
|
dFree(dpidrc);
|
||||||
|
dpidrc = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!dpidrc || (sys_dpidir = get_dpi_dir(dpidrc)) == NULL)
|
||||||
|
sys_dpidir = NULL;
|
||||||
|
dFree(dpidrc);
|
||||||
|
|
||||||
|
if (!user_dpidir && !sys_dpidir) {
|
||||||
|
ERRMSG("register_all", "Fatal error ", 0);
|
||||||
|
MSG_ERR("\n - Can't find the directory for dpis.\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get list of services in user's .dillo/dpi directory */
|
||||||
|
snum = 0;
|
||||||
|
if (user_dpidir) {
|
||||||
|
DIR *user_dir_stream = opendir(user_dpidir);
|
||||||
|
/* Only complain if error is other than not found as the user may not have
|
||||||
|
* any DPIs installed. */
|
||||||
|
if (user_dir_stream == NULL && errno != ENOENT) {
|
||||||
|
MSG_ERR("cannot open user dpi directory '%s': %s\n",
|
||||||
|
user_dpidir, strerror(errno));
|
||||||
|
} else {
|
||||||
|
while ((user_dirent = readdir(user_dir_stream)) != NULL) {
|
||||||
|
if (user_dirent->d_name[0] == '.')
|
||||||
|
continue;
|
||||||
|
*attlist = (struct dp *) dRealloc(*attlist, (snum + 1) * dp_sz);
|
||||||
|
st=get_dpi_attr(user_dpidir, user_dirent->d_name, &(*attlist)[snum]);
|
||||||
|
if (st == 0)
|
||||||
|
snum++;
|
||||||
|
}
|
||||||
|
closedir(user_dir_stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sys_dpidir) {
|
||||||
|
DIR *sys_dir_stream = opendir(sys_dpidir);
|
||||||
|
/* For system DPIs always complain. */
|
||||||
|
if (sys_dir_stream == NULL) {
|
||||||
|
MSG_ERR("cannot open system dpi directory '%s': %s\n",
|
||||||
|
sys_dpidir, strerror(errno));
|
||||||
|
} else {
|
||||||
|
/* if system service is not in user list then add it */
|
||||||
|
while ((sys_dirent = readdir(sys_dir_stream)) != NULL) {
|
||||||
|
if (sys_dirent->d_name[0] == '.')
|
||||||
|
continue;
|
||||||
|
*attlist = (struct dp *) dRealloc(*attlist, (snum + 1) * dp_sz);
|
||||||
|
st=get_dpi_attr(sys_dpidir, sys_dirent->d_name, &(*attlist)[snum]);
|
||||||
|
if (st == 0)
|
||||||
|
snum++;
|
||||||
|
}
|
||||||
|
closedir(sys_dir_stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dFree(sys_dpidir);
|
||||||
|
dFree(user_dpidir);
|
||||||
|
|
||||||
|
/* TODO: do we consider snum == 0 an error?
|
||||||
|
* (if so, we should return -1 ) */
|
||||||
|
return (snum);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compare two struct service pointers
|
||||||
|
* This function is used for sorting services
|
||||||
|
*/
|
||||||
|
static int services_alpha_comp(const struct service *s1,
|
||||||
|
const struct service *s2)
|
||||||
|
{
|
||||||
|
return -strcmp(s1->name, s2->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Add services reading a dpidrc file
|
||||||
|
* each non empty or commented line has the form
|
||||||
|
* service = path_relative_to_dpidir
|
||||||
|
* \Return:
|
||||||
|
* \li Returns number of available services on success
|
||||||
|
* \li -1 on failure
|
||||||
|
*/
|
||||||
|
int fill_services_list(struct dp *attlist, int numdpis, Dlist **services_list)
|
||||||
|
{
|
||||||
|
FILE *dpidrc_stream;
|
||||||
|
char *p, *line = NULL, *service, *path;
|
||||||
|
int i, st;
|
||||||
|
struct service *s;
|
||||||
|
char *user_dpidir = NULL, *sys_dpidir = NULL, *dpidrc = NULL;
|
||||||
|
|
||||||
|
user_dpidir = dStrconcat(dGethomedir(), "/", dotDILLO_DPI, NULL);
|
||||||
|
if (access(user_dpidir, F_OK) == -1) {
|
||||||
|
/* no dpis in user's space */
|
||||||
|
dFree(user_dpidir);
|
||||||
|
user_dpidir = NULL;
|
||||||
|
}
|
||||||
|
dpidrc = dStrconcat(dGethomedir(), "/", dotDILLO_DPIDRC, NULL);
|
||||||
|
if (access(dpidrc, F_OK) == -1) {
|
||||||
|
dFree(dpidrc);
|
||||||
|
dpidrc = dStrdup(DPIDRC_SYS);
|
||||||
|
if (access(dpidrc, F_OK) == -1) {
|
||||||
|
dFree(dpidrc);
|
||||||
|
dpidrc = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!dpidrc || (sys_dpidir = get_dpi_dir(dpidrc)) == NULL)
|
||||||
|
sys_dpidir = NULL;
|
||||||
|
|
||||||
|
if (!user_dpidir && !sys_dpidir) {
|
||||||
|
ERRMSG("fill_services_list", "Fatal error ", 0);
|
||||||
|
MSG_ERR("\n - Can't find the directory for dpis.\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((dpidrc_stream = fopen(dpidrc, "r")) == NULL) {
|
||||||
|
ERRMSG("fill_services_list", "popen failed", errno);
|
||||||
|
dFree(dpidrc);
|
||||||
|
dFree(sys_dpidir);
|
||||||
|
dFree(user_dpidir);
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*services_list != NULL) {
|
||||||
|
ERRMSG("fill_services_list", "services_list parameter is not NULL", 0);
|
||||||
|
fclose(dpidrc_stream);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
*services_list = dList_new(8);
|
||||||
|
|
||||||
|
/* dpidrc parser loop */
|
||||||
|
for (;(line = dGetline(dpidrc_stream)) != NULL; dFree(line)) {
|
||||||
|
st = dParser_parse_rc_line(&line, &service, &path);
|
||||||
|
if (st < 0) {
|
||||||
|
MSG_ERR("dpid: Syntax error in %s: service=\"%s\" path=\"%s\"\n",
|
||||||
|
dpidrc, service, path);
|
||||||
|
continue;
|
||||||
|
} else if (st != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_MSG("dpid: service=%s, path=%s\n", service, path);
|
||||||
|
|
||||||
|
/* ignore dpi_dir silently */
|
||||||
|
if (strcmp(service, "dpi_dir") == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
s = dNew(struct service, 1);
|
||||||
|
/* init services list entry */
|
||||||
|
s->name = dStrdup(service);
|
||||||
|
s->dp_index = -1;
|
||||||
|
|
||||||
|
dList_append(*services_list, s);
|
||||||
|
/* search the dpi for a service by its path */
|
||||||
|
for (i = 0; i < numdpis; i++)
|
||||||
|
if ((p = strstr(attlist[i].path, path)) && *(p - 1) == '/' &&
|
||||||
|
strlen(p) == strlen(path))
|
||||||
|
break;
|
||||||
|
/* if the dpi exist bind service and dpi */
|
||||||
|
if (i < numdpis)
|
||||||
|
s->dp_index = i;
|
||||||
|
}
|
||||||
|
fclose(dpidrc_stream);
|
||||||
|
|
||||||
|
dList_sort(*services_list, (dCompareFunc)services_alpha_comp);
|
||||||
|
|
||||||
|
dFree(dpidrc);
|
||||||
|
dFree(sys_dpidir);
|
||||||
|
dFree(user_dpidir);
|
||||||
|
|
||||||
|
return (dList_length(*services_list));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return a socket file descriptor
|
||||||
|
* (useful to set socket options in a uniform way)
|
||||||
|
*/
|
||||||
|
static int make_socket_fd(void)
|
||||||
|
{
|
||||||
|
int ret, one = 1;
|
||||||
|
|
||||||
|
if ((ret = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
|
||||||
|
ERRMSG("make_socket_fd", "socket", errno);
|
||||||
|
} else {
|
||||||
|
/* avoid delays when sending small pieces of data */
|
||||||
|
setsockopt(ret, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set some buffering to increase the transfer's speed */
|
||||||
|
//setsockopt(sock_fd, SOL_SOCKET, SO_SNDBUF,
|
||||||
|
// &sock_buflen, (socklen_t)sizeof(sock_buflen));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Bind a socket port on localhost. Try to be close to base_port.
|
||||||
|
* \Return
|
||||||
|
* \li listening socket file descriptor on success
|
||||||
|
* \li -1 on failure
|
||||||
|
*/
|
||||||
|
int bind_socket_fd(int base_port, int *p_port)
|
||||||
|
{
|
||||||
|
int sock_fd, port;
|
||||||
|
struct sockaddr_in sin;
|
||||||
|
int ok = 0, last_port = base_port + 50;
|
||||||
|
|
||||||
|
if ((sock_fd = make_socket_fd()) == -1) {
|
||||||
|
return (-1); /* avoids nested ifs */
|
||||||
|
}
|
||||||
|
/* Set the socket FD to close on exec */
|
||||||
|
fcntl(sock_fd, F_SETFD, FD_CLOEXEC | fcntl(sock_fd, F_GETFD));
|
||||||
|
|
||||||
|
|
||||||
|
memset(&sin, 0, sizeof(sin));
|
||||||
|
sin.sin_family = AF_INET;
|
||||||
|
sin.sin_addr.s_addr = inet_addr("127.0.0.1");
|
||||||
|
|
||||||
|
/* Try to bind a port on localhost */
|
||||||
|
for (port = base_port; port <= last_port; ++port) {
|
||||||
|
sin.sin_port = htons(port);
|
||||||
|
if ((bind(sock_fd, (struct sockaddr *)&sin, sizeof(sin))) == -1) {
|
||||||
|
if (errno == EADDRINUSE || errno == EADDRNOTAVAIL)
|
||||||
|
continue;
|
||||||
|
ERRMSG("bind_socket_fd", "bind", errno);
|
||||||
|
} else if (listen(sock_fd, QUEUE) == -1) {
|
||||||
|
ERRMSG("bind_socket_fd", "listen", errno);
|
||||||
|
} else {
|
||||||
|
*p_port = port;
|
||||||
|
ok = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (port > last_port) {
|
||||||
|
MSG_ERR("Hey! Can't find an available port from %d to %d\n",
|
||||||
|
base_port, last_port);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ok ? sock_fd : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Save the current port and a shared secret in a file so dillo can find it.
|
||||||
|
* \Return:
|
||||||
|
* \li -1 on failure
|
||||||
|
*/
|
||||||
|
int save_comm_keys(int srs_port)
|
||||||
|
{
|
||||||
|
int fd, ret = -1;
|
||||||
|
char *fname, port_str[32];
|
||||||
|
|
||||||
|
fname = dStrconcat(dGethomedir(), "/", dotDILLO_DPID_COMM_KEYS, NULL);
|
||||||
|
fd = open(fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
|
||||||
|
dFree(fname);
|
||||||
|
if (fd == -1) {
|
||||||
|
MSG("save_comm_keys: open %s\n", dStrerror(errno));
|
||||||
|
} else {
|
||||||
|
snprintf(port_str, 16, "%d %s\n", srs_port, SharedKey);
|
||||||
|
if (CKD_WRITE(fd, port_str) != -1 && CKD_CLOSE(fd) != -1) {
|
||||||
|
ret = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Initialise the service request socket (IDS)
|
||||||
|
* \Return:
|
||||||
|
* \li Number of sockets (1 == success)
|
||||||
|
* \li -1 on failure
|
||||||
|
*/
|
||||||
|
int init_ids_srs_socket(void)
|
||||||
|
{
|
||||||
|
int srs_port, ret = -1;
|
||||||
|
|
||||||
|
FD_ZERO(&sock_set);
|
||||||
|
|
||||||
|
if ((srs_fd = bind_socket_fd(DPID_BASE_PORT, &srs_port)) != -1) {
|
||||||
|
/* create the shared secret */
|
||||||
|
SharedKey = a_Misc_mksecret(8);
|
||||||
|
/* save port number and SharedKey */
|
||||||
|
if (save_comm_keys(srs_port) != -1) {
|
||||||
|
FD_SET(srs_fd, &sock_set);
|
||||||
|
ret = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Initialize a single dpi socket
|
||||||
|
* \Return
|
||||||
|
* \li 1 on success
|
||||||
|
* \li -1 on failure
|
||||||
|
*/
|
||||||
|
int init_dpi_socket(struct dp *dpi_attr)
|
||||||
|
{
|
||||||
|
int s_fd, port, ret = -1;
|
||||||
|
|
||||||
|
if ((s_fd = bind_socket_fd(DPID_BASE_PORT, &port)) != -1) {
|
||||||
|
dpi_attr->sock_fd = s_fd;
|
||||||
|
dpi_attr->port = port;
|
||||||
|
FD_SET(s_fd, &sock_set);
|
||||||
|
ret = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Setup sockets for the plugins and add them to
|
||||||
|
* the set of sockets (sock_set) watched by select.
|
||||||
|
* \Return
|
||||||
|
* \li Number of sockets on success
|
||||||
|
* \li -1 on failure
|
||||||
|
* \Modifies
|
||||||
|
* dpi_attr_list.sa, dpi_attr_list.socket, numsocks, sock_set, srs
|
||||||
|
* \Uses
|
||||||
|
* numdpis, srs, srs_name
|
||||||
|
*/
|
||||||
|
int init_all_dpi_sockets(struct dp *dpi_attr_list)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Initialise sockets for each dpi */
|
||||||
|
for (i = 0; i < numdpis; i++) {
|
||||||
|
if (init_dpi_socket(dpi_attr_list + i) == -1)
|
||||||
|
return (-1);
|
||||||
|
numsocks++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (numsocks);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! SIGCHLD handler
|
||||||
|
*/
|
||||||
|
void dpi_sigchld(int sig)
|
||||||
|
{
|
||||||
|
if (sig == SIGCHLD)
|
||||||
|
caught_sigchld = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Called by main loop when caught_sigchld == 1 */
|
||||||
|
void handle_sigchld(void)
|
||||||
|
{
|
||||||
|
// pid_t pid;
|
||||||
|
int i, status; //, num_active;
|
||||||
|
|
||||||
|
/* For all of the dpis in the current list
|
||||||
|
* add the ones that have exited to the set of sockets being
|
||||||
|
* watched by 'select'.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < numdpis; i++) {
|
||||||
|
if (waitpid(dpi_attr_list[i].pid, &status, WNOHANG) > 0) {
|
||||||
|
dpi_attr_list[i].pid = 1;
|
||||||
|
FD_SET(dpi_attr_list[i].sock_fd, &sock_set);
|
||||||
|
numsocks++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait for any old dpis that have exited */
|
||||||
|
while (waitpid(-1, &status, WNOHANG) > 0)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Establish SIGCHLD handler */
|
||||||
|
void est_dpi_sigchld(void)
|
||||||
|
{
|
||||||
|
struct sigaction sigact;
|
||||||
|
sigset_t set;
|
||||||
|
|
||||||
|
(void) sigemptyset(&set);
|
||||||
|
sigact.sa_handler = dpi_sigchld;
|
||||||
|
sigact.sa_mask = set;
|
||||||
|
sigact.sa_flags = SA_NOCLDSTOP;
|
||||||
|
if (sigaction(SIGCHLD, &sigact, NULL) == -1) {
|
||||||
|
ERRMSG("est_dpi_sigchld", "sigaction", errno);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! EINTR aware connect() call */
|
||||||
|
int ckd_connect (int sock_fd, struct sockaddr *addr, socklen_t len)
|
||||||
|
{
|
||||||
|
ssize_t ret;
|
||||||
|
|
||||||
|
do {
|
||||||
|
ret = connect(sock_fd, addr, len);
|
||||||
|
} while (ret == -1 && errno == EINTR);
|
||||||
|
if (ret == -1) {
|
||||||
|
ERRMSG("dpid.c", "connect", errno);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Send DpiBye command to all active non-filter dpis
|
||||||
|
*/
|
||||||
|
void stop_active_dpis(struct dp *dpi_attr_list, int numdpis)
|
||||||
|
{
|
||||||
|
char *bye_cmd, *auth_cmd;
|
||||||
|
int i, sock_fd;
|
||||||
|
struct sockaddr_in sin;
|
||||||
|
|
||||||
|
bye_cmd = a_Dpip_build_cmd("cmd=%s", "DpiBye");
|
||||||
|
auth_cmd = a_Dpip_build_cmd("cmd=%s msg=%s", "auth", SharedKey);
|
||||||
|
|
||||||
|
memset(&sin, 0, sizeof(sin));
|
||||||
|
sin.sin_family = AF_INET;
|
||||||
|
sin.sin_addr.s_addr = inet_addr("127.0.0.1");
|
||||||
|
|
||||||
|
for (i = 0; i < numdpis; i++) {
|
||||||
|
/* Skip inactive dpis and filters */
|
||||||
|
if (dpi_attr_list[i].pid == 1 || dpi_attr_list[i].filter)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ((sock_fd = make_socket_fd()) == -1) {
|
||||||
|
ERRMSG("stop_active_dpis", "socket", errno);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
sin.sin_port = htons(dpi_attr_list[i].port);
|
||||||
|
if (ckd_connect(sock_fd, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
|
||||||
|
ERRMSG("stop_active_dpis", "connect", errno);
|
||||||
|
MSG_ERR("%s\n", dpi_attr_list[i].path);
|
||||||
|
} else if (CKD_WRITE(sock_fd, auth_cmd) == -1) {
|
||||||
|
ERRMSG("stop_active_dpis", "write", errno);
|
||||||
|
} else if (CKD_WRITE(sock_fd, bye_cmd) == -1) {
|
||||||
|
ERRMSG("stop_active_dpis", "write", errno);
|
||||||
|
}
|
||||||
|
dClose(sock_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
dFree(auth_cmd);
|
||||||
|
dFree(bye_cmd);
|
||||||
|
|
||||||
|
/* Allow child dpis some time to read dpid_comm_keys before erasing it */
|
||||||
|
sleep (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Removes dpis in dpi_attr_list from the
|
||||||
|
* set of sockets watched by select and
|
||||||
|
* closes their sockets.
|
||||||
|
*/
|
||||||
|
void ignore_dpi_sockets(struct dp *dpi_attr_list, int numdpis)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < numdpis; i++) {
|
||||||
|
FD_CLR(dpi_attr_list[i].sock_fd, &sock_set);
|
||||||
|
dClose(dpi_attr_list[i].sock_fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Registers available dpis and stops active non-filter dpis.
|
||||||
|
* Called when dpid receives
|
||||||
|
* cmd='register' service='all'
|
||||||
|
* command
|
||||||
|
* \Return
|
||||||
|
* Number of available dpis
|
||||||
|
*/
|
||||||
|
int register_all_cmd(void)
|
||||||
|
{
|
||||||
|
stop_active_dpis(dpi_attr_list, numdpis);
|
||||||
|
free_plugin_list(&dpi_attr_list, numdpis);
|
||||||
|
free_services_list(services_list);
|
||||||
|
services_list = NULL;
|
||||||
|
numdpis = 0;
|
||||||
|
numsocks = 1; /* the srs socket */
|
||||||
|
FD_ZERO(&sock_set);
|
||||||
|
FD_SET(srs_fd, &sock_set);
|
||||||
|
numdpis = register_all(&dpi_attr_list);
|
||||||
|
fill_services_list(dpi_attr_list, numdpis, &services_list);
|
||||||
|
numsocks = init_all_dpi_sockets(dpi_attr_list);
|
||||||
|
return (numdpis);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Get value of msg field from dpi_tag
|
||||||
|
* \Return
|
||||||
|
* message on success, NULL on failure
|
||||||
|
*/
|
||||||
|
char *get_message(int sock_fd, char *dpi_tag)
|
||||||
|
{
|
||||||
|
char *msg, *d_cmd;
|
||||||
|
|
||||||
|
msg = a_Dpip_get_attr(dpi_tag, "msg");
|
||||||
|
if (msg == NULL) {
|
||||||
|
ERRMSG("get_message", "failed to parse msg", 0);
|
||||||
|
d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s",
|
||||||
|
"DpiError", "Failed to parse request");
|
||||||
|
(void) CKD_WRITE(sock_fd, d_cmd);
|
||||||
|
dFree(d_cmd);
|
||||||
|
}
|
||||||
|
return (msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compare a struct service pointer and a service name
|
||||||
|
* This function is used for searching services by name
|
||||||
|
*/
|
||||||
|
int service_match(const struct service *A, const char *B)
|
||||||
|
{
|
||||||
|
int A_len, B_len, len;
|
||||||
|
|
||||||
|
A_len = strlen(A->name);
|
||||||
|
B_len = strlen(B);
|
||||||
|
len = MAX (A_len, B_len);
|
||||||
|
|
||||||
|
if (A->name[A_len - 1] == '*')
|
||||||
|
len = A_len - 1;
|
||||||
|
|
||||||
|
return(dStrnAsciiCasecmp(A->name, B, len));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Send socket port that matches dpi_id to client
|
||||||
|
*/
|
||||||
|
void send_sockport(int sock_fd, char *dpi_tag, struct dp *dpi_attr_list)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
char *dpi_id, *d_cmd, port_str[16];
|
||||||
|
struct service *serv;
|
||||||
|
|
||||||
|
dReturn_if_fail((dpi_id = get_message(sock_fd, dpi_tag)) != NULL);
|
||||||
|
|
||||||
|
serv = dList_find_custom(services_list,dpi_id,(dCompareFunc)service_match);
|
||||||
|
|
||||||
|
if (serv == NULL || (i = serv->dp_index) == -1)
|
||||||
|
for (i = 0; i < numdpis; i++)
|
||||||
|
if (!strncmp(dpi_attr_list[i].id, dpi_id,
|
||||||
|
dpi_attr_list[i].id - strchr(dpi_attr_list[i].id, '.')))
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (i < numdpis) {
|
||||||
|
/* found */
|
||||||
|
snprintf(port_str, 8, "%d", dpi_attr_list[i].port);
|
||||||
|
d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s", "send_data", port_str);
|
||||||
|
(void) CKD_WRITE(sock_fd, d_cmd);
|
||||||
|
dFree(d_cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
dFree(dpi_id);
|
||||||
|
}
|
||||||