| <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> |
| § ! " # ¤ % & / ( ) = ? ` Back- |
| | 1 2@ 3£ 4$ 5 6 7{ 8[ 9] 0} + \´ space |
| |
| Tab Q W E R T Y U I O P Å ^ |
| ¨~ |
| Enter |
| Caps A S D F G H J K L Ø Æ * |
| 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] = |
| { |
| "|§","1!","2\"@","3#£","4¤$","5%","6&","7/{","8([","9)]","0=}","+?","\\´", |
| "qQ","wW","eE","rR","tT","yY","uU","iI","oO","pP","åÅ","¨^~", |
| "aA","sS","dD","fF","gG","hH","jJ","kK","lL","øØ","æÆ","'*", |
| "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: |
| --> |