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:
|
||
-->
|