674 lines
24 KiB
Plaintext
674 lines
24 KiB
Plaintext
|
<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>
|
|||
|
<EFBFBD> ! " # <20> % & / ( ) = ? ` Back-
|
|||
|
| 1 2@ 3<> 4$ 5 6 7{ 8[ 9] 0} + \<5C> space
|
|||
|
|
|||
|
Tab Q W E R T Y U I O P <20> ^
|
|||
|
<20>~
|
|||
|
Enter
|
|||
|
Caps A S D F G H J K L <20> <20> *
|
|||
|
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] =
|
|||
|
{
|
|||
|
"|<7C>","1!","2\"@","3#<23>","4<>$","5%","6&","7/{","8([","9)]","0=}","+?","\\<5C>",
|
|||
|
"qQ","wW","eE","rR","tT","yY","uU","iI","oO","pP","<22><>","<22>^~",
|
|||
|
"aA","sS","dD","fF","gG","hH","jJ","kK","lL","<22><>","<22><>","'*",
|
|||
|
"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:
|
|||
|
-->
|