Alexandre Julliard | 23946ad | 1997-06-16 17:43:53 +0000 | [diff] [blame] | 1 | KERNEL MODULE |
| 2 | ============= |
| 3 | |
| 4 | ... |
| 5 | |
| 6 | GDI MODULE |
| 7 | ========== |
| 8 | |
Alexandre Julliard | 60ce85c | 1998-02-01 18:33:27 +0000 | [diff] [blame] | 9 | 1. X Windows System interface |
| 10 | ----------------------------- |
| 11 | |
| 12 | The X libraries used to implement X clients (such as Wine) do not work |
| 13 | properly if multiple threads access the same display concurrently. It is |
| 14 | possible to compile the X libraries to perform their own synchronization |
| 15 | (initiated by calling XInitThreads()). However, Wine does not use this |
| 16 | approach. Instead Wine performs its own synchronization py putting a |
| 17 | wrapper around every X call that is used. This wrapper protects library |
| 18 | access with a critical section, and also arranges things so that X |
| 19 | libraries compiled without -D_REENTRANT (eg. with global errno variable) |
| 20 | will work with Wine. |
| 21 | |
| 22 | To make this scheme work, all calls to X must use the proper wrapper |
| 23 | functions (or do their own synchronization that is compatible with the |
| 24 | wrappers). The wrapper for a function X...() is calles TSX...() (for |
| 25 | "Thread Safe X ..."). So for example, instead of calling XOpenDisplay() |
| 26 | in the code, TSXOpenDisplay() must be used. Likewise, X include files |
Alexandre Julliard | 03468f7 | 1998-02-15 19:40:49 +0000 | [diff] [blame] | 27 | that contain function prototypes are wrapped, so that eg. "ts_xutil.h" |
| 28 | must be included rather than <X11/Xutil.h>. It is important that this |
| 29 | scheme is used everywhere to avoid the introduction of nondeterministic |
| 30 | and hard-to-find errors in Wine. |
Alexandre Julliard | 60ce85c | 1998-02-01 18:33:27 +0000 | [diff] [blame] | 31 | |
| 32 | The code for the thread safe X wrappers is contained in the tsx11/ |
Alexandre Julliard | 03468f7 | 1998-02-15 19:40:49 +0000 | [diff] [blame] | 33 | directory and in include/ts*.h. To use a new (ie. not previously used) X |
Alexandre Julliard | 60ce85c | 1998-02-01 18:33:27 +0000 | [diff] [blame] | 34 | function in Wine, a new wrapper must be created. The wrappers are |
| 35 | generated (semi-)automatically from the X11R6 includes using the |
| 36 | tools/make_X11wrappers perl script. In simple cases it should be enough |
| 37 | to add the name of the new function to the list in tsx11/X11_calls; if |
| 38 | this does not work the wrapper must be added manually to the |
| 39 | make_X11wrappers script. See comments in tsx11/X11_calls and |
| 40 | tools/make_X11wrappers for further details. |
| 41 | |
Alexandre Julliard | 23946ad | 1997-06-16 17:43:53 +0000 | [diff] [blame] | 42 | |
Alexandre Julliard | 8b91563 | 1996-06-16 16:16:05 +0000 | [diff] [blame] | 43 | USER MODULE |
| 44 | =========== |
| 45 | |
| 46 | USER implements windowing and messaging subsystems. It also |
| 47 | contains code for common controls and for other miscellaneous |
| 48 | stuff (rectangles, clipboard, WNet, etc). Wine USER code is |
| 49 | located in windows/, controls/, and misc/ directories. |
| 50 | |
| 51 | 1. Windowing subsystem |
Alexandre Julliard | a0b2b1d | 1997-11-16 17:38:29 +0000 | [diff] [blame] | 52 | ---------------------- |
Alexandre Julliard | 8b91563 | 1996-06-16 16:16:05 +0000 | [diff] [blame] | 53 | |
Alexandre Julliard | 491502b | 1997-11-01 19:08:16 +0000 | [diff] [blame] | 54 | windows/win.c |
| 55 | windows/winpos.c |
| 56 | |
Alexandre Julliard | 8b91563 | 1996-06-16 16:16:05 +0000 | [diff] [blame] | 57 | Windows are arranged into parent/child hierarchy with one |
| 58 | common ancestor for all windows (desktop window). Each window |
| 59 | structure contains a pointer to the immediate ancestor (parent |
| 60 | window if WS_CHILD style bit is set), a pointer to the sibling |
| 61 | (returned by GetWindow(..., GW_NEXT)), a pointer to the owner |
| 62 | window (set only for popup window if it was created with valid |
| 63 | hwndParent parameter), and a pointer to the first child |
| 64 | window (GetWindow(.., GW_CHILD)). All popup and non-child windows |
| 65 | are therefore placed in the first level of this hierarchy and their |
| 66 | ancestor link (wnd->parent) points to the desktop window. |
| 67 | |
| 68 | Desktop window - root window |
Alexandre Julliard | 23946ad | 1997-06-16 17:43:53 +0000 | [diff] [blame] | 69 | | \ `-. |
| 70 | | \ `-. |
Alexandre Julliard | 8b91563 | 1996-06-16 16:16:05 +0000 | [diff] [blame] | 71 | popup -> wnd1 -> wnd2 - top level windows |
Alexandre Julliard | 23946ad | 1997-06-16 17:43:53 +0000 | [diff] [blame] | 72 | | \ `-. `-. |
| 73 | | \ `-. `-. |
Alexandre Julliard | 8b91563 | 1996-06-16 16:16:05 +0000 | [diff] [blame] | 74 | child1 child2 -> child3 child4 - child windows |
| 75 | |
| 76 | Horizontal arrows denote sibling relationship, vertical lines |
| 77 | - ancestor/child. To summarize, all windows with the same immediate |
| 78 | ancestor are sibling windows, all windows which do not have desktop |
| 79 | as their immediate ancestor are child windows. Popup windows behave |
| 80 | as topmost top-level windows unless they are owned. In this case the |
| 81 | only requirement is that they must precede their owners in the top-level |
| 82 | sibling list (they are not topmost). Child windows are confined to the |
| 83 | client area of their parent windows (client area is where window gets |
| 84 | to do its own drawing, non-client area consists of caption, menu, borders, |
Alexandre Julliard | 23946ad | 1997-06-16 17:43:53 +0000 | [diff] [blame] | 85 | intrinsic scrollbars, and minimize/maximize/close/help buttons). |
Alexandre Julliard | 8b91563 | 1996-06-16 16:16:05 +0000 | [diff] [blame] | 86 | |
| 87 | Another fairly important concept is "z-order". It is derived from |
| 88 | the ancestor/child hierarchy and is used to determine "above/below" |
| 89 | relationship. For instance, in the example above, z-order is |
| 90 | child1->popup->child2->child3->wnd1->child4->wnd2->desktop. Current |
| 91 | active window ("foreground window" in Win32) is moved to the front |
| 92 | of z-order unless its top-level ancestor owns popup windows. |
| 93 | |
Alexandre Julliard | a0b2b1d | 1997-11-16 17:38:29 +0000 | [diff] [blame] | 94 | All these issues are dealt with (or supposed to be) in windows/winpos.c |
| 95 | with SetWindowPos() being the primary interface to the window manager. |
Alexandre Julliard | 8b91563 | 1996-06-16 16:16:05 +0000 | [diff] [blame] | 96 | |
| 97 | Wine specifics: in default and managed mode each top-level window |
| 98 | gets its own X counterpart with desktop window being basically a |
Alexandre Julliard | 23946ad | 1997-06-16 17:43:53 +0000 | [diff] [blame] | 99 | fake stub. In desktop mode, however, only desktop window has an X |
Alexandre Julliard | a0b2b1d | 1997-11-16 17:38:29 +0000 | [diff] [blame] | 100 | window associated with it. Also, SetWindowPos() should eventually be |
| 101 | implemented via Begin/End/DeferWindowPos() calls and not the other way |
| 102 | around. |
Alexandre Julliard | 8b91563 | 1996-06-16 16:16:05 +0000 | [diff] [blame] | 103 | |
Alexandre Julliard | a0b2b1d | 1997-11-16 17:38:29 +0000 | [diff] [blame] | 104 | 1.1 Visible region, clipping region and update region |
Alexandre Julliard | 491502b | 1997-11-01 19:08:16 +0000 | [diff] [blame] | 105 | |
| 106 | windows/dce.c |
| 107 | windows/winpos.c |
| 108 | windows/painting.c |
| 109 | |
| 110 | ________________________ |
| 111 | |_________ | A and B are child windows of C |
| 112 | | A |______ | |
| 113 | | | | | |
| 114 | |---------' | | |
| 115 | | | B | | |
| 116 | | | | | |
| 117 | | `------------' | |
| 118 | | C | |
| 119 | `------------------------' |
| 120 | |
| 121 | Visible region determines which part of the window is not obscured |
| 122 | by other windows. If a window has the WS_CLIPCHILDREN style then all |
| 123 | areas below its children are considered invisible. Similarily, if |
| 124 | the WS_CLIPSIBLINGS bit is in effect then all areas obscured by its |
| 125 | siblings are invisible. Child windows are always clipped by the |
| 126 | boundaries of their parent windows. |
| 127 | |
| 128 | B has a WS_CLIPSIBLINGS style: |
| 129 | . ______ |
| 130 | : | | |
| 131 | | ,-----' | |
| 132 | | | B | - visible region of B |
| 133 | | | | |
| 134 | : `------------' |
| 135 | |
| 136 | When the program requests a display context (DC) for a window it |
| 137 | can specify an optional clipping region that further restricts the |
| 138 | area where the graphics output can appear. This area is calculated |
| 139 | as an intersection of the visible region and a clipping region. |
| 140 | |
| 141 | Program asked for a DC with a clipping region: |
| 142 | ______ |
| 143 | ,--|--. | . ,--. |
| 144 | ,--+--' | | : _: | |
| 145 | | | B | | => | | | - DC region where the painting will |
| 146 | | | | | | | | be visible |
| 147 | `--|-----|---' : `----' |
| 148 | `-----' |
| 149 | |
| 150 | When the window manager detects that some part of the window |
| 151 | became visible it adds this area to the update region of this |
| 152 | window and then generates WM_ERASEBKGND and WM_PAINT messages. |
| 153 | In addition, WM_NCPAINT message is sent when the uncovered area |
| 154 | intersects a nonclient part of the window. Application must reply |
| 155 | to the WM_PAINT message by calling BeginPaint()/EndPaint() pair of |
| 156 | functions. BeginPaint() returns a DC that uses accumulated update |
| 157 | region as a clipping region. This operation cleans up invalidated |
| 158 | area and the window will not receive another WM_PAINT until the |
| 159 | window manager creates a new update region. |
| 160 | |
| 161 | A was moved to the left: |
| 162 | ________________________ ... / C update region |
| 163 | |______ | : .___ / |
| 164 | | A |_________ | => | ...|___|.. |
| 165 | | | | | | : | | |
| 166 | |------' | | | : '---' |
| 167 | | | B | | | : \ |
| 168 | | | | | : \ |
| 169 | | `------------' | B update region |
| 170 | | C | |
| 171 | `------------------------' |
| 172 | |
| 173 | |
| 174 | Windows maintains a display context cache consisting of entries that |
| 175 | include DC itself, window to which it belongs, and an optional clipping |
| 176 | region (visible region is stored in the DC itself). When an API call |
| 177 | changes the state of the window tree, window manager has to go through |
| 178 | the DC cache to recalculate visible regions for entries whose windows |
| 179 | were involved in the operation. DC entries (DCE) can be either private |
| 180 | to the window, or private to the window class, or shared between all |
Alexandre Julliard | a0b2b1d | 1997-11-16 17:38:29 +0000 | [diff] [blame] | 181 | windows (Windows 3.1 limits the number of shared DCEs to 5). |
Alexandre Julliard | 491502b | 1997-11-01 19:08:16 +0000 | [diff] [blame] | 182 | |
Alexandre Julliard | a0b2b1d | 1997-11-16 17:38:29 +0000 | [diff] [blame] | 183 | 1.2 |
| 184 | |
| 185 | 2. Messaging subsystem |
| 186 | ---------------------- |
Alexandre Julliard | 491502b | 1997-11-01 19:08:16 +0000 | [diff] [blame] | 187 | |
| 188 | windows/queue.c |
| 189 | windows/message.c |
Alexandre Julliard | 8b91563 | 1996-06-16 16:16:05 +0000 | [diff] [blame] | 190 | |
| 191 | Each Windows task/thread has its own message queue - this is where |
| 192 | it gets messages from. Messages can be generated on the fly |
| 193 | (WM_PAINT, WM_NCPAINT, WM_TIMER), they can be created by the system |
| 194 | (hardware messages), they can be posted by other tasks/threads |
| 195 | (PostMessage), or they can be sent by other tasks/threads (SendMessage). |
| 196 | |
| 197 | Message priority: |
| 198 | |
| 199 | First the system looks for sent messages, then for posted messages, |
| 200 | then for hardware messages, then it checks if the queue has the |
| 201 | "dirty window" bit set, and, finally, it checks for expired |
| 202 | timers. See windows/message.c. |
| 203 | |
| 204 | From all these different types of messages, only posted messages go |
| 205 | directly into the private message queue. System messages (even in |
| 206 | Win95) are first collected in the system message queue and then |
| 207 | they either sit there until Get/PeekMessage gets to process them |
| 208 | or, as in Win95, if system queue is getting clobbered, a special |
| 209 | thread ("raw input thread") assigns them to the private |
| 210 | queues. Sent messages are queued separately and the sender sleeps |
| 211 | until it gets a reply. Special messages are generated on the fly |
| 212 | depending on the window/queue state. If the window update region is |
| 213 | not empty, the system sets the QS_PAINT bit in the owning queue and |
| 214 | eventually this window receives a WM_PAINT message (WM_NCPAINT too |
| 215 | if the update region intersects with the non-client area). A timer |
| 216 | event is raised when one of the queue timers expire. Depending on |
| 217 | the timer parameters DispatchMessage either calls the callback |
| 218 | function or the window procedure. If there are no messages pending |
| 219 | the task/thread sleeps until messages appear. |
| 220 | |
| 221 | There are several tricky moments (open for discussion) - |
| 222 | |
| 223 | a) System message order has to be honored and messages should be |
| 224 | processed within correct task/thread context. Therefore when |
| 225 | Get/PeekMessage encounters unassigned system message and this |
| 226 | message appears not to be for the current task/thread it should |
| 227 | either skip it (or get rid of it by moving it into the private |
| 228 | message queue of the target task/thread - Win95, AFAIK) and |
| 229 | look further or roll back and then yield until this message |
| 230 | gets processed when system switches to the correct context |
| 231 | (Win16). In the first case we lose correct message ordering, in |
| 232 | the second case we have the infamous synchronous system message |
| 233 | queue. Here is a post to one of the OS/2 newsgroup I found to |
| 234 | be relevant: |
| 235 | |
| 236 | " Here's the problem in a nutshell, and there is no good solution. |
| 237 | Every possible solution creates a different problem. |
| 238 | |
| 239 | With a windowing system, events can go to many different windows. |
| 240 | Most are sent by applications or by the OS when things relating to |
| 241 | that window happen (like repainting, timers, etc.) |
| 242 | |
| 243 | Mouse input events go to the window you click on (unless some window |
| 244 | captures the mouse). |
| 245 | |
| 246 | So far, no problem. Whenever an event happens, you put a message on |
| 247 | the target window's message queue. Every process has a message |
| 248 | queue. If the process queue fills up, the messages back up onto the |
| 249 | system queue. |
| 250 | |
| 251 | This is the first cause of apps hanging the GUI. If an app doesn't |
| 252 | handle messages and they back up into the system queue, other apps |
| 253 | can't get any more messages. The reason is that the next message in |
| 254 | line can't go anywhere, and the system won't skip over it. |
| 255 | |
| 256 | This can be fixed by making apps have bigger private message queues. |
| 257 | The SIQ fix does this. PMQSIZE does this for systems without the SIQ |
| 258 | fix. Applications can also request large queues on their own. |
| 259 | |
| 260 | Another source of the problem, however, happens when you include |
| 261 | keyboard events. When you press a key, there's no easy way to know |
| 262 | what window the keystroke message should be delivered to. |
| 263 | |
| 264 | Most windowing systems use a concept known as "focus". The window |
| 265 | with focus gets all incoming keyboard messages. Focus can be changed |
| 266 | from window to window by apps or by users clicking on winodws. |
| 267 | |
| 268 | This is the second source of the problem. Suppose window A has focus. |
| 269 | You click on window B and start typing before the window gets focus. |
| 270 | Where should the keystrokes go? On the one hand, they should go to A |
| 271 | until the focus actually changes to B. On the other hand, you |
| 272 | probably want the keystrokes to go to B, since you clicked there |
| 273 | first. |
| 274 | |
| 275 | OS/2's solution is that when a focus-changing event happens (like |
| 276 | clicking on a window), OS/2 holds all messages in the system queue |
| 277 | until the focus change actually happens. This way, subsequent |
| 278 | keystrokes go to the window you clicked on, even if it takes a while |
| 279 | for that window to get focus. |
| 280 | |
| 281 | The downside is that if the window takes a real long time to get focus |
| 282 | (maybe it's not handling events, or maybe the window losing focus |
| 283 | isn't handling events), everything backs up in the system queue and |
| 284 | the system appears hung. |
| 285 | |
| 286 | There are a few solutions to this problem. |
| 287 | |
| 288 | One is to make focus policy asynchronous. That is, focus changing has |
| 289 | absolutely nothing to do with the keyboard. If you click on a window |
| 290 | and start typing before the focus actually changes, the keystrokes go |
| 291 | to the first window until focus changes, then they go to the second. |
| 292 | This is what X-windows does. |
| 293 | |
| 294 | Another is what NT does. When focus changes, keyboard events are held |
| 295 | in the system message queue, but other events are allowed through. |
| 296 | This is "asynchronous" because the messages in the system queue are |
| 297 | delivered to the application queues in a different order from that |
| 298 | with which they were posted. If a bad app won't handle the "lose |
| 299 | focus" message, it's of no consequence - the app receiving focus will |
| 300 | get its "gain focus" message, and the keystrokes will go to it. |
| 301 | |
| 302 | The NT solution also takes care of the application queue filling up |
| 303 | problem. Since the system delivers messages asynchronously, messages |
| 304 | waiting in the system queue will just sit there and the rest of the |
| 305 | messages will be delivered to their apps. |
| 306 | |
| 307 | The OS/2 SIQ solution is this: When a focus-changing event happens, |
| 308 | in addition to blocking further messages from the application queues, |
| 309 | a timer is started. When the timer goes off, if the focus change has |
| 310 | not yet happened, the bad app has its focus taken away and all |
| 311 | messages targetted at that window are skipped. When the bad app |
| 312 | finally handles the focus change message, OS/2 will detect this and |
| 313 | stop skipping its messages. |
| 314 | |
| 315 | |
| 316 | As for the pros and cons: |
| 317 | |
| 318 | The X-windows solution is probably the easiest. The problem is that |
| 319 | users generally don't like having to wait for the focus to change |
| 320 | before they start typing. On many occasions, you can type and the |
| 321 | characters end up in the wrong window because something (usually heavy |
| 322 | system load) is preventing the focus change from happening in a timely |
| 323 | manner. |
| 324 | |
| 325 | The NT solution seems pretty nice, but making the system message queue |
| 326 | asynchronous can cause similar problems to the X-windows problem. |
| 327 | Since messages can be delivered out of order, programs must not assume |
| 328 | that two messages posted in a particular order will be delivered in |
| 329 | that same order. This can break legacy apps, but since Win32 always |
| 330 | had an asynchronous queue, it is fair to simply tell app designers |
| 331 | "don't do that". It's harder to tell app designers something like |
| 332 | that on OS/2 - they'll complain "you changed the rules and our apps |
| 333 | are breaking." |
| 334 | |
| 335 | The OS/2 solution's problem is that nothing happens until you try to |
| 336 | change window focus, and then wait for the timeout. Until then, the |
| 337 | bad app is not detected and nothing is done." (by David Charlap) |
| 338 | |
| 339 | |
| 340 | b) Intertask/interthread SendMessage. The system has to inform the |
Alexandre Julliard | 8cc3a5e | 1996-08-11 15:49:51 +0000 | [diff] [blame] | 341 | target queue about the forthcoming message, then it has to carry |
| 342 | out the context switch and wait until the result is available. |
| 343 | Win16 stores necessary parameters in the queue structure and then |
| 344 | calls DirectedYield() function. However, in Win32 there could be |
| 345 | several messages pending sent by preemptively executing threads, |
| 346 | and in this case SendMessage has to build some sort of message |
| 347 | queue for sent messages. Another issue is what to do with messages |
| 348 | sent to the sender when it is blocked inside its own SendMessage. |
Alexandre Julliard | 8cc3a5e | 1996-08-11 15:49:51 +0000 | [diff] [blame] | 349 | |
Alexandre Julliard | 23946ad | 1997-06-16 17:43:53 +0000 | [diff] [blame] | 350 | |