                              Wine Documentation                               
Prev                          Chapter 7. Overview                          Next
-------------------------------------------------------------------------------

7.2. Module Overview

written by (???)

(Extracted from wine/documentation/internals)

7.2.1. KERNEL Module

Needs some content...

7.2.2. GDI Module

7.2.2.1. X Windows System interface

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 XInitThreads()). However, Wine does not use this approach. Instead Wine
performs its own synchronization by putting a wrapper around every X call that
is used. This wrapper protects library access with a critical section, and also
arranges things so that X libraries compiled without -D_REENTRANT (eg. with
global errno variable) will work with Wine.

To make this scheme work, all calls to X must use the proper wrapper functions
(or do their own synchronization that is compatible with the wrappers). The
wrapper for a function X...() is calles TSX...() (for "Thread Safe X ..."). So
for example, instead of calling XOpenDisplay() in the code, TSXOpenDisplay()
must be used. Likewise, X header files that contain function prototypes are
wrapped, so that eg. "ts_xutil.h" must be included rather than <X11/Xutil.h>.
It is important that this scheme is used everywhere to avoid the introduction
of nondeterministic and hard-to-find errors in Wine.

The code for the thread safe X wrappers is contained in the tsx11/ directory
and in include/ts*.h. To use a new (ie. not previously used) X function in
Wine, a new wrapper must be created. The wrappers are generated (semi-)
automatically from the X11R6 includes using the tools/make_X11wrappers perl
script. In simple cases it should be enough to add the name of the new function
to the list in tsx11/X11_calls; if this does not work the wrapper must be added
manually to the make_X11wrappers script. See comments in tsx11/X11_calls and
tools/make_X11wrappers for further details.

7.2.3. USER Module

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 windows/, controls/, and misc/ directories.

7.2.3.1. Windowing subsystem

windows/win.c

windows/winpos.c

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 WS_CHILD style bit is set), a pointer to
the sibling (returned by GetWindow(..., GW_NEXT)), a pointer to the owner
window (set only for popup window if it was created with valid hwndParent
parameter), and a pointer to the first child window (GetWindow(.., GW_CHILD)).
All popup and non-child windows are therefore placed in the first level of this
hierarchy and their ancestor link (wnd->parent) points to the desktop window.

   Desktop window                       - root window
    |     \      `-.
    |      \        `-.
   popup -> wnd1  ->  wnd2              - top level windows    
    |       \   `-.      `-.
    |        \     `-.      `-.
   child1  child2 -> child3  child4     - child windows
          

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).

Another fairly important concept is z-order. 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

child1->popup->child2->child3->wnd1->child4->wnd2->desktop.
          

Current active window ("foreground window" in Win32) is moved to the front of
z-order unless its top-level ancestor owns popup windows.

All these issues are dealt with (or supposed to be) in windows/winpos.c with
SetWindowPos() being the primary interface to the window manager.

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,
SetWindowPos() should eventually be implemented via Begin/End/DeferWindowPos()
calls and not the other way around.

7.2.3.1.1. Visible region, clipping region and update region

windows/dce.c

windows/winpos.c

windows/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. Similarily, 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).

7.2.3.2. Messaging subsystem

windows/queue.c

windows/message.c

Each Windows task/thread has its own message queue - this is where it gets
messages from. Messages can be:

 1. generated on the fly (WM_PAINT, WM_NCPAINT, WM_TIMER)
   
 2. created by the system (hardware messages)
   
 3. posted by other tasks/threads (PostMessage)
   
 4. sent by other tasks/threads (SendMessage)
   
Message priority:

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 windows/message.c.

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 Get/
PeekMessage 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
QS_PAINT bit in the owning queue and eventually this window receives a WM_PAINT
message (WM_NCPAINT 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 DispatchMessage either calls the callback function or
the window procedure. If there are no messages pending the task/thread sleeps
until messages appear.

There are several tricky moments (open for discussion) -

  * System message order has to be honored and messages should be processed
    within correct task/thread context. Therefore when Get/PeekMessage
    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:
   
            " Here's the problem in a nutshell, and there is no good           
            solution. Every possible solution creates a different              
            problem.                                                           
                                                                               
            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.)                                                      
                                                                               
            Mouse input events go to the window you click on (unless           
            some window captures the mouse).                                   
                                                                               
            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.                            
                                                                               
            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.                                         
                                                                               
            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.                                 
                                                                               
            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.                                                   
                                                                               
            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 winodws.                                         
                                                                               
            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.              
                                                                               
            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.             
                                                                               
            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.          
                                                                               
            There are a few solutions to this problem.                         
                                                                               
            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.                                               
                                                                               
            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.                                                     
                                                                               
            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.                                           
                                                                               
            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 targetted 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.                                             
                                                                               
            As for the pros and cons:                                          
                                                                               
            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.                                                            
                                                                               
            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."                              
                                                                               
            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."                                                  
                                                                               
                                                     --by David Charlap        
  * Intertask/interthread SendMessage. 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 DirectedYield() function.
    However, in Win32 there could be several messages pending sent by
    preemptively executing threads, and in this case SendMessage 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
    SendMessage.
   
-------------------------------------------------------------------------------
Prev                                 Home                                  Next
Overview                              Up                      WINE/WINDOWS DLLs
