USER32 Module
The USER32 module was originally where all public APIs went, before Microsoft started splitting them into separate DLLs. As a result, user32.dll contains exports with a wide variety of functions. Mostly, though, these fall into the categories of:
- window management
- code for some built-in windows and controls: dialogs, buttons, static text, input, etc.
- message queues (which are closely related to window management)
- keyboard accelerators
- clipboard management
- embedded resources
- string functions
USER32 code is located in the directory dlls/user32/
. The 16-bit
version of user32.dll is called user.exe, and it lives in the directory
dlls/user.exe16/
.
Windowing subsystem
-
dlls/user32/win.c
— contains most windowing functions (some of these are in winpos.c or focus.c) -
dlls/user32/winpos.c
— functions relating to window position (some of these are in win.c or focus.c) -
dlls/user32/focus.c
— functions relating to window focus (some of these are in winpos.c or win.c) -
dlls/user32/defwnd.c
— implementation of DefWindowProc() (some cases are in nonclient.c) -
dlls/user32/desktop.c
— implementation of the desktop window -
dlls/user32/driver.c
— interface to the USER driver -
dlls/user32/mdi.c
— Multi-Document Interface (MDI) windowing code -
dlls/user32/nonclient.c
— painting and message handling for the non-client area -
dlls/user32/painting.c
— window painting -
dlls/user32/property.c
— window properties [GetProp() etc.] -
dlls/user32/winstation.c
— window stations and desktops -
dlls/user.exe16/window.c
— 16-bit windowing code; mostly thunks to 32-bit with little modification (some functions are in user.c) -
server/window.c
— server-side windowing code -
server/winstation.c
— server-side window station code
Window basics
Windows is based on windows. As is usual for Windows APIs, there is a quite large amount of data that is associated with its particular window. This includes obvious information like position, size, and title text, but also some other information that you might not necessarily expect:
- Window style — a collection of bitfields detailing various aspects of the window, like whether it should clip its siblings, or whether it has a minimize box.
- Thread — all windows are tied to the thread in which they are created, due to the way that messages are implemented.
The window tree
Windows are arranged in a tree-based hierarchy. All windows have an immediate ancestor (i.e. a parent), and they may have descendants (i.e. children) and siblings as well. In general there are three kinds of windows with respect to this hierarchy:
- Top-level windows are windows created without an explicit parent. Internally (and, depending on the function, externally), these are children of the desktop window, which is a special window that represents the entire desktop.
-
Child windows are windows created with an explicit parent and
using the WS_CHILD style. These windows appear inside of the area of
their immediate ancestor, and don't show up in the taskbar.
- The Multi-Document Interface (MDI) is an API created to make navigating between multiple documents in one application easier, mostly used in older applications. MDI child windows are created with the style WS_EX_MDICHILD instead of WS_CHILD. They operate mostly like regular child windows, but have extra functionality making it easier to minimize, arrange, and switch between child windows.
- Owned windows are windows created with an explicit parent but without the WS_CHILD style. These windows act like normal windows, except that they always appear in front of their owner. An example use of owned windows to create a dialog that must be dealt with before returning to the main window.
Note the distinction between child windows and owned windows. These are mutually exclusive, and both are treated as the immediate ancestor as far as the window hierarchy is concerned, but the two act quite differently in some ways (though not in others). Accordingly they are stored separately under Wine.
The window tree does not have a limited depth—child and owned windows can have their own child and owned windows.
Z-order
Children of a window are stored in Z-order from top to bottom, so normally determining Z-order is just a matter of enumerating this list. However, some complications arise due to special window styles:
- Owned windows always appear above their owners.
- Topmost windows have the WS_EX_TOPMOST style. A topmost window always appears above all non-topmost windows, but follows normal z-order rules with regard to other topmost windows.
- Pop-up windows have the WS_POPUP style. Pop-up windows always act like topmost windows unless they are owned, in which case they behave normally (of course they still must appear above their owner).
The following example demonstrates window order. Note that popup1
behaves like a topmost window and so is listed before its siblings.
Desktop window - root window
|
+-------+------------------+
| | |
popup1 wnd2 wnd3 - top-level windows
| |
+----------+ |
| | |
owned2a owned2b owned3 - child windows
The full z-order in this example would be:
popup1->owned2a->owned2b->wnd2->owned3->wnd3->desktop.
Whenever a window is set to the foreground (usually by being activated), it is moved to the top of the z-order—at least, as far as possible; it will still be behind topmost windows, its owned windows, and non-owned popups.
Visible region, clipping region and update region
dlls/user32/winpos.c
dlls/user32/painting.c
________________________
|_________ | A and B are child windows of C
| A |______ |
| | | |
|---------' | |
| | B | |
| | | |
| `------------' |
| C |
`------------------------'
Visible region determines which part of the window is not obscured by
other windows. If a window has the WS_CLIPCHILDREN
style then all
areas below its children are considered invisible. Similarly, if the
WS_CLIPSIBLINGS
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.
B has a WS_CLIPSIBLINGS
style:
. ______
: | |
| ,-----' |
| | B | - visible region of B
| | |
: `------------'
When the program requests a display context (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.
Program asked for a DC with a clipping region:
______
,--|--. | . ,--.
,--+--' | | : _: |
| | B | | => | | | - DC region where the painting will
| | | | | | | be visible
`--|-----|---' : `----'
`-----'
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 WM_ERASEBKGND
and WM_PAINT
messages. In addition,
WM_NCPAINT
message is sent when the uncovered area intersects a
nonclient part of the window. Application must reply to the WM_PAINT
message by calling the BeginPaint()/EndPaint()
pair of functions.
BeginPaint()
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 WM_PAINT
until the window manager
creates a new update region.
A was moved to the left:
________________________ ... / C update region
|______ | : .___ /
| A |_________ | => | ...|___|..
| | | | | : | |
|------' | | | : '---'
| | B | | | : \
| | | | : \
| `------------' | B update region
| C |
`------------------------'
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).
Server-side window management
Most window information is kept per-process. However, there exist many functions that can modify or query windows that belong to a different process, and so it is necessary to bring the wineserver into the matter. Most information about a window is stored both in user32 and in the server. The server is notified when window information is changed, so that it can be read in current form by other processes. Operations that modify other processes' windows are in some cases relayed through the server directly, and in some cases handled with internal messages (and, in some cases, not implemented at all).
Debugging windows
The channel +win gives most of the relevant output to window size, position, focus, etc. Oftentimes you will want to use +win in conjunction with +msg, or, depending on the nature of the problem, one of the driver-specific log channels.
Messaging subsystem
-
dlls/user32/message.c
— contains code related to sending, receiving, and waiting for messages -
dlls/user32/winproc.c
— code directly related to calling window procedures -
dlls/user32/dde_client.c
— DDE client-side code -
dlls/user32/dde_server.c
— DDE server-side code -
dlls/user32/dde_misc.c
— miscellaneous DDE functions -
dlls/user32/spy.c
— extensive message debug tracing -
server/queue.c
— server-side message queue handling
Message basics
Windows interact with each other and with the user via messages. A message has, essentially, four parts: the window to which it is sent, the message type, and two parameters. The message type is taken from a constant list found in include/winuser.rh, and generally looks like WM_FOOBAR. The parameters are called wparam and lparam, and their meaning is specific to the message type sent.
Messages can be sent from application or system code; when sent they are placed in a queue. Each Windows thread (or, in Win16, task) has its own message queue. While in the queue messages are futher distinguished by their type, which influence how they will be taken out of the queue. The most important of these types are sent and posted messages (also called nonqueued and queued messages, respectively); other types include hardware messages and timer messages. The following enumerates how messages may be put into the queue and how they may be taken out:
-
SendMessage(...)
places a sent message into the window's queue, and blocks until it is received. -
PostMessage(...)
places a posted message into the window's queue, and returns immediately. -
SendNotifyMessage(...)
places a sent message into the window's queue, and returns immediately. -
SendMessageTimeout(...)
places a sent message into the window's queue, and blocks until it is received or until the given timeout expires. -
GetMessage(...)
blocks until a posted message is received. While it is blocking, it dispatches sent messages. -
PeekMessage(...)
dispatches sent messages, then checks whether a posted message exists in the queue, and optionally removes it. This function includes other optional parameters which act as a filter on the message type. -
The functions
SendMessage()
andSendMessageTimeout()
, when used to send a message to a window belonging to a different thread, process all sent messages in the queue. SendMessageTimeout() has an optional parameter to prevent this. -
GetQueueStatus(...)
returns the types of messages currently in the queue. -
MsgWaitForMultipleObjects(...)
is used to wait for zero or more synchronizable handles, as well as some to all types of messages. MsgWaitForMultipleObjects() does not process sent messages, but rather returns immediately when they are received.
The other types of messages are mainly distinguished by the order in which they are processed by GetMessage() and PeekMessage(). These functions (unless filtered) process all sent messages first, then posted messages, then hardware messages, then sent messages (again), then WM_PAINT messages, then finally WM_TIMER messages.
Message queues and the server
Since messages can be sent to windows in other threads and processes (and since the presence of a message can be synchronized on), it's necessary to keep server-side message queues. Each thread actually has three message queues in wineserver: one for sent messages, one for posted messages, and one (associated with the thread input descriptor) for hardware messages. A message sent by SendMessage() etc. to a window in the same thread will be delivered synchronously and directly to that window, but any other message must be added to one of these queues, which will then be emptied by GetMessage(), PeekMessage(), etc.
Debugging messages
There are basically three channels to use when debugging messages: +relay, +msg, and +message. +relay is normally used to print every call (and return) to a Windows API function; it also prints every call (and return) to a window procedure. +msg is the usual channel to resort to when the messaging subsystem does not appear to be functioning correctly (or, as is more often the case, it is being misused by some other component); this will trace calls to the functions given above and others. +message is a heavy-duty channel used to debug the messaging subsystem; it traces when a message is sent or posted, when it is received, when the window procedure returns, and when the returned value is received by the calling thread.
Accelerators
There are three differently sized accelerator structures exposed to the user:
- Accelerators in NE resources. This is also the internal layout of the global handle HACCEL (16 and 32) in Windows 95 and Wine. Exposed to the user as Win16 global handles HACCEL16 and HACCEL32 by the Win16/Win32 API. These are 5 bytes long, with no padding: BYTE fVirt; WORD key; WORD cmd;
- Accelerators in PE resources. They are exposed to the user only by direct accessing PE resources. These have a size of 8 bytes: BYTE fVirt; BYTE pad0; WORD key; WORD cmd; WORD pad1;
- Accelerators in the Win32 API. These are exposed to the user by the
CopyAcceleratorTable
andCreateAcceleratorTable
functions in the Win32 API. These have a size of 6 bytes: BYTE fVirt; BYTE pad0; WORD key; WORD cmd;
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 ACCEL is defined using #pragma(2)
for the
compiler but without any packing for RC, so it will assume #pragma(4)
.
Window drivers
dlls/wineandroid.drv/
dlls/winemac.drv/
dlls/winex11.drv/
The main goal of Wine is not to emulate Windows, but to reimplement its API within the context of the host operating system. This means, for one thing, that the windows created by a Windows application should be the same kinds of windows that the host operating system creates. To that end Wine maintains a set of window drivers, which translate Windows API calls into the host windowing system's API, and similarly translate events received from that API (moving windows, mouse clicks, etc.) into Windows messages. The details of the implementation of each driver are given below.
Quartz (Mac) driver
Wanted: more documentation here.
X11 driver
Wanted: more documentation here.
Debugging the X11 driver
All too often, windows don't behave like they should. This is sometimes due to bugs or inherent limitations in X11, and sometimes due to bugs in the X11 driver. The main channels for debugging window behaviour in X11 are +x11drv and +event. Another useful utility is xtruss, a program which records all communication with the X server (similar to apitrace) and works quite well with Wine.
Keyboard mapping
dlls/winex11.drv/keyboard.c
Wine 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.
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.
To do this, open the file dlls/winex11.drv/keyboard.c
and take a look
at the existing tables.
What you really would need to do, is find out which scancode each key
needs to generate. Find it in the main_key_scan
table, which looks
like this:
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) */
};
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 keyboard.c
. It also shows that if there is no 102nd
key, you can skip that.
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 main_key_scan
, 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 Z), must be placed on a separate
line after the last line.
After you have written such a table, you need to add it to the
main_key_tab[]
layout index table. This will look like this:
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},
...
After you have added your table, recompile Wine and test that it works. If it fails to detect your table, try running
WINEDEBUG=+key,+keyboard wine > key.log 2>&1
and look in the resulting key.log
file to find the error messages it
gives for your layout.
Note that the LANG_*
and SUBLANG_*
definitions are in
include/winnls.h
, 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 (SUBLANG * 0x400 + LANG
), so, for example the
combination LANG_NORWEGIAN (0x14)
and SUBLANG_DEFAULT (0x1)
will be
(in hex) 14 + 1*400 = 414
, so since I'm Norwegian, I could look for
0414
in the WINEDEBUG output to find out why my keyboard won't detect.