Wine-Devel
By thread
wine-devel@list.winehq.org
By month
Messages by month
- ----- 2026 -----
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2004 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2003 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2002 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2001 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
April 2021
- 70 participants
- 1934 messages
Re: [PATCH] combase: Return initial values for global options.
by Marvin
Hi,
While running your changed tests, I think I found new failures.
Being a bot and all I'm not very good at pattern recognition, so I might be
wrong, but could you please double-check?
Full results can be found at:
https://testbot.winehq.org/JobDetails.pl?Key=88065
Your paranoid android.
=== w7u_2qxl (32 bit report) ===
ole32:
compobj.c:831: Test failed: Section not found.
compobj.c:841: Test failed: Unexpected return value.
compobj.c:850: Test failed: Unexpected return value.
compobj.c:856: Test failed: Unexpected module name L"testlib.dll".
April 1, 2021
[PATCH] combase: Return initial values for global options.
by Nikolay Sivov
Signed-off-by: Nikolay Sivov <nsivov(a)codeweavers.com>
---
dlls/combase/combase.c | 11 +++++++++--
dlls/ole32/tests/compobj.c | 13 +++++++++++++
include/objidlbase.idl | 28 +++++++++++++++++++++++++++-
3 files changed, 49 insertions(+), 3 deletions(-)
diff --git a/dlls/combase/combase.c b/dlls/combase/combase.c
index cda0d52f579..4677972656f 100644
--- a/dlls/combase/combase.c
+++ b/dlls/combase/combase.c
@@ -38,6 +38,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(ole);
HINSTANCE hProxyDll;
+static ULONG_PTR global_options[COMGLB_PROPERTIES_RESERVED3 + 1];
+
/* Ole32 exports */
extern void WINAPI DestroyRunningObjectTable(void);
extern HRESULT WINAPI Ole32DllGetClassObject(REFCLSID rclsid, REFIID riid, void **obj);
@@ -490,9 +492,14 @@ static HRESULT WINAPI global_options_Set(IGlobalOptions *iface, GLOBALOPT_PROPER
static HRESULT WINAPI global_options_Query(IGlobalOptions *iface, GLOBALOPT_PROPERTIES property, ULONG_PTR *value)
{
- FIXME("%p, %u, %p.\n", iface, property, value);
+ TRACE("%p, %u, %p.\n", iface, property, value);
- return E_NOTIMPL;
+ if (property < COMGLB_EXCEPTION_HANDLING || property > COMGLB_PROPERTIES_RESERVED3)
+ return E_INVALIDARG;
+
+ *value = global_options[property];
+
+ return S_OK;
}
static const IGlobalOptionsVtbl global_options_vtbl =
diff --git a/dlls/ole32/tests/compobj.c b/dlls/ole32/tests/compobj.c
index 2e5006463da..d1e947e393f 100644
--- a/dlls/ole32/tests/compobj.c
+++ b/dlls/ole32/tests/compobj.c
@@ -3919,6 +3919,7 @@ todo_wine {
static void test_GlobalOptions(void)
{
IGlobalOptions *global_options;
+ ULONG_PTR value;
HRESULT hres;
CoInitialize(NULL);
@@ -3933,6 +3934,18 @@ static void test_GlobalOptions(void)
return;
}
+ hres = IGlobalOptions_Query(global_options, 0, &value);
+ ok(FAILED(hres), "Unexpected hr %#x.\n", hres);
+
+ hres = IGlobalOptions_Query(global_options, COMGLB_PROPERTIES_RESERVED3 + 1, &value);
+ ok(FAILED(hres), "Unexpected hr %#x.\n", hres);
+
+ value = ~0u;
+ hres = IGlobalOptions_Query(global_options, COMGLB_EXCEPTION_HANDLING, &value);
+ ok(hres == S_OK || broken(hres == E_FAIL) /* Vista */, "Unexpected hr %#x.\n", hres);
+ if (SUCCEEDED(hres))
+ ok(value == COMGLB_EXCEPTION_HANDLE, "Unexpected value %ld.\n", value);
+
IGlobalOptions_Release(global_options);
hres = CoCreateInstance(&CLSID_GlobalOptions, (IUnknown*)0xdeadbeef, CLSCTX_INPROC_SERVER,
diff --git a/include/objidlbase.idl b/include/objidlbase.idl
index 6449be8417b..bd5a9b12409 100644
--- a/include/objidlbase.idl
+++ b/include/objidlbase.idl
@@ -1095,7 +1095,12 @@ typedef enum tagGLOBALOPT_PROPERTIES
{
COMGLB_EXCEPTION_HANDLING = 1,
COMGLB_APPID = 2,
- COMGLB_RPC_THREADPOOL_SETTING = 3
+ COMGLB_RPC_THREADPOOL_SETTING = 3,
+ COMGLB_RO_SETTINGS = 4,
+ COMGLB_UNMARSHALING_POLICY = 5,
+ COMGLB_PROPERTIES_RESERVED1 = 6,
+ COMGLB_PROPERTIES_RESERVED2 = 7,
+ COMGLB_PROPERTIES_RESERVED3 = 8,
} GLOBALOPT_PROPERTIES;
typedef enum tagGLOBALOPT_EH_VALUES
@@ -1112,6 +1117,27 @@ typedef enum tagGLOBALOPT_RPCTP_VALUES
COMGLB_RPC_THREADPOOL_SETTING_PRIVATE_POOL = 1
} GLOBALOPT_RPCTP_VALUES;
+typedef enum tagGLOBALOPT_RO_FLAGS
+{
+ COMGLB_STA_MODALLOOP_REMOVE_TOUCH_MESSAGES = 0x1,
+ COMGLB_STA_MODALLOOP_SHARED_QUEUE_REMOVE_INPUT_MESSAGES = 0x2,
+ COMGLB_STA_MODALLOOP_SHARED_QUEUE_DONOT_REMOVE_INPUT_MESSAGES = 0x4,
+ COMGLB_FAST_RUNDOWN = 0x8,
+ COMGLB_RESERVED1 = 0x10,
+ COMGLB_RESERVED2 = 0x20,
+ COMGLB_RESERVED3 = 0x40,
+ COMGLB_STA_MODALLOOP_SHARED_QUEUE_REORDER_POINTER_MESSAGES = 0x80,
+ COMGLB_RESERVED4 = 0x100,
+ COMGLB_RESERVED5 = 0x200,
+ COMGLB_RESERVED6 = 0x400,
+} GLOBALOPT_RO_FLAGS;
+
+typedef enum tagGLOBALOPT_UNMARSHALING_POLICY_VALUES
+{
+ COMGLB_UNMARSHALING_POLICY_NORMAL = 0,
+ COMGLB_UNMARSHALING_POLICY_STRONG = 1,
+ COMGLB_UNMARSHALING_POLICY_HYBRID = 2,
+} GLOBALOPT_UNMARSHALING_POLICY_VALUES;
[
object,
--
2.30.2
April 1, 2021
Re: [PATCH] d2d1: Implement ID2D1Multithread.
by Marvin
Hi,
While running your changed tests, I think I found new failures.
Being a bot and all I'm not very good at pattern recognition, so I might be
wrong, but could you please double-check?
Full results can be found at:
https://testbot.winehq.org/JobDetails.pl?Key=88058
Your paranoid android.
=== w2008s64 (32 bit report) ===
d2d1:
d2d1.c:9641: Test failed: Failed to get interface, hr 0x80004002.
0390:d2d1: unhandled exception c0000005 at 004019B7
=== w7u_el (32 bit report) ===
d2d1:
0a44:d2d1: unhandled exception c0000005 at 00000000
=== wvistau64 (64 bit report) ===
d2d1:
d2d1.c:9641: Test failed: Failed to get interface, hr 0x80004002.
09f0:d2d1: unhandled exception c0000005 at 0000000000401917
=== w2008s64 (64 bit report) ===
d2d1:
d2d1.c:9641: Test failed: Failed to get interface, hr 0x80004002.
0390:d2d1: unhandled exception c0000005 at 0000000000401917
=== w1064_2qxl (64 bit report) ===
d2d1:
096c:d2d1: unhandled exception c0000005 at 0000000000000000
=== w10pro64_he (64 bit report) ===
d2d1:
1d8c:d2d1: unhandled exception c0000005 at 0000000000000000
=== w10pro64_ja (64 bit report) ===
d2d1:
0fd4:d2d1: unhandled exception c0000005 at 0000000000000000
=== debiant2 (32 bit Japanese:Japan report) ===
d2d1:
Unhandled exception: page fault on read access to 0x00000014 in 32-bit code (0x004014f8).
April 1, 2021
[PATCH] d2d1: Implement ID2D1Multithread.
by Nikolay Sivov
Signed-off-by: Nikolay Sivov <nsivov(a)codeweavers.com>
---
dlls/d2d1/factory.c | 102 ++++++++++++++++++++++++++++++++++++++++-
dlls/d2d1/tests/d2d1.c | 59 ++++++++++++++++++++++++
2 files changed, 159 insertions(+), 2 deletions(-)
diff --git a/dlls/d2d1/factory.c b/dlls/d2d1/factory.c
index 2a9de448c6e..b65727d3a32 100644
--- a/dlls/d2d1/factory.c
+++ b/dlls/d2d1/factory.c
@@ -30,12 +30,15 @@ struct d2d_settings d2d_settings =
struct d2d_factory
{
ID2D1Factory2 ID2D1Factory2_iface;
+ ID2D1Multithread ID2D1Multithread_iface;
LONG refcount;
ID3D10Device1 *device;
float dpi_x;
float dpi_y;
+
+ CRITICAL_SECTION cs;
};
static inline struct d2d_factory *impl_from_ID2D1Factory2(ID2D1Factory2 *iface)
@@ -43,6 +46,11 @@ static inline struct d2d_factory *impl_from_ID2D1Factory2(ID2D1Factory2 *iface)
return CONTAINING_RECORD(iface, struct d2d_factory, ID2D1Factory2_iface);
}
+static inline struct d2d_factory *impl_from_ID2D1Multithread(ID2D1Multithread *iface)
+{
+ return CONTAINING_RECORD(iface, struct d2d_factory, ID2D1Multithread_iface);
+}
+
static HRESULT d2d_factory_reload_sysmetrics(struct d2d_factory *factory)
{
HDC hdc;
@@ -63,6 +71,8 @@ static HRESULT d2d_factory_reload_sysmetrics(struct d2d_factory *factory)
static HRESULT STDMETHODCALLTYPE d2d_factory_QueryInterface(ID2D1Factory2 *iface, REFIID iid, void **out)
{
+ struct d2d_factory *factory = impl_from_ID2D1Factory2(iface);
+
TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out);
if ((IsEqualGUID(iid, &IID_ID2D1Factory2) && d2d_settings.max_version_factory >= 2)
@@ -74,6 +84,12 @@ static HRESULT STDMETHODCALLTYPE d2d_factory_QueryInterface(ID2D1Factory2 *iface
*out = iface;
return S_OK;
}
+ else if (IsEqualGUID(iid, &IID_ID2D1Multithread))
+ {
+ ID2D1Factory2_AddRef(iface);
+ *out = &factory->ID2D1Multithread_iface;
+ return S_OK;
+ }
WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid));
@@ -102,6 +118,7 @@ static ULONG STDMETHODCALLTYPE d2d_factory_Release(ID2D1Factory2 *iface)
{
if (factory->device)
ID3D10Device1_Release(factory->device);
+ DeleteCriticalSection(&factory->cs);
heap_free(factory);
}
@@ -593,17 +610,92 @@ static const struct ID2D1Factory2Vtbl d2d_factory_vtbl =
d2d_factory_ID2D1Factory1_CreateDevice,
};
+static HRESULT STDMETHODCALLTYPE d2d_factory_mt_QueryInterface(ID2D1Multithread *iface, REFIID iid, void **out)
+{
+ struct d2d_factory *factory = impl_from_ID2D1Multithread(iface);
+ return d2d_factory_QueryInterface(&factory->ID2D1Factory2_iface, iid, out);
+}
+
+static ULONG STDMETHODCALLTYPE d2d_factory_mt_AddRef(ID2D1Multithread *iface)
+{
+ struct d2d_factory *factory = impl_from_ID2D1Multithread(iface);
+ return d2d_factory_AddRef(&factory->ID2D1Factory2_iface);
+}
+
+static ULONG STDMETHODCALLTYPE d2d_factory_mt_Release(ID2D1Multithread *iface)
+{
+ struct d2d_factory *factory = impl_from_ID2D1Multithread(iface);
+ return d2d_factory_Release(&factory->ID2D1Factory2_iface);
+}
+
+static BOOL STDMETHODCALLTYPE d2d_factory_mt_GetMultithreadProtected(ID2D1Multithread *iface)
+{
+ return TRUE;
+}
+
+static void STDMETHODCALLTYPE d2d_factory_mt_Enter(ID2D1Multithread *iface)
+{
+ struct d2d_factory *factory = impl_from_ID2D1Multithread(iface);
+
+ TRACE("%p.\n", iface);
+
+ return EnterCriticalSection(&factory->cs);
+}
+
+static void STDMETHODCALLTYPE d2d_factory_mt_Leave(ID2D1Multithread *iface)
+{
+ struct d2d_factory *factory = impl_from_ID2D1Multithread(iface);
+
+ TRACE("%p.\n", iface);
+
+ return LeaveCriticalSection(&factory->cs);
+}
+
+static BOOL STDMETHODCALLTYPE d2d_factory_st_GetMultithreadProtected(ID2D1Multithread *iface)
+{
+ return FALSE;
+}
+
+static void STDMETHODCALLTYPE d2d_factory_st_Enter(ID2D1Multithread *iface)
+{
+}
+
+static void STDMETHODCALLTYPE d2d_factory_st_Leave(ID2D1Multithread *iface)
+{
+}
+
+static const struct ID2D1MultithreadVtbl d2d_factory_multithread_vtbl =
+{
+ d2d_factory_mt_QueryInterface,
+ d2d_factory_mt_AddRef,
+ d2d_factory_mt_Release,
+ d2d_factory_mt_GetMultithreadProtected,
+ d2d_factory_mt_Enter,
+ d2d_factory_mt_Leave,
+};
+
+static const struct ID2D1MultithreadVtbl d2d_factory_multithread_noop_vtbl =
+{
+ d2d_factory_mt_QueryInterface,
+ d2d_factory_mt_AddRef,
+ d2d_factory_mt_Release,
+ d2d_factory_st_GetMultithreadProtected,
+ d2d_factory_st_Enter,
+ d2d_factory_st_Leave,
+};
+
static void d2d_factory_init(struct d2d_factory *factory, D2D1_FACTORY_TYPE factory_type,
const D2D1_FACTORY_OPTIONS *factory_options)
{
- if (factory_type != D2D1_FACTORY_TYPE_SINGLE_THREADED)
- FIXME("Ignoring factory type %#x.\n", factory_type);
if (factory_options && factory_options->debugLevel != D2D1_DEBUG_LEVEL_NONE)
WARN("Ignoring debug level %#x.\n", factory_options->debugLevel);
factory->ID2D1Factory2_iface.lpVtbl = &d2d_factory_vtbl;
+ factory->ID2D1Multithread_iface.lpVtbl = factory_type == D2D1_FACTORY_TYPE_SINGLE_THREADED ?
+ &d2d_factory_multithread_noop_vtbl : &d2d_factory_multithread_vtbl;
factory->refcount = 1;
d2d_factory_reload_sysmetrics(factory);
+ InitializeCriticalSection(&factory->cs);
}
HRESULT WINAPI D2D1CreateFactory(D2D1_FACTORY_TYPE factory_type, REFIID iid,
@@ -615,6 +707,12 @@ HRESULT WINAPI D2D1CreateFactory(D2D1_FACTORY_TYPE factory_type, REFIID iid,
TRACE("factory_type %#x, iid %s, factory_options %p, factory %p.\n",
factory_type, debugstr_guid(iid), factory_options, factory);
+ if (factory_type != D2D1_FACTORY_TYPE_SINGLE_THREADED &&
+ factory_type != D2D1_FACTORY_TYPE_MULTI_THREADED)
+ {
+ return E_INVALIDARG;
+ }
+
if (!(object = heap_alloc_zero(sizeof(*object))))
return E_OUTOFMEMORY;
diff --git a/dlls/d2d1/tests/d2d1.c b/dlls/d2d1/tests/d2d1.c
index a046a21c7f6..8db2aad813e 100644
--- a/dlls/d2d1/tests/d2d1.c
+++ b/dlls/d2d1/tests/d2d1.c
@@ -9614,6 +9614,64 @@ static void test_geometry_group(BOOL d3d11)
ID2D1Factory_Release(factory);
}
+static DWORD WINAPI mt_factory_test_thread_func(void *param)
+{
+ ID2D1Multithread *multithread = param;
+
+ ID2D1Multithread_Enter(multithread);
+
+ return 0;
+}
+
+static void test_mt_factory(BOOL d3d11)
+{
+ ID2D1Multithread *multithread;
+ ID2D1Factory *factory;
+ HANDLE thread;
+ HRESULT hr;
+ DWORD ret;
+
+ hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED + 1, &IID_ID2D1Factory, NULL, (void **)&factory);
+ ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr);
+
+ hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &IID_ID2D1Factory, NULL, (void **)&factory);
+ ok(SUCCEEDED(hr), "Failed to create factory, hr %#x.\n", hr);
+
+ hr = ID2D1Factory_QueryInterface(factory, &IID_ID2D1Multithread, (void **)&multithread);
+ ok(SUCCEEDED(hr), "Failed to get interface, hr %#x.\n", hr);
+
+ ret = ID2D1Multithread_GetMultithreadProtected(multithread);
+ ok(!ret, "Unexpected return value.\n");
+
+ ID2D1Multithread_Enter(multithread);
+ thread = CreateThread(NULL, 0, mt_factory_test_thread_func, multithread, 0, NULL);
+ ok(!!thread, "Failed to create a thread.\n");
+ WaitForSingleObject(thread, INFINITE);
+ CloseHandle(thread);
+
+ ID2D1Multithread_Release(multithread);
+ ID2D1Factory_Release(factory);
+
+ hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, &IID_ID2D1Factory, NULL, (void **)&factory);
+ ok(SUCCEEDED(hr), "Failed to create factory, hr %#x.\n", hr);
+
+ hr = ID2D1Factory_QueryInterface(factory, &IID_ID2D1Multithread, (void **)&multithread);
+ ok(SUCCEEDED(hr), "Failed to get interface, hr %#x.\n", hr);
+
+ ret = ID2D1Multithread_GetMultithreadProtected(multithread);
+ ok(!!ret, "Unexpected return value.\n");
+
+ ID2D1Multithread_Enter(multithread);
+ thread = CreateThread(NULL, 0, mt_factory_test_thread_func, multithread, 0, NULL);
+ ok(!!thread, "Failed to create a thread.\n");
+ ret = WaitForSingleObject(thread, 10);
+ ok(ret == WAIT_TIMEOUT, "Expected timeout.\n");
+ CloseHandle(thread);
+ ID2D1Multithread_Release(multithread);
+
+ ID2D1Factory_Release(factory);
+}
+
START_TEST(d2d1)
{
HMODULE d2d1_dll = GetModuleHandleA("d2d1.dll");
@@ -9674,6 +9732,7 @@ START_TEST(d2d1)
queue_d3d10_test(test_math);
queue_d3d10_test(test_colour_space);
queue_test(test_geometry_group);
+ queue_test(test_mt_factory);
run_queued_tests();
}
--
2.30.2
April 1, 2021
Re: [PATCH] server: Make region.c a little less ugly.
by Alexandre Julliard
Zebediah Figura <z.figura12(a)gmail.com> writes:
> This patch introduces no functional change, but makes some ugly code a little
> prettier.
Finally a coding standard that we can all agree on!
Everybody please reformat your code accordingly.
--
Alexandre Julliard
julliard(a)winehq.org
April 1, 2021
Re: [PATCH 2/2] gdi32: Generate and use a lookup cache when looking up RGB values for a color table.
by Huw Davies
On Wed, Mar 31, 2021 at 03:35:58PM +0300, Gabriel Ivăncescu wrote:
> This vastly improves the performance. The cache generation is relatively
> constant in terms of algorithm complexity, around O(n) with some extra
> overhead for the entire cache (depending on the color table's colors, because
> they get "blocked" soon enough), which is fixed at 32768 entries. It scales
> well with large amount of colors in the color table. The lookup after that
> should be fast.
>
> In contrast, the current method is O(N * M) where N is the amount of pixels
> and M is the number of colors in the table, which is very slow for larger M
> (especially 256 colors).
>
> More detailed information in the bug report.
>
> Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=50898
> Signed-off-by: Gabriel Ivăncescu <gabrielopcode(a)gmail.com>
> ---
> dlls/gdi32/dibdrv/primitives.c | 272 +++++++++++++++++++++++++++++++--
> 1 file changed, 256 insertions(+), 16 deletions(-)
>
> diff --git a/dlls/gdi32/dibdrv/primitives.c b/dlls/gdi32/dibdrv/primitives.c
> index 569baae..cc86ed9 100644
> --- a/dlls/gdi32/dibdrv/primitives.c
> +++ b/dlls/gdi32/dibdrv/primitives.c
> @@ -3497,22 +3497,255 @@ static void convert_to_16(dib_info *dst, const dib_info *src, const RECT *src_re
> }
> }
>
> -static inline BOOL color_tables_match(const dib_info *d1, const dib_info *d2)
> +/*
> + * To lookup RGB values into nearest color in the color table, Windows uses 5-bits of the RGB
> + * at the "center" of the RGB cube, presumably to do a similar lookup cache. The lowest 3 bits
> + * of the color are thus set to halfway (0x04) and then it's used in the distance calculation
> + * to the exact color in the color table. We exploit this as well to create a lookup cache.
> + *
> + * Generating the table is done by going "outwards" from the center of each color in the table.
> + * First, we find out the center RGB cube spot into the map for each color in the color table.
> + * In case of conflict, we calculate the *true* distance, since color table is not quantized.
> + * Each color in the color table should then have an associated center in the lookup cache map.
> + *
> + * Next, we go "outwards" from each center by increasing an offset for the main axis. This is
> + * always expressed as an offset from the center, and starts from 1 and goes up. Each offset
> + * has multiple generations. A generation defines the distance from the center, and is always
> + * increasing. Thus when we're at a generation, we go through *all* colors in the color table
> + * first, fill the entire generation for all of them, then move to the next generation. Since
> + * the next generation will always have a larger distance than the current one, we know that
> + * once we filled all the previous generations, we're done with them and don't need to check.
> + *
> + * Note that an offset spans multiple generations, due to how distances work in 3 dimensions.
> + * For example, an offset of 1 is simple and has 3 generations. The examples below are all
> + * offsets from the center cube, e.g. (0,-1,1) means +1 for first axis, -1 for the second.
> + *
> + * First generation (offset = 1):
> + * ( 0, 0, 1) ( 0, 0,-1) ( 0, 1, 0) ( 0,-1, 0) ( 1, 0, 0) (-1, 0, 0)
> + *
> + * In the first generation, the distance is always 1. For each color in the color table, we
> + * go through all of the above displacements from the center, and fill the lookup cache map.
> + *
> + * Second generation (offset = 1 still, but distance is larger now; some are dups, that's OK):
> + * ( 0, 1, 1) ( 0,-1, 1) ( 1, 0, 1) (-1, 0, 1) ( 0, 1,-1) ( 0,-1,-1) ( 1, 0,-1) (-1, 0,-1)
> + * ( 0, 1, 1) ( 0, 1,-1) ( 1, 1, 0) (-1, 1, 0) ( 0,-1, 1) ( 0,-1,-1) ( 1,-1, 0) (-1,-1, 0)
> + * ( 1, 0, 1) ( 1, 0,-1) ( 1, 1, 0) ( 1,-1, 0) (-1, 0, 1) (-1, 0,-1) (-1, 1, 0) (-1,-1, 0)
> + *
> + * On the 2nd line, we swapped the main axis from (x,x,1) to (x,1,x), and on 3rd to (1,x,x).
> + * Note that the distance is always the same (sqrt(2)), and we just permute the axis values.
> + *
> + * Third generation (offset = 1 still, but distance is even larger: sqrt(3)):
> + * ( 1, 1, 1) ( 1,-1, 1) (-1, 1, 1) (-1,-1, 1) ( 1, 1,-1) ( 1,-1,-1) (-1, 1,-1) (-1,-1,-1)
> + * ...
> + *
> + * Only after all of the coordinate absolute values are equal to the offset do we increase it.
> + * When the offset is increased, the main axis' absolute value is increased, and the distance.
> + * For other offsets, such as offset = 3, generations start with zeros for the other axis, for
> + * example (0,0,3). The next generation will increase one axis by 1, e.g. (0,1,3) and (1,0,3)
> + * which are part of the same generation (same distance). The next generation will increase the
> + * third axis, e.g. (1,1,3). Then the second axis is raised again, and the process repeats for
> + * the third axis until it's equal to the second axis, e.g. (0,2,3)->(1,2,3)->(2,2,3).
> + *
> + * As each generation is increased, the lowest value in the axis is increased, and the other
> + * starts from zero again. Then they are swapped and permuted. This is due to how the distance
> + * is calculated in 3 dimensions. For each generation we fill *each* color in the color table's
> + * displacement in all directions, since they have the same distance.
> + *
> + * Lastly, we keep track of each of the six directions (in 3 dimensions) and if one direction
> + * was not filled at all in the current offset, we mark it as "blocked" and we no longer check
> + * for it the next offset. When all six directions are blocked, the entry is no longer checked.
> + *
> + * The lookup cache map is completely filled when *all* color table entries are blocked.
> +*/
Perhaps I'm misunderstanding your algorithm, but doesn't this end up with
(2,2,2) coming before (3,0,0)?
> +static void rgb_lookup_colortable_init(const dib_info *dib, struct rgb_lookup_colortable_ctx *ctx)
> {
> - /* Windows reduces precision to 5 bits, probably in order to build some sort of lookup cache */
> - return rgb_to_pixel_colortable( dst, (r & ~7) + 4, (g & ~7) + 4, (b & ~7) + 4 );
> + BYTE indices[256], available_directions[256], tmp_directions[256], map_set_bits[ARRAY_SIZE(ctx->map) / 8];
> + unsigned color_table_size = dib->color_table ? dib->color_table_size : 1 << dib->bit_count;
> + const RGBQUAD *color_table = get_dib_color_table(dib);
> + unsigned idx, offset, num_entries;
> + int i, j;
> +
> + /* Testing shows that for low amount of colors, the overhead is larger than the
> + O(N*M) algorithm, presumably due to branch prediction and cache locality, but
> + it gets quickly out of hand (up to 5x slower) for larger amount of colors... */
> + if (color_table_size <= 40)
> + {
As a side note, there are going to be a vanishingly small number of
colour tables with sizes [17,255], so if you need to special case,
special case for sizes <= 16.
> + unsigned r, g, b;
> +
> + for (b = 4; b < 256; b += 1 << 3)
> + for (g = 4; g < 256; g += 1 << 3)
> + for (r = 4; r < 256; r += 1 << 3)
> + ctx->map[r >> 3 | (g & ~7) << 2 | (b & ~7) << 7] = rgb_to_pixel_colortable(dib, r, g, b);
> + return;
> + }
> +
> + memset(map_set_bits, 0, sizeof(map_set_bits));
> + memset(available_directions, 0xff, color_table_size);
> + memset(tmp_directions, 0, color_table_size);
> +
> + /* indirect list of valid color table indices, as we remove those fully surrounded */
> + for (idx = 0; idx < color_table_size; idx++)
> + indices[idx] = idx;
> +
> + /* first, fill the centers (offset = 0) of each quantized table color in the map */
> + for (idx = 0; idx < color_table_size; idx++)
> + {
> + int dr1, dg1, db1, dr2, dg2, db2, pos_r, pos_g, pos_b;
> + unsigned pos = (color_table[idx].rgbRed >> 3) |
> + (color_table[idx].rgbGreen & ~7) << 2 |
> + (color_table[idx].rgbBlue & ~7) << 7;
> +
> + if (map_set_bits[pos / 8] & (1 << pos % 8))
> + {
> + pos_r = (color_table[idx].rgbRed & ~7) | 4;
> + pos_g = (color_table[idx].rgbGreen & ~7) | 4;
> + pos_b = (color_table[idx].rgbBlue & ~7) | 4;
> + dr1 = pos_r - color_table[idx].rgbRed;
> + dg1 = pos_g - color_table[idx].rgbGreen;
> + db1 = pos_b - color_table[idx].rgbBlue;
> + dr2 = pos_r - color_table[ctx->map[pos]].rgbRed;
> + dg2 = pos_g - color_table[ctx->map[pos]].rgbGreen;
> + db2 = pos_b - color_table[ctx->map[pos]].rgbBlue;
> +
> + if (dr1*dr1 + dg1*dg1 + db1*db1 >= dr2*dr2 + dg2*dg2 + db2*db2)
> + continue;
> + }
> + else
> + map_set_bits[pos / 8] |= 1 << pos % 8;
> +
> + ctx->map[pos] = idx;
> + }
> +
> + /* now do the rest */
> + for (offset = 1, num_entries = color_table_size; num_entries != 0; offset++)
> + {
> + for (i = 0; i <= offset; i++)
> + {
> + for (j = 0; j <= i; j++)
> + {
> + /* we're at one generation now, go through each color in the color table */
> + for (idx = 0; idx < num_entries; idx++)
> + {
> + unsigned direction_mask = 1, color_table_index = indices[idx];
> + unsigned shift2 = 5, shift3 = 10;
> + int center, main_axis = offset;
> +
> + center = (color_table[color_table_index].rgbRed >> 3) |
> + (color_table[color_table_index].rgbGreen & ~7) << 2 |
> + (color_table[color_table_index].rgbBlue & ~7) << 7;
> + do
> + {
> + do
> + {
> + if (available_directions[idx] & direction_mask)
> + {
> + BOOL direction_blocked = TRUE;
> + do
> + {
> + unsigned shift_tmp;
> + do
> + {
> + do
> + {
> + unsigned pos = center + main_axis + (i << shift2) + (j << shift3);
> +
> + if (rgb_lookup_colortable_set_pos(ctx, pos, color_table, color_table_index, map_set_bits))
> + direction_blocked = FALSE;
> + j = -j;
> + } while (j < 0);
> + i = -i;
> + } while (i < 0);
> +
> + /* repeat once and swap the two non-main axis */
> + shift_tmp = shift2; shift2 = shift3; shift3 = shift_tmp;
> + } while (shift2 > shift3 && i != j);
> +
> + if (!direction_blocked)
> + tmp_directions[idx] |= direction_mask;
> + }
> + direction_mask <<= 1;
> + main_axis = -main_axis;
> + } while (main_axis < 0);
> +
> + /* change main to next axis */
> + shift2 = 0;
> + shift3 = (main_axis < 32) ? 10 : 5;
> + main_axis <<= 5;
> + } while (main_axis < 32768);
> + }
> + }
> + }
I suspect things might become clearer if one were to unwind some of these loops.
Did you consider using a lazy initialization of the lookup table instead?
Huw.
April 1, 2021
Re: [PATCH 1/2] gdi32: Move the loop through each clipped rectangle to the primitive blend funcs.
by Huw Davies
On Wed, Mar 31, 2021 at 03:35:57PM +0300, Gabriel Ivăncescu wrote:
> @@ -189,8 +196,8 @@ typedef struct primitive_funcs
> const dib_info *brush, const rop_mask_bits *bits);
> void (* copy_rect)(const dib_info *dst, const RECT *rc, const dib_info *src,
> const POINT *origin, int rop2, int overlap);
> - void (* blend_rect)(const dib_info *dst, const RECT *rc, const dib_info *src,
> - const POINT *origin, BLENDFUNCTION blend);
> + void (* blend_rect)(const dib_info *dst, const dib_info *src, LONG diff_x, LONG diff_y,
> + const struct clipped_rects *clipped_rects, BLENDFUNCTION blend);
Using a POINT *offset instead of diff_[xy] would be cleaner.
Huw.
April 1, 2021
[PATCH v2 3/3] winex11.drv: Use X11 root-relative coordinates when possible.
by Rémi Bernon
From: Gabriel Ivăncescu <gabrielopcode(a)gmail.com>
Since whole_rect / client_rect are updated asynchronously, there may be
a small lag between X11 and Wine regarding the expected window position.
Then, as events' x and y fields are reported relative to the X11 window
position, this lag can cause inconsistencies when we compute absolute
mouse positions.
Also, applications that control their own position while being moved
cause additional whole_rect / client_rect updates, before X11 knows
about it.
This can make applications like Winamp go nuts when they are being moved
and move all over the place "randomly".
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46309
Signed-off-by: Rémi Bernon <rbernon(a)codeweavers.com>
---
dlls/winex11.drv/mouse.c | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c
index 8110070c2bf..a87c147cbd4 100644
--- a/dlls/winex11.drv/mouse.c
+++ b/dlls/winex11.drv/mouse.c
@@ -592,13 +592,14 @@ static BOOL is_old_motion_event( unsigned long serial )
*
* Map the input event coordinates so they're relative to the desktop.
*/
-static void map_event_coords( HWND hwnd, Window window, INPUT *input )
+static void map_event_coords( HWND hwnd, Window window, Window event_root, int x_root, int y_root, INPUT *input )
{
struct x11drv_thread_data *thread_data;
struct x11drv_win_data *data = NULL;
POINT pt = { input->u.mi.dx, input->u.mi.dy };
- TRACE( "hwnd %p, window %lx, input %p\n", hwnd, window, input );
+ TRACE( "hwnd %p, window %lx, event_root %lx, x_root %d, y_root %d, input %p\n", hwnd, window, event_root,
+ x_root, y_root, input );
if (!hwnd)
{
@@ -610,6 +611,7 @@ static void map_event_coords( HWND hwnd, Window window, INPUT *input )
}
else if (!(data = get_win_data( hwnd ))) return;
else if (window == root_window) pt = root_to_virtual_screen( pt.x, pt.y );
+ else if (event_root == root_window) pt = root_to_virtual_screen( x_root, y_root );
else
{
if (window == data->whole_window)
@@ -1707,7 +1709,7 @@ BOOL X11DRV_ButtonPress( HWND hwnd, XEvent *xev )
input.u.mi.dwExtraInfo = 0;
update_user_time( event->time );
- map_event_coords( hwnd, event->window, &input );
+ map_event_coords( hwnd, event->window, event->root, event->x_root, event->y_root, &input );
send_mouse_input( hwnd, event->window, event->state, &input );
return TRUE;
}
@@ -1733,7 +1735,7 @@ BOOL X11DRV_ButtonRelease( HWND hwnd, XEvent *xev )
input.u.mi.time = EVENT_x11_time_to_win32_time( event->time );
input.u.mi.dwExtraInfo = 0;
- map_event_coords( hwnd, event->window, &input );
+ map_event_coords( hwnd, event->window, event->root, event->x_root, event->y_root, &input );
send_mouse_input( hwnd, event->window, event->state, &input );
return TRUE;
}
@@ -1762,7 +1764,7 @@ BOOL X11DRV_MotionNotify( HWND hwnd, XEvent *xev )
TRACE( "pos %d,%d old serial %lu, ignoring\n", input.u.mi.dx, input.u.mi.dy, event->serial );
return FALSE;
}
- map_event_coords( hwnd, event->window, &input );
+ map_event_coords( hwnd, event->window, event->root, event->x_root, event->y_root, &input );
send_mouse_input( hwnd, event->window, event->state, &input );
return TRUE;
}
@@ -1794,7 +1796,7 @@ BOOL X11DRV_EnterNotify( HWND hwnd, XEvent *xev )
TRACE( "pos %d,%d old serial %lu, ignoring\n", input.u.mi.dx, input.u.mi.dy, event->serial );
return FALSE;
}
- map_event_coords( hwnd, event->window, &input );
+ map_event_coords( hwnd, event->window, event->root, event->x_root, event->y_root, &input );
send_mouse_input( hwnd, event->window, event->state, &input );
return TRUE;
}
--
2.30.2
April 1, 2021
[PATCH v2 2/3] winex11.drv: Map coordinates before calling send_mouse_input.
by Rémi Bernon
Based on a patch from Gabriel Ivăncescu <gabrielopcode(a)gmail.com>.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46309
Signed-off-by: Rémi Bernon <rbernon(a)codeweavers.com>
---
dlls/winex11.drv/mouse.c | 26 +++++++++++++++++++-------
1 file changed, 19 insertions(+), 7 deletions(-)
diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c
index e567a03c61c..8110070c2bf 100644
--- a/dlls/winex11.drv/mouse.c
+++ b/dlls/winex11.drv/mouse.c
@@ -592,13 +592,24 @@ static BOOL is_old_motion_event( unsigned long serial )
*
* Map the input event coordinates so they're relative to the desktop.
*/
-static void map_event_coords( HWND hwnd, Window window, struct x11drv_win_data *data, INPUT *input )
+static void map_event_coords( HWND hwnd, Window window, INPUT *input )
{
+ struct x11drv_thread_data *thread_data;
+ struct x11drv_win_data *data = NULL;
POINT pt = { input->u.mi.dx, input->u.mi.dy };
- TRACE( "hwnd %p, window %lx, data %p, input %p\n", hwnd, window, data, input );
+ TRACE( "hwnd %p, window %lx, input %p\n", hwnd, window, input );
- if (window == root_window) pt = root_to_virtual_screen( pt.x, pt.y );
+ if (!hwnd)
+ {
+ thread_data = x11drv_thread_data();
+ if (!thread_data->clip_hwnd) return;
+ if (thread_data->clip_window != window) return;
+ pt.x += clip_rect.left;
+ pt.y += clip_rect.top;
+ }
+ else if (!(data = get_win_data( hwnd ))) return;
+ else if (window == root_window) pt = root_to_virtual_screen( pt.x, pt.y );
else
{
if (window == data->whole_window)
@@ -611,6 +622,7 @@ static void map_event_coords( HWND hwnd, Window window, struct x11drv_win_data *
pt.x = data->client_rect.right - data->client_rect.left - 1 - pt.x;
MapWindowPoints( hwnd, 0, &pt, 1 );
}
+ if (data) release_win_data( data );
TRACE( "mapped %s to %s\n", wine_dbgstr_point( (POINT *)&input->u.mi.dx ), wine_dbgstr_point( &pt ) );
@@ -643,15 +655,11 @@ static void send_mouse_input( HWND hwnd, Window window, unsigned int state, INPU
sync_window_cursor( window );
last_cursor_change = input->u.mi.time;
}
- input->u.mi.dx += clip_rect.left;
- input->u.mi.dy += clip_rect.top;
__wine_send_input( hwnd, input );
return;
}
if (!(data = get_win_data( hwnd ))) return;
- map_event_coords( hwnd, window, data, input );
-
if (InterlockedExchangePointer( (void **)&cursor_window, hwnd ) != hwnd ||
input->u.mi.time - last_cursor_change > 100)
{
@@ -1699,6 +1707,7 @@ BOOL X11DRV_ButtonPress( HWND hwnd, XEvent *xev )
input.u.mi.dwExtraInfo = 0;
update_user_time( event->time );
+ map_event_coords( hwnd, event->window, &input );
send_mouse_input( hwnd, event->window, event->state, &input );
return TRUE;
}
@@ -1724,6 +1733,7 @@ BOOL X11DRV_ButtonRelease( HWND hwnd, XEvent *xev )
input.u.mi.time = EVENT_x11_time_to_win32_time( event->time );
input.u.mi.dwExtraInfo = 0;
+ map_event_coords( hwnd, event->window, &input );
send_mouse_input( hwnd, event->window, event->state, &input );
return TRUE;
}
@@ -1752,6 +1762,7 @@ BOOL X11DRV_MotionNotify( HWND hwnd, XEvent *xev )
TRACE( "pos %d,%d old serial %lu, ignoring\n", input.u.mi.dx, input.u.mi.dy, event->serial );
return FALSE;
}
+ map_event_coords( hwnd, event->window, &input );
send_mouse_input( hwnd, event->window, event->state, &input );
return TRUE;
}
@@ -1783,6 +1794,7 @@ BOOL X11DRV_EnterNotify( HWND hwnd, XEvent *xev )
TRACE( "pos %d,%d old serial %lu, ignoring\n", input.u.mi.dx, input.u.mi.dy, event->serial );
return FALSE;
}
+ map_event_coords( hwnd, event->window, &input );
send_mouse_input( hwnd, event->window, event->state, &input );
return TRUE;
}
--
2.30.2
April 1, 2021
[PATCH v2 1/3] winex11.drv: Don't remap root_window relative coordinates.
by Rémi Bernon
We already converted coordinates from root to virtual screen, we
shouldn't need to remap them.
Based on a patch from Gabriel Ivăncescu <gabrielopcode(a)gmail.com>.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46309
Signed-off-by: Rémi Bernon <rbernon(a)codeweavers.com>
---
v2: Remap coordinates for client_window too, if we ever receive events
on them.
dlls/winex11.drv/mouse.c | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c
index 8b39458fd37..e567a03c61c 100644
--- a/dlls/winex11.drv/mouse.c
+++ b/dlls/winex11.drv/mouse.c
@@ -599,15 +599,18 @@ static void map_event_coords( HWND hwnd, Window window, struct x11drv_win_data *
TRACE( "hwnd %p, window %lx, data %p, input %p\n", hwnd, window, data, input );
if (window == root_window) pt = root_to_virtual_screen( pt.x, pt.y );
- if (window == data->whole_window)
+ else
{
- pt.x += data->whole_rect.left - data->client_rect.left;
- pt.y += data->whole_rect.top - data->client_rect.top;
- }
+ if (window == data->whole_window)
+ {
+ pt.x += data->whole_rect.left - data->client_rect.left;
+ pt.y += data->whole_rect.top - data->client_rect.top;
+ }
- if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL)
- pt.x = data->client_rect.right - data->client_rect.left - 1 - pt.x;
- MapWindowPoints( hwnd, 0, &pt, 1 );
+ if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL)
+ pt.x = data->client_rect.right - data->client_rect.left - 1 - pt.x;
+ MapWindowPoints( hwnd, 0, &pt, 1 );
+ }
TRACE( "mapped %s to %s\n", wine_dbgstr_point( (POINT *)&input->u.mi.dx ), wine_dbgstr_point( &pt ) );
--
2.30.2
April 1, 2021