mirror of
https://github.com/reactos/wine.git
synced 2024-11-24 12:20:07 +00:00
Reorganizing wine-devel:
- killing the advanced part (and moving its chapters in both developing Wine and Wine archi parts) - creating a new book on coding practices from i18n.sgml, patches.sgml and porting.sgml - creating a new book on some debugging strategies from the old advanced book and cvs-regression.sgml - creating a new book on the kernel modules (NTDLL & KERNEL32) from architecture.sgml / related DLLs and address-space.sgml, console.sgml, threading.sgml - creating a new book on the windowing from architecture.sgml / USER32 and related - creating a new book on the graphical parts from architecture.sgml / GDI32 and related Other changes: - removed list of DLLs and their role (from the modules overview) - removed in X11 keyboard mapping section the part related to submit a patch
This commit is contained in:
parent
0e3c524f4c
commit
e86389e057
@ -26,20 +26,20 @@ WINE_USER_SRCS = \
|
||||
WINE_DEVEL_SRCS = \
|
||||
address-space.sgml \
|
||||
architecture.sgml \
|
||||
consoles.sgml \
|
||||
cvs-regression.sgml \
|
||||
ddraw.sgml \
|
||||
debugger.sgml \
|
||||
debugging.sgml \
|
||||
documentation.sgml \
|
||||
i18n.sgml \
|
||||
implementation.sgml \
|
||||
multimedia.sgml \
|
||||
ole.sgml \
|
||||
opengl.sgml \
|
||||
patches.sgml \
|
||||
porting.sgml \
|
||||
testing.sgml
|
||||
testing.sgml \
|
||||
winedev-coding.sgml \
|
||||
winedev-graphical.sgml \
|
||||
winedev-kernel.sgml \
|
||||
winedev-otherdebug.sgml \
|
||||
winedev-windowing.sgml
|
||||
|
||||
WINELIB_USER_SRCS = \
|
||||
winelib-bindlls.sgml \
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,174 +0,0 @@
|
||||
<chapter id="consoles">
|
||||
<title>Consoles in Wine</title>
|
||||
|
||||
<para>
|
||||
As described in the Wine User Guide's CUI section, Wine
|
||||
manipulates three kinds of "consoles" in order to support
|
||||
properly the Win32 CUI API.
|
||||
</para>
|
||||
<para>
|
||||
The following table describes the main implementation
|
||||
differences between the three approaches.
|
||||
|
||||
<table>
|
||||
<title>Function consoles implementation comparison</title>
|
||||
<tgroup cols="4" align="left">
|
||||
<thead>
|
||||
<row>
|
||||
<entry>Function</entry>
|
||||
<entry>Bare streams</entry>
|
||||
<entry>Wineconsole & user backend</entry>
|
||||
<entry>Wineconsole & curses backend</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>
|
||||
Console as a Win32 Object (and associated
|
||||
handles)
|
||||
</entry>
|
||||
<entry>
|
||||
No specific Win32 object is used in this case. The
|
||||
handles manipulated for the standard Win32 streams
|
||||
are in fact "bare handles" to their corresponding
|
||||
Unix streams. The mode manipulation functions
|
||||
(<function>GetConsoleMode</function> /
|
||||
<function>SetConsoleMode</function>) are not
|
||||
supported.
|
||||
</entry>
|
||||
<entry>
|
||||
Implemented in server, and a specific Winelib
|
||||
program (wineconsole) is in charge of the
|
||||
rendering and user input. The mode manipulation
|
||||
functions behave as expected.
|
||||
</entry>
|
||||
<entry>
|
||||
Implemented in server, and a specific Winelib
|
||||
program (wineconsole) is in charge of the
|
||||
rendering and user input. The mode manipulation
|
||||
functions behave as expected.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
Inheritance (including handling in
|
||||
<function>CreateProcess</function> of
|
||||
<constant>CREATE_DETACHED</constant>,
|
||||
<constant>CREATE_NEW_CONSOLE</constant> flags).
|
||||
</entry>
|
||||
<entry>
|
||||
Not supported. Every process child of a process
|
||||
will inherit the Unix streams, so will also
|
||||
inherit the Win32 standard streams.
|
||||
</entry>
|
||||
<entry>
|
||||
Fully supported (each new console creation will be
|
||||
handled by the creation of a new USER32 window)
|
||||
</entry>
|
||||
<entry>
|
||||
Fully supported, except for the creation of a new
|
||||
console, which will be rendered on the same Unix
|
||||
terminal as the previous one, leading to
|
||||
unpredictable results.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><function>ReadFile</function> / <function>WriteFile</function> operations</entry>
|
||||
<entry>Fully supported</entry>
|
||||
<entry>Fully supported</entry>
|
||||
<entry>Fully supported</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
Screen-buffer manipulation (creation, deletion, resizing...)
|
||||
</entry>
|
||||
<entry>Not supported</entry>
|
||||
<entry>Fully supported</entry>
|
||||
<entry>
|
||||
Partly supported (this won't work too well as we
|
||||
don't control (so far) the size of underlying Unix
|
||||
terminal
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
APIs for reading/writing screen-buffer content,
|
||||
cursor position
|
||||
</entry>
|
||||
<entry>Not supported</entry>
|
||||
<entry>Fully supported</entry>
|
||||
<entry>Fully supported</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
APIs for manipulating the rendering window size
|
||||
</entry>
|
||||
<entry>Not supported</entry>
|
||||
<entry>Fully supported</entry>
|
||||
<entry>
|
||||
Partly supported (this won't work too well as we
|
||||
don't control (so far) the size of underlying Unix
|
||||
terminal
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
Signaling (in particular, Ctrl-C handling)
|
||||
</entry>
|
||||
<entry>
|
||||
Nothing is done, which means that Ctrl-C will
|
||||
generate (as usual) a <constant>SIGINT</constant>
|
||||
which will terminate the program.
|
||||
</entry>
|
||||
<entry>
|
||||
Partly supported (Ctrl-C behaves as expected,
|
||||
however the other Win32 CUI signaling isn't
|
||||
properly implemented).
|
||||
</entry>
|
||||
<entry>
|
||||
Partly supported (Ctrl-C behaves as expected,
|
||||
however the other Win32 CUI signaling isn't
|
||||
properly implemented).
|
||||
</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The Win32 objects behind a console can be created in several occasions:
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
When the program is started from wineconsole, a new
|
||||
console object is created and will be used (inherited)
|
||||
by the process launched from wineconsole.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
When a program, which isn't attached to a console, calls
|
||||
<function>AllocConsole</function>, Wine then launches
|
||||
wineconsole, and attaches the current program to this
|
||||
console. In this mode, the USER32 mode is always
|
||||
selected as Wine cannot tell the current state of the
|
||||
Unix console.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
<para>
|
||||
Please also note, that starting a child process with the
|
||||
<constant>CREATE_NEW_CONSOLE</constant> flag, will end-up
|
||||
calling <function>AllocConsole</function> in the child
|
||||
process, hence creating a wineconsole with the USER32 backend.
|
||||
</para>
|
||||
</chapter>
|
||||
|
||||
<!-- Keep this comment at the end of the file
|
||||
Local variables:
|
||||
mode: sgml
|
||||
sgml-parent-document:("wine-devel.sgml" "set" "book" "part" "chapter" "")
|
||||
End:
|
||||
-->
|
@ -1,142 +0,0 @@
|
||||
<chapter id="cvs-regression">
|
||||
<title>How to do regression testing using CVS</title>
|
||||
|
||||
<para>
|
||||
A problem that can happen sometimes is 'it used to work
|
||||
before, now it doesn't anymore...'. Here is a step by step
|
||||
procedure to try to pinpoint when the problem occurred. This is
|
||||
<emphasis>NOT</emphasis> for casual users.
|
||||
</para>
|
||||
|
||||
<orderedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
Get the <quote>full CVS</quote> archive from winehq. This archive is
|
||||
the CVS tree but with the tags controlling the versioning
|
||||
system. It's a big file (> 40 meg) with a name like
|
||||
full-cvs-<last update date> (it's more than 100mb
|
||||
when uncompressed, you can't very well do this with
|
||||
small, old computers or slow Internet connections).
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
untar it into a repository directory:
|
||||
<screen>
|
||||
cd /home/gerard
|
||||
tar -zxf full-cvs-2003-08-18.tar.gz
|
||||
mv wine repository
|
||||
</screen>
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
extract a new destination directory. This directory must
|
||||
not be in a subdirectory of the repository else
|
||||
<command>cvs</command> will think it's part of the
|
||||
repository and deny you an extraction in the repository:
|
||||
<screen>
|
||||
cd /home/gerard
|
||||
mv wine wine_current (-> this protects your current wine sandbox, if any)
|
||||
export CVSROOT=/home/gerard/repository
|
||||
cvs -d $CVSROOT checkout wine
|
||||
</screen>
|
||||
</para>
|
||||
<para>
|
||||
Note that it's not possible to do a checkout at a given
|
||||
date; you always do the checkout for the last date where
|
||||
the full-cvs-xxx snapshot was generated.
|
||||
</para>
|
||||
<para>
|
||||
Note also that it is possible to do all this with a direct
|
||||
CVS connection, of course. The full CVS file method is less
|
||||
painful for the WineHQ CVS server and probably a bit faster
|
||||
if you don't have a very good net connection.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
you will have now in the <filename>~/wine</filename>
|
||||
directory an image of the CVS tree, on the client side.
|
||||
Now update this image to the date you want:
|
||||
<screen>
|
||||
cd /home/gerard/wine
|
||||
cvs update -PAd -D "2004-08-23 CDT"
|
||||
</screen>
|
||||
</para>
|
||||
<para>
|
||||
The date format is <literal>YYYY-MM-DD HH:MM:SS</literal>.
|
||||
Using the CST date format ensure that you will be able to
|
||||
extract patches in a way that will be compatible with the
|
||||
wine-cvs archive
|
||||
<ulink url="http://www.winehq.org/hypermail/wine-cvs">
|
||||
http://www.winehq.org/hypermail/wine-cvs</ulink>
|
||||
</para>
|
||||
<para>
|
||||
Many messages will inform you that more recent files have
|
||||
been deleted to set back the client cvs tree to the date
|
||||
you asked, for example:
|
||||
<screen>
|
||||
cvs update: tsx11/ts_xf86dga2.c is no longer in the repository
|
||||
</screen>
|
||||
</para>
|
||||
<para>
|
||||
<command>cvs update</command> is not limited to upgrade to
|
||||
a <emphasis>newer</emphasis> version as I have believed for
|
||||
far too long :-(
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Now proceed as for a normal update:
|
||||
</para>
|
||||
<screen>
|
||||
./configure
|
||||
make depend && make
|
||||
</screen>
|
||||
<para>
|
||||
If any non-programmer reads this, the fastest method to get
|
||||
at the point where the problem occurred is to use a binary
|
||||
search, that is, if the problem occurred in 1999, start at
|
||||
mid-year, then is the problem is already here, back to 1st
|
||||
April, if not, to 1st October, and so on.
|
||||
</para>
|
||||
<para>
|
||||
If you have lot of hard disk free space (a full compile currently
|
||||
takes 400 Mb), copy the oldest known working version before
|
||||
updating it, it will save time if you need to go back. (it's
|
||||
better to <command>make distclean</command> before going back in
|
||||
time, so you have to make everything if you don't backup the older
|
||||
version)
|
||||
</para>
|
||||
<para>
|
||||
When you have found the day where the problem happened, continue
|
||||
the search using the wine-cvs archive (sorted by date) and a
|
||||
more precise cvs update including hour, minute, second :
|
||||
<screen>
|
||||
cvs update -PAd -D "2004-08-23 15:17:25 CDT"
|
||||
</screen>
|
||||
This will allow you to find easily the exact patch that did it.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
If you find the patch that is the cause of the problem, you have
|
||||
almost won; report about it to
|
||||
<ulink url="http://bugs.winehq.org/">Wine Bugzilla</ulink>
|
||||
or subscribe to wine-devel and post it there. There is a chance
|
||||
that the author
|
||||
will jump in to suggest a fix; or there is always the possibility
|
||||
to look hard at the patch until it is coerced to reveal where is
|
||||
the bug :-)
|
||||
</para>
|
||||
</listitem>
|
||||
</orderedlist>
|
||||
</chapter>
|
||||
|
||||
<!-- Keep this comment at the end of the file
|
||||
Local variables:
|
||||
mode: sgml
|
||||
sgml-parent-document:("wine-devel.sgml" "set" "book" "part" "chapter" "")
|
||||
End:
|
||||
-->
|
@ -1,66 +0,0 @@
|
||||
<chapter id="i18n">
|
||||
<title>Internationalization</title>
|
||||
|
||||
<sect1 id="adding-languages">
|
||||
<title>Adding New Languages</title>
|
||||
|
||||
<para>
|
||||
This file documents the necessary procedure for adding a new
|
||||
language to the list of languages that Wine can display system
|
||||
menus and forms in. Adding new translations is not hard as
|
||||
it requires no programming knowledge or special skills.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Language dependent resources reside in files
|
||||
named <filename>somefile_Xx.rc</filename> or
|
||||
<filename>Xx.rc</filename>, where <literal>Xx</literal>
|
||||
is your language abbreviation (look for it in
|
||||
<filename>include/winnls.h</filename>). These are included
|
||||
in a master file named <filename>somefile.rc</filename> or
|
||||
<filename>rsrc.rc</filename>, located in the same
|
||||
directory as the language files.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To add a new language to one of these resources you
|
||||
need to make a copy of the English resource (located
|
||||
in the <filename>somefile_En.rc</filename> file) over to
|
||||
your <filename>somefile_Xx.rc</filename> file, include this
|
||||
file in the master <filename>somefile.rc</filename> file,
|
||||
and edit the new file to translate the English text.
|
||||
You may also need to rearrange some of the controls
|
||||
to better fit the newly translated strings. Test your changes
|
||||
to make sure they properly layout on the screen.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
In menus, the character "&" means that the next
|
||||
character will be highlighted and that pressing that
|
||||
letter will select the item. You should place these
|
||||
"&" characters suitably for your language, not just
|
||||
copy the positions from English. In particular,
|
||||
items within one menu should have different highlighted
|
||||
letters.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To get a list of the files that need translating,
|
||||
run the following command in the root of your Wine tree:
|
||||
<command>find -name "*En.rc"</command>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
When adding a new language, also make sure the parameters
|
||||
defined in <filename>./dlls/kernel/nls/*.nls</filename>
|
||||
fit your local habits and language.
|
||||
</para>
|
||||
</sect1>
|
||||
</chapter>
|
||||
|
||||
<!-- Keep this comment at the end of the file
|
||||
Local variables:
|
||||
mode: sgml
|
||||
sgml-parent-document:("wine-devel.sgml" "set" "book" "part" "chapter" "")
|
||||
End:
|
||||
-->
|
@ -1,608 +0,0 @@
|
||||
<chapter id="implementation">
|
||||
<title>Low-level Implementation</title>
|
||||
<para>Details of Wine's Low-level Implementation...</para>
|
||||
|
||||
<sect1 id="config-keyboard">
|
||||
<title>Keyboard</title>
|
||||
|
||||
<para>
|
||||
Wine now needs to know about your keyboard layout. This
|
||||
requirement comes from a need from many apps to have the
|
||||
correct scancodes available, since they read these directly,
|
||||
instead of just taking the characters returned by the X
|
||||
server. This means that Wine now needs to have a mapping from
|
||||
X keys to the scancodes these programs expect.
|
||||
</para>
|
||||
<para>
|
||||
On startup, Wine will try to recognize the active X layout by
|
||||
seeing if it matches any of the defined tables. If it does,
|
||||
everything is alright. If not, you need to define it.
|
||||
</para>
|
||||
<para>
|
||||
To do this, open the file
|
||||
<filename>dlls/x11drv/keyboard.c</filename> and take a look
|
||||
at the existing tables. Make a backup copy of it, especially
|
||||
if you don't use CVS.
|
||||
</para>
|
||||
<para>
|
||||
What you really would need to do, is find out which scancode
|
||||
each key needs to generate. Find it in the
|
||||
<function>main_key_scan</function> table, which looks like
|
||||
this:
|
||||
</para>
|
||||
<programlisting>
|
||||
static const int main_key_scan[MAIN_LEN] =
|
||||
{
|
||||
/* this is my (102-key) keyboard layout, sorry if it doesn't quite match yours */
|
||||
0x29,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,
|
||||
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,
|
||||
0x1E,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x2B,
|
||||
0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33,0x34,0x35,
|
||||
0x56 /* the 102nd key (actually to the right of l-shift) */
|
||||
};
|
||||
</programlisting>
|
||||
<para>
|
||||
Next, assign each scancode the characters imprinted on the
|
||||
keycaps. This was done (sort of) for the US 101-key keyboard,
|
||||
which you can find near the top in
|
||||
<filename>keyboard.c</filename>. It also shows that if there
|
||||
is no 102nd key, you can skip that.
|
||||
</para>
|
||||
<para>
|
||||
However, for most international 102-key keyboards, we have
|
||||
done it easy for you. The scancode layout for these already
|
||||
pretty much matches the physical layout in the
|
||||
<function>main_key_scan</function>, so all you need to do is
|
||||
to go through all the keys that generate characters on your
|
||||
main keyboard (except spacebar), and stuff those into an
|
||||
appropriate table. The only exception is that the 102nd key,
|
||||
which is usually to the left of the first key of the last line
|
||||
(usually <keycap>Z</keycap>), must be placed on a separate
|
||||
line after the last line.
|
||||
</para>
|
||||
<para>
|
||||
For example, my Norwegian keyboard looks like this
|
||||
</para>
|
||||
<screen>
|
||||
§ ! " # ¤ % & / ( ) = ? ` Back-
|
||||
| 1 2@ 3£ 4$ 5 6 7{ 8[ 9] 0} + \´ space
|
||||
|
||||
Tab Q W E R T Y U I O P Å ^
|
||||
¨~
|
||||
Enter
|
||||
Caps A S D F G H J K L Ø Æ *
|
||||
Lock '
|
||||
|
||||
Sh- > Z X C V B N M ; : _ Shift
|
||||
ift < , . -
|
||||
|
||||
Ctrl Alt Spacebar AltGr Ctrl
|
||||
</screen>
|
||||
<para>
|
||||
Note the 102nd key, which is the <keycap><></keycap> key, to
|
||||
the left of <keycap>Z</keycap>. The character to the right of
|
||||
the main character is the character generated by
|
||||
<keycap>AltGr</keycap>.
|
||||
</para>
|
||||
<para>
|
||||
This keyboard is defined as follows:
|
||||
</para>
|
||||
<programlisting>
|
||||
static const char main_key_NO[MAIN_LEN][4] =
|
||||
{
|
||||
"|§","1!","2\"@","3#£","4¤$","5%","6&","7/{","8([","9)]","0=}","+?","\\´",
|
||||
"qQ","wW","eE","rR","tT","yY","uU","iI","oO","pP","åÅ","¨^~",
|
||||
"aA","sS","dD","fF","gG","hH","jJ","kK","lL","øØ","æÆ","'*",
|
||||
"zZ","xX","cC","vV","bB","nN","mM",",;",".:","-_",
|
||||
"<>"
|
||||
};
|
||||
</programlisting>
|
||||
<para>
|
||||
Except that " and \ needs to be quoted with a backslash, and
|
||||
that the 102nd key is on a separate line, it's pretty
|
||||
straightforward.
|
||||
</para>
|
||||
<para>
|
||||
After you have written such a table, you need to add it to the
|
||||
<function>main_key_tab[]</function> layout index table. This
|
||||
will look like this:
|
||||
</para>
|
||||
<programlisting>
|
||||
static struct {
|
||||
WORD lang, ansi_codepage, oem_codepage;
|
||||
const char (*key)[MAIN_LEN][4];
|
||||
} main_key_tab[]={
|
||||
...
|
||||
...
|
||||
{MAKELANGID(LANG_NORWEGIAN,SUBLANG_DEFAULT), 1252, 865, &main_key_NO},
|
||||
...
|
||||
</programlisting>
|
||||
<para>
|
||||
After you have added your table, recompile Wine and test that
|
||||
it works. If it fails to detect your table, try running
|
||||
</para>
|
||||
<screen>
|
||||
WINEDEBUG=+key,+keyboard wine > key.log 2>&1
|
||||
</screen>
|
||||
<para>
|
||||
and look in the resulting <filename>key.log</filename> file to
|
||||
find the error messages it gives for your layout.
|
||||
</para>
|
||||
<para>
|
||||
Note that the <constant>LANG_*</constant> and
|
||||
<constant>SUBLANG_*</constant> definitions are in
|
||||
<filename>include/winnls.h</filename>, which you might need to
|
||||
know to find out which numbers your language is assigned, and
|
||||
find it in the WINEDEBUG output. The numbers will be
|
||||
<literal>(SUBLANG * 0x400 + LANG)</literal>, so, for example
|
||||
the combination <literal>LANG_NORWEGIAN (0x14)</literal> and
|
||||
<literal>SUBLANG_DEFAULT (0x1)</literal> will be (in hex)
|
||||
<literal>14 + 1*400 = 414</literal>, so since I'm Norwegian, I
|
||||
could look for <literal>0414</literal> in the WINEDEBUG output
|
||||
to find out why my keyboard won't detect.
|
||||
</para>
|
||||
<para>
|
||||
Once it works, submit it to the Wine project. If you use CVS,
|
||||
you will just have to do
|
||||
</para>
|
||||
<screen>
|
||||
cvs -z3 diff -u dlls/x11drv/keyboard.c > layout.diff
|
||||
</screen>
|
||||
<para>
|
||||
from your main Wine directory, then submit
|
||||
<filename>layout.diff</filename> to
|
||||
<email>wine-patches@winehq.org</email> along with a brief note
|
||||
of what it is.
|
||||
</para>
|
||||
<para>
|
||||
If you don't use CVS, you need to do
|
||||
</para>
|
||||
<screen>
|
||||
diff -u the_backup_file_you_made dlls/x11drv/keyboard.c > layout.diff
|
||||
</screen>
|
||||
<para>
|
||||
and submit it as explained above.
|
||||
</para>
|
||||
<para>
|
||||
If you did it right, it will be included in the next Wine
|
||||
release, and all the troublesome programs (especially
|
||||
remote-control programs) and games that use scancodes will
|
||||
be happily using your keyboard layout, and you won't get those
|
||||
annoying fixme messages either.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
|
||||
<sect1 id="undoc-func">
|
||||
<title>Undocumented APIs</title>
|
||||
|
||||
<para>
|
||||
Some background: On the i386 class of machines, stack entries are
|
||||
usually dword (4 bytes) in size, little-endian. The stack grows
|
||||
downward in memory. The stack pointer, maintained in the
|
||||
<literal>esp</literal> register, points to the last valid entry;
|
||||
thus, the operation of pushing a value onto the stack involves
|
||||
decrementing <literal>esp</literal> and then moving the value into
|
||||
the memory pointed to by <literal>esp</literal>
|
||||
(i.e., <literal>push p</literal> in assembly resembles
|
||||
<literal>*(--esp) = p;</literal> in C). Removing (popping)
|
||||
values off the stack is the reverse (i.e., <literal>pop p</literal>
|
||||
corresponds to <literal>p = *(esp++);</literal> in C).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
In the <literal>stdcall</literal> calling convention, arguments are
|
||||
pushed onto the stack right-to-left. For example, the C call
|
||||
<function>myfunction(40, 20, 70, 30);</function> is expressed in
|
||||
Intel assembly as:
|
||||
<screen>
|
||||
push 30
|
||||
push 70
|
||||
push 20
|
||||
push 40
|
||||
call myfunction
|
||||
</screen>
|
||||
The called function is responsible for removing the arguments
|
||||
off the stack. Thus, before the call to myfunction, the
|
||||
stack would look like:
|
||||
<screen>
|
||||
[local variable or temporary]
|
||||
[local variable or temporary]
|
||||
30
|
||||
70
|
||||
20
|
||||
esp -> 40
|
||||
</screen>
|
||||
After the call returns, it should look like:
|
||||
<screen>
|
||||
[local variable or temporary]
|
||||
esp -> [local variable or temporary]
|
||||
</screen>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To restore the stack to this state, the called function must know how
|
||||
many arguments to remove (which is the number of arguments it takes).
|
||||
This is a problem if the function is undocumented.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
One way to attempt to document the number of arguments each function
|
||||
takes is to create a wrapper around that function that detects the
|
||||
stack offset. Essentially, each wrapper assumes that the function will
|
||||
take a large number of arguments. The wrapper copies each of these
|
||||
arguments into its stack, calls the actual function, and then calculates
|
||||
the number of arguments by checking esp before and after the call.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The main problem with this scheme is that the function must actually
|
||||
be called from another program. Many of these functions are seldom
|
||||
used. An attempt was made to aggressively query each function in a
|
||||
given library (<filename>ntdll.dll</filename>) by passing 64 arguments,
|
||||
all 0, to each function. Unfortunately, Windows NT quickly goes to a
|
||||
blue screen of death, even if the program is run from a
|
||||
non-administrator account.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Another method that has been much more successful is to attempt to
|
||||
figure out how many arguments each function is removing from the
|
||||
stack. This instruction, <literal>ret hhll</literal> (where
|
||||
<symbol>hhll</symbol> is the number of bytes to remove, i.e. the
|
||||
number of arguments times 4), contains the bytes
|
||||
<literal>0xc2 ll hh</literal> in memory. It is a reasonable
|
||||
assumption that few, if any, functions take more than 16 arguments;
|
||||
therefore, simply searching for
|
||||
<literal>hh == 0 && ll < 0x40</literal> starting from the
|
||||
address of a function yields the correct number of arguments most
|
||||
of the time.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Of course, this is not without errors. <literal>ret 00ll</literal>
|
||||
is not the only instruction that can have the byte sequence
|
||||
<literal>0xc2 ll 0x0</literal>; for example,
|
||||
<literal>push 0x000040c2</literal> has the byte sequence
|
||||
<literal>0x68 0xc2 0x40 0x0 0x0</literal>, which matches
|
||||
the above. Properly, the utility should look for this sequence
|
||||
only on an instruction boundary; unfortunately, finding
|
||||
instruction boundaries on an i386 requires implementing a full
|
||||
disassembler -- quite a daunting task. Besides, the probability
|
||||
of having such a byte sequence that is not the actual return
|
||||
instruction is fairly low.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Much more troublesome is the non-linear flow of a function. For
|
||||
example, consider the following two functions:
|
||||
<screen>
|
||||
somefunction1:
|
||||
jmp somefunction1_impl
|
||||
|
||||
somefunction2:
|
||||
ret 0004
|
||||
|
||||
somefunction1_impl:
|
||||
ret 0008
|
||||
</screen>
|
||||
In this case, we would incorrectly detect both
|
||||
<function>somefunction1</function> and
|
||||
<function>somefunction2</function> as taking only a single
|
||||
argument, whereas <function>somefunction1</function> really
|
||||
takes two arguments.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
With these limitations in mind, it is possible to implement more stubs
|
||||
in Wine and, eventually, the functions themselves.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="accel-impl">
|
||||
<title>Accelerators</title>
|
||||
|
||||
<para>
|
||||
There are <emphasis>three</emphasis> differently sized
|
||||
accelerator structures exposed to the user:
|
||||
</para>
|
||||
<orderedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
Accelerators in NE resources. This is also the internal
|
||||
layout of the global handle <type>HACCEL</type> (16 and
|
||||
32) in Windows 95 and Wine. Exposed to the user as Win16
|
||||
global handles <type>HACCEL16</type> and
|
||||
<type>HACCEL32</type> by the Win16/Win32 API.
|
||||
These are 5 bytes long, with no padding:
|
||||
<programlisting>
|
||||
BYTE fVirt;
|
||||
WORD key;
|
||||
WORD cmd;
|
||||
</programlisting>
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Accelerators in PE resources. They are exposed to the user
|
||||
only by direct accessing PE resources.
|
||||
These have a size of 8 bytes:
|
||||
</para>
|
||||
<programlisting>
|
||||
BYTE fVirt;
|
||||
BYTE pad0;
|
||||
WORD key;
|
||||
WORD cmd;
|
||||
WORD pad1;
|
||||
</programlisting>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Accelerators in the Win32 API. These are exposed to the
|
||||
user by the <function>CopyAcceleratorTable</function>
|
||||
and <function>CreateAcceleratorTable</function> functions
|
||||
in the Win32 API.
|
||||
These have a size of 6 bytes:
|
||||
</para>
|
||||
<programlisting>
|
||||
BYTE fVirt;
|
||||
BYTE pad0;
|
||||
WORD key;
|
||||
WORD cmd;
|
||||
</programlisting>
|
||||
</listitem>
|
||||
</orderedlist>
|
||||
|
||||
<para>
|
||||
Why two types of accelerators in the Win32 API? We can only
|
||||
guess, but my best bet is that the Win32 resource compiler
|
||||
can/does not handle struct packing. Win32 <type>ACCEL</type>
|
||||
is defined using <function>#pragma(2)</function> for the
|
||||
compiler but without any packing for RC, so it will assume
|
||||
<function>#pragma(4)</function>.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="hardware-trace">
|
||||
<title>Doing A Hardware Trace</title>
|
||||
|
||||
<para>
|
||||
The primary reason to do this is to reverse engineer a
|
||||
hardware device for which you don't have documentation, but
|
||||
can get to work under Wine.
|
||||
</para>
|
||||
<para>
|
||||
This lot is aimed at parallel port devices, and in particular
|
||||
parallel port scanners which are now so cheap they are
|
||||
virtually being given away. The problem is that few
|
||||
manufactures will release any programming information which
|
||||
prevents drivers being written for Sane, and the traditional
|
||||
technique of using DOSemu to produce the traces does not work
|
||||
as the scanners invariably only have drivers for Windows.
|
||||
</para>
|
||||
<para>
|
||||
Presuming that you have compiled and installed wine the first
|
||||
thing to do is is to enable direct hardware access to your
|
||||
parallel port. To do this edit <filename>config</filename>
|
||||
(usually in <filename>~/.wine/</filename>) and in the
|
||||
ports section add the following two lines
|
||||
</para>
|
||||
<programlisting>
|
||||
read=0x378,0x379,0x37a,0x37c,0x77a
|
||||
write=0x378,x379,0x37a,0x37c,0x77a
|
||||
</programlisting>
|
||||
<para>
|
||||
This adds the necessary access required for SPP/PS2/EPP/ECP
|
||||
parallel port on LPT1. You will need to adjust these number
|
||||
accordingly if your parallel port is on LPT2 or LPT0.
|
||||
</para>
|
||||
<para>
|
||||
When starting wine use the following command line, where
|
||||
<literal>XXXX</literal> is the program you need to run in
|
||||
order to access your scanner, and <literal>YYYY</literal> is
|
||||
the file your trace will be stored in:
|
||||
</para>
|
||||
<programlisting>
|
||||
WINEDEBUG=+io wine XXXX 2> >(sed 's/^[^:]*:io:[^ ]* //' > YYYY)
|
||||
</programlisting>
|
||||
<para>
|
||||
You will need large amounts of hard disk space (read hundreds
|
||||
of megabytes if you do a full page scan), and for reasonable
|
||||
performance a really fast processor and lots of RAM.
|
||||
</para>
|
||||
<para>
|
||||
You will need to postprocess the output into a more manageable
|
||||
format, using the <command>shrink</command> program. First
|
||||
you need to compile the source (which is located at the end of
|
||||
this section):
|
||||
<programlisting>
|
||||
cc shrink.c -o shrink
|
||||
</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
Use the <command>shrink</command> program to reduce the
|
||||
physical size of the raw log as follows:
|
||||
</para>
|
||||
<programlisting>
|
||||
cat log | shrink > log2
|
||||
</programlisting>
|
||||
<para>
|
||||
The trace has the basic form of
|
||||
</para>
|
||||
<programlisting>
|
||||
XXXX > YY @ ZZZZ:ZZZZ
|
||||
</programlisting>
|
||||
<para>
|
||||
where <literal>XXXX</literal> is the port in hexadecimal being
|
||||
accessed, <literal>YY</literal> is the data written (or read)
|
||||
from the port, and <literal>ZZZZ:ZZZZ</literal> is the address
|
||||
in memory of the instruction that accessed the port. The
|
||||
direction of the arrow indicates whether the data was written
|
||||
or read from the port.
|
||||
</para>
|
||||
<programlisting>
|
||||
> data was written to the port
|
||||
< data was read from the port
|
||||
</programlisting>
|
||||
<para>
|
||||
My basic tip for interpreting these logs is to pay close
|
||||
attention to the addresses of the IO instructions. Their
|
||||
grouping and sometimes proximity should reveal the presence of
|
||||
subroutines in the driver. By studying the different versions
|
||||
you should be able to work them out. For example consider the
|
||||
following section of trace from my UMAX Astra 600P
|
||||
</para>
|
||||
<programlisting>
|
||||
0x378 > 55 @ 0297:01ec
|
||||
0x37a > 05 @ 0297:01f5
|
||||
0x379 < 8f @ 0297:01fa
|
||||
0x37a > 04 @ 0297:0211
|
||||
0x378 > aa @ 0297:01ec
|
||||
0x37a > 05 @ 0297:01f5
|
||||
0x379 < 8f @ 0297:01fa
|
||||
0x37a > 04 @ 0297:0211
|
||||
0x378 > 00 @ 0297:01ec
|
||||
0x37a > 05 @ 0297:01f5
|
||||
0x379 < 8f @ 0297:01fa
|
||||
0x37a > 04 @ 0297:0211
|
||||
0x378 > 00 @ 0297:01ec
|
||||
0x37a > 05 @ 0297:01f5
|
||||
0x379 < 8f @ 0297:01fa
|
||||
0x37a > 04 @ 0297:0211
|
||||
0x378 > 00 @ 0297:01ec
|
||||
0x37a > 05 @ 0297:01f5
|
||||
0x379 < 8f @ 0297:01fa
|
||||
0x37a > 04 @ 0297:0211
|
||||
0x378 > 00 @ 0297:01ec
|
||||
0x37a > 05 @ 0297:01f5
|
||||
0x379 < 8f @ 0297:01fa
|
||||
0x37a > 04 @ 0297:0211
|
||||
</programlisting>
|
||||
<para>
|
||||
As you can see there is a repeating structure starting at
|
||||
address <literal>0297:01ec</literal> that consists of four io
|
||||
accesses on the parallel port. Looking at it the first io
|
||||
access writes a changing byte to the data port the second
|
||||
always writes the byte <literal>0x05</literal> to the control
|
||||
port, then a value which always seems to
|
||||
<literal>0x8f</literal> is read from the status port at which
|
||||
point a byte <literal>0x04</literal> is written to the control
|
||||
port. By studying this and other sections of the trace we can
|
||||
write a C routine that emulates this, shown below with some
|
||||
macros to make reading/writing on the parallel port easier to
|
||||
read.
|
||||
</para>
|
||||
<programlisting>
|
||||
#define r_dtr(x) inb(x)
|
||||
#define r_str(x) inb(x+1)
|
||||
#define r_ctr(x) inb(x+2)
|
||||
#define w_dtr(x,y) outb(y, x)
|
||||
#define w_str(x,y) outb(y, x+1)
|
||||
#define w_ctr(x,y) outb(y, x+2)
|
||||
|
||||
/* Seems to be sending a command byte to the scanner */
|
||||
int udpp_put(int udpp_base, unsigned char command)
|
||||
{
|
||||
int loop, value;
|
||||
|
||||
w_dtr(udpp_base, command);
|
||||
w_ctr(udpp_base, 0x05);
|
||||
|
||||
for (loop=0; loop < 10; loop++)
|
||||
if ((value = r_str(udpp_base)) & 0x80)
|
||||
{
|
||||
w_ctr(udpp_base, 0x04);
|
||||
return value & 0xf8;
|
||||
}
|
||||
|
||||
return (value & 0xf8) | 0x01;
|
||||
}
|
||||
</programlisting>
|
||||
<para>
|
||||
For the UMAX Astra 600P only seven such routines exist (well
|
||||
14 really, seven for SPP and seven for EPP). Whether you
|
||||
choose to disassemble the driver at this point to verify the
|
||||
routines is your own choice. If you do, the address from the
|
||||
trace should help in locating them in the disassembly.
|
||||
</para>
|
||||
<para>
|
||||
You will probably then find it useful to write a script/perl/C
|
||||
program to analyse the logfile and decode them futher as this
|
||||
can reveal higher level grouping of the low level routines.
|
||||
For example from the logs from my UMAX Astra 600P when decoded
|
||||
further reveal (this is a small snippet)
|
||||
</para>
|
||||
<programlisting>
|
||||
start:
|
||||
put: 55 8f
|
||||
put: aa 8f
|
||||
put: 00 8f
|
||||
put: 00 8f
|
||||
put: 00 8f
|
||||
put: c2 8f
|
||||
wait: ff
|
||||
get: af,87
|
||||
wait: ff
|
||||
get: af,87
|
||||
end: cc
|
||||
start:
|
||||
put: 55 8f
|
||||
put: aa 8f
|
||||
put: 00 8f
|
||||
put: 03 8f
|
||||
put: 05 8f
|
||||
put: 84 8f
|
||||
wait: ff
|
||||
</programlisting>
|
||||
<para>
|
||||
From this it is easy to see that <varname>put</varname>
|
||||
routine is often grouped together in five successive calls
|
||||
sending information to the scanner. Once these are understood
|
||||
it should be possible to process the logs further to show the
|
||||
higher level routines in an easy to see format. Once the
|
||||
highest level format that you can derive from this process is
|
||||
understood, you then need to produce a series of scans varying
|
||||
only one parameter between them, so you can discover how to
|
||||
set the various parameters for the scanner.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The following is the <filename>shrink.c</filename> program:
|
||||
<programlisting>
|
||||
/* Copyright David Campbell <campbell@torque.net> */
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
int main (void)
|
||||
{
|
||||
char buff[256], lastline[256] = "";
|
||||
int count = 0;
|
||||
|
||||
while (!feof (stdin))
|
||||
{
|
||||
fgets (buff, sizeof (buff), stdin);
|
||||
if (strcmp (buff, lastline))
|
||||
{
|
||||
if (count > 1)
|
||||
printf ("# Last line repeated %i times #\n", count);
|
||||
printf ("%s", buff);
|
||||
strcpy (lastline, buff);
|
||||
count = 1;
|
||||
}
|
||||
else count++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
</programlisting>
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
||||
|
||||
<!-- Keep this comment at the end of the file
|
||||
Local variables:
|
||||
mode: sgml
|
||||
sgml-parent-document:("wine-devel.sgml" "set" "book" "part" "chapter" "")
|
||||
End:
|
||||
-->
|
@ -1,219 +0,0 @@
|
||||
<chapter id="porting">
|
||||
<title>Porting Wine to new Platforms</title>
|
||||
<para>
|
||||
This document provides a few tips on porting Wine to your
|
||||
favorite (UNIX-based) operating system.
|
||||
</para>
|
||||
|
||||
<sect1 id="wine-porting">
|
||||
<title>Porting Wine to new Platforms</title>
|
||||
<sect2>
|
||||
<title>Why <symbol>#ifdef MyOS</symbol> is probably a mistake.</title>
|
||||
|
||||
<para>
|
||||
Operating systems change. Maybe yours doesn't have the
|
||||
<filename>foo.h</filename> header, but maybe a future
|
||||
version will have it. If you want to <symbol>#include
|
||||
<foo.h></symbol>, it doesn't matter what operating
|
||||
system you are using; it only matters whether
|
||||
<filename>foo.h</filename> is there.
|
||||
</para>
|
||||
<para>
|
||||
Furthermore, operating systems change names or "fork" into
|
||||
several ones. An <symbol>#ifdef MyOs</symbol> will break
|
||||
over time.
|
||||
</para>
|
||||
<para>
|
||||
If you use the feature of <command>autoconf</command> -- the
|
||||
Gnu auto-configuration utility -- wisely, you will help
|
||||
future porters automatically because your changes will test
|
||||
for <emphasis>features</emphasis>, not names of operating
|
||||
systems. A feature can be many things:
|
||||
</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
existence of a header file
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
existence of a library function
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
existence of libraries
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
bugs in header files, library functions, the compiler, ...
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
<para>
|
||||
You will need Gnu Autoconf, which you can get from your
|
||||
friendly Gnu mirror. This program takes Wine's
|
||||
<filename>configure.ac</filename> file and produces a
|
||||
<filename>configure</filename> shell script that users use
|
||||
to configure Wine to their system.
|
||||
</para>
|
||||
<para>
|
||||
There <emphasis>are</emphasis> exceptions to the "avoid
|
||||
<symbol>#ifdef MyOS</symbol>" rule. Wine, for example, needs
|
||||
the internals of the signal stack -- that cannot easily be
|
||||
described in terms of features. Moreover, you can not use
|
||||
<filename>autoconf</filename>'s <symbol>HAVE_*</symbol>
|
||||
symbols in Wine's headers, as these may be used by Winelib
|
||||
users who may not be using a <filename>configure</filename>
|
||||
script.
|
||||
</para>
|
||||
<para>
|
||||
Let's now turn to specific porting problems and how to solve
|
||||
them.
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>MyOS doesn't have the <filename>foo.h</filename> header!</title>
|
||||
|
||||
<para>
|
||||
This first step is to make <command>autoconf</command> check
|
||||
for this header. In <filename>configure.in</filename> you
|
||||
add a segment like this in the section that checks for
|
||||
header files (search for "header files"):
|
||||
</para>
|
||||
<programlisting>
|
||||
AC_CHECK_HEADER(foo.h, AC_DEFINE(HAVE_FOO_H))
|
||||
</programlisting>
|
||||
<para>
|
||||
If your operating system supports a header file with the
|
||||
same contents but a different name, say
|
||||
<filename>bar.h</filename>, add a check for that also.
|
||||
</para>
|
||||
<para>
|
||||
Now you can change
|
||||
</para>
|
||||
<programlisting>
|
||||
#include <foo.h>
|
||||
</programlisting>
|
||||
<para>
|
||||
to
|
||||
</para>
|
||||
<programlisting>
|
||||
#ifdef HAVE_FOO_H
|
||||
#include <foo.h>
|
||||
#elif defined (HAVE_BAR_H)
|
||||
#include <bar.h>
|
||||
#endif
|
||||
</programlisting>
|
||||
<para>
|
||||
If your system doesn't have a corresponding header file even
|
||||
though it has the library functions being used, you might
|
||||
have to add an <symbol>#else</symbol> section to the
|
||||
conditional. Avoid this if you can.
|
||||
</para>
|
||||
<para>
|
||||
You will also need to add <symbol>#undef HAVE_FOO_H</symbol>
|
||||
(etc.) to <filename>include/config.h.in</filename>
|
||||
</para>
|
||||
<para>
|
||||
Finish up with <command>make configure</command> and
|
||||
<command>./configure</command>.
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>MyOS doesn't have the <function>bar</function> function!</title>
|
||||
|
||||
<para>
|
||||
A typical example of this is the
|
||||
<function>memmove</function> function. To solve this
|
||||
problem you would add <function>memmove</function> to the
|
||||
list of functions that <command>autoconf</command> checks
|
||||
for. In <filename>configure.in</filename> you search for
|
||||
<function>AC_CHECK_FUNCS</function> and add
|
||||
<function>memmove</function>. (You will notice that someone
|
||||
already did this for this particular function.)
|
||||
</para>
|
||||
<para>
|
||||
Secondly, you will also need to add <symbol>#undef
|
||||
HAVE_BAR</symbol> to
|
||||
<filename>include/config.h.in</filename>
|
||||
</para>
|
||||
<para>
|
||||
The next step depends on the nature of the missing function.
|
||||
</para>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>Case 1:</term>
|
||||
<listitem>
|
||||
<para>
|
||||
It's easy to write a complete implementation of the
|
||||
function. (<function>memmove</function> belongs to
|
||||
this case.)
|
||||
</para>
|
||||
<para>
|
||||
You add your implementation in
|
||||
<filename>misc/port.c</filename> surrounded by
|
||||
<symbol>#ifndef HAVE_MEMMOVE</symbol> and
|
||||
<symbol>#endif</symbol>.
|
||||
</para>
|
||||
<para>
|
||||
You might have to add a prototype for your function.
|
||||
If so, <filename>include/miscemu.h</filename> might be the place. Don't
|
||||
forget to protect that definition by <symbol>#ifndef
|
||||
HAVE_MEMMOVE</symbol> and <symbol>#endif</symbol> also!
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>Case 2:</term>
|
||||
<listitem>
|
||||
<para>
|
||||
A general implementation is hard, but Wine is only
|
||||
using a special case.
|
||||
</para>
|
||||
<para>
|
||||
An example is the various <function>wait</function>
|
||||
calls used in <function>SIGNAL_child</function> from
|
||||
<filename>loader/signal.c</filename>. Here we have a
|
||||
multi-branch case on features:
|
||||
</para>
|
||||
<programlisting>
|
||||
#ifdef HAVE_THIS
|
||||
...
|
||||
#elif defined (HAVE_THAT)
|
||||
...
|
||||
#elif defined (HAVE_SOMETHING_ELSE)
|
||||
...
|
||||
#endif
|
||||
</programlisting>
|
||||
<para>
|
||||
Note that this is very different from testing on
|
||||
operating systems. If a new version of your operating
|
||||
systems comes out and adds a new function, this code
|
||||
will magically start using it.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
<para>
|
||||
Finish up with <command>make configure</command> and
|
||||
<command>./configure</command>.
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
||||
|
||||
<!-- Keep this comment at the end of the file
|
||||
Local variables:
|
||||
mode: sgml
|
||||
sgml-parent-document:("wine-devel.sgml" "set" "book" "part" "chapter" "")
|
||||
End:
|
||||
-->
|
@ -1,24 +1,20 @@
|
||||
<!doctype book PUBLIC "-//OASIS//DTD DocBook V3.1//EN" [
|
||||
|
||||
<!entity debugger SYSTEM "debugger.sgml">
|
||||
<!entity documentation SYSTEM "documentation.sgml">
|
||||
<!entity patches SYSTEM "patches.sgml">
|
||||
<!entity debugging SYSTEM "debugging.sgml">
|
||||
<!entity otherdebug SYSTEM "winedev-otherdebug.sgml">
|
||||
<!entity codingpractice SYSTEM "winedev-coding.sgml">
|
||||
<!entity testing SYSTEM "testing.sgml">
|
||||
<!entity i18n SYSTEM "i18n.sgml">
|
||||
<!entity documentation SYSTEM "documentation.sgml">
|
||||
|
||||
<!entity architecture SYSTEM "architecture.sgml">
|
||||
<!entity debugging SYSTEM "debugging.sgml">
|
||||
<!entity kernel SYSTEM "winedev-kernel.sgml">
|
||||
<!entity graphical SYSTEM "winedev-graphical.sgml">
|
||||
<!entity windowing SYSTEM "winedev-windowing.sgml">
|
||||
<!entity ole SYSTEM "ole.sgml">
|
||||
<!entity opengl SYSTEM "opengl.sgml">
|
||||
<!entity ddraw SYSTEM "ddraw.sgml">
|
||||
<!entity multimedia SYSTEM "multimedia.sgml">
|
||||
<!entity threading SYSTEM "threading.sgml">
|
||||
<!entity address-space SYSTEM "address-space.sgml">
|
||||
|
||||
<!entity implementation SYSTEM "implementation.sgml">
|
||||
<!entity porting SYSTEM "porting.sgml">
|
||||
<!entity consoles SYSTEM "consoles.sgml">
|
||||
<!entity cvs-regression SYSTEM "cvs-regression.sgml">
|
||||
|
||||
]>
|
||||
|
||||
@ -114,31 +110,23 @@
|
||||
<title>Developing Wine</title>
|
||||
|
||||
&debugger;
|
||||
&documentation;
|
||||
&patches;
|
||||
&debugging;
|
||||
&otherdebug;
|
||||
&codingpractice;
|
||||
&testing;
|
||||
&i18n;
|
||||
&documentation;
|
||||
</part>
|
||||
|
||||
<part id="part-two">
|
||||
<title>Wine Architecture</title>
|
||||
|
||||
&architecture;
|
||||
&debugging;
|
||||
&kernel;
|
||||
&graphical;
|
||||
&windowing;
|
||||
&ole;
|
||||
&opengl;
|
||||
&ddraw;
|
||||
&multimedia;
|
||||
&threading;
|
||||
</part>
|
||||
|
||||
<part id="part-three">
|
||||
<title>Advanced Topics</title>
|
||||
&implementation;
|
||||
&porting;
|
||||
&consoles;
|
||||
&address-space;
|
||||
&cvs-regression;
|
||||
</part>
|
||||
|
||||
</book>
|
||||
|
514
documentation/winedev-coding.sgml
Normal file
514
documentation/winedev-coding.sgml
Normal file
@ -0,0 +1,514 @@
|
||||
<chapter id="codingpractice">
|
||||
<title>Coding Practice</title>
|
||||
|
||||
<para>
|
||||
This chapter describes the relevant coding practices in Wine,
|
||||
that you should be aware of before doing any serious development
|
||||
in Wine.
|
||||
</para>
|
||||
<sect1 id="patch-format">
|
||||
<title>Patch Format</title>
|
||||
|
||||
<para>
|
||||
Patches are submitted via email to the Wine patches mailing
|
||||
list, <email>wine-patches@winehq.org</email>. Your patch
|
||||
should include:
|
||||
</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
A meaningful subject (very short description of patch)
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
A long (paragraph) description of what was wrong and what
|
||||
is now better. (recommended)
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
A change log entry (short description of what was
|
||||
changed).
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
The patch in <command>diff -u</command> format
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para></para>
|
||||
|
||||
<para>
|
||||
<command>cvs diff -u</command> works great for the common case
|
||||
where a file is edited. However, if you add or remove a file
|
||||
<command>cvs diff</command> will not report that correctly so
|
||||
make sure you explicitly take care of this rare case.
|
||||
</para>
|
||||
<para>
|
||||
For additions simply include them by appending the
|
||||
<command>diff -u /dev/null /my/new/file</command> output of
|
||||
them to any <command>cvs diff -u</command> output you may
|
||||
have. Alternatively, use <command>diff -Nu olddir/
|
||||
newdir/</command> in case of multiple new files to add.
|
||||
</para>
|
||||
<para>
|
||||
For removals, clearly list the files in the description of the
|
||||
patch.
|
||||
</para>
|
||||
<para>
|
||||
Since wine is constantly changing due to development it is
|
||||
strongly recommended that you use cvs for patches, if you
|
||||
cannot use cvs for some reason, you can submit patches against
|
||||
the latest tarball. To do this make a copy of the files that
|
||||
you will be modifying and <command>diff -u</command> against
|
||||
the old file. I.E.
|
||||
</para>
|
||||
<screen>
|
||||
diff -u file.old file.c > file.txt
|
||||
</screen>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="Style-notes">
|
||||
<title>Some notes about style</title>
|
||||
|
||||
<para>
|
||||
There are a few conventions that about coding style that have
|
||||
been adopted over the years of development. The rational for
|
||||
these <quote>rules</quote> is explained for each one.
|
||||
</para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
No HTML mail, since patches should be in-lined and HTML
|
||||
turns the patch into garbage. Also it is considered bad
|
||||
etiquette as it uglifies the message, and is not viewable
|
||||
by many of the subscribers.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Only one change set per patch. Patches should address only
|
||||
one bug/problem at a time. If a lot of changes need to be
|
||||
made then it is preferred to break it into a series of
|
||||
patches. This makes it easier to find regressions.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Tabs are not forbidden but discouraged. A tab is defined
|
||||
as 8 characters and the usual amount of indentation is 4
|
||||
characters.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
C++ style comments are discouraged since some compilers
|
||||
choke on them.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Commenting out a block of code is usually done by
|
||||
enclosing it in <command>#if 0 ... #endif</command>
|
||||
Statements. For example.
|
||||
</para>
|
||||
<screen>
|
||||
/* note about reason for commenting block */
|
||||
#if 0
|
||||
code
|
||||
code /* comments */
|
||||
code
|
||||
#endif
|
||||
</screen>
|
||||
<para>
|
||||
The reason for using this method is that it does not
|
||||
require that you edit comments that may be inside the
|
||||
block of code.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Patches should be in-lined (if you can configure your
|
||||
email client to not wrap lines), or attached as plain text
|
||||
attachments so they can be read inline. This may mean some
|
||||
more work for you. However it allows others to review your
|
||||
patch easily and decreases the chances of it being
|
||||
overlooked or forgotten.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Code is usually limited to 80 columns. This helps prevent
|
||||
mailers mangling patches by line wrap. Also it generally
|
||||
makes code easier to read.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
If the patch fixes a bug in Bugzilla please provide a link
|
||||
to the bug in the comments of the patch. This will make it
|
||||
easier for the maintainers of Bugzilla.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
<sect2 id="Inline-Attachments-with-OE">
|
||||
<title>Inline attachments with Outlook Express</title>
|
||||
<para>
|
||||
Outlook Express is notorious for mangling
|
||||
attachments. Giving the patch a <filename>.txt</filename>
|
||||
extension and attaching will solve the problem for most
|
||||
mailers including Outlook. Also, there is a way to enable
|
||||
Outlook Express send <filename>.diff</filename>
|
||||
attachments.
|
||||
</para>
|
||||
<para>
|
||||
You need following two things to make it work.
|
||||
</para>
|
||||
<orderedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
Make sure that <filename>.diff</filename> files have
|
||||
\r\n line ends, because if OE detects that there is no
|
||||
\r\n line endings it switches to quoted-printable format
|
||||
attachments.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Using regedit add key "Content Type"
|
||||
with value "text/plain" to the
|
||||
<filename>.diff</filename> extension under
|
||||
HKEY_CLASSES_ROOT (same as for <filename>.txt</filename>
|
||||
extension). This tells OE to use
|
||||
Content-Type: text/plain instead of
|
||||
application/octet-stream.
|
||||
</para>
|
||||
</listitem>
|
||||
</orderedlist>
|
||||
<para>
|
||||
Item #1 is important. After you hit "Send" button, go to
|
||||
"Outbox" and using "Properties" verify the message source to
|
||||
make sure that the mail has correct format. You might want
|
||||
to send several test emails to yourself too.
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2 id="Alexandre-Bottom-Line">
|
||||
<title>Alexandre's Bottom Line</title>
|
||||
<para>
|
||||
<quote>The basic rules are: no attachments, no MIME crap, no
|
||||
line wrapping, a single patch per mail. Basically if I can't
|
||||
do <command>"cat raw_mail | patch -p0"</command> it's in the
|
||||
wrong format.</quote>
|
||||
</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="patch-quality">
|
||||
<title>Quality Assurance</title>
|
||||
|
||||
<para>
|
||||
(Or, "How do I get Alexandre to apply my patch quickly so I
|
||||
can build on it and it will not go stale?")
|
||||
</para>
|
||||
<para>
|
||||
Make sure your patch applies to the current CVS head
|
||||
revisions. If a bunch of patches are committed to CVS that may
|
||||
affect whether your patch will apply cleanly then verify that
|
||||
your patch does apply! <command>cvs update</command> is your
|
||||
friend!
|
||||
</para>
|
||||
<para>
|
||||
Save yourself some embarrassment and run your patched code
|
||||
against more than just your current test example. Experience
|
||||
will tell you how much effort to apply here. If there are
|
||||
any conformance tests for the code you're working on, run them
|
||||
and make sure they still pass after your patch is applied. Running
|
||||
tests can be done by running <command>make test</command>. You may
|
||||
need to run <command>make testclean</command> to undo the results
|
||||
of a previous test run. See the <quote>testing</quote> guide for
|
||||
more details on Wine's conformance tests.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
<sect1 id="porting">
|
||||
<title>Porting Wine to new Platforms</title>
|
||||
<para>
|
||||
This document provides a few tips on porting Wine to your
|
||||
favorite (UNIX-based) operating system.
|
||||
</para>
|
||||
|
||||
<sect2>
|
||||
<title>
|
||||
Why <symbol>#ifdef MyOS</symbol> is probably a mistake.
|
||||
</title>
|
||||
|
||||
<para>
|
||||
Operating systems change. Maybe yours doesn't have the
|
||||
<filename>foo.h</filename> header, but maybe a future
|
||||
version will have it. If you want to <symbol>#include
|
||||
<foo.h></symbol>, it doesn't matter what operating
|
||||
system you are using; it only matters whether
|
||||
<filename>foo.h</filename> is there.
|
||||
</para>
|
||||
<para>
|
||||
Furthermore, operating systems change names or "fork" into
|
||||
several ones. An <symbol>#ifdef MyOs</symbol> will break
|
||||
over time.
|
||||
</para>
|
||||
<para>
|
||||
If you use the feature of <command>autoconf</command> -- the
|
||||
Gnu auto-configuration utility -- wisely, you will help
|
||||
future porters automatically because your changes will test
|
||||
for <emphasis>features</emphasis>, not names of operating
|
||||
systems. A feature can be many things:
|
||||
</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
existence of a header file
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
existence of a library function
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
existence of libraries
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
bugs in header files, library functions, the compiler, ...
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
<para>
|
||||
You will need Gnu Autoconf, which you can get from your
|
||||
friendly Gnu mirror. This program takes Wine's
|
||||
<filename>configure.ac</filename> file and produces a
|
||||
<filename>configure</filename> shell script that users use
|
||||
to configure Wine to their system.
|
||||
</para>
|
||||
<para>
|
||||
There <emphasis>are</emphasis> exceptions to the "avoid
|
||||
<symbol>#ifdef MyOS</symbol>" rule. Wine, for example, needs
|
||||
the internals of the signal stack -- that cannot easily be
|
||||
described in terms of features. Moreover, you can not use
|
||||
<filename>autoconf</filename>'s <symbol>HAVE_*</symbol>
|
||||
symbols in Wine's headers, as these may be used by Winelib
|
||||
users who may not be using a <filename>configure</filename>
|
||||
script.
|
||||
</para>
|
||||
<para>
|
||||
Let's now turn to specific porting problems and how to solve
|
||||
them.
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>
|
||||
MyOS doesn't have the <filename>foo.h</filename> header!
|
||||
</title>
|
||||
|
||||
<para>
|
||||
This first step is to make <command>autoconf</command> check
|
||||
for this header. In <filename>configure.in</filename> you
|
||||
add a segment like this in the section that checks for
|
||||
header files (search for "header files"):
|
||||
</para>
|
||||
<programlisting>
|
||||
AC_CHECK_HEADER(foo.h, AC_DEFINE(HAVE_FOO_H))
|
||||
</programlisting>
|
||||
<para>
|
||||
If your operating system supports a header file with the
|
||||
same contents but a different name, say
|
||||
<filename>bar.h</filename>, add a check for that also.
|
||||
</para>
|
||||
<para>
|
||||
Now you can change
|
||||
</para>
|
||||
<programlisting>
|
||||
#include <foo.h>
|
||||
</programlisting>
|
||||
<para>
|
||||
to
|
||||
</para>
|
||||
<programlisting>
|
||||
#ifdef HAVE_FOO_H
|
||||
#include <foo.h>
|
||||
#elif defined (HAVE_BAR_H)
|
||||
#include <bar.h>
|
||||
#endif
|
||||
</programlisting>
|
||||
<para>
|
||||
If your system doesn't have a corresponding header file even
|
||||
though it has the library functions being used, you might
|
||||
have to add an <symbol>#else</symbol> section to the
|
||||
conditional. Avoid this if you can.
|
||||
</para>
|
||||
<para>
|
||||
You will also need to add <symbol>#undef HAVE_FOO_H</symbol>
|
||||
(etc.) to <filename>include/config.h.in</filename>
|
||||
</para>
|
||||
<para>
|
||||
Finish up with <command>make configure</command> and
|
||||
<command>./configure</command>.
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>
|
||||
MyOS doesn't have the <function>bar</function> function!
|
||||
</title>
|
||||
|
||||
<para>
|
||||
A typical example of this is the <function>memmove</function>
|
||||
function. To solve this problem you would add
|
||||
<function>memmove</function> to the list of functions that
|
||||
<command>autoconf</command> checks for. In
|
||||
<filename>configure.in</filename> you search for
|
||||
<function>AC_CHECK_FUNCS</function> and add
|
||||
<function>memmove</function>. (You will notice that someone
|
||||
already did this for this particular function.)
|
||||
</para>
|
||||
<para>
|
||||
Secondly, you will also need to add
|
||||
<symbol>#undef HAVE_BAR</symbol> to
|
||||
<filename>include/config.h.in</filename>
|
||||
</para>
|
||||
<para>
|
||||
The next step depends on the nature of the missing function.
|
||||
</para>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>Case 1:</term>
|
||||
<listitem>
|
||||
<para>
|
||||
It's easy to write a complete implementation of the
|
||||
function. (<function>memmove</function> belongs to
|
||||
this case.)
|
||||
</para>
|
||||
<para>
|
||||
You add your implementation in
|
||||
<filename>misc/port.c</filename> surrounded by
|
||||
<symbol>#ifndef HAVE_MEMMOVE</symbol> and
|
||||
<symbol>#endif</symbol>.
|
||||
</para>
|
||||
<para>
|
||||
You might have to add a prototype for your function.
|
||||
If so, <filename>include/miscemu.h</filename> might be
|
||||
the place. Don't forget to protect that definition by
|
||||
<symbol>#ifndef HAVE_MEMMOVE</symbol> and
|
||||
<symbol>#endif</symbol> also!
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>Case 2:</term>
|
||||
<listitem>
|
||||
<para>
|
||||
A general implementation is hard, but Wine is only
|
||||
using a special case.
|
||||
</para>
|
||||
<para>
|
||||
An example is the various <function>wait</function>
|
||||
calls used in <function>SIGNAL_child</function> from
|
||||
<filename>loader/signal.c</filename>. Here we have a
|
||||
multi-branch case on features:
|
||||
</para>
|
||||
<programlisting>
|
||||
#ifdef HAVE_THIS
|
||||
...
|
||||
#elif defined (HAVE_THAT)
|
||||
...
|
||||
#elif defined (HAVE_SOMETHING_ELSE)
|
||||
...
|
||||
#endif
|
||||
</programlisting>
|
||||
<para>
|
||||
Note that this is very different from testing on
|
||||
operating systems. If a new version of your operating
|
||||
systems comes out and adds a new function, this code
|
||||
will magically start using it.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
<para>
|
||||
Finish up with <command>make configure</command> and
|
||||
<command>./configure</command>.
|
||||
</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="adding-languages">
|
||||
<title>Adding New Languages</title>
|
||||
|
||||
<para>
|
||||
This file documents the necessary procedure for adding a new
|
||||
language to the list of languages that Wine can display system
|
||||
menus and forms in. Adding new translations is not hard as it
|
||||
requires no programming knowledge or special skills.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Language dependent resources reside in files
|
||||
named <filename>somefile_Xx.rc</filename> or
|
||||
<filename>Xx.rc</filename>, where <literal>Xx</literal>
|
||||
is your language abbreviation (look for it in
|
||||
<filename>include/winnls.h</filename>). These are included
|
||||
in a master file named <filename>somefile.rc</filename> or
|
||||
<filename>rsrc.rc</filename>, located in the same
|
||||
directory as the language files.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To add a new language to one of these resources you
|
||||
need to make a copy of the English resource (located
|
||||
in the <filename>somefile_En.rc</filename> file) over to
|
||||
your <filename>somefile_Xx.rc</filename> file, include this
|
||||
file in the master <filename>somefile.rc</filename> file,
|
||||
and edit the new file to translate the English text.
|
||||
You may also need to rearrange some of the controls
|
||||
to better fit the newly translated strings. Test your changes
|
||||
to make sure they properly layout on the screen.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
In menus, the character "&" means that the next
|
||||
character will be highlighted and that pressing that
|
||||
letter will select the item. You should place these
|
||||
"&" characters suitably for your language, not just
|
||||
copy the positions from English. In particular,
|
||||
items within one menu should have different highlighted
|
||||
letters.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To get a list of the files that need translating,
|
||||
run the following command in the root of your Wine tree:
|
||||
<command>find -name "*En.rc"</command>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
When adding a new language, also make sure the parameters
|
||||
defined in <filename>./dlls/kernel/nls/*.nls</filename>
|
||||
fit your local habits and language.
|
||||
</para>
|
||||
</sect1>
|
||||
</chapter>
|
||||
|
||||
<!-- Keep this comment at the end of the file
|
||||
Local variables:
|
||||
mode: sgml
|
||||
sgml-parent-document:("wine-devel.sgml" "set" "book" "part" "chapter" "")
|
||||
End:
|
||||
-->
|
40
documentation/winedev-graphical.sgml
Normal file
40
documentation/winedev-graphical.sgml
Normal file
@ -0,0 +1,40 @@
|
||||
<chapter>
|
||||
<title>Graphical modules</title>
|
||||
<sect1>
|
||||
<title>GDI Module</title>
|
||||
|
||||
<sect2>
|
||||
<title>X Windows System interface</title>
|
||||
|
||||
<para>
|
||||
The X libraries used to implement X clients (such as Wine)
|
||||
do not work properly if multiple threads access the same
|
||||
display concurrently. It is possible to compile the X
|
||||
libraries to perform their own synchronization (initiated
|
||||
by calling <function>XInitThreads()</function>). However,
|
||||
Wine does not use this approach. Instead Wine performs its
|
||||
own synchronization using the
|
||||
<function>wine_tsx11_lock()</function> / <function>wine_tsx11_unlock()</function>
|
||||
functions. This locking protects library access
|
||||
with a critical section, and also arranges things so that
|
||||
X libraries compiled without <option>-D_REENTRANT</option>
|
||||
(eg. with global <varname>errno</varname> variable) will
|
||||
work with Wine.
|
||||
</para>
|
||||
<para>
|
||||
In the past, all calls to X used to go through a wrapper called
|
||||
<function>TSX...()</function> (for "Thread Safe X ...").
|
||||
While it is still being used in the code, it's inefficient
|
||||
as the lock is potentially aquired and released unnecessarily.
|
||||
New code should explicitly aquire the lock.
|
||||
</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
</chapter>
|
||||
|
||||
<!-- Keep this comment at the end of the file
|
||||
Local variables:
|
||||
mode: sgml
|
||||
sgml-parent-document:("wine-devel.sgml" "set" "book" "part" "chapter" "")
|
||||
End:
|
||||
-->
|
823
documentation/winedev-kernel.sgml
Normal file
823
documentation/winedev-kernel.sgml
Normal file
@ -0,0 +1,823 @@
|
||||
<chapter>
|
||||
<title>Kernel modules</title>
|
||||
<para>
|
||||
This section cover the kernel modules. As already stated, Wine
|
||||
implements the NT architecture, hence provides NTDLL for the
|
||||
core kernel functions, and KERNEL32, which is the
|
||||
implementation of the basis of the Win32 subsystem, on top of
|
||||
NTDLL.
|
||||
</para>
|
||||
<sect1 id="ntdll">
|
||||
<title>NTDLL</title>
|
||||
<para>
|
||||
NTDLL provides most of the services you'd expect from a
|
||||
kernel.
|
||||
</para>
|
||||
<para>
|
||||
Process and thread management are part of them (even if
|
||||
process management is still mainly done in KERNEL32, unlike
|
||||
NT). A Windows process runs as a Unix process, and a Windows
|
||||
thread runs as a Unix thread.
|
||||
</para>
|
||||
<para>
|
||||
Wine also provide fibers (which is the Windows name of
|
||||
co-routines).
|
||||
</para>
|
||||
<para>
|
||||
Most of the Windows memory handling (Heap, Global and Local
|
||||
functions, virtual memory...) are easily mapped upon their
|
||||
Unix equivalents. Note the NTDLL doesn't know about 16 bit
|
||||
memory, which is only handled in KERNEL32/KRNL386.EXE (and
|
||||
also the DOS routines).
|
||||
</para>
|
||||
|
||||
<sect2>
|
||||
<title>File management</title>
|
||||
<para>
|
||||
Wine uses some configuration in order to map Windows
|
||||
filenames (either defined with drive letters, or as UNC
|
||||
names) to the unix filenames. Wine also uses some
|
||||
incantation so that most of file related APIs can also
|
||||
take full unix names. This is handy when passing filenames
|
||||
on the command line.
|
||||
</para>
|
||||
<para>
|
||||
File handles can be waitable objects, as Windows define
|
||||
them.
|
||||
</para>
|
||||
<para>
|
||||
Asynchronous I/O is implemented on file handles by
|
||||
queueing pseudo APC. They are not real APC in the sense
|
||||
that they have the same priority as the threads in the
|
||||
considered process (while APCs on NT have normally a
|
||||
higher priority). These APCs get called when invoking
|
||||
Wine server (which should lead to correct behavior when the
|
||||
program ends up waiting on some object - waiting always
|
||||
implies calling Wine server).
|
||||
</para>
|
||||
<para>
|
||||
FIXME: this should be enhanced and updated to latest work
|
||||
on FS.
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Synchronization</title>
|
||||
<para>
|
||||
Most of the synchronization (between threads or processes)
|
||||
is done in Wine server, which handles both the waiting
|
||||
operation (on a single object or a set of objects) and the
|
||||
signaling of objects.
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Module (DLL) loading</title>
|
||||
<para>
|
||||
Wine is able to load any NE and PE module. In all cases,
|
||||
the module's binary code is directly executed by the
|
||||
processor.
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Device management</title>
|
||||
<para>
|
||||
Wine allows usage a wide variety of devices:
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
Communication ports are mapped to Unix
|
||||
communication ports (if they have sufficient
|
||||
permissions).
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Parallel ports are mapped to Unix parallel ports (if
|
||||
they have sufficient permissions).
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
CDROM: the Windows device I/O control calls are
|
||||
mapped onto Unix <function>ioctl()</function>.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Some Win9x VxDs are supported, by rewriting some of
|
||||
their internal behavior. But this support is
|
||||
limited. Portable programs to Windows NT shouldn't
|
||||
need them.
|
||||
</para>
|
||||
<para>
|
||||
Wine will not support native VxD.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2 id="threading">
|
||||
<title>Multi-threading in Wine</title>
|
||||
|
||||
<para>
|
||||
This section will assume you understand the basics of
|
||||
multithreading. If not there are plenty of good tutorials
|
||||
available on the net to get you started.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Threading in Wine is somewhat complex due to several
|
||||
factors. The first is the advanced level of multithreading
|
||||
support provided by Windows - there are far more threading
|
||||
related constructs available in Win32 than the Linux
|
||||
equivalent (pthreads). The second is the need to be able to
|
||||
map Win32 threads to native Linux threads which provides us
|
||||
with benefits like having the kernel schedule them without
|
||||
our intervention. While it's possible to implement threading
|
||||
entirely without kernel support, doing so is not desirable
|
||||
on most platforms that Wine runs on.
|
||||
</para>
|
||||
|
||||
<sect3>
|
||||
<title> Threading support in Win32 </title>
|
||||
|
||||
<para>
|
||||
Win32 is an unusually thread friendly API. Not only is it
|
||||
entirely thread safe, but it provides many different
|
||||
facilities for working with threads. These range from the
|
||||
basics such as starting and stopping threads, to the
|
||||
extremely complex such as injecting threads into other
|
||||
processes and COM inter-thread marshalling.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
One of the primary challenges of writing Wine code
|
||||
therefore is ensuring that all our DLLs are thread safe,
|
||||
free of race conditions and so on. This isn't simple -
|
||||
don't be afraid to ask if you aren't sure whether a piece
|
||||
of code is thread safe or not!
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Win32 provides many different ways you can make your code
|
||||
thread safe however the most common are <emphasis>critical
|
||||
section</emphasis> and the <emphasis>interlocked
|
||||
functions</emphasis>. Critical sections are a type of
|
||||
mutex designed to protect a geographic area of code. If
|
||||
you don't want multiple threads running in a piece of code
|
||||
at once, you can protect them with calls to
|
||||
<function>EnterCriticalSection</function> and
|
||||
<function>LeaveCriticalSection</function>. The first call
|
||||
to <function>EnterCriticalSection</function> by a thread
|
||||
will lock the section and continue without stopping. If
|
||||
another thread calls it then it will block until the
|
||||
original thread calls
|
||||
<function>LeaveCriticalSection</function> again.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
It is therefore vitally important that if you use critical
|
||||
sections to make some code thread-safe, that you check
|
||||
every possible codepath out of the code to ensure that any
|
||||
held sections are left. Code like this:
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
if (res != ERROR_SUCCESS) return res;
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
is extremely suspect in a function that also contains a
|
||||
call to <function>EnterCriticalSection</function>. Be
|
||||
careful.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If a thread blocks while waiting for another thread to
|
||||
leave a critical section, you will see an error from the
|
||||
<function>RtlpWaitForCriticalSection</function> function,
|
||||
along with a note of which thread is holding the
|
||||
lock. This only appears after a certain timeout, normally
|
||||
a few seconds. It's possible the thread holding the lock
|
||||
is just being really slow which is why Wine won't
|
||||
terminate the app like a non-checked build of Windows
|
||||
would, but the most common cause is that for some reason a
|
||||
thread forgot to call
|
||||
<function>LeaveCriticalSection</function>, or died while
|
||||
holding the lock (perhaps because it was in turn waiting
|
||||
for another lock). This doesn't just happen in Wine code:
|
||||
a deadlock while waiting for a critical section could be
|
||||
due to a bug in the app triggered by a slight difference
|
||||
in the emulation.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Another popular mechanism available is the use of
|
||||
functions like <function>InterlockedIncrement</function>
|
||||
and <function>InterlockedExchange</function>. These make
|
||||
use of native CPU abilities to execute a single
|
||||
instruction while ensuring any other processors on the
|
||||
system cannot access memory, and allow you to do common
|
||||
operations like add/remove/check a variable in thread-safe
|
||||
code without holding a mutex. These are useful for
|
||||
reference counting especially in free-threaded (thread
|
||||
safe) COM objects.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Finally, the usage of TLS slots are also popular. TLS
|
||||
stands for thread-local storage, and is a set of slots
|
||||
scoped local to a thread which you can store pointers
|
||||
in. Look on MSDN for the <function>TlsAlloc</function>
|
||||
function to learn more about the Win32 implementation of
|
||||
this. Essentially, the contents of a given slot will be
|
||||
different in each thread, so you can use this to store
|
||||
data that is only meaningful in the context of a single
|
||||
thread. On recent versions of Linux the __thread keyword
|
||||
provides a convenient interface to this functionality - a
|
||||
more portable API is exposed in the pthread
|
||||
library. However, these facilities is not used by Wine,
|
||||
rather, we implement Win32 TLS entirely ourselves.
|
||||
</para>
|
||||
</sect3>
|
||||
|
||||
<sect3>
|
||||
<title> SysLevels </title>
|
||||
|
||||
<para>
|
||||
SysLevels are an undocumented Windows-internal
|
||||
thread-safety system. They are basically critical sections
|
||||
which must be taken in a particular order. The mechanism
|
||||
is generic but there are always three syslevels: level 1
|
||||
is the Win16 mutex, level 2 is the USER mutex and level 3
|
||||
is the GDI mutex.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
When entering a syslevel, the code (in
|
||||
<filename>dlls/kernel/syslevel.c</filename>) will check
|
||||
that a higher syslevel is not already held and produce an
|
||||
error if so. This is because it's not legal to enter level
|
||||
2 while holding level 3 - first, you must leave level 3.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Throughout the code you may see calls to
|
||||
<function>_ConfirmSysLevel()</function> and
|
||||
<function>_CheckNotSysLevel()</function>. These functions
|
||||
are essentially assertions about the syslevel states and
|
||||
can be used to check that the rules have not been
|
||||
accidentally violated. In particular,
|
||||
<function>_CheckNotSysLevel()</function> will break
|
||||
(probably into the debugger) if the check fails. If this
|
||||
happens the solution is to get a backtrace and find out,
|
||||
by reading the source of the wine functions called along
|
||||
the way, how Wine got into the invalid state.
|
||||
</para>
|
||||
|
||||
</sect3>
|
||||
|
||||
<sect3>
|
||||
<title> POSIX threading vs kernel threading </title>
|
||||
|
||||
<para>
|
||||
Wine runs in one of two modes: either pthreads (posix
|
||||
threading) or kthreads (kernel threading). This section
|
||||
explains the differences between them. The one that is
|
||||
used is automatically selected on startup by a small test
|
||||
program which then execs the correct binary, either
|
||||
wine-kthread or wine-pthread. On NPTL-enabled systems
|
||||
pthreads will be used, and on older non-NPTL systems
|
||||
kthreads is selected.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Let's start with a bit of history. Back in the dark ages
|
||||
when Wines threading support was first implemented a
|
||||
problem was faced - Windows had much more capable
|
||||
threading APIs than Linux did. This presented a problem -
|
||||
Wine works either by reimplementing an API entirely or by
|
||||
mapping it onto the underlying systems equivalent. How
|
||||
could Win32 threading be implemented using a library which
|
||||
did not have all the neeed features? The answer, of
|
||||
course, was that it couldn't be.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
On Linux the pthreads interface is used to start, stop and
|
||||
control threads. The pthreads library in turn is based on
|
||||
top of so-called "kernel threads" which are created using
|
||||
the <function>clone(2)</function> syscall. Pthreads
|
||||
provides a nicer (more portable) interface to this
|
||||
functionality and also provides APIs for controlling
|
||||
mutexes. There is a <ulink
|
||||
url="http://www.llnl.gov/computing/tutorials/pthreads/">
|
||||
good tutorial on pthreads </ulink> available if you want
|
||||
to learn more.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
As pthreads did not provide the necessary semantics to
|
||||
implement Win32 threading, the decision was made to
|
||||
implement Win32 threading on top of the underlying kernel
|
||||
threads by using syscalls like <function>clone</function>
|
||||
directly. This provided maximum flexibility and allowed a
|
||||
correct implementation but caused some bad side
|
||||
effects. Most notably, all the userland Linux APIs assumed
|
||||
that the user was utilising the pthreads library. Some
|
||||
only enabled thread safety when they detected that
|
||||
pthreads was in use - this is true of glibc, for
|
||||
instance. Worse, pthreads and pure kernel threads had
|
||||
strange interactions when run in the same process yet some
|
||||
libraries used by Wine used pthreads internally. Throw in
|
||||
source code porting using WineLib - where you have both
|
||||
UNIX and Win32 code in the same process - and chaos was
|
||||
the result.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The solution was simple yet ingenius: Wine would provide
|
||||
its own implementation of the pthread library
|
||||
<emphasis>inside</emphasis> its own binary. Due to the
|
||||
semantics of ELF symbol scoping, this would cause Wines
|
||||
own implementations to override any implementation loaded
|
||||
later on (like the real libpthread.so). Therefore, any
|
||||
calls to the pthread APIs in external libraries would be
|
||||
linked to Wines instead of the systems pthreads library,
|
||||
and Wine implemented pthreads by using the standard
|
||||
Windows threading APIs it in turn implemented itself.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
As a result, libraries that only became thread-safe in the
|
||||
presence of a loaded pthreads implementation would now do
|
||||
so, and any external code that used pthreads would
|
||||
actually end up creating Win32 threads that Wine was aware
|
||||
of and controlled. This worked quite nicely for a long
|
||||
time, even though it required doing some extremely
|
||||
un-kosher things like overriding internal libc structures
|
||||
and functions. That is, it worked until NPTL was developed
|
||||
at which point the underlying thread implementation on
|
||||
Linux changed dramatically.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The fake pthread implementation can be found in
|
||||
<filename>loader/kthread.c</filename>, which is used to
|
||||
produce to wine-kthread binary. In contrast,
|
||||
loader/pthread.c produces the wine-pthread binary which is
|
||||
used on newer NPTL systems.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
NPTL is a new threading subsystem for Linux that hugely
|
||||
improves its performance and flexibility. By allowing
|
||||
threads to become much more scalable and adding new
|
||||
pthread APIs, NPTL made Linux competitive with Windows in
|
||||
the multi-threaded world. Unfortunately it also broke many
|
||||
assumptions made by Wine (as well as other applications
|
||||
such as the Sun JVM and RealPlayer) in the process.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
There was, however, some good news. NPTL made Linux
|
||||
threading powerful enough that Win32 threads could now be
|
||||
implemented on top of pthreads like any other normal
|
||||
application. There would no longer be problems with mixing
|
||||
win32-kthreads and pthreads created by external libraries,
|
||||
and no need to override glibc internals. As you can see
|
||||
from the relative sizes of the
|
||||
<filename>loader/kthread.c</filename> and
|
||||
<filename>loader/pthread.c</filename> files, the
|
||||
difference in code complexity is considerable. NPTL also
|
||||
made several other semantic changes to things such as
|
||||
signal delivery so changes were required in many different
|
||||
places in Wine.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
On non-Linux systems the threading interface is typically
|
||||
not powerful enough to replicate the semantics Win32
|
||||
applications expect and so kthreads with the pthread
|
||||
overrides are used.
|
||||
</para>
|
||||
</sect3>
|
||||
|
||||
<sect3>
|
||||
<title> The Win32 thread environment </title>
|
||||
|
||||
<para>
|
||||
All Win32 code, whether from a native EXE/DLL or in Wine
|
||||
itself, expects certain constructs to be present in its
|
||||
environment. This section explores what those constructs
|
||||
are and how Wine sets them up. The lack of this
|
||||
environment is one thing that makes it hard to use Wine
|
||||
code directly from standard Linux applications - in order
|
||||
to interact with Win32 code a thread must first be
|
||||
"adopted" by Wine.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The first thing Win32 code requires is the
|
||||
<emphasis>TEB</emphasis> or "Thread Environment
|
||||
Block". This is an internal (undocumented) Windows
|
||||
structure associated with every thread which stores a
|
||||
variety of things such as TLS slots, a pointer to the
|
||||
threads message queue, the last error code and so on. You
|
||||
can see the definition of the TEB in
|
||||
<filename>include/thread.h</filename>, or at least what we
|
||||
know of it so far. Being internal and subject to change,
|
||||
the layout of the TEB has had to be reverse engineered
|
||||
from scratch.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
A pointer to the TEB is stored in the %fs register and can
|
||||
be accessed using <function>NtCurrentTeb()</function> from
|
||||
within Wine code. %fs actually stores a selector, and
|
||||
setting it therefore requires modifying the processes
|
||||
local descriptor table (LDT) - the code to do this is in
|
||||
<filename>lib/wine/ldt.c</filename>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The TEB is required by nearly all Win32 code run in the
|
||||
Wine environment, as any wineserver RPC will use it, which
|
||||
in turn implies that any code which could possibly block
|
||||
(for instance by using a critical section) needs it. The
|
||||
TEB also holds the SEH exception handler chain as the
|
||||
first element, so if when disassembling you see code like
|
||||
this:
|
||||
</para>
|
||||
|
||||
<programlisting> movl %esp, %fs:0 </programlisting>
|
||||
|
||||
<para>
|
||||
... then you are seeing the program set up an SEH handler
|
||||
frame. All threads must have at least one SEH entry, which
|
||||
normally points to the backstop handler which is
|
||||
ultimately responsible for popping up the all-too-familiar
|
||||
"This program has performed an illegal operation and will
|
||||
be terminated" message. On Wine we just drop straight into
|
||||
the debugger. A full description of SEH is out of the
|
||||
scope of this section, however there are some good
|
||||
articles in MSJ if you are interested.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
All Win32-aware threads must have a wineserver
|
||||
connection. Many different APIs require the ability to
|
||||
communicate with the wineserver. In turn, the wineserver
|
||||
must be aware of Win32 threads in order to be able to
|
||||
accurately report information to other parts of the program
|
||||
and do things like route inter-thread messages, dispatch
|
||||
APCs (asynchronous procedure calls) and so on. Therefore a
|
||||
part of thread initialization is initializing the thread
|
||||
serverside. The result is not only correct information in
|
||||
the server, but a set of file descriptors the thread can use
|
||||
to communicate with the server - the request fd, reply fd
|
||||
and wait fd (used for blocking).
|
||||
</para>
|
||||
</sect3>
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>KERNEL Module</title>
|
||||
|
||||
<para>
|
||||
FIXME: Needs some content...
|
||||
</para>
|
||||
<sect2 id="consoles">
|
||||
<title>Consoles in Wine</title>
|
||||
<para>
|
||||
As described in the Wine User Guide's CUI section, Wine
|
||||
manipulates three kinds of "consoles" in order to support
|
||||
properly the Win32 CUI API.
|
||||
</para>
|
||||
<para>
|
||||
The following table describes the main implementation
|
||||
differences between the three approaches.
|
||||
<table>
|
||||
<title>Function consoles implementation comparison</title>
|
||||
<tgroup cols="4" align="left">
|
||||
<thead>
|
||||
<row>
|
||||
<entry>Function</entry>
|
||||
<entry>Bare streams</entry>
|
||||
<entry>Wineconsole & user backend</entry>
|
||||
<entry>Wineconsole & curses backend</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>
|
||||
Console as a Win32 Object (and associated
|
||||
handles)
|
||||
</entry>
|
||||
<entry>
|
||||
No specific Win32 object is used in this
|
||||
case. The handles manipulated for the standard
|
||||
Win32 streams are in fact "bare handles" to
|
||||
their corresponding Unix streams. The mode
|
||||
manipulation functions
|
||||
(<function>GetConsoleMode</function> /
|
||||
<function>SetConsoleMode</function>) are not
|
||||
supported.
|
||||
</entry>
|
||||
<entry>
|
||||
Implemented in server, and a specific Winelib
|
||||
program (wineconsole) is in charge of the
|
||||
rendering and user input. The mode manipulation
|
||||
functions behave as expected.
|
||||
</entry>
|
||||
<entry>
|
||||
Implemented in server, and a specific Winelib
|
||||
program (wineconsole) is in charge of the
|
||||
rendering and user input. The mode manipulation
|
||||
functions behave as expected.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
Inheritance (including handling in
|
||||
<function>CreateProcess</function> of
|
||||
<constant>CREATE_DETACHED</constant>,
|
||||
<constant>CREATE_NEW_CONSOLE</constant> flags).
|
||||
</entry>
|
||||
<entry>
|
||||
Not supported. Every process child of a process
|
||||
will inherit the Unix streams, so will also
|
||||
inherit the Win32 standard streams.
|
||||
</entry>
|
||||
<entry>
|
||||
Fully supported (each new console creation will
|
||||
be handled by the creation of a new USER32
|
||||
window)
|
||||
</entry>
|
||||
<entry>
|
||||
Fully supported, except for the creation of a
|
||||
new console, which will be rendered on the same
|
||||
Unix terminal as the previous one, leading to
|
||||
unpredictable results.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<function>ReadFile</function> /
|
||||
<function>WriteFile</function>
|
||||
operations
|
||||
</entry>
|
||||
<entry>Fully supported</entry>
|
||||
<entry>Fully supported</entry>
|
||||
<entry>Fully supported</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
Screen-buffer manipulation (creation, deletion,
|
||||
resizing...)
|
||||
</entry>
|
||||
<entry>Not supported</entry>
|
||||
<entry>Fully supported</entry>
|
||||
<entry>
|
||||
Partly supported (this won't work too well as we
|
||||
don't control (so far) the size of underlying
|
||||
Unix terminal
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
APIs for reading/writing screen-buffer content,
|
||||
cursor position
|
||||
</entry>
|
||||
<entry>Not supported</entry>
|
||||
<entry>Fully supported</entry>
|
||||
<entry>Fully supported</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
APIs for manipulating the rendering window size
|
||||
</entry>
|
||||
<entry>Not supported</entry>
|
||||
<entry>Fully supported</entry>
|
||||
<entry>
|
||||
Partly supported (this won't work too well as we
|
||||
don't control (so far) the size of underlying
|
||||
Unix terminal
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
Signaling (in particular, Ctrl-C handling)
|
||||
</entry>
|
||||
<entry>
|
||||
Nothing is done, which means that Ctrl-C will
|
||||
generate (as usual) a
|
||||
<constant>SIGINT</constant> which will terminate
|
||||
the program.
|
||||
</entry>
|
||||
<entry>
|
||||
Partly supported (Ctrl-C behaves as expected,
|
||||
however the other Win32 CUI signaling isn't
|
||||
properly implemented).
|
||||
</entry>
|
||||
<entry>
|
||||
Partly supported (Ctrl-C behaves as expected,
|
||||
however the other Win32 CUI signaling isn't
|
||||
properly implemented).
|
||||
</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The Win32 objects behind a console can be created in
|
||||
several occasions:
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
When the program is started from wineconsole, a new
|
||||
console object is created and will be used
|
||||
(inherited) by the process launched from
|
||||
wineconsole.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
When a program, which isn't attached to a console,
|
||||
calls <function>AllocConsole</function>, Wine then
|
||||
launches wineconsole, and attaches the current
|
||||
program to this console. In this mode, the USER32
|
||||
mode is always selected as Wine cannot tell the
|
||||
current state of the Unix console.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
<para>
|
||||
Please also note, that starting a child process with the
|
||||
<constant>CREATE_NEW_CONSOLE</constant> flag, will end-up
|
||||
calling <function>AllocConsole</function> in the child
|
||||
process, hence creating a wineconsole with the USER32
|
||||
backend.
|
||||
</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="initialization">
|
||||
|
||||
<title> The Wine initialization process </title>
|
||||
|
||||
<para>
|
||||
Wine has a rather complex startup procedure, so unlike many
|
||||
programs the best place to begin exploring the code-base is
|
||||
<emphasis>not</emphasis> in fact at the
|
||||
<function>main()</function> function but instead at some of the
|
||||
more straightforward DLLs that exist on the periphery such as
|
||||
MSI, the widget library (in USER and COMCTL32) etc. The purpose
|
||||
of this section is to document and explain how Wine starts up
|
||||
from the moment the user runs "wine myprogram.exe" to the point
|
||||
at which myprogram gets control.
|
||||
</para>
|
||||
|
||||
<sect2>
|
||||
<title> First Steps </title>
|
||||
|
||||
<para>
|
||||
The actual wine binary that the user runs does not do very much, in fact it is only
|
||||
responsible for checking the threading model in use (NPTL vs LinuxThreads) and then invoking
|
||||
a new binary which performs the next stage in the startup sequence. See the threading chapter
|
||||
for more information on this check and why it's necessary. You can find this code in
|
||||
<filename>loader/glibc.c</filename>. The result of this check is an exec of either
|
||||
wine-pthread or wine-kthread, potentially (on Linux) via
|
||||
the <emphasis>preloader</emphasis>. We need to use separate binaries here because overriding
|
||||
the native pthreads library requires us to exploit a property of ELF symbol fixup semantics:
|
||||
it's not possible to do this without starting a new process.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The Wine preloader is found in <filename>loader/preloader.c</filename>, and is required in
|
||||
order to impose a Win32 style address space layout upon the newly created Win32 process. The
|
||||
details of what this does is covered in the address space layout chapter. The preloader is a
|
||||
statically linked ELF binary which is passed the name of the actual Wine binary to run (either
|
||||
wine-kthread or wine-pthread) along with the arguments the user passed in from the command
|
||||
line. The preloader is an unusual program: it does not have a main() function. In standard ELF
|
||||
applications, the entry point is actually at a symbol named _start: this is provided by the
|
||||
standard gcc infrastructure and normally jumps to <function>__libc_start_main</function> which
|
||||
initializes glibc before passing control to the main function as defined by the programmer.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The preloader takes control direct from the entry point for a few reasons. Firstly, it is
|
||||
required that glibc is not initialized twice: the result of such behaviour is undefined and
|
||||
subject to change without notice. Secondly, it's possible that as part of initializing glibc,
|
||||
the address space layout could be changed - for instance, any call to malloc will initialize a
|
||||
heap arena which modifies the VM mappings. Finally, glibc does not return to _start at any
|
||||
point, so by reusing it we avoid the need to recreate the ELF bootstrap stack (env, argv,
|
||||
auxiliary array etc).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The preloader is responsible for two things: protecting important regions of the address
|
||||
space so the dynamic linker does not map shared libraries into them, and once that is done
|
||||
loading the real Wine binary off disk, linking it and starting it up. Normally all this is
|
||||
done automatically by glibc and the kernel but as we intercepted this process by using a
|
||||
static binary it's up to us to restart the process. The bulk of the code in the preloader is
|
||||
about loading wine-[pk]thread and ld-linux.so.2 off disk, linking them together, then
|
||||
starting the dynamic linking process.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
One of the last things the preloader does before jumping into the dynamic linker is scan the
|
||||
symbol table of the loaded Wine binary and set the value of a global variable directly: this
|
||||
is a more efficient way of passing information to the main Wine program than flattening the
|
||||
data structures into an environment variable or command line parameter then unpacking it on
|
||||
the other side, but it achieves pretty much the same thing. The global variable set points to
|
||||
the preload descriptor table, which contains the VMA regions protected by the preloader. This
|
||||
allows Wine to unmap them once the dynamic linker has been run, so leaving gaps we can
|
||||
initialize properly later on.
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title> Starting the emulator </title>
|
||||
|
||||
<para>
|
||||
The process of starting up the emulator itself is mostly one of chaining through various
|
||||
initializer functions defined in the core libraries and DLLs: libwine, then NTDLL, then kernel32.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Both the wine-pthread and wine-kthread binaries share a common <function>main</function>
|
||||
function, defined in <filename>loader/main.c</filename>, so no matter which binary is selected
|
||||
after the preloader has run we start here. This passes the information provided by the
|
||||
preloader into libwine and then calls wine_init, defined
|
||||
in <filename>libs/wine/loader.c</filename>. This is where the emulation really starts:
|
||||
<function>wine_init</function> can, with the correct preparation,
|
||||
be called from programs other than the wine loader itself.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<function>wine_init</function> does some very basic setup tasks such as initializing the
|
||||
debugging infrastructure, yet more address space manipulation (see the information on the
|
||||
4G/4G VM split in the address space chapter), before loading NTDLL - the core of both Wine and
|
||||
the Windows NT series - and jumping to the <function>__wine_process_init</function> function defined
|
||||
in <filename>dlls/ntdll/loader.c</filename>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This function is responsible for initializing the primary Win32 environment. In thread_init(),
|
||||
it sets up the TEB, the wineserver connection for the main thread and the process heap. See
|
||||
the threading chapter for more information on this.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Finally, it loads and jumps to <function>__wine_kernel_init</function> in kernel32.dll: this
|
||||
is defined in <filename>dlls/kernel32/process.c</filename>. This is where the bulk of the work
|
||||
is done. The kernel32 initialization code retrieves the startup info for the process from the
|
||||
server, initializes the registry, sets up the drive mapping system and locale data, then
|
||||
begins loading the requested application itself. Each process has a STARTUPINFO block that can
|
||||
be passed into <function>CreateProcess</function> specifying various things like how the first
|
||||
window should be displayed: this is sent to the new process via the wineserver.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
After determining the type of file given to Wine by the user (a Win32 EXE file, a Win16 EXE, a
|
||||
Winelib app etc), the program is loaded into memory (which may involve loading and
|
||||
initializing other DLLs, the bulk of Wines startup code), before control reaches the end of
|
||||
<function>__wine_kernel_init</function>. This function ends with the new process stack being
|
||||
initialized, and start_process being called on the new stack. Nearly there!
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The final element of initializing Wine is starting the newly loaded program
|
||||
itself. <function>start_process</function> sets up the SEH backstop handler, calls
|
||||
<function>LdrInitializeThunk</function> which performs the last part of the process
|
||||
initialization (such as performing relocations and calling the DllMains with PROCESS_ATTACH),
|
||||
grabs the entry point of the executable and then on this line:
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
ExitProcess( entry( peb ) );
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
... jumps to the entry point of the program. At this point the users program is running and
|
||||
the API provided by Wine is ready to be used. When entry returns,
|
||||
the <function>ExitProcess</function> API will be used to initialize a graceful shutdown.
|
||||
</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
</chapter>
|
||||
|
||||
<!-- Keep this comment at the end of the file
|
||||
Local variables:
|
||||
mode: sgml
|
||||
sgml-parent-document:("wine-devel.sgml" "set" "book" "part" "chapter" "")
|
||||
End:
|
||||
-->
|
508
documentation/winedev-otherdebug.sgml
Normal file
508
documentation/winedev-otherdebug.sgml
Normal file
@ -0,0 +1,508 @@
|
||||
<chapter id="otherdebug">
|
||||
<title>Other debugging techniques</title>
|
||||
<sect1 id="hardware-trace">
|
||||
<title>Doing A Hardware Trace</title>
|
||||
|
||||
<para>
|
||||
The primary reason to do this is to reverse engineer a
|
||||
hardware device for which you don't have documentation, but
|
||||
can get to work under Wine.
|
||||
</para>
|
||||
<para>
|
||||
This lot is aimed at parallel port devices, and in particular
|
||||
parallel port scanners which are now so cheap they are
|
||||
virtually being given away. The problem is that few
|
||||
manufactures will release any programming information which
|
||||
prevents drivers being written for Sane, and the traditional
|
||||
technique of using DOSemu to produce the traces does not work
|
||||
as the scanners invariably only have drivers for Windows.
|
||||
</para>
|
||||
<para>
|
||||
Presuming that you have compiled and installed wine the first
|
||||
thing to do is is to enable direct hardware access to your
|
||||
parallel port. To do this edit <filename>config</filename>
|
||||
(usually in <filename>~/.wine/</filename>) and in the
|
||||
ports section add the following two lines
|
||||
</para>
|
||||
<programlisting>
|
||||
read=0x378,0x379,0x37a,0x37c,0x77a
|
||||
write=0x378,x379,0x37a,0x37c,0x77a
|
||||
</programlisting>
|
||||
<para>
|
||||
This adds the necessary access required for SPP/PS2/EPP/ECP
|
||||
parallel port on LPT1. You will need to adjust these number
|
||||
accordingly if your parallel port is on LPT2 or LPT0.
|
||||
</para>
|
||||
<para>
|
||||
When starting wine use the following command line, where
|
||||
<literal>XXXX</literal> is the program you need to run in
|
||||
order to access your scanner, and <literal>YYYY</literal> is
|
||||
the file your trace will be stored in:
|
||||
</para>
|
||||
<programlisting>
|
||||
WINEDEBUG=+io wine XXXX 2> >(sed 's/^[^:]*:io:[^ ]* //' > YYYY)
|
||||
</programlisting>
|
||||
<para>
|
||||
You will need large amounts of hard disk space (read hundreds
|
||||
of megabytes if you do a full page scan), and for reasonable
|
||||
performance a really fast processor and lots of RAM.
|
||||
</para>
|
||||
<para>
|
||||
You will need to postprocess the output into a more manageable
|
||||
format, using the <command>shrink</command> program. First
|
||||
you need to compile the source (which is located at the end of
|
||||
this section):
|
||||
<programlisting>
|
||||
cc shrink.c -o shrink
|
||||
</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
Use the <command>shrink</command> program to reduce the
|
||||
physical size of the raw log as follows:
|
||||
</para>
|
||||
<programlisting>
|
||||
cat log | shrink > log2
|
||||
</programlisting>
|
||||
<para>
|
||||
The trace has the basic form of
|
||||
</para>
|
||||
<programlisting>
|
||||
XXXX > YY @ ZZZZ:ZZZZ
|
||||
</programlisting>
|
||||
<para>
|
||||
where <literal>XXXX</literal> is the port in hexadecimal being
|
||||
accessed, <literal>YY</literal> is the data written (or read)
|
||||
from the port, and <literal>ZZZZ:ZZZZ</literal> is the address
|
||||
in memory of the instruction that accessed the port. The
|
||||
direction of the arrow indicates whether the data was written
|
||||
or read from the port.
|
||||
</para>
|
||||
<programlisting>
|
||||
> data was written to the port
|
||||
< data was read from the port
|
||||
</programlisting>
|
||||
<para>
|
||||
My basic tip for interpreting these logs is to pay close
|
||||
attention to the addresses of the IO instructions. Their
|
||||
grouping and sometimes proximity should reveal the presence of
|
||||
subroutines in the driver. By studying the different versions
|
||||
you should be able to work them out. For example consider the
|
||||
following section of trace from my UMAX Astra 600P
|
||||
</para>
|
||||
<programlisting>
|
||||
0x378 > 55 @ 0297:01ec
|
||||
0x37a > 05 @ 0297:01f5
|
||||
0x379 < 8f @ 0297:01fa
|
||||
0x37a > 04 @ 0297:0211
|
||||
0x378 > aa @ 0297:01ec
|
||||
0x37a > 05 @ 0297:01f5
|
||||
0x379 < 8f @ 0297:01fa
|
||||
0x37a > 04 @ 0297:0211
|
||||
0x378 > 00 @ 0297:01ec
|
||||
0x37a > 05 @ 0297:01f5
|
||||
0x379 < 8f @ 0297:01fa
|
||||
0x37a > 04 @ 0297:0211
|
||||
0x378 > 00 @ 0297:01ec
|
||||
0x37a > 05 @ 0297:01f5
|
||||
0x379 < 8f @ 0297:01fa
|
||||
0x37a > 04 @ 0297:0211
|
||||
0x378 > 00 @ 0297:01ec
|
||||
0x37a > 05 @ 0297:01f5
|
||||
0x379 < 8f @ 0297:01fa
|
||||
0x37a > 04 @ 0297:0211
|
||||
0x378 > 00 @ 0297:01ec
|
||||
0x37a > 05 @ 0297:01f5
|
||||
0x379 < 8f @ 0297:01fa
|
||||
0x37a > 04 @ 0297:0211
|
||||
</programlisting>
|
||||
<para>
|
||||
As you can see there is a repeating structure starting at
|
||||
address <literal>0297:01ec</literal> that consists of four io
|
||||
accesses on the parallel port. Looking at it the first io
|
||||
access writes a changing byte to the data port the second
|
||||
always writes the byte <literal>0x05</literal> to the control
|
||||
port, then a value which always seems to
|
||||
<literal>0x8f</literal> is read from the status port at which
|
||||
point a byte <literal>0x04</literal> is written to the control
|
||||
port. By studying this and other sections of the trace we can
|
||||
write a C routine that emulates this, shown below with some
|
||||
macros to make reading/writing on the parallel port easier to
|
||||
read.
|
||||
</para>
|
||||
<programlisting>
|
||||
#define r_dtr(x) inb(x)
|
||||
#define r_str(x) inb(x+1)
|
||||
#define r_ctr(x) inb(x+2)
|
||||
#define w_dtr(x,y) outb(y, x)
|
||||
#define w_str(x,y) outb(y, x+1)
|
||||
#define w_ctr(x,y) outb(y, x+2)
|
||||
|
||||
/* Seems to be sending a command byte to the scanner */
|
||||
int udpp_put(int udpp_base, unsigned char command)
|
||||
{
|
||||
int loop, value;
|
||||
|
||||
w_dtr(udpp_base, command);
|
||||
w_ctr(udpp_base, 0x05);
|
||||
|
||||
for (loop=0; loop < 10; loop++)
|
||||
if ((value = r_str(udpp_base)) & 0x80)
|
||||
{
|
||||
w_ctr(udpp_base, 0x04);
|
||||
return value & 0xf8;
|
||||
}
|
||||
|
||||
return (value & 0xf8) | 0x01;
|
||||
}
|
||||
</programlisting>
|
||||
<para>
|
||||
For the UMAX Astra 600P only seven such routines exist (well
|
||||
14 really, seven for SPP and seven for EPP). Whether you
|
||||
choose to disassemble the driver at this point to verify the
|
||||
routines is your own choice. If you do, the address from the
|
||||
trace should help in locating them in the disassembly.
|
||||
</para>
|
||||
<para>
|
||||
You will probably then find it useful to write a script/perl/C
|
||||
program to analyse the logfile and decode them futher as this
|
||||
can reveal higher level grouping of the low level routines.
|
||||
For example from the logs from my UMAX Astra 600P when decoded
|
||||
further reveal (this is a small snippet)
|
||||
</para>
|
||||
<programlisting>
|
||||
start:
|
||||
put: 55 8f
|
||||
put: aa 8f
|
||||
put: 00 8f
|
||||
put: 00 8f
|
||||
put: 00 8f
|
||||
put: c2 8f
|
||||
wait: ff
|
||||
get: af,87
|
||||
wait: ff
|
||||
get: af,87
|
||||
end: cc
|
||||
start:
|
||||
put: 55 8f
|
||||
put: aa 8f
|
||||
put: 00 8f
|
||||
put: 03 8f
|
||||
put: 05 8f
|
||||
put: 84 8f
|
||||
wait: ff
|
||||
</programlisting>
|
||||
<para>
|
||||
From this it is easy to see that <varname>put</varname>
|
||||
routine is often grouped together in five successive calls
|
||||
sending information to the scanner. Once these are understood
|
||||
it should be possible to process the logs further to show the
|
||||
higher level routines in an easy to see format. Once the
|
||||
highest level format that you can derive from this process is
|
||||
understood, you then need to produce a series of scans varying
|
||||
only one parameter between them, so you can discover how to
|
||||
set the various parameters for the scanner.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The following is the <filename>shrink.c</filename> program:
|
||||
<programlisting>
|
||||
/* Copyright David Campbell <campbell@torque.net> */
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
int main (void)
|
||||
{
|
||||
char buff[256], lastline[256] = "";
|
||||
int count = 0;
|
||||
|
||||
while (!feof (stdin))
|
||||
{
|
||||
fgets (buff, sizeof (buff), stdin);
|
||||
if (strcmp (buff, lastline))
|
||||
{
|
||||
if (count > 1)
|
||||
printf ("# Last line repeated %i times #\n", count);
|
||||
printf ("%s", buff);
|
||||
strcpy (lastline, buff);
|
||||
count = 1;
|
||||
}
|
||||
else count++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
</programlisting>
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="undoc-func">
|
||||
<title>Understanding undocumented APIs</title>
|
||||
|
||||
<para>
|
||||
Some background: On the i386 class of machines, stack entries are
|
||||
usually dword (4 bytes) in size, little-endian. The stack grows
|
||||
downward in memory. The stack pointer, maintained in the
|
||||
<literal>esp</literal> register, points to the last valid entry;
|
||||
thus, the operation of pushing a value onto the stack involves
|
||||
decrementing <literal>esp</literal> and then moving the value into
|
||||
the memory pointed to by <literal>esp</literal>
|
||||
(i.e., <literal>push p</literal> in assembly resembles
|
||||
<literal>*(--esp) = p;</literal> in C). Removing (popping)
|
||||
values off the stack is the reverse (i.e., <literal>pop p</literal>
|
||||
corresponds to <literal>p = *(esp++);</literal> in C).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
In the <literal>stdcall</literal> calling convention, arguments are
|
||||
pushed onto the stack right-to-left. For example, the C call
|
||||
<function>myfunction(40, 20, 70, 30);</function> is expressed in
|
||||
Intel assembly as:
|
||||
<screen>
|
||||
push 30
|
||||
push 70
|
||||
push 20
|
||||
push 40
|
||||
call myfunction
|
||||
</screen>
|
||||
The called function is responsible for removing the arguments
|
||||
off the stack. Thus, before the call to myfunction, the
|
||||
stack would look like:
|
||||
<screen>
|
||||
[local variable or temporary]
|
||||
[local variable or temporary]
|
||||
30
|
||||
70
|
||||
20
|
||||
esp -> 40
|
||||
</screen>
|
||||
After the call returns, it should look like:
|
||||
<screen>
|
||||
[local variable or temporary]
|
||||
esp -> [local variable or temporary]
|
||||
</screen>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To restore the stack to this state, the called function must know how
|
||||
many arguments to remove (which is the number of arguments it takes).
|
||||
This is a problem if the function is undocumented.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
One way to attempt to document the number of arguments each function
|
||||
takes is to create a wrapper around that function that detects the
|
||||
stack offset. Essentially, each wrapper assumes that the function will
|
||||
take a large number of arguments. The wrapper copies each of these
|
||||
arguments into its stack, calls the actual function, and then calculates
|
||||
the number of arguments by checking esp before and after the call.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The main problem with this scheme is that the function must actually
|
||||
be called from another program. Many of these functions are seldom
|
||||
used. An attempt was made to aggressively query each function in a
|
||||
given library (<filename>ntdll.dll</filename>) by passing 64 arguments,
|
||||
all 0, to each function. Unfortunately, Windows NT quickly goes to a
|
||||
blue screen of death, even if the program is run from a
|
||||
non-administrator account.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Another method that has been much more successful is to attempt to
|
||||
figure out how many arguments each function is removing from the
|
||||
stack. This instruction, <literal>ret hhll</literal> (where
|
||||
<symbol>hhll</symbol> is the number of bytes to remove, i.e. the
|
||||
number of arguments times 4), contains the bytes
|
||||
<literal>0xc2 ll hh</literal> in memory. It is a reasonable
|
||||
assumption that few, if any, functions take more than 16 arguments;
|
||||
therefore, simply searching for
|
||||
<literal>hh == 0 && ll < 0x40</literal> starting from the
|
||||
address of a function yields the correct number of arguments most
|
||||
of the time.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Of course, this is not without errors. <literal>ret 00ll</literal>
|
||||
is not the only instruction that can have the byte sequence
|
||||
<literal>0xc2 ll 0x0</literal>; for example,
|
||||
<literal>push 0x000040c2</literal> has the byte sequence
|
||||
<literal>0x68 0xc2 0x40 0x0 0x0</literal>, which matches
|
||||
the above. Properly, the utility should look for this sequence
|
||||
only on an instruction boundary; unfortunately, finding
|
||||
instruction boundaries on an i386 requires implementing a full
|
||||
disassembler -- quite a daunting task. Besides, the probability
|
||||
of having such a byte sequence that is not the actual return
|
||||
instruction is fairly low.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Much more troublesome is the non-linear flow of a function. For
|
||||
example, consider the following two functions:
|
||||
<screen>
|
||||
somefunction1:
|
||||
jmp somefunction1_impl
|
||||
|
||||
somefunction2:
|
||||
ret 0004
|
||||
|
||||
somefunction1_impl:
|
||||
ret 0008
|
||||
</screen>
|
||||
In this case, we would incorrectly detect both
|
||||
<function>somefunction1</function> and
|
||||
<function>somefunction2</function> as taking only a single
|
||||
argument, whereas <function>somefunction1</function> really
|
||||
takes two arguments.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
With these limitations in mind, it is possible to implement
|
||||
more stubs
|
||||
in Wine and, eventually, the functions themselves.
|
||||
</para>
|
||||
</sect1>
|
||||
<sect1>
|
||||
<title>How to do regression testing using CVS</title>
|
||||
|
||||
<para>
|
||||
A problem that can happen sometimes is 'it used to work
|
||||
before, now it doesn't anymore...'. Here is a step by step
|
||||
procedure to try to pinpoint when the problem occurred. This
|
||||
is <emphasis>NOT</emphasis> for casual users.
|
||||
</para>
|
||||
|
||||
<orderedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
Get the <quote>full CVS</quote> archive from winehq. This
|
||||
archive is the CVS tree but with the tags controlling the
|
||||
versioning system. It's a big file (> 40 meg) with a name
|
||||
like full-cvs-<last update date> (it's more than 100mb
|
||||
when uncompressed, you can't very well do this with
|
||||
small, old computers or slow Internet connections).
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
untar it into a repository directory:
|
||||
<screen>
|
||||
cd /home/gerard
|
||||
tar -zxf full-cvs-2003-08-18.tar.gz
|
||||
mv wine repository
|
||||
</screen>
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
extract a new destination directory. This directory must
|
||||
not be in a subdirectory of the repository else
|
||||
<command>cvs</command> will think it's part of the
|
||||
repository and deny you an extraction in the repository:
|
||||
<screen>
|
||||
cd /home/gerard
|
||||
mv wine wine_current (-> this protects your current wine sandbox, if any)
|
||||
export CVSROOT=/home/gerard/repository
|
||||
cvs -d $CVSROOT checkout wine
|
||||
</screen>
|
||||
</para>
|
||||
<para>
|
||||
Note that it's not possible to do a checkout at a given
|
||||
date; you always do the checkout for the last date where
|
||||
the full-cvs-xxx snapshot was generated.
|
||||
</para>
|
||||
<para>
|
||||
Note also that it is possible to do all this with a direct
|
||||
CVS connection, of course. The full CVS file method is less
|
||||
painful for the WineHQ CVS server and probably a bit faster
|
||||
if you don't have a very good net connection.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
you will have now in the <filename>~/wine</filename>
|
||||
directory an image of the CVS tree, on the client side.
|
||||
Now update this image to the date you want:
|
||||
<screen>
|
||||
cd /home/gerard/wine
|
||||
cvs update -PAd -D "2004-08-23 CDT"
|
||||
</screen>
|
||||
</para>
|
||||
<para>
|
||||
The date format is <literal>YYYY-MM-DD HH:MM:SS</literal>.
|
||||
Using the CST date format ensure that you will be able to
|
||||
extract patches in a way that will be compatible with the
|
||||
wine-cvs archive
|
||||
<ulink url="http://www.winehq.org/hypermail/wine-cvs">
|
||||
http://www.winehq.org/hypermail/wine-cvs</ulink>
|
||||
</para>
|
||||
<para>
|
||||
Many messages will inform you that more recent files have
|
||||
been deleted to set back the client cvs tree to the date
|
||||
you asked, for example:
|
||||
<screen>
|
||||
cvs update: tsx11/ts_xf86dga2.c is no longer in the repository
|
||||
</screen>
|
||||
</para>
|
||||
<para>
|
||||
<command>cvs update</command> is not limited to upgrade to
|
||||
a <emphasis>newer</emphasis> version as I have believed for
|
||||
far too long :-(
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Now proceed as for a normal update:
|
||||
</para>
|
||||
<screen>
|
||||
./configure
|
||||
make depend && make
|
||||
</screen>
|
||||
<para>
|
||||
If any non-programmer reads this, the fastest method to
|
||||
get at the point where the problem occurred is to use a
|
||||
binary search, that is, if the problem occurred in 1999,
|
||||
start at mid-year, then is the problem is already here,
|
||||
back to 1st April, if not, to 1st October, and so on.
|
||||
</para>
|
||||
<para>
|
||||
If you have lot of hard disk free space (a full compile
|
||||
currently takes 400 Mb), copy the oldest known working
|
||||
version before updating it, it will save time if you need
|
||||
to go back. (it's better to <command>make
|
||||
distclean</command> before going back in time, so you
|
||||
have to make everything if you don't backup the older
|
||||
version)
|
||||
</para>
|
||||
<para>
|
||||
When you have found the day where the problem happened,
|
||||
continue the search using the wine-cvs archive (sorted by
|
||||
date) and a more precise cvs update including hour,
|
||||
minute, second:
|
||||
<screen>
|
||||
cvs update -PAd -D "2004-08-23 15:17:25 CDT"
|
||||
</screen>
|
||||
This will allow you to find easily the exact patch that
|
||||
did it.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
If you find the patch that is the cause of the problem,
|
||||
you have almost won; report about it to
|
||||
<ulink url="http://bugs.winehq.org/">Wine Bugzilla</ulink>
|
||||
or subscribe to wine-devel and post it there. There is a
|
||||
chance that the author will jump in to suggest a fix; or
|
||||
there is always the possibility to look hard at the patch
|
||||
until it is coerced to reveal where is the bug :-)
|
||||
</para>
|
||||
</listitem>
|
||||
</orderedlist>
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
||||
|
||||
<!-- Keep this comment at the end of the file
|
||||
Local variables:
|
||||
mode: sgml
|
||||
sgml-parent-document:("wine-devel.sgml" "set" "book" "part" "chapter" "")
|
||||
End:
|
||||
-->
|
673
documentation/winedev-windowing.sgml
Normal file
673
documentation/winedev-windowing.sgml
Normal file
@ -0,0 +1,673 @@
|
||||
<chapter>
|
||||
<title>Windowing system</title>
|
||||
<sect1>
|
||||
<title>USER Module</title>
|
||||
|
||||
<para>
|
||||
USER implements windowing and messaging subsystems. It also
|
||||
contains code for common controls and for other
|
||||
miscellaneous stuff (rectangles, clipboard, WNet, etc).
|
||||
Wine USER code is located in <filename>windows/</filename>,
|
||||
<filename>controls/</filename>, and
|
||||
<filename>misc/</filename> directories.
|
||||
</para>
|
||||
|
||||
<sect2>
|
||||
<title>Windowing subsystem</title>
|
||||
|
||||
<para><filename>windows/win.c</filename></para>
|
||||
<para><filename>windows/winpos.c</filename></para>
|
||||
<para>
|
||||
Windows are arranged into parent/child hierarchy with one
|
||||
common ancestor for all windows (desktop window). Each
|
||||
window structure contains a pointer to the immediate
|
||||
ancestor (parent window if <constant>WS_CHILD</constant>
|
||||
style bit is set), a pointer to the sibling (returned by
|
||||
<function>GetWindow(..., GW_NEXT)</function>), a pointer
|
||||
to the owner window (set only for popup window if it was
|
||||
created with valid <varname>hwndParent</varname>
|
||||
parameter), and a pointer to the first child window
|
||||
(<function>GetWindow(.., GW_CHILD)</function>). All popup
|
||||
and non-child windows are therefore placed in the first
|
||||
level of this hierarchy and their ancestor link
|
||||
(<varname>wnd->parent</varname>) points to the desktop
|
||||
window.
|
||||
</para>
|
||||
<screen>
|
||||
Desktop window - root window
|
||||
| \ `-.
|
||||
| \ `-.
|
||||
popup -> wnd1 -> wnd2 - top level windows
|
||||
| \ `-. `-.
|
||||
| \ `-. `-.
|
||||
child1 child2 -> child3 child4 - child windows
|
||||
</screen>
|
||||
<para>
|
||||
Horizontal arrows denote sibling relationship, vertical
|
||||
lines - ancestor/child. To summarize, all windows with the
|
||||
same immediate ancestor are sibling windows, all windows
|
||||
which do not have desktop as their immediate ancestor are
|
||||
child windows. Popup windows behave as topmost top-level
|
||||
windows unless they are owned. In this case the only
|
||||
requirement is that they must precede their owners in the
|
||||
top-level sibling list (they are not topmost). Child
|
||||
windows are confined to the client area of their parent
|
||||
windows (client area is where window gets to do its own
|
||||
drawing, non-client area consists of caption, menu,
|
||||
borders, intrinsic scrollbars, and
|
||||
minimize/maximize/close/help buttons).
|
||||
</para>
|
||||
<para>
|
||||
Another fairly important concept is
|
||||
<firstterm>z-order</firstterm>. It is derived from the
|
||||
ancestor/child hierarchy and is used to determine
|
||||
"above/below" relationship. For instance, in the example
|
||||
above, z-order is
|
||||
</para>
|
||||
<screen>
|
||||
child1->popup->child2->child3->wnd1->child4->wnd2->desktop.
|
||||
</screen>
|
||||
<para>
|
||||
Current active window ("foreground window" in Win32) is
|
||||
moved to the front of z-order unless its top-level
|
||||
ancestor owns popup windows.
|
||||
</para>
|
||||
<para>
|
||||
All these issues are dealt with (or supposed to be) in
|
||||
<filename>windows/winpos.c</filename> with
|
||||
<function>SetWindowPos()</function> being the primary
|
||||
interface to the window manager.
|
||||
</para>
|
||||
<para>
|
||||
Wine specifics: in default and managed mode each top-level
|
||||
window gets its own X counterpart with desktop window
|
||||
being basically a fake stub. In desktop mode, however,
|
||||
only desktop window has an X window associated with it.
|
||||
Also, <function>SetWindowPos()</function> should
|
||||
eventually be implemented via
|
||||
<function>Begin/End/DeferWindowPos()</function> calls and
|
||||
not the other way around.
|
||||
</para>
|
||||
|
||||
<sect3>
|
||||
<title>Visible region, clipping region and update region</title>
|
||||
|
||||
<para><filename>windows/dce.c</filename></para>
|
||||
<para><filename>windows/winpos.c</filename></para>
|
||||
<para><filename>windows/painting.c</filename></para>
|
||||
|
||||
<screen>
|
||||
________________________
|
||||
|_________ | A and B are child windows of C
|
||||
| A |______ |
|
||||
| | | |
|
||||
|---------' | |
|
||||
| | B | |
|
||||
| | | |
|
||||
| `------------' |
|
||||
| C |
|
||||
`------------------------'
|
||||
</screen>
|
||||
<para>
|
||||
Visible region determines which part of the window is
|
||||
not obscured by other windows. If a window has the
|
||||
<constant>WS_CLIPCHILDREN</constant> style then all
|
||||
areas below its children are considered invisible.
|
||||
Similarly, if the <constant>WS_CLIPSIBLINGS</constant>
|
||||
bit is in effect then all areas obscured by its siblings
|
||||
are invisible. Child windows are always clipped by the
|
||||
boundaries of their parent windows.
|
||||
</para>
|
||||
<para>
|
||||
B has a <constant>WS_CLIPSIBLINGS</constant> style:
|
||||
</para>
|
||||
<screen>
|
||||
. ______
|
||||
: | |
|
||||
| ,-----' |
|
||||
| | B | - visible region of B
|
||||
| | |
|
||||
: `------------'
|
||||
</screen>
|
||||
<para>
|
||||
When the program requests a <firstterm>display
|
||||
context</firstterm> (DC) for a window it can specify
|
||||
an optional clipping region that further restricts the
|
||||
area where the graphics output can appear. This area is
|
||||
calculated as an intersection of the visible region and
|
||||
a clipping region.
|
||||
</para>
|
||||
<para>
|
||||
Program asked for a DC with a clipping region:
|
||||
</para>
|
||||
<screen>
|
||||
______
|
||||
,--|--. | . ,--.
|
||||
,--+--' | | : _: |
|
||||
| | B | | => | | | - DC region where the painting will
|
||||
| | | | | | | be visible
|
||||
`--|-----|---' : `----'
|
||||
`-----'
|
||||
</screen>
|
||||
<para>
|
||||
When the window manager detects that some part of the window
|
||||
became visible it adds this area to the update region of this
|
||||
window and then generates <constant>WM_ERASEBKGND</constant> and
|
||||
<constant>WM_PAINT</constant> messages. In addition,
|
||||
<constant>WM_NCPAINT</constant> message is sent when the
|
||||
uncovered area intersects a nonclient part of the window.
|
||||
Application must reply to the <constant>WM_PAINT</constant>
|
||||
message by calling the
|
||||
<function>BeginPaint()</function>/<function>EndPaint()</function>
|
||||
pair of functions. <function>BeginPaint()</function> returns a DC
|
||||
that uses accumulated update region as a clipping region. This
|
||||
operation cleans up invalidated area and the window will not
|
||||
receive another <constant>WM_PAINT</constant> until the window
|
||||
manager creates a new update region.
|
||||
</para>
|
||||
<para>
|
||||
A was moved to the left:
|
||||
</para>
|
||||
<screen>
|
||||
________________________ ... / C update region
|
||||
|______ | : .___ /
|
||||
| A |_________ | => | ...|___|..
|
||||
| | | | | : | |
|
||||
|------' | | | : '---'
|
||||
| | B | | | : \
|
||||
| | | | : \
|
||||
| `------------' | B update region
|
||||
| C |
|
||||
`------------------------'
|
||||
</screen>
|
||||
<para>
|
||||
Windows maintains a display context cache consisting of
|
||||
entries that include the DC itself, the window to which
|
||||
it belongs, and an optional clipping region (visible
|
||||
region is stored in the DC itself). When an API call
|
||||
changes the state of the window tree, window manager has
|
||||
to go through the DC cache to recalculate visible
|
||||
regions for entries whose windows were involved in the
|
||||
operation. DC entries (DCE) can be either private to the
|
||||
window, or private to the window class, or shared
|
||||
between all windows (Windows 3.1 limits the number of
|
||||
shared DCEs to 5).
|
||||
</para>
|
||||
</sect3>
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Messaging subsystem</title>
|
||||
|
||||
<para><filename>windows/queue.c</filename></para>
|
||||
<para><filename>windows/message.c</filename></para>
|
||||
|
||||
<para>
|
||||
Each Windows task/thread has its own message queue - this
|
||||
is where it gets messages from. Messages can be:
|
||||
<orderedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
generated on the fly (<constant>WM_PAINT</constant>,
|
||||
<constant>WM_NCPAINT</constant>,
|
||||
<constant>WM_TIMER</constant>)
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
created by the system (hardware messages)
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
posted by other tasks/threads (<function>PostMessage</function>)
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
sent by other tasks/threads (<function>SendMessage</function>)
|
||||
</para>
|
||||
</listitem>
|
||||
</orderedlist>
|
||||
</para>
|
||||
<para>
|
||||
Message priority:
|
||||
</para>
|
||||
<para>
|
||||
First the system looks for sent messages, then for posted
|
||||
messages, then for hardware messages, then it checks if
|
||||
the queue has the "dirty window" bit set, and, finally, it
|
||||
checks for expired timers. See
|
||||
<filename>windows/message.c</filename>.
|
||||
</para>
|
||||
<para>
|
||||
From all these different types of messages, only posted
|
||||
messages go directly into the private message queue.
|
||||
System messages (even in Win95) are first collected in the
|
||||
system message queue and then they either sit there until
|
||||
<function>Get/PeekMessage</function> gets to process them
|
||||
or, as in Win95, if system queue is getting clobbered, a
|
||||
special thread ("raw input thread") assigns them to the
|
||||
private queues. Sent messages are queued separately and
|
||||
the sender sleeps until it gets a reply. Special messages
|
||||
are generated on the fly depending on the window/queue
|
||||
state. If the window update region is not empty, the
|
||||
system sets the <constant>QS_PAINT</constant> bit in the
|
||||
owning queue and eventually this window receives a
|
||||
<constant>WM_PAINT</constant> message
|
||||
(<constant>WM_NCPAINT</constant> too if the update region
|
||||
intersects with the non-client area). A timer event is
|
||||
raised when one of the queue timers expire. Depending on
|
||||
the timer parameters <function>DispatchMessage</function>
|
||||
either calls the callback function or the window
|
||||
procedure. If there are no messages pending the
|
||||
task/thread sleeps until messages appear.
|
||||
</para>
|
||||
<para>
|
||||
There are several tricky moments (open for discussion) -
|
||||
</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
System message order has to be honored and messages
|
||||
should be processed within correct task/thread
|
||||
context. Therefore when <function>Get/PeekMessage</function> encounters
|
||||
unassigned system message and this message appears not
|
||||
to be for the current task/thread it should either
|
||||
skip it (or get rid of it by moving it into the
|
||||
private message queue of the target task/thread -
|
||||
Win95, AFAIK) and look further or roll back and then
|
||||
yield until this message gets processed when system
|
||||
switches to the correct context (Win16). In the first
|
||||
case we lose correct message ordering, in the second
|
||||
case we have the infamous synchronous system message
|
||||
queue. Here is a post to one of the OS/2 newsgroup I
|
||||
found to be relevant:
|
||||
</para>
|
||||
<blockquote>
|
||||
<attribution>by David Charlap</attribution>
|
||||
<para>
|
||||
" Here's the problem in a nutshell, and there is no
|
||||
good solution. Every possible solution creates a
|
||||
different problem.
|
||||
</para>
|
||||
<para>
|
||||
With a windowing system, events can go to many
|
||||
different windows. Most are sent by applications or
|
||||
by the OS when things relating to that window happen
|
||||
(like repainting, timers, etc.)
|
||||
</para>
|
||||
<para>
|
||||
Mouse input events go to the window you click on
|
||||
(unless some window captures the mouse).
|
||||
</para>
|
||||
<para>
|
||||
So far, no problem. Whenever an event happens, you
|
||||
put a message on the target window's message queue.
|
||||
Every process has a message queue. If the process
|
||||
queue fills up, the messages back up onto the system
|
||||
queue.
|
||||
</para>
|
||||
<para>
|
||||
This is the first cause of apps hanging the GUI. If
|
||||
an app doesn't handle messages and they back up into
|
||||
the system queue, other apps can't get any more
|
||||
messages. The reason is that the next message in
|
||||
line can't go anywhere, and the system won't skip
|
||||
over it.
|
||||
</para>
|
||||
<para>
|
||||
This can be fixed by making apps have bigger private
|
||||
message queues. The SIQ fix does this. PMQSIZE does
|
||||
this for systems without the SIQ fix. Applications
|
||||
can also request large queues on their own.
|
||||
</para>
|
||||
<para>
|
||||
Another source of the problem, however, happens when
|
||||
you include keyboard events. When you press a key,
|
||||
there's no easy way to know what window the
|
||||
keystroke message should be delivered to.
|
||||
</para>
|
||||
<para>
|
||||
Most windowing systems use a concept known as
|
||||
"focus". The window with focus gets all incoming
|
||||
keyboard messages. Focus can be changed from window
|
||||
to window by apps or by users clicking on windows.
|
||||
</para>
|
||||
<para>
|
||||
This is the second source of the problem. Suppose
|
||||
window A has focus. You click on window B and start
|
||||
typing before the window gets focus. Where should
|
||||
the keystrokes go? On the one hand, they should go
|
||||
to A until the focus actually changes to B. On the
|
||||
other hand, you probably want the keystrokes to go
|
||||
to B, since you clicked there first.
|
||||
</para>
|
||||
<para>
|
||||
OS/2's solution is that when a focus-changing event
|
||||
happens (like clicking on a window), OS/2 holds all
|
||||
messages in the system queue until the focus change
|
||||
actually happens. This way, subsequent keystrokes
|
||||
go to the window you clicked on, even if it takes a
|
||||
while for that window to get focus.
|
||||
</para>
|
||||
<para>
|
||||
The downside is that if the window takes a real long
|
||||
time to get focus (maybe it's not handling events,
|
||||
or maybe the window losing focus isn't handling
|
||||
events), everything backs up in the system queue and
|
||||
the system appears hung.
|
||||
</para>
|
||||
<para>
|
||||
There are a few solutions to this problem.
|
||||
</para>
|
||||
<para>
|
||||
One is to make focus policy asynchronous. That is,
|
||||
focus changing has absolutely nothing to do with the
|
||||
keyboard. If you click on a window and start typing
|
||||
before the focus actually changes, the keystrokes go
|
||||
to the first window until focus changes, then they
|
||||
go to the second. This is what X-windows does.
|
||||
</para>
|
||||
<para>
|
||||
Another is what NT does. When focus changes,
|
||||
keyboard events are held in the system message
|
||||
queue, but other events are allowed through. This is
|
||||
"asynchronous" because the messages in the system
|
||||
queue are delivered to the application queues in a
|
||||
different order from that with which they were
|
||||
posted. If a bad app won't handle the "lose focus"
|
||||
message, it's of no consequence - the app receiving
|
||||
focus will get its "gain focus" message, and the
|
||||
keystrokes will go to it.
|
||||
</para>
|
||||
<para>
|
||||
The NT solution also takes care of the application
|
||||
queue filling up problem. Since the system delivers
|
||||
messages asynchronously, messages waiting in the
|
||||
system queue will just sit there and the rest of the
|
||||
messages will be delivered to their apps.
|
||||
</para>
|
||||
<para>
|
||||
The OS/2 SIQ solution is this: When a
|
||||
focus-changing event happens, in addition to
|
||||
blocking further messages from the application
|
||||
queues, a timer is started. When the timer goes
|
||||
off, if the focus change has not yet happened, the
|
||||
bad app has its focus taken away and all messages
|
||||
targeted at that window are skipped. When the bad
|
||||
app finally handles the focus change message, OS/2
|
||||
will detect this and stop skipping its messages.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
As for the pros and cons:
|
||||
</para>
|
||||
<para>
|
||||
The X-windows solution is probably the easiest. The
|
||||
problem is that users generally don't like having to
|
||||
wait for the focus to change before they start
|
||||
typing. On many occasions, you can type and the
|
||||
characters end up in the wrong window because
|
||||
something (usually heavy system load) is preventing
|
||||
the focus change from happening in a timely manner.
|
||||
</para>
|
||||
<para>
|
||||
The NT solution seems pretty nice, but making the
|
||||
system message queue asynchronous can cause similar
|
||||
problems to the X-windows problem. Since messages
|
||||
can be delivered out of order, programs must not
|
||||
assume that two messages posted in a particular
|
||||
order will be delivered in that same order. This
|
||||
can break legacy apps, but since Win32 always had an
|
||||
asynchronous queue, it is fair to simply tell app
|
||||
designers "don't do that". It's harder to tell app
|
||||
designers something like that on OS/2 - they'll
|
||||
complain "you changed the rules and our apps are
|
||||
breaking."
|
||||
</para>
|
||||
<para>
|
||||
The OS/2 solution's problem is that nothing happens
|
||||
until you try to change window focus, and then wait
|
||||
for the timeout. Until then, the bad app is not
|
||||
detected and nothing is done."
|
||||
</para>
|
||||
</blockquote>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
Intertask/interthread
|
||||
<function>SendMessage</function>. The system has to
|
||||
inform the target queue about the forthcoming message,
|
||||
then it has to carry out the context switch and wait
|
||||
until the result is available. Win16 stores necessary
|
||||
parameters in the queue structure and then calls
|
||||
<function>DirectedYield()</function> function.
|
||||
However, in Win32 there could be several messages
|
||||
pending sent by preemptively executing threads, and in
|
||||
this case <function>SendMessage</function> has to
|
||||
build some sort of message queue for sent messages.
|
||||
Another issue is what to do with messages sent to the
|
||||
sender when it is blocked inside its own
|
||||
<function>SendMessage</function>.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</sect2>
|
||||
<sect2 id="accel-impl">
|
||||
<title>Accelerators</title>
|
||||
|
||||
<para>
|
||||
There are <emphasis>three</emphasis> differently sized
|
||||
accelerator structures exposed to the user:
|
||||
</para>
|
||||
<orderedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
Accelerators in NE resources. This is also the internal
|
||||
layout of the global handle <type>HACCEL</type> (16 and
|
||||
32) in Windows 95 and Wine. Exposed to the user as Win16
|
||||
global handles <type>HACCEL16</type> and
|
||||
<type>HACCEL32</type> by the Win16/Win32 API.
|
||||
These are 5 bytes long, with no padding:
|
||||
<programlisting>
|
||||
BYTE fVirt;
|
||||
WORD key;
|
||||
WORD cmd;
|
||||
</programlisting>
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Accelerators in PE resources. They are exposed to the
|
||||
user only by direct accessing PE resources. These have a
|
||||
size of 8 bytes:
|
||||
</para>
|
||||
<programlisting>
|
||||
BYTE fVirt;
|
||||
BYTE pad0;
|
||||
WORD key;
|
||||
WORD cmd;
|
||||
WORD pad1;
|
||||
</programlisting>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Accelerators in the Win32 API. These are exposed to the
|
||||
user by the <function>CopyAcceleratorTable</function>
|
||||
and <function>CreateAcceleratorTable</function> functions
|
||||
in the Win32 API.
|
||||
These have a size of 6 bytes:
|
||||
</para>
|
||||
<programlisting>
|
||||
BYTE fVirt;
|
||||
BYTE pad0;
|
||||
WORD key;
|
||||
WORD cmd;
|
||||
</programlisting>
|
||||
</listitem>
|
||||
</orderedlist>
|
||||
|
||||
<para>
|
||||
Why two types of accelerators in the Win32 API? We can only
|
||||
guess, but my best bet is that the Win32 resource compiler
|
||||
can/does not handle struct packing. Win32 <type>ACCEL</type>
|
||||
is defined using <function>#pragma(2)</function> for the
|
||||
compiler but without any packing for RC, so it will assume
|
||||
<function>#pragma(4)</function>.
|
||||
</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
<sect1>
|
||||
<title>X Windows System interface</title>
|
||||
<para></para>
|
||||
<sect2>
|
||||
<title>Keyboard mapping</title>
|
||||
<para>
|
||||
Wine now needs to know about your keyboard layout. This
|
||||
requirement comes from a need from many apps to have the
|
||||
correct scancodes available, since they read these directly,
|
||||
instead of just taking the characters returned by the X
|
||||
server. This means that Wine now needs to have a mapping
|
||||
from X keys to the scancodes these programs expect.
|
||||
</para>
|
||||
<para>
|
||||
On startup, Wine will try to recognize the active X layout
|
||||
by seeing if it matches any of the defined tables. If it
|
||||
does, everything is alright. If not, you need to define it.
|
||||
</para>
|
||||
<para>
|
||||
To do this, open the file
|
||||
<filename>dlls/x11drv/keyboard.c</filename> and take a look
|
||||
at the existing tables. Make a backup copy of it, especially
|
||||
if you don't use CVS.
|
||||
</para>
|
||||
<para>
|
||||
What you really would need to do, is find out which scancode
|
||||
each key needs to generate. Find it in the
|
||||
<function>main_key_scan</function> table, which looks like
|
||||
this:
|
||||
</para>
|
||||
<programlisting>
|
||||
static const int main_key_scan[MAIN_LEN] =
|
||||
{
|
||||
/* this is my (102-key) keyboard layout, sorry if it doesn't quite match yours */
|
||||
0x29,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,
|
||||
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,
|
||||
0x1E,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x2B,
|
||||
0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33,0x34,0x35,
|
||||
0x56 /* the 102nd key (actually to the right of l-shift) */
|
||||
};
|
||||
</programlisting>
|
||||
<para>
|
||||
Next, assign each scancode the characters imprinted on the
|
||||
keycaps. This was done (sort of) for the US 101-key keyboard,
|
||||
which you can find near the top in
|
||||
<filename>keyboard.c</filename>. It also shows that if there
|
||||
is no 102nd key, you can skip that.
|
||||
</para>
|
||||
<para>
|
||||
However, for most international 102-key keyboards, we have
|
||||
done it easy for you. The scancode layout for these already
|
||||
pretty much matches the physical layout in the
|
||||
<function>main_key_scan</function>, so all you need to do is
|
||||
to go through all the keys that generate characters on your
|
||||
main keyboard (except spacebar), and stuff those into an
|
||||
appropriate table. The only exception is that the 102nd key,
|
||||
which is usually to the left of the first key of the last
|
||||
line (usually <keycap>Z</keycap>), must be placed on a
|
||||
separate line after the last line.
|
||||
</para>
|
||||
<para>
|
||||
For example, my Norwegian keyboard looks like this
|
||||
</para>
|
||||
<screen>
|
||||
§ ! " # ¤ % & / ( ) = ? ` Back-
|
||||
| 1 2@ 3£ 4$ 5 6 7{ 8[ 9] 0} + \´ space
|
||||
|
||||
Tab Q W E R T Y U I O P Å ^
|
||||
¨~
|
||||
Enter
|
||||
Caps A S D F G H J K L Ø Æ *
|
||||
Lock '
|
||||
|
||||
Sh- > Z X C V B N M ; : _ Shift
|
||||
ift < , . -
|
||||
|
||||
Ctrl Alt Spacebar AltGr Ctrl
|
||||
</screen>
|
||||
<para>
|
||||
Note the 102nd key, which is the <keycap><></keycap> key, to
|
||||
the left of <keycap>Z</keycap>. The character to the right of
|
||||
the main character is the character generated by
|
||||
<keycap>AltGr</keycap>.
|
||||
</para>
|
||||
<para>
|
||||
This keyboard is defined as follows:
|
||||
</para>
|
||||
<programlisting>
|
||||
static const char main_key_NO[MAIN_LEN][4] =
|
||||
{
|
||||
"|§","1!","2\"@","3#£","4¤$","5%","6&","7/{","8([","9)]","0=}","+?","\\´",
|
||||
"qQ","wW","eE","rR","tT","yY","uU","iI","oO","pP","åÅ","¨^~",
|
||||
"aA","sS","dD","fF","gG","hH","jJ","kK","lL","øØ","æÆ","'*",
|
||||
"zZ","xX","cC","vV","bB","nN","mM",",;",".:","-_",
|
||||
"<>"
|
||||
};
|
||||
</programlisting>
|
||||
<para>
|
||||
Except that " and \ needs to be quoted with a backslash, and
|
||||
that the 102nd key is on a separate line, it's pretty
|
||||
straightforward.
|
||||
</para>
|
||||
<para>
|
||||
After you have written such a table, you need to add it to the
|
||||
<function>main_key_tab[]</function> layout index table. This
|
||||
will look like this:
|
||||
</para>
|
||||
<programlisting>
|
||||
static struct {
|
||||
WORD lang, ansi_codepage, oem_codepage;
|
||||
const char (*key)[MAIN_LEN][4];
|
||||
} main_key_tab[]={
|
||||
...
|
||||
...
|
||||
{MAKELANGID(LANG_NORWEGIAN,SUBLANG_DEFAULT), 1252, 865, &main_key_NO},
|
||||
...
|
||||
</programlisting>
|
||||
<para>
|
||||
After you have added your table, recompile Wine and test that
|
||||
it works. If it fails to detect your table, try running
|
||||
</para>
|
||||
<screen>
|
||||
WINEDEBUG=+key,+keyboard wine > key.log 2>&1
|
||||
</screen>
|
||||
<para>
|
||||
and look in the resulting <filename>key.log</filename> file to
|
||||
find the error messages it gives for your layout.
|
||||
</para>
|
||||
<para>
|
||||
Note that the <constant>LANG_*</constant> and
|
||||
<constant>SUBLANG_*</constant> definitions are in
|
||||
<filename>include/winnls.h</filename>, which you might need
|
||||
to know to find out which numbers your language is assigned,
|
||||
and find it in the WINEDEBUG output. The numbers will be
|
||||
<literal>(SUBLANG * 0x400 + LANG)</literal>, so, for example
|
||||
the combination <literal>LANG_NORWEGIAN (0x14)</literal> and
|
||||
<literal>SUBLANG_DEFAULT (0x1)</literal> will be (in hex)
|
||||
<literal>14 + 1*400 = 414</literal>, so since I'm Norwegian,
|
||||
I could look for <literal>0414</literal> in the WINEDEBUG
|
||||
output to find out why my keyboard won't detect.
|
||||
</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
</chapter>
|
||||
|
||||
<!-- Keep this comment at the end of the file
|
||||
Local variables:
|
||||
mode: sgml
|
||||
sgml-parent-document:("wine-devel.sgml" "set" "book" "part" "chapter" "")
|
||||
End:
|
||||
-->
|
Loading…
Reference in New Issue
Block a user