Wine Just Got an Update That Fixes an Ultima 9-Related Bug

u9-sidebox-cropped

No, this doesn’t pertain to DOUG the Eagle Dragon’s ill-fated attempt to run Ultima 9 on Linux using a Direct3D emulator. Instead, what has been fixed is a bug in Wine, the Windows emulator for Linux, that was first identified in 2008:

When opening an in-game book for reading, there is nothing to work as a mouse pointer to help clicking the page corners or other clickable elements with. The books do work, but finding the correct spot to click without any pointer is quite hard. Saving and loading the game as well as selecting spells are also implemented by an in-game book, so these functions are likewise affected.

A similar issue affects using the inventories and toolbelt, though the pointer is not so much missing as being drawn under instead of over the inventory panel. Otherwise the game seems to work well.

In November of last year, one Christopher Thielen was finally able to hone in on the cause of the issue:

I’ve confirmed this bug is due to a missing WM_CAPTURECHANGED message that Wine fails to send near the beginning of execution. The lParam of the message matches the HWND of u9.exe’s window.

Windows XP (and likely others) send this message while Wine does not. Forcing Wine to send this message causes the cursor to appear correctly during gameplay.

I’m currently investigating where Wine is supposed to be sending this message. Simple WM_CAPTURECHANGED test examples don’t seem to exhibit any incorrect behavior so far.

Note the WM_CAPTURECHANGED message does _not_ happen everytime the cursor is supposed to be shown – it’s simply fired off once toward the beginning of execution and this apparently tells u9.exe to load or use the missing cursor.

Using API Monitor, it appears u9.exe is calling SetCapture which ends up calling DDRAW.DLL’s DDGetAttachedSurfaceLd which is calling d3d9.dll’s CheckFullscreen which is sending WM_CAPTURECHANGED to u9.exe referencing u9.exe’s hWnd and setting lParam to the exact same value. In this case it’s 0x000d0102 but that’s surely specific to my instance of Windows.

He also identified a means of solving the issue:

I should note the parameter being set to in SetCapture is also u9.exe’s HWND.

I’m not sure if this is the relevant difference but I found one difference between Wine and Windows XP regarding a WM_CAPTURECHANGED message where lParam == hWnd.

If you compile a basic Win32 app and call SetCapture() before the main message processing loop, Windows will send you a WM_CAPTURECHANGED message with your own hwnd as the lParam while Wine will send nothing.

He then went on to develop fix for Wine’s handling of the relevant events:

I can confirm the following patch resolves this bug.

It also causes Wine to correctly mimic Windows’ behavior in my demo. I am not sure why the original author of user32.dll was making the check I removed. Is there a good way to determine who has solid knowledge of user32.dll to ensure I’m not removing behavior erroneously?

(Generated against wine.git on 11/15/15 at 19:28 PST):

diff --git a/dlls/user32/input.c b/dlls/user32/input.c
index 40e35a9..63fae67 100644
--- a/dlls/user32/input.c
+++ b/dlls/user32/input.c
@@ -108,7 +108,7 @@ BOOL set_capture_window( HWND hwnd, UINT gui_flags, HWND *prev_ret )
{
USER_Driver->pSetCapture( hwnd, gui_flags );

- if (previous && previous != hwnd)
+ if (previous)
SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );

if (prev_ret) *prev_ret = previous;

The person who originally reported the bug was quick to test the change, and reported some good news about it:

Confirming that the patch fixes the issue for me.
Thank you for your hard work on this, Christopher.

Christopher’s fix has been incorporated into Wine as of version 1.9.3 (the latest development release version of Wine, as of this writing, is 1.9.4). I think some kudos are in order for the good Mr. Thielen, for his hard work in getting this issue resolved.