| KERNEL MODULE | |
| ============= | |
| ... | |
| GDI MODULE | |
| ========== | |
| 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 py 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 include 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. | |
| 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. | |
| 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. | |
| 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 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 DC itself, 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). | |
| 1.2 | |
| 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 generated on the fly | |
| (WM_PAINT, WM_NCPAINT, WM_TIMER), they can be created by the system | |
| (hardware messages), they can be posted by other tasks/threads | |
| (PostMessage), or they can be 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) - | |
| a) 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) | |
| b) 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. | |