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
December 2019
- 73 participants
- 1394 messages
[PATCH 5/5] qedit/samplegrabber: Use BaseFilterImpl_JoinFilterGraph().
by Zebediah Figura
Manual testing shows that adding or removing the filter does not affect whether
it is in one-shot mode.
Signed-off-by: Zebediah Figura <z.figura12(a)gmail.com>
---
dlls/qedit/samplegrabber.c | 23 +----------------------
1 file changed, 1 insertion(+), 22 deletions(-)
diff --git a/dlls/qedit/samplegrabber.c b/dlls/qedit/samplegrabber.c
index 9dd8c02b23d..af05966c140 100644
--- a/dlls/qedit/samplegrabber.c
+++ b/dlls/qedit/samplegrabber.c
@@ -66,11 +66,6 @@ static inline SG_Impl *impl_from_strmbase_filter(struct strmbase_filter *iface)
return CONTAINING_RECORD(iface, SG_Impl, filter);
}
-static inline SG_Impl *impl_from_IBaseFilter(IBaseFilter *iface)
-{
- return CONTAINING_RECORD(iface, SG_Impl, filter.IBaseFilter_iface);
-}
-
static inline SG_Impl *impl_from_ISampleGrabber(ISampleGrabber *iface)
{
return CONTAINING_RECORD(iface, SG_Impl, ISampleGrabber_iface);
@@ -198,22 +193,6 @@ static void SampleGrabber_callback(SG_Impl *This, IMediaSample *sample)
}
}
-/* IBaseFilter */
-static HRESULT WINAPI
-SampleGrabber_IBaseFilter_JoinFilterGraph(IBaseFilter *iface, IFilterGraph *graph, LPCWSTR name)
-{
- SG_Impl *This = impl_from_IBaseFilter(iface);
-
- TRACE("(%p)->(%p, %s)\n", This, graph, debugstr_w(name));
-
- BaseFilterImpl_JoinFilterGraph(iface, graph, name);
- This->oneShot = OneShot_None;
-
- return S_OK;
-}
-
-/* SampleGrabber implementation of ISampleGrabber interface */
-
/* IUnknown */
static HRESULT WINAPI
SampleGrabber_ISampleGrabber_QueryInterface(ISampleGrabber *iface, REFIID riid, void **ppv)
@@ -525,7 +504,7 @@ static const IBaseFilterVtbl IBaseFilter_VTable =
BaseFilterImpl_EnumPins,
BaseFilterImpl_FindPin,
BaseFilterImpl_QueryFilterInfo,
- SampleGrabber_IBaseFilter_JoinFilterGraph,
+ BaseFilterImpl_JoinFilterGraph,
BaseFilterImpl_QueryVendorInfo,
};
--
2.23.0
Dec. 4, 2019
[PATCH 4/5] wineqtdecoder: Don't expose IMediaSeeking from the filter.
by Zebediah Figura
Signed-off-by: Zebediah Figura <z.figura12(a)gmail.com>
---
dlls/wineqtdecoder/qtsplitter.c | 35 ++-------------------------------
1 file changed, 2 insertions(+), 33 deletions(-)
diff --git a/dlls/wineqtdecoder/qtsplitter.c b/dlls/wineqtdecoder/qtsplitter.c
index c021fec8af3..28ea36785a8 100644
--- a/dlls/wineqtdecoder/qtsplitter.c
+++ b/dlls/wineqtdecoder/qtsplitter.c
@@ -381,37 +381,6 @@ IUnknown * CALLBACK QTSplitter_create(IUnknown *outer, HRESULT *phr)
return &This->filter.IUnknown_inner;
}
-static HRESULT WINAPI QT_QueryInterface(IBaseFilter *iface, REFIID riid, LPVOID *ppv)
-{
- QTSplitter *This = impl_from_IBaseFilter(iface);
- TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
-
- *ppv = NULL;
-
- if (IsEqualIID(riid, &IID_IUnknown))
- *ppv = This;
- else if (IsEqualIID(riid, &IID_IPersist))
- *ppv = This;
- else if (IsEqualIID(riid, &IID_IMediaFilter))
- *ppv = This;
- else if (IsEqualIID(riid, &IID_IBaseFilter))
- *ppv = This;
- else if (IsEqualIID(riid, &IID_IMediaSeeking))
- *ppv = &This->sourceSeeking;
-
- if (*ppv)
- {
- IUnknown_AddRef((IUnknown *)(*ppv));
- return S_OK;
- }
-
- if (!IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IVideoWindow) &&
- !IsEqualIID(riid, &IID_IAMFilterMiscFlags))
- FIXME("No interface for %s!\n", debugstr_guid(riid));
-
- return E_NOINTERFACE;
-}
-
static OSErr QT_Create_Extract_Session(QTSplitter *filter)
{
AudioStreamBasicDescription aDesc;
@@ -753,7 +722,7 @@ audio_error:
}
static const IBaseFilterVtbl QT_Vtbl = {
- QT_QueryInterface,
+ BaseFilterImpl_QueryInterface,
BaseFilterImpl_AddRef,
BaseFilterImpl_Release,
BaseFilterImpl_GetClassID,
@@ -1220,7 +1189,7 @@ static HRESULT WINAPI QTOutPin_QueryInterface(IPin *iface, REFIID riid, void **p
else if (IsEqualIID(riid, &IID_IPin))
*ppv = iface;
else if (IsEqualIID(riid, &IID_IMediaSeeking))
- return IBaseFilter_QueryInterface(&This->pin.pin.filter->IBaseFilter_iface, &IID_IMediaSeeking, ppv);
+ *ppv = &This->sourceSeeking.IMediaSeeking_iface;
else if (IsEqualIID(riid, &IID_IQualityControl))
*ppv = &This->IQualityControl_iface;
--
2.23.0
Dec. 4, 2019
[PATCH 3/5] wineqtdecoder: Don't expose IMediaSeeking from the sink pin.
by Zebediah Figura
Signed-off-by: Zebediah Figura <z.figura12(a)gmail.com>
---
dlls/wineqtdecoder/qtsplitter.c | 28 +---------------------------
1 file changed, 1 insertion(+), 27 deletions(-)
diff --git a/dlls/wineqtdecoder/qtsplitter.c b/dlls/wineqtdecoder/qtsplitter.c
index f22e014e1b9..c021fec8af3 100644
--- a/dlls/wineqtdecoder/qtsplitter.c
+++ b/dlls/wineqtdecoder/qtsplitter.c
@@ -1168,34 +1168,8 @@ static HRESULT WINAPI QTInPin_NewSegment(IPin *iface, REFERENCE_TIME tStart, REF
return S_OK;
}
-static HRESULT WINAPI QTInPin_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv)
-{
- QTInPin *This = impl_from_IPin(iface);
-
- TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppv);
-
- *ppv = NULL;
-
- if (IsEqualIID(riid, &IID_IUnknown))
- *ppv = iface;
- else if (IsEqualIID(riid, &IID_IPin))
- *ppv = iface;
- else if (IsEqualIID(riid, &IID_IMediaSeeking))
- return IBaseFilter_QueryInterface(&This->pin.filter->IBaseFilter_iface, &IID_IMediaSeeking, ppv);
-
- if (*ppv)
- {
- IUnknown_AddRef((IUnknown *)(*ppv));
- return S_OK;
- }
-
- FIXME("No interface for %s!\n", debugstr_guid(riid));
-
- return E_NOINTERFACE;
-}
-
static const IPinVtbl QT_InputPin_Vtbl = {
- QTInPin_QueryInterface,
+ BasePinImpl_QueryInterface,
BasePinImpl_AddRef,
BasePinImpl_Release,
BaseInputPinImpl_Connect,
--
2.23.0
Dec. 4, 2019
[PATCH 2/5] wineqtdecoder: Use strmbase filter state change methods.
by Zebediah Figura
Signed-off-by: Zebediah Figura <z.figura12(a)gmail.com>
---
Not compile-tested.
dlls/wineqtdecoder/qtsplitter.c | 107 ++++++++++++--------------------
1 file changed, 40 insertions(+), 67 deletions(-)
diff --git a/dlls/wineqtdecoder/qtsplitter.c b/dlls/wineqtdecoder/qtsplitter.c
index 81e18d34205..f22e014e1b9 100644
--- a/dlls/wineqtdecoder/qtsplitter.c
+++ b/dlls/wineqtdecoder/qtsplitter.c
@@ -160,7 +160,6 @@ typedef struct QTSplitter {
HANDLE runEvent;
DWORD outputSize;
- FILTER_STATE state;
CRITICAL_SECTION csReceive;
SourceSeeking sourceSeeking;
@@ -281,10 +280,46 @@ static void qt_splitter_destroy(struct strmbase_filter *iface)
CoTaskMemFree(filter);
}
+static HRESULT qt_splitter_start_stream(struct strmbase_filter *iface, REFERENCE_TIME time)
+{
+ QTSplitter *filter = impl_from_strmbase_filter(iface);
+ HRESULT hr = VFW_E_NOT_CONNECTED, pin_hr;
+
+ EnterCriticalSection(&This->csReceive);
+
+ if (filter->pVideo_Pin)
+ pin_hr = BaseOutputPinImpl_Active(&filter->pVideo_Pin->pin);
+ if (SUCCEEDED(pin_hr))
+ hr = pin_hr;
+ if (filter->pAudio_Pin)
+ pin_hr = BaseOutputPinImpl_Active(&filter->pAudio_Pin->pin);
+ if (SUCCEEDED(pin_hr))
+ hr = pin_hr;
+ SetEvent(filter->runEvent);
+
+ LeaveCriticalSection(&This->csReceive);
+
+ return hr;
+}
+
+static HRESULT qt_splitter_cleanup_stream(struct strmbase_filter *iface)
+{
+ QTSplitter *filter = impl_from_strmbase_filter(iface);
+
+ EnterCriticalSection(&This->csReceive);
+ IAsyncReader_BeginFlush(filter->pInputPin.pReader);
+ IAsyncReader_EndFlush(filter->pInputPin.pReader);
+ LeaveCriticalSection(&This->csReceive);
+
+ return S_OK;
+}
+
static const struct strmbase_filter_ops filter_ops =
{
.filter_get_pin = qt_splitter_get_pin,
.filter_destroy = qt_splitter_destroy,
+ .filter_start_stream = qt_splitter_start_stream,
+ .filter_cleanup_stream = qt_splitter_cleanup_stream,
};
static HRESULT sink_query_accept(struct strmbase_pin *iface, const AM_MEDIA_TYPE *mt)
@@ -329,7 +364,6 @@ IUnknown * CALLBACK QTSplitter_create(IUnknown *outer, HRESULT *phr)
This->pVideo_Pin = NULL;
This->pAudio_Pin = NULL;
- This->state = State_Stopped;
This->aSession = NULL;
This->runEvent = CreateEventW(NULL, 0, 0, NULL);
@@ -378,28 +412,6 @@ static HRESULT WINAPI QT_QueryInterface(IBaseFilter *iface, REFIID riid, LPVOID
return E_NOINTERFACE;
}
-static HRESULT WINAPI QT_Stop(IBaseFilter *iface)
-{
- QTSplitter *This = impl_from_IBaseFilter(iface);
-
- TRACE("()\n");
-
- EnterCriticalSection(&This->csReceive);
- IAsyncReader_BeginFlush(This->pInputPin.pReader);
- IAsyncReader_EndFlush(This->pInputPin.pReader);
- LeaveCriticalSection(&This->csReceive);
-
- return S_OK;
-}
-
-static HRESULT WINAPI QT_Pause(IBaseFilter *iface)
-{
- HRESULT hr = S_OK;
- TRACE("()\n");
-
- return hr;
-}
-
static OSErr QT_Create_Extract_Session(QTSplitter *filter)
{
AudioStreamBasicDescription aDesc;
@@ -534,7 +546,6 @@ static DWORD WINAPI QTSplitter_thread(LPVOID data)
return 0;
}
- This->state = State_Running;
/* Prime the pump: Needed for MPEG streams */
GetMovieNextInterestingTime(This->pQTMovie, nextTimeEdgeOK | nextTimeStep, 0, NULL, This->movie_time, 1, &next_time, NULL);
@@ -733,7 +744,6 @@ audio_error:
LeaveCriticalSection(&This->csReceive);
} while (hr == S_OK);
- This->state = State_Stopped;
if (This->pAudio_Pin)
OutputQueue_EOS(This->pAudio_Pin->queue);
if (This->pVideo_Pin)
@@ -742,52 +752,15 @@ audio_error:
return hr;
}
-static HRESULT WINAPI QT_Run(IBaseFilter *iface, REFERENCE_TIME tStart)
-{
- HRESULT hr = S_OK;
- QTSplitter *This = impl_from_IBaseFilter(iface);
- HRESULT hr_any = VFW_E_NOT_CONNECTED;
-
- TRACE("(%s)\n", wine_dbgstr_longlong(tStart));
-
- EnterCriticalSection(&This->csReceive);
-
- if (This->pVideo_Pin)
- hr = BaseOutputPinImpl_Active(&This->pVideo_Pin->pin);
- if (SUCCEEDED(hr))
- hr_any = hr;
- if (This->pAudio_Pin)
- hr = BaseOutputPinImpl_Active(&This->pAudio_Pin->pin);
- if (SUCCEEDED(hr))
- hr_any = hr;
-
- hr = hr_any;
-
- SetEvent(This->runEvent);
- LeaveCriticalSection(&This->csReceive);
-
- return hr;
-}
-
-static HRESULT WINAPI QT_GetState(IBaseFilter *iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
-{
- QTSplitter *This = impl_from_IBaseFilter(iface);
- TRACE("(%d, %p)\n", dwMilliSecsTimeout, pState);
-
- *pState = This->state;
-
- return S_OK;
-}
-
static const IBaseFilterVtbl QT_Vtbl = {
QT_QueryInterface,
BaseFilterImpl_AddRef,
BaseFilterImpl_Release,
BaseFilterImpl_GetClassID,
- QT_Stop,
- QT_Pause,
- QT_Run,
- QT_GetState,
+ BaseFilterImpl_Stop,
+ BaseFilterImpl_Pause,
+ BaseFilterImpl_Run,
+ BaseFilterImpl_GetState,
BaseFilterImpl_SetSyncSource,
BaseFilterImpl_GetSyncSource,
BaseFilterImpl_EnumPins,
--
2.23.0
Dec. 4, 2019
[PATCH 1/5] winegstreamer: Fix the timeout passed to gst_element_get_state().
by Zebediah Figura
Signed-off-by: Zebediah Figura <z.figura12(a)gmail.com>
---
dlls/winegstreamer/gstdemux.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/winegstreamer/gstdemux.c b/dlls/winegstreamer/gstdemux.c
index 753286a7189..e024d271974 100644
--- a/dlls/winegstreamer/gstdemux.c
+++ b/dlls/winegstreamer/gstdemux.c
@@ -1324,7 +1324,7 @@ static HRESULT gstdemux_wait_state(struct strmbase_filter *iface, DWORD timeout)
return S_OK;
ret = gst_element_get_state(filter->container, NULL, NULL,
- timeout == INFINITE ? GST_CLOCK_TIME_NONE : timeout * 1000);
+ timeout == INFINITE ? GST_CLOCK_TIME_NONE : timeout * 1000000);
if (ret == GST_STATE_CHANGE_FAILURE)
{
ERR("Failed to get state.\n");
--
2.23.0
Dec. 4, 2019
[PATCH v4 2/2] ntdll/tests: Add tests for UMIP instructions.
by Brendan Shanks
Signed-off-by: Brendan Shanks <bshanks(a)codeweavers.com>
---
dlls/ntdll/tests/exception.c | 375 +++++++++++++++++++++++++++++++++++
1 file changed, 375 insertions(+)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c
index fb58c0ee7a..0d63731b56 100644
--- a/dlls/ntdll/tests/exception.c
+++ b/dlls/ntdll/tests/exception.c
@@ -327,6 +327,19 @@ static const struct exception
0xba, 0xba, 0xba, 0xba, 0xba, /* mov $0xbabababa, %edx */
0xcd, 0x2d, 0xc3 }, /* int $0x2d; ret */
17, 0, FALSE, STATUS_BREAKPOINT, 3, { 0xb8b8b8b8, 0xb9b9b9b9, 0xbabababa } },
+
+ /* test UMIP-covered instructions storing to a NULL pointer */
+ { { 0x31, 0xc0, 0x0f, 0x01, 0x00, 0xc3 }, /* xor %eax,%eax; sgdt (%eax); ret */
+ 2, 3, FALSE, STATUS_ACCESS_VIOLATION, 2, { 1, 0 } },
+/* 45 */
+ { { 0x31, 0xc0, 0x0f, 0x01, 0x08, 0xc3 }, /* xor %eax,%eax; sidt (%eax); ret */
+ 2, 3, FALSE, STATUS_ACCESS_VIOLATION, 2, { 1, 0 } },
+ { { 0x31, 0xc0, 0x0f, 0x00, 0x00, 0xc3 }, /* xor %eax,%eax; sldt (%eax); ret */
+ 2, 3, FALSE, STATUS_ACCESS_VIOLATION, 2, { 1, 0 } },
+ { { 0x31, 0xc0, 0x0f, 0x01, 0x20, 0xc3 }, /* xor %eax,%eax; smsw (%eax); ret */
+ 2, 3, FALSE, STATUS_ACCESS_VIOLATION, 2, { 1, 0 } },
+ { { 0x31, 0xc0, 0x0f, 0x00, 0x08, 0xc3 }, /* xor %eax,%eax; str (%eax); ret */
+ 2, 3, FALSE, STATUS_ACCESS_VIOLATION, 2, { 1, 0 } },
};
static int got_exception;
@@ -2420,6 +2433,19 @@ static const struct exception
1, 1, STATUS_SINGLE_STEP, 0 },
{ { 0xcd, 0x2c, 0xc3 },
0, 2, STATUS_ASSERTION_FAILURE, 0 },
+
+ /* test UMIP-covered instructions storing to a NULL pointer */
+ { { 0x48, 0x31, 0xc0, 0x0f, 0x01, 0x00, 0xc3 }, /* xor %rax,%rax; sgdt (%rax); ret */
+ 3, 3, STATUS_ACCESS_VIOLATION, 2, { 1, 0 } },
+ { { 0x48, 0x31, 0xc0, 0x0f, 0x01, 0x08, 0xc3 }, /* xor %rax,%rax; sidt (%rax); ret */
+ 3, 3, STATUS_ACCESS_VIOLATION, 2, { 1, 0 } },
+/* 40 */
+ { { 0x48, 0x31, 0xc0, 0x0f, 0x00, 0x00, 0xc3 }, /* xor %rax,%rax; sldt (%rax); ret */
+ 3, 3, STATUS_ACCESS_VIOLATION, 2, { 1, 0 } },
+ { { 0x48, 0x31, 0xc0, 0x0f, 0x01, 0x20, 0xc3 }, /* xor %rax,%rax; smsw (%rax); ret */
+ 3, 3, STATUS_ACCESS_VIOLATION, 2, { 1, 0 } },
+ { { 0x48, 0x31, 0xc0, 0x0f, 0x00, 0x08, 0xc3 }, /* xor %rax,%rax; str (%rax); ret */
+ 3, 3, STATUS_ACCESS_VIOLATION, 2, { 1, 0 } },
};
static int got_exception;
@@ -3383,6 +3409,353 @@ static void test_unload_trace(void)
ok(found, "Unloaded module wasn't found.\n");
}
+#if defined(__x86_64__)
+static DWORD WINAPI umip_handler( EXCEPTION_RECORD *rec, ULONG64 frame,
+ CONTEXT *context, DISPATCHER_CONTEXT *dispatcher )
+{
+ const UINT *umip_insn_len = *(const UINT **)(dispatcher->HandlerData);
+
+ got_exception++;
+ trace( "umip_handler exception: %x flags:%x addr:%p\n",
+ rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress );
+
+ context->Rip += *umip_insn_len;
+ return ExceptionContinueExecution;
+}
+#else
+static DWORD umip_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
+ CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
+{
+ const UINT *umip_insn_len = *(const UINT **)(frame + 1);
+
+ got_exception++;
+ trace( "umip_handler exception: %x flags:%x addr:%p\n",
+ rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress );
+
+ context->Eip += *umip_insn_len;
+ return ExceptionContinueExecution;
+}
+#endif
+
+/* Each UMIP test is run first using run_exception_test() in case it throws an exception. If it doesn't, the
+ * test is run again to check the returned value.
+ * Tests writing to memory have two versions: one used for run_exception_test() that writes to the stack, and the
+ * other writing to a pointer passed as an argument.
+ */
+static void umip_test_reg_mem_16(const char *insn_name,
+ const BYTE *reg16_code, UINT reg16_code_size, UINT reg16_insn_len,
+ const BYTE *reg32_code, UINT reg32_code_size, UINT reg32_insn_len,
+ const BYTE *reg64_code, UINT reg64_code_size, UINT reg64_insn_len,
+ const BYTE *mem16_arg_code, UINT mem16_arg_code_size,
+ const BYTE *mem16_stack_code, UINT mem16_stack_code_size, UINT mem16_stack_insn_len)
+{
+ /* sldt, str, smsw all store a 16-bit value to a register or memory.
+ * When the destination is a register, the 16-bit value is zero-extended to the destination register size.
+ * When the destination is memory, only the 16-bit value is stored.
+ */
+ UINT16 value16;
+ UINT32 value32;
+
+ UINT16 (*reg16_func)(void) = code_mem;
+ UINT32 (*reg32_func)(void) = code_mem;
+ void (*mem16_func)(UINT16 *value) = code_mem;
+
+ /* Destination is 16-bit register */
+ got_exception = 0;
+ run_exception_test(umip_handler, ®16_insn_len, reg16_code, reg16_code_size, 0);
+ ok(!got_exception, "%s reg16 caused exception\n", insn_name);
+ if (!got_exception)
+ {
+ memset(&value16, 0xcc, sizeof(value16));
+ memcpy(code_mem, reg16_code, reg16_code_size);
+ value16 = reg16_func();
+ trace("%s reg16: 0x%x\n", insn_name, value16);
+ }
+
+ /* Destination is 32-bit register. */
+ got_exception = 0;
+ run_exception_test(umip_handler, ®32_insn_len, reg32_code, reg32_code_size, 0);
+ ok(!got_exception, "%s reg32 caused exception\n", insn_name);
+ if (!got_exception)
+ {
+ memset(&value32, 0xcc, sizeof(value32));
+ memcpy(code_mem, reg32_code, reg32_code_size);
+ value32 = reg32_func();
+ trace("%s reg32: 0x%x\n", insn_name, value32);
+#if defined(__x86_64__)
+ /* For sldt/str in 64-bit mode, the upper 16 bits is defined to be zero */
+ if (!strcmp(insn_name, "sldt") || !strcmp(insn_name, "str"))
+ ok(value32 >> 16 == 0, "%s expected upper 16 bits = 0, got 0x%x\n", insn_name, value32 >> 16);
+#endif
+ }
+
+#if defined(__x86_64__)
+ /* Destination is 64-bit register */
+ got_exception = 0;
+ run_exception_test(umip_handler, ®64_insn_len, reg64_code, reg64_code_size, 0);
+ ok(!got_exception, "%s reg64 caused exception\n", insn_name);
+ if (!got_exception)
+ {
+ UINT64 value64;
+ UINT64 (*reg64_func)(void) = code_mem;
+
+ memset(&value64, 0xcc, sizeof(value64));
+ memcpy(code_mem, reg64_code, reg64_code_size);
+ value64 = reg64_func();
+ trace("%s reg64: 0x%llx\n", insn_name, value64);
+
+ /* For sldt/str the upper 48 bits is defined to be zero */
+ if (!strcmp(insn_name, "sldt") || !strcmp(insn_name, "str"))
+ ok(value64 >> 16 == 0, "%s expected upper 48 bits = 0, got 0x%llx\n", insn_name, value64 >> 16);
+ }
+#endif
+
+ /* Destination is memory (only the low 16 bits are defined to be written) */
+ got_exception = 0;
+ run_exception_test(umip_handler, &mem16_stack_insn_len, mem16_stack_code, mem16_stack_code_size, 0);
+ ok(!got_exception, "%s mem16_stack caused exception\n", insn_name);
+ if (!got_exception)
+ {
+ memset(&value32, 0xcc, sizeof(value32));
+ memcpy(code_mem, mem16_arg_code, mem16_arg_code_size);
+ mem16_func((UINT16 *)&value32);
+ trace("%s mem: 0x%x\n", insn_name, value32);
+ ok(value32 >> 16 == 0xcccc, "%s expected upper 16 bits = 0xcccc, got 0x%x\n", insn_name, value32 >> 16);
+ }
+}
+
+static void umip_test_mem_descriptor(const char *insn_name,
+ const BYTE *code_arg, UINT code_arg_size,
+ const BYTE *code_stack, UINT code_stack_size, UINT code_stack_insn_len)
+{
+ /* sgdt and sidt write the descriptor table register to memory.
+ * The descriptor consists of a 2-byte limit field, and a base field which
+ * is 4 bytes in 32-bit mode and 8 bytes in 64-bit mode.
+ */
+ BYTE descriptor[10];
+ void (*func)(BYTE *value) = code_mem;
+
+ got_exception = 0;
+ run_exception_test(umip_handler, &code_stack_insn_len, code_stack, code_stack_size, 0);
+ ok(!got_exception, "%s caused exception\n", insn_name);
+ if (!got_exception)
+ {
+ memset(descriptor, 0xcc, sizeof(descriptor));
+ memcpy(code_mem, code_arg, code_arg_size);
+ func(descriptor);
+ trace("%s limit: 0x%x\n", insn_name, *(UINT16 *)&descriptor[0]);
+ trace("%s base: 0x%p\n", insn_name, (void *) *(UINT_PTR *)&descriptor[2]);
+ }
+}
+
+static void umip_test_sldt(void)
+{
+ static const BYTE reg16[] = {
+ 0x66, 0x0f, 0x00, 0xc0, /* sldtw %ax */
+ 0xc3, /* ret */
+ };
+ static const BYTE reg32[] = {
+ 0x0f, 0x00, 0xc0, /* sldtl %eax */
+ 0xc3, /* ret */
+ };
+ static const BYTE reg64[] = {
+ 0x48, 0x0f, 0x00, 0xc0, /* sldtq %rax */
+ 0xc3, /* ret */
+ };
+#if defined(__x86_64__)
+ static const BYTE mem16_arg[] = {
+ 0x0f, 0x00, 0x01, /* sldtw (%rcx) */
+ 0xc3, /* ret */
+ };
+ static const BYTE mem16_stack[] = {
+ 0x0f, 0x00, 0x44, 0x24, 0x08, /* sldtw 0x8(%rsp) */
+ 0xc3, /* ret */
+ };
+#else
+ static const BYTE mem16_arg[] = {
+ 0x8b, 0x4c, 0x24, 0x04, /* movl 0x4(%esp), %ecx */
+ 0x0f, 0x00, 0x01, /* sldtw (%ecx) */
+ 0xc3, /* ret */
+ };
+ static const BYTE mem16_stack[] = {
+ 0x83, 0xec, 0x02, /* subl $0x2,%esp */
+ 0x0f, 0x00, 0x04, 0x24, /* sldtw (%esp) */
+ 0x90, /* nop (padding so mem16_stack_insn_len is same for x86_64 and i386) */
+ 0x83, 0xc4, 0x02, /* addl $0x2,%esp */
+ 0xc3, /* ret */
+ };
+#endif
+
+ umip_test_reg_mem_16("sldt",
+ reg16, sizeof(reg16), 4,
+ reg32, sizeof(reg32), 3,
+ reg64, sizeof(reg64), 4,
+ mem16_arg, sizeof(mem16_arg),
+ mem16_stack, sizeof(mem16_stack), 5);
+}
+
+static void umip_test_str(void)
+{
+ static const BYTE reg16[] = {
+ 0x66, 0x0f, 0x00, 0xc8, /* strw %ax */
+ 0xc3, /* ret */
+ };
+ static const BYTE reg32[] = {
+ 0x0f, 0x00, 0xc8, /* strl %eax */
+ 0xc3, /* ret */
+ };
+ static const BYTE reg64[] = {
+ 0x48, 0x0f, 0x00, 0xc8, /* strq %rax */
+ 0xc3, /* ret */
+ };
+#if defined(__x86_64__)
+ static const BYTE mem16_arg[] = {
+ 0x0f, 0x00, 0x09, /* strw (%rcx) */
+ 0xc3, /* ret */
+ };
+ static const BYTE mem16_stack[] = {
+ 0x0f, 0x00, 0x4c, 0x24, 0x08, /* strw 0x8(%rsp) */
+ 0xc3, /* ret */
+ };
+#else
+ static const BYTE mem16_arg[] = {
+ 0x8b, 0x4c, 0x24, 0x04, /* movl 0x4(%esp), %ecx */
+ 0x0f, 0x00, 0x09, /* strw (%ecx) */
+ 0xc3, /* ret */
+ };
+ static const BYTE mem16_stack[] = {
+ 0x83, 0xec, 0x02, /* subl $0x2,%esp */
+ 0x0f, 0x00, 0x0c, 0x24, /* strw (%esp) */
+ 0x90, /* nop (padding so mem16_stack_insn_len is same for x86_64 and i386) */
+ 0x83, 0xc4, 0x02, /* addl $0x2,%esp */
+ 0xc3, /* ret */
+ };
+#endif
+
+ umip_test_reg_mem_16("str",
+ reg16, sizeof(reg16), 4,
+ reg32, sizeof(reg32), 3,
+ reg64, sizeof(reg64), 4,
+ mem16_arg, sizeof(mem16_arg),
+ mem16_stack, sizeof(mem16_stack), 5);
+}
+
+static void umip_test_sgdt(void)
+{
+ /* sgdt destination must be memory */
+#if defined(__x86_64__)
+ static const BYTE mem_arg[] = {
+ 0x0f, 0x01, 0x01, /* sgdtt (%rcx) */
+ 0xc3, /* ret */
+ };
+ static const BYTE mem_stack[] = {
+ 0x0f, 0x01, 0x44, 0x24, 0x08, /* sgdtt 0x8(%rsp) */
+ 0xc3, /* ret */
+ };
+#else
+ static const BYTE mem_arg[] = {
+ 0x8b, 0x4c, 0x24, 0x04, /* movl 0x4(%esp), %ecx */
+ 0x0f, 0x01, 0x01, /* sgdt (%ecx) */
+ 0xc3, /* ret */
+ };
+ static const BYTE mem_stack[] = {
+ 0x83, 0xec, 0x06, /* subl $0x6,%esp */
+ 0x0f, 0x01, 0x04, 0x24, /* sgdt (%esp) */
+ 0x90, /* nop (padding so mem16_stack_insn_len is same for x86_64 and i386) */
+ 0x83, 0xc4, 0x06, /* addl $0x6,%esp */
+ 0xc3, /* ret */
+ };
+#endif
+
+ umip_test_mem_descriptor("sgdt", mem_arg, sizeof(mem_arg), mem_stack, sizeof(mem_stack), 5);
+}
+
+static void umip_test_sidt(void)
+{
+ /* sidt destination must be memory */
+#if defined(__x86_64__)
+ static const BYTE mem_arg[] = {
+ 0x0f, 0x01, 0x09, /* sidtt (%rcx) */
+ 0xc3, /* ret */
+ };
+ static const BYTE mem_stack[] = {
+ 0x0f, 0x01, 0x4c, 0x24, 0x08, /* sidtt 0x8(%rsp) */
+ 0xc3, /* ret */
+ };
+#else
+ static const BYTE mem_arg[] = {
+ 0x8b, 0x4c, 0x24, 0x04, /* movl 0x4(%esp), %ecx */
+ 0x0f, 0x01, 0x09, /* sidt (%ecx) */
+ 0xc3, /* ret */
+ };
+ static const BYTE mem_stack[] = {
+ 0x83, 0xec, 0x06, /* subl $0x6,%esp */
+ 0x0f, 0x01, 0x0c, 0x24, /* sidt (%esp) */
+ 0x90, /* nop (padding so mem16_stack_insn_len is same for x86_64 and i386) */
+ 0x83, 0xc4, 0x06, /* addl $0x6,%esp */
+ 0xc3, /* ret */
+ };
+#endif
+
+ umip_test_mem_descriptor("sidt", mem_arg, sizeof(mem_arg), mem_stack, sizeof(mem_stack), 5);
+}
+
+static void umip_test_smsw(void)
+{
+ static const BYTE reg16[] = {
+ 0x66, 0x0f, 0x01, 0xe0, /* smsww %ax */
+ 0xc3, /* ret */
+ };
+ static const BYTE reg32[] = {
+ 0x0f, 0x01, 0xe0, /* smswl %eax */
+ 0xc3, /* ret */
+ };
+ static const BYTE reg64[] = {
+ 0x48, 0x0f, 0x01, 0xe0, /* smswq %rax */
+ 0xc3, /* ret */
+ };
+
+#if defined(__x86_64__)
+ static const BYTE mem16_arg[] = {
+ 0x0f, 0x01, 0x21, /* smsww (%rcx) */
+ 0xc3, /* ret */
+ };
+ static const BYTE mem16_stack[] = {
+ 0x0f, 0x01, 0x64, 0x24, 0x08, /* smsww 0x8(%rsp) */
+ 0xc3, /* ret */
+ };
+#else
+ static const BYTE mem16_arg[] = {
+ 0x8b, 0x4c, 0x24, 0x04, /* movl 0x4(%esp), %ecx */
+ 0x0f, 0x01, 0x21, /* smsww (%ecx) */
+ 0xc3, /* ret */
+ };
+ static const BYTE mem16_stack[] = {
+ 0x83, 0xec, 0x02, /* subl $0x2,%esp */
+ 0x0f, 0x01, 0x24, 0x24, /* smsww (%esp) */
+ 0x90, /* nop (padding so mem16_stack_insn_len is same for x86_64 and i386) */
+ 0x83, 0xc4, 0x02, /* addl $0x2,%esp */
+ 0xc3, /* ret */
+ };
+#endif
+
+ umip_test_reg_mem_16("smsw",
+ reg16, sizeof(reg16), 4,
+ reg32, sizeof(reg32), 3,
+ reg64, sizeof(reg64), 4,
+ mem16_arg, sizeof(mem16_arg),
+ mem16_stack, sizeof(mem16_stack), 5);
+}
+
+static void test_umip(void)
+{
+ umip_test_sldt();
+ umip_test_str();
+ umip_test_sgdt();
+ umip_test_sidt();
+ umip_test_smsw();
+}
+
START_TEST(exception)
{
HMODULE hntdll = GetModuleHandleA("ntdll.dll");
@@ -3513,6 +3886,7 @@ START_TEST(exception)
test_suspend_thread();
test_suspend_process();
test_unload_trace();
+ test_umip();
#elif defined(__x86_64__)
@@ -3551,6 +3925,7 @@ START_TEST(exception)
test_suspend_thread();
test_suspend_process();
test_unload_trace();
+ test_umip();
if (pRtlAddFunctionTable && pRtlDeleteFunctionTable && pRtlInstallFunctionTableCallback && pRtlLookupFunctionEntry)
test_dynamic_unwind();
--
2.23.0
Dec. 4, 2019
[PATCH v4 1/2] ntdll: Add emulation for UMIP instructions.
by Brendan Shanks
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=47571
Signed-off-by: Brendan Shanks <bshanks(a)codeweavers.com>
---
dlls/ntdll/signal_i386.c | 311 +++++++++++++++++++++++++++++++-
dlls/ntdll/signal_x86_64.c | 350 ++++++++++++++++++++++++++++++++++++-
2 files changed, 645 insertions(+), 16 deletions(-)
diff --git a/dlls/ntdll/signal_i386.c b/dlls/ntdll/signal_i386.c
index e9dd0de2fc..dc30fc3ad2 100644
--- a/dlls/ntdll/signal_i386.c
+++ b/dlls/ntdll/signal_i386.c
@@ -1521,37 +1521,302 @@ __ASM_STDCALL_FUNC( NtGetContextThread, 8,
__ASM_CFI(".cfi_same_value %ebp\n\t")
"ret $8" )
+/***********************************************************************
+ * User-Mode Instruction Prevention (UMIP) is an x86 feature that prevents user-space
+ * code (CPL > 0) from executing the sgdt, sidt, sldt, smsw, and str instructions.
+ * If one of these instructions is executed, a general protection fault is issued.
+ *
+ * UMIP was first implemented by the AMD Zen2 and Intel Sunny Cove microarchitectures
+ * (both released in 2019).
+ *
+ * On Linux:
+ * The kernel traps the GPF and emulates sgdt, sidt, and smsw.
+ * sldt and str are not emulated.
+ *
+ * When the kernel doesn't emulate an instruction, the process receives a SIGSEGV.
+ * emulate_umip_instr() emulates all instructions not emulated by the Linux kernel.
+ */
+
+/* Dummy LDT, matches what I see on Windows and Linux */
+#define UMIP_DUMMY_LDT 0
+
+/* Dummy task register, matches what I see on Windows and Linux */
+#define UMIP_DUMMY_TR 0x40
+
+static void *get_reg_address( CONTEXT *context, BYTE rm )
+{
+ switch (rm & 7)
+ {
+ case 0: return &context->Eax;
+ case 1: return &context->Ecx;
+ case 2: return &context->Edx;
+ case 3: return &context->Ebx;
+ case 4: return &context->Esp;
+ case 5: return &context->Ebp;
+ case 6: return &context->Esi;
+ case 7: return &context->Edi;
+ }
+ return NULL;
+}
+
+/***********************************************************************
+ * INSTR_GetOperandAddr
+ *
+ * Return the address of an instruction operand (from the mod/rm byte).
+ */
+static int INSTR_GetOperandAddr( CONTEXT *context, BYTE *instr, unsigned int instr_len,
+ int long_addr, int segprefix, int *len, void **addr )
+{
+ int mod, rm, base = 0, index = 0, ss = 0, off;
+ unsigned int i = 0;
+
+#define GET_VAL( val, type ) \
+ { if (sizeof(type) > (instr_len - i)) return 0; \
+ *val = *(type *)&instr[i]; i += sizeof(type); *len += sizeof(type); }
+
+ *len = 0;
+ GET_VAL( &mod, BYTE );
+ rm = mod & 7;
+ mod >>= 6;
+
+ if (mod == 3)
+ {
+ *addr = get_reg_address( context, rm );
+ return 1;
+ }
+
+ if (long_addr)
+ {
+ if (rm == 4)
+ {
+ BYTE sib;
+ GET_VAL( &sib, BYTE );
+ rm = sib & 7;
+ ss = sib >> 6;
+ switch((sib >> 3) & 7)
+ {
+ case 0: index = context->Eax; break;
+ case 1: index = context->Ecx; break;
+ case 2: index = context->Edx; break;
+ case 3: index = context->Ebx; break;
+ case 4: index = 0; break;
+ case 5: index = context->Ebp; break;
+ case 6: index = context->Esi; break;
+ case 7: index = context->Edi; break;
+ }
+ }
+
+ switch(rm)
+ {
+ case 0: base = context->Eax; break;
+ case 1: base = context->Ecx; break;
+ case 2: base = context->Edx; break;
+ case 3: base = context->Ebx; break;
+ case 4: base = context->Esp; break;
+ case 5: base = context->Ebp; break;
+ case 6: base = context->Esi; break;
+ case 7: base = context->Edi; break;
+ }
+ switch (mod)
+ {
+ case 0:
+ if (rm == 5) /* special case: ds:(disp32) */
+ {
+ GET_VAL( &base, DWORD );
+ }
+ break;
+
+ case 1: /* 8-bit disp */
+ GET_VAL( &off, BYTE );
+ base += (signed char)off;
+ break;
+
+ case 2: /* 32-bit disp */
+ GET_VAL( &off, DWORD );
+ base += (signed long)off;
+ break;
+ }
+ }
+ else /* short address */
+ {
+ switch(rm)
+ {
+ case 0: /* ds:(bx,si) */
+ base = LOWORD(context->Ebx) + LOWORD(context->Esi);
+ break;
+ case 1: /* ds:(bx,di) */
+ base = LOWORD(context->Ebx) + LOWORD(context->Edi);
+ break;
+ case 2: /* ss:(bp,si) */
+ base = LOWORD(context->Ebp) + LOWORD(context->Esi);
+ break;
+ case 3: /* ss:(bp,di) */
+ base = LOWORD(context->Ebp) + LOWORD(context->Edi);
+ break;
+ case 4: /* ds:(si) */
+ base = LOWORD(context->Esi);
+ break;
+ case 5: /* ds:(di) */
+ base = LOWORD(context->Edi);
+ break;
+ case 6: /* ss:(bp) */
+ base = LOWORD(context->Ebp);
+ break;
+ case 7: /* ds:(bx) */
+ base = LOWORD(context->Ebx);
+ break;
+ }
+
+ switch(mod)
+ {
+ case 0:
+ if (rm == 6) /* special case: ds:(disp16) */
+ {
+ GET_VAL( &base, WORD );
+ }
+ break;
+
+ case 1: /* 8-bit disp */
+ GET_VAL( &off, BYTE );
+ base += (signed char)off;
+ break;
+
+ case 2: /* 16-bit disp */
+ GET_VAL( &off, WORD );
+ base += (signed short)off;
+ break;
+ }
+ base &= 0xffff;
+ }
+ /* FIXME: we assume that all segments have a base of 0 */
+ *addr = (void *)(base + (index << ss));
+ return 1;
+#undef GET_VAL
+}
+
+/***********************************************************************
+ * emulate_umip_instr
+ *
+ * Emulate a UMIP-protected instruction.
+ * Returns: 0 if no instruction emulated,
+ * 1 if instruction emulated successfully,
+ * 2 if instruction's write to memory failed.
+ */
+static int emulate_umip_instr( CONTEXT *context, BYTE *instr, unsigned int len, void **err_addr,
+ int segprefix, unsigned int prefixlen, int long_op, int long_addr )
+{
+ if (len < 2) return 0;
+
+ if (instr[0] == 0x00) /* sldt/str */
+ {
+ int reg = (instr[1] >> 3) & 7;
+ switch (reg)
+ {
+ case 0: /* sldt */
+ case 1: /* str */
+ {
+ int instr_len;
+ int mod = instr[1] >> 6;
+ void *data;
+ UINT16 dummy_value;
+
+ if (!INSTR_GetOperandAddr( context, &instr[1], len - 1, long_addr,
+ segprefix, &instr_len, &data ))
+ return 0;
+
+ if (reg == 0)
+ {
+ /* sldt */
+ dummy_value = UMIP_DUMMY_LDT;
+ TRACE( "sldt at 0x%08x\n", context->Eip );
+ }
+ else
+ {
+ /* str */
+ dummy_value = UMIP_DUMMY_TR;
+ TRACE( "str at 0x%08x\n", context->Eip );
+ }
+
+ if (mod == 3)
+ {
+ /* Destination operand is a register.
+ * Zero-extend the dummy value and store to the register. */
+ UINT32 dummy_value_32 = dummy_value;
+ memcpy( data, &dummy_value_32, long_op ? 4 : 2 );
+ }
+ else
+ {
+ /* Destination operand is a memory location.
+ * Only copy 16 bits regardless of operand size. */
+ if (virtual_uninterrupted_write_memory( data, &dummy_value, sizeof(dummy_value) ))
+ {
+ TRACE( "memory write by 0x%08x to %p failed\n", context->Eip, data );
+ *err_addr = data;
+ return 2;
+ }
+ }
+ context->Eip += prefixlen + instr_len + 2;
+ return 1;
+ }
+ }
+ }
+ else if (instr[0] == 0x01) /* sgdt/sidt/str */
+ {
+ /* The Linux kernel already emulates these instructions for 32-bit processes,
+ * Wine should never get an exception for them.
+ * However, it may be necessary in the future to emulate these instructions
+ * for other OSes if they don't do any emulation.
+ */
+ }
+
+ return 0;
+}
/***********************************************************************
* is_privileged_instr
*
- * Check if the fault location is a privileged instruction.
+ * Check if the fault location is a privileged instruction, and emulate a UMIP-protected instruction if found.
* Based on the instruction emulation code in dlls/kernel/instr.c.
+ * Returns: 0 if no privileged instruction found or emulation failed,
+ * 1 if instruction found and ExceptionCode has been set,
+ * 2 if instruction was successfully emulated.
*/
-static inline DWORD is_privileged_instr( CONTEXT *context )
+static inline int is_privileged_instr( struct stack_layout *stack )
{
BYTE instr[16];
+ CONTEXT *context = &stack->context;
unsigned int i, len, prefix_count = 0;
+ int long_op = 1, long_addr = 1;
+ int segprefix = -1; /* no prefix */
if (!wine_ldt_is_system( context->SegCs )) return 0;
len = virtual_uninterrupted_read_memory( (BYTE *)context->Eip, instr, sizeof(instr) );
- for (i = 0; i < len; i++) switch (instr[i])
+ for (i = 0; i < len; i++)
+ {
+ switch (instr[i])
{
/* instruction prefixes */
case 0x2e: /* %cs: */
+ segprefix = context->SegCs; prefix_count++; break;
case 0x36: /* %ss: */
+ segprefix = context->SegSs; prefix_count++; break;
case 0x3e: /* %ds: */
+ segprefix = context->SegDs; prefix_count++; break;
case 0x26: /* %es: */
+ segprefix = context->SegEs; prefix_count++; break;
case 0x64: /* %fs: */
+ segprefix = context->SegFs; prefix_count++; break;
case 0x65: /* %gs: */
+ segprefix = context->SegGs; prefix_count++; break;
case 0x66: /* opcode size */
+ long_op = !long_op; prefix_count++; break;
case 0x67: /* addr size */
+ long_addr = !long_addr; prefix_count++; break;
case 0xf0: /* lock */
case 0xf2: /* repne */
case 0xf3: /* repe */
- if (++prefix_count >= 15) return EXCEPTION_ILLEGAL_INSTRUCTION;
- continue;
+ prefix_count++; break;
case 0x0f: /* extended instruction */
if (i == len - 1) return 0;
@@ -1561,7 +1826,28 @@ static inline DWORD is_privileged_instr( CONTEXT *context )
case 0x21: /* mov drX, reg */
case 0x22: /* mov reg, crX */
case 0x23: /* mov reg drX */
- return EXCEPTION_PRIV_INSTRUCTION;
+ stack->rec.ExceptionCode = EXCEPTION_PRIV_INSTRUCTION;
+ return 1;
+
+ case 0x00: /* sldt/str */
+ case 0x01: /* sgdt/sidt/smsw */
+ {
+ void *err_addr;
+ int result = emulate_umip_instr( context, &instr[i+1], len - i - 1, &err_addr,
+ segprefix, prefix_count, long_op, long_addr );
+ if (result == 1)
+ return 2;
+ else if (result == 2)
+ {
+ stack->rec.ExceptionCode = EXCEPTION_ACCESS_VIOLATION;
+ stack->rec.NumberParameters = 2;
+ stack->rec.ExceptionInformation[0] = 1;
+ stack->rec.ExceptionInformation[1] = (ULONG_PTR)err_addr;
+ return 1;
+ }
+ else
+ return 0;
+ }
}
return 0;
case 0x6c: /* insb (%dx) */
@@ -1580,10 +1866,14 @@ static inline DWORD is_privileged_instr( CONTEXT *context )
case 0xf4: /* hlt */
case 0xfa: /* cli */
case 0xfb: /* sti */
- return EXCEPTION_PRIV_INSTRUCTION;
+ stack->rec.ExceptionCode = EXCEPTION_PRIV_INSTRUCTION;
+ return 1;
default:
return 0;
}
+
+ if (prefix_count >= 15) { stack->rec.ExceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION; return 1; }
+ }
return 0;
}
@@ -2016,7 +2306,12 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext )
case TRAP_x86_UNKNOWN: /* Unknown fault code */
{
WORD err = get_error_code(context);
- if (!err && (stack->rec.ExceptionCode = is_privileged_instr( &stack->context ))) break;
+ int result;
+ if (!err && (result = is_privileged_instr( stack )))
+ {
+ if (result == 1) break;
+ else { restore_context( &stack->context, sigcontext ); return; }
+ }
if ((err & 7) == 2 && handle_interrupt( err >> 3, context, stack )) return;
stack->rec.ExceptionCode = EXCEPTION_ACCESS_VIOLATION;
stack->rec.NumberParameters = 2;
diff --git a/dlls/ntdll/signal_x86_64.c b/dlls/ntdll/signal_x86_64.c
index c372f65b34..b496a47ae5 100644
--- a/dlls/ntdll/signal_x86_64.c
+++ b/dlls/ntdll/signal_x86_64.c
@@ -2752,25 +2752,325 @@ static void setup_raise_exception( ucontext_t *sigcontext, struct stack_layout *
EFL_sig(sigcontext) &= ~(0x100|0x400|0x40000);
}
+/***********************************************************************
+ * User-Mode Instruction Prevention (UMIP) is an x86 feature that prevents user-space
+ * code (CPL > 0) from executing the sgdt, sidt, sldt, smsw, and str instructions.
+ * If one of these instructions is executed, a general protection fault is issued.
+ *
+ * UMIP was first implemented by the AMD Zen2 and Intel Sunny Cove microarchitectures
+ * (both released in 2019).
+ *
+ * On Linux:
+ * Kernel 5.4 and newer emulate sgdt, sidt, and smsw. sldt and str are not emulated.
+ * Kernels older than v5.4 do not emulate any instructions.
+ *
+ * When the kernel doesn't emulate an instruction, the process receives a SIGSEGV.
+ * emulate_umip_instr() emulates all instructions not emulated by the Linux kernel.
+ */
+
+/* Dummy base addresses are the same as used by the kernel */
+#define UMIP_DUMMY_GDT_BASE 0xfffffffffffe0000ULL
+#define UMIP_DUMMY_IDT_BASE 0xffffffffffff0000ULL
+#define UMIP_DUMMY_GDT_IDT_LIMIT 0
+
+#define UMIP_GDT_IDT_BASE_SIZE_64BIT 8
+#define UMIP_GDT_IDT_LIMIT_SIZE 2
+
+/* Dummy LDT, matches what I see on Windows and Linux */
+#define UMIP_DUMMY_LDT 0
+
+/* Dummy task register, matches what I see on Windows and Linux */
+#define UMIP_DUMMY_TR 0x40
+
+/* Dummy MSW is same value used by kernel, defined as:
+ * (X86_CR0_PE | X86_CR0_MP | X86_CR0_ET |
+ * X86_CR0_NE | X86_CR0_WP | X86_CR0_AM |
+ * X86_CR0_PG)
+ */
+#define UMIP_DUMMY_MSW 0x33
+
+#define REX_B 1
+#define REX_X 2
+#define REX_R 4
+#define REX_W 8
+
+#define REGMODRM_MOD( regmodrm, rex ) ((regmodrm) >> 6)
+#define REGMODRM_REG( regmodrm, rex ) (((regmodrm) >> 3) & 7) | (((rex) & REX_R) ? 8 : 0)
+#define REGMODRM_RM( regmodrm, rex ) (((regmodrm) & 7) | (((rex) & REX_B) ? 8 : 0))
+
+#define SIB_SS( sib, rex ) ((sib) >> 6)
+#define SIB_INDEX( sib, rex ) (((sib) >> 3) & 7) | (((rex) & REX_X) ? 8 : 0)
+#define SIB_BASE( sib, rex ) (((sib) & 7) | (((rex) & REX_B) ? 8 : 0))
+
+static inline DWORD64 *get_int_reg_ptr( CONTEXT *context, int index )
+{
+ return &context->Rax + index; /* index should be in range 0 .. 15 */
+}
+
+static inline int get_op_size( int long_op, int rex )
+{
+ if (rex & REX_W)
+ return sizeof(DWORD64);
+ else if (long_op)
+ return sizeof(DWORD);
+ else
+ return sizeof(WORD);
+}
+
+/***********************************************************************
+ * INSTR_GetOperandAddr
+ *
+ * Return the address of an instruction operand (from the mod/rm byte).
+ */
+static int INSTR_GetOperandAddr( CONTEXT *context, BYTE *instr, unsigned int instr_len,
+ int long_addr, int rex, int segprefix, int *len, BYTE **addr )
+{
+ int mod, rm, ss = 0, off, have_sib = 0;
+ unsigned int i = 0;
+ DWORD64 base = 0, index = 0;
+
+#define GET_VAL( val, type ) \
+ { if (sizeof(type) > (instr_len - i)) return 0; \
+ *val = *(type *)&instr[i]; i += sizeof(type); *len += sizeof(type); }
+
+ *len = 0;
+ GET_VAL( &mod, BYTE );
+ rm = REGMODRM_RM( mod, rex );
+ mod = REGMODRM_MOD( mod, rex );
+
+ if (mod == 3)
+ {
+ *addr = (BYTE *)get_int_reg_ptr( context, rm );
+ return 1;
+ }
+
+ if ((rm & 7) == 4)
+ {
+ BYTE sib;
+ int id;
+
+ GET_VAL( &sib, BYTE );
+ rm = SIB_BASE( sib, rex );
+ id = SIB_INDEX( sib, rex );
+ ss = SIB_SS( sib, rex );
+
+ index = (id != 4) ? *get_int_reg_ptr( context, id ) : 0;
+ if (!long_addr) index &= 0xffffffff;
+ have_sib = 1;
+ }
+
+ base = *get_int_reg_ptr( context, rm );
+ if (!long_addr) base &= 0xffffffff;
+
+ switch (mod)
+ {
+ case 0:
+ if (rm == 5) /* special case */
+ {
+ base = have_sib ? 0 : context->Rip;
+ if (!long_addr) base &= 0xffffffff;
+ GET_VAL( &off, DWORD );
+ base += (signed long)off;
+ }
+ break;
+
+ case 1: /* 8-bit disp */
+ GET_VAL( &off, BYTE );
+ base += (signed char)off;
+ break;
+
+ case 2: /* 32-bit disp */
+ GET_VAL( &off, DWORD );
+ base += (signed long)off;
+ break;
+ }
+
+ /* FIXME: we assume that all segments have a base of 0 */
+ *addr = (BYTE *)(base + (index << ss));
+ return 1;
+#undef GET_VAL
+}
+
+/***********************************************************************
+ * emulate_umip_instr
+ *
+ * Emulate a UMIP-protected instruction.
+ * Returns: 0 if no instruction emulated,
+ * 1 if instruction emulated successfully,
+ * 2 if instruction's write to memory failed.
+ */
+static int emulate_umip_instr( CONTEXT *context, BYTE *instr, unsigned int len, void **err_addr,
+ int segprefix, unsigned int prefixlen, int long_op, int long_addr, int rex )
+{
+ if (len < 2) return 0;
+
+ if (instr[0] == 0x00) /* sldt/str */
+ {
+ int reg = REGMODRM_REG( instr[1], rex );
+ switch (reg)
+ {
+ case 0: /* sldt */
+ case 1: /* str */
+ {
+ int instr_len;
+ BYTE *data;
+ UINT16 dummy_value;
+
+ if (!INSTR_GetOperandAddr( context, &instr[1], len - 1, long_addr,
+ rex, segprefix, &instr_len, &data ))
+ return 0;
+
+ if (reg == 0)
+ {
+ /* sldt */
+ dummy_value = UMIP_DUMMY_LDT;
+ TRACE( "sldt at 0x%lx\n", context->Rip );
+ }
+ else
+ {
+ /* str */
+ dummy_value = UMIP_DUMMY_TR;
+ TRACE( "str at 0x%lx\n", context->Rip );
+ }
+
+ if (REGMODRM_MOD( instr[1], rex ) == 3)
+ {
+ /* Destination operand is a register.
+ * Zero-extend the dummy LDT and store to the register. */
+ UINT64 dummy_value_64 = dummy_value;
+ memcpy( data, &dummy_value_64, get_op_size( long_op, rex ) );
+ }
+ else
+ {
+ /* Destination operand is a memory location.
+ * Only copy 16 bits regardless of operand size. */
+ if (virtual_uninterrupted_write_memory( data, &dummy_value, sizeof(dummy_value) ))
+ {
+ TRACE( "memory write by 0x%lx to %p failed\n", context->Rip, data );
+ *err_addr = data;
+ return 2;
+ }
+ }
+ context->Rip += prefixlen + instr_len + 2;
+ return 1;
+ }
+ default: break;
+ }
+ }
+ else if (instr[0] == 0x01) /* sgdt/sidt/str */
+ {
+ int instr_len;
+ int reg = REGMODRM_REG( instr[1], rex );
+ switch (reg)
+ {
+ case 0: /* sgdt */
+ case 1: /* sidt */
+ {
+ BYTE *data;
+ UINT16 dummy_limit = UMIP_DUMMY_GDT_IDT_LIMIT;
+ UINT64 dummy_base_addr;
+
+ /* sgdt/sidt cannot use a register as the destination operand */
+ if (REGMODRM_MOD( instr[1], rex ) == 3)
+ return 0;
+
+ if (!INSTR_GetOperandAddr( context, &instr[1], len - 1, long_addr,
+ rex, segprefix, &instr_len, &data ))
+ return 0;
+
+ if (reg == 0)
+ {
+ /* sgdt */
+ dummy_base_addr = UMIP_DUMMY_GDT_BASE;
+ TRACE( "sgdt at %lx\n", context->Rip );
+ }
+ else if (reg == 1)
+ {
+ /* sidt */
+ dummy_base_addr = UMIP_DUMMY_IDT_BASE;
+ TRACE( "sidt at %lx\n", context->Rip );
+ }
+ if (virtual_uninterrupted_write_memory( data, &dummy_limit, UMIP_GDT_IDT_LIMIT_SIZE ) ||
+ virtual_uninterrupted_write_memory( data + UMIP_GDT_IDT_LIMIT_SIZE, &dummy_base_addr, UMIP_GDT_IDT_BASE_SIZE_64BIT ))
+ {
+ TRACE( "memory write by 0x%lx to %p failed\n", context->Rip, data );
+ *err_addr = data;
+ return 2;
+ }
+
+ context->Rip += prefixlen + instr_len + 2;
+ return 1;
+ }
+ case 4: /* smsw */
+ {
+ BYTE *data;
+
+ if (!INSTR_GetOperandAddr( context, &instr[1], len - 1, long_addr,
+ rex, segprefix, &instr_len, &data ))
+ return 0;
+
+ if (REGMODRM_MOD( instr[1], rex ) == 3)
+ {
+ /* Destination operand is a register.
+ * Zero-extend the dummy MSW and store to the register. */
+ UINT64 dummy_msw = UMIP_DUMMY_MSW;
+ TRACE( "smsw at %lx\n", context->Rip );
+ memcpy( data, &dummy_msw, get_op_size( long_op, rex ) );
+ }
+ else
+ {
+ /* Destination operand is a memory location.
+ * Only copy 16 bits regardless of operand size. */
+ UINT16 dummy_msw = UMIP_DUMMY_MSW;
+ TRACE( "smsw at %lx\n", context->Rip );
+ if (virtual_uninterrupted_write_memory( data, &dummy_msw, sizeof(dummy_msw) ))
+ {
+ TRACE( "memory write by 0x%lx to %p failed\n", context->Rip, data );
+ *err_addr = data;
+ return 2;
+ }
+ }
+ context->Rip += prefixlen + instr_len + 2;
+ return 1;
+ }
+ default: break;
+ }
+ }
+
+ return 0;
+}
/***********************************************************************
* is_privileged_instr
*
- * Check if the fault location is a privileged instruction.
+ * Check if the fault location is a privileged instruction, and emulate a UMIP-protected instruction if found.
+ * Returns: 0 if no privileged instruction found or emulation failed,
+ * 1 if instruction found and ExceptionCode has been set,
+ * 2 if instruction was successfully emulated.
*/
-static inline DWORD is_privileged_instr( CONTEXT *context )
+static inline int is_privileged_instr( struct stack_layout *stack )
{
BYTE instr[16];
+ CONTEXT *context = &stack->context;
+ int segprefix, long_op = 1, long_addr = 1, rex;
unsigned int i, prefix_count = 0;
unsigned int len = virtual_uninterrupted_read_memory( (BYTE *)context->Rip, instr, sizeof(instr) );
- for (i = 0; i < len; i++) switch (instr[i])
+ segprefix = -1; /* no seg prefix */
+ rex = 0; /* no rex prefix */
+ for (i = 0; i < len; i++)
+ {
+ switch (instr[i])
{
/* instruction prefixes */
case 0x2e: /* %cs: */
+ segprefix = context->SegCs; prefix_count++; break;
case 0x36: /* %ss: */
+ segprefix = context->SegSs; prefix_count++; break;
case 0x3e: /* %ds: */
+ segprefix = context->SegDs; prefix_count++; break;
case 0x26: /* %es: */
+ segprefix = context->SegEs; prefix_count++; break;
case 0x40: /* rex */
case 0x41: /* rex */
case 0x42: /* rex */
@@ -2787,15 +3087,19 @@ static inline DWORD is_privileged_instr( CONTEXT *context )
case 0x4d: /* rex */
case 0x4e: /* rex */
case 0x4f: /* rex */
+ rex = instr[i]; prefix_count++; break;
case 0x64: /* %fs: */
+ segprefix = context->SegFs; prefix_count++; break;
case 0x65: /* %gs: */
+ segprefix = context->SegGs; prefix_count++; break;
case 0x66: /* opcode size */
+ long_op = !long_op; prefix_count++; break;
case 0x67: /* addr size */
+ long_addr = !long_addr; prefix_count++; break;
case 0xf0: /* lock */
case 0xf2: /* repne */
case 0xf3: /* repe */
- if (++prefix_count >= 15) return EXCEPTION_ILLEGAL_INSTRUCTION;
- continue;
+ prefix_count++; break;
case 0x0f: /* extended instruction */
if (i == len - 1) return 0;
@@ -2808,7 +3112,28 @@ static inline DWORD is_privileged_instr( CONTEXT *context )
case 0x21: /* mov drX, reg */
case 0x22: /* mov reg, crX */
case 0x23: /* mov reg drX */
- return EXCEPTION_PRIV_INSTRUCTION;
+ stack->rec.ExceptionCode = EXCEPTION_PRIV_INSTRUCTION;
+ return 1;
+
+ case 0x00: /* sldt/str */
+ case 0x01: /* sgdt/sidt/smsw */
+ {
+ void *err_addr;
+ int result = emulate_umip_instr( context, &instr[i+1], len - i - 1, &err_addr,
+ segprefix, prefix_count, long_op, long_addr, rex );
+ if (result == 1)
+ return 2;
+ else if (result == 2)
+ {
+ stack->rec.ExceptionCode = EXCEPTION_ACCESS_VIOLATION;
+ stack->rec.NumberParameters = 2;
+ stack->rec.ExceptionInformation[0] = 1;
+ stack->rec.ExceptionInformation[1] = (ULONG_PTR)err_addr;
+ return 1;
+ }
+ else
+ return 0;
+ }
}
return 0;
case 0x6c: /* insb (%dx) */
@@ -2827,10 +3152,14 @@ static inline DWORD is_privileged_instr( CONTEXT *context )
case 0xf4: /* hlt */
case 0xfa: /* cli */
case 0xfb: /* sti */
- return EXCEPTION_PRIV_INSTRUCTION;
+ stack->rec.ExceptionCode = EXCEPTION_PRIV_INSTRUCTION;
+ return 1;
default:
return 0;
}
+
+ if (prefix_count >= 15) { stack->rec.ExceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION; return 1; }
+ }
return 0;
}
@@ -2925,8 +3254,13 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext )
case TRAP_x86_PROTFLT: /* General protection fault */
case TRAP_x86_UNKNOWN: /* Unknown fault code */
{
+ int result;
WORD err = ERROR_sig(ucontext);
- if (!err && (stack->rec.ExceptionCode = is_privileged_instr( &stack->context ))) break;
+ if (!err && (result = is_privileged_instr( stack )))
+ {
+ if (result == 1) break;
+ else { restore_context( &stack->context, sigcontext ); return; }
+ }
if ((err & 7) == 2 && handle_interrupt( ucontext, stack )) return;
stack->rec.ExceptionCode = EXCEPTION_ACCESS_VIOLATION;
stack->rec.NumberParameters = 2;
--
2.23.0
Dec. 4, 2019
[PATCH v4 0/2] Add emulation for UMIP instructions
by Brendan Shanks
This patch adds emulation for instructions protected by User-Mode
Instruction Prevention (currently implemented by AMD Ryzen 3000 CPUs and
some rare Intel CPUs).
Changes from previous patch: removed CPUID checking and Linux #ifdefs
Brendan Shanks (2):
ntdll: Add emulation for UMIP instructions.
ntdll/tests: Add tests for UMIP instructions.
dlls/ntdll/signal_i386.c | 311 ++++++++++++++++++++++++++++-
dlls/ntdll/signal_x86_64.c | 350 +++++++++++++++++++++++++++++++-
dlls/ntdll/tests/exception.c | 375 +++++++++++++++++++++++++++++++++++
3 files changed, 1020 insertions(+), 16 deletions(-)
--
2.23.0
Dec. 4, 2019
[PATCH 9/9] dmime/tests: Add GetParam/SetParam tests for unsupported types
by Michael Stefaniuc
Signed-off-by: Michael Stefaniuc <mstefani(a)winehq.org>
---
dlls/dmime/tests/dmime.c | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c
index a35470b4cd..e8fa6a596d 100644
--- a/dlls/dmime/tests/dmime.c
+++ b/dlls/dmime/tests/dmime.c
@@ -712,10 +712,20 @@ static void test_track(void)
else if (class[i].clsid == &CLSID_DirectMusicWaveTrack)
expect_getparam(dmt, param_types[j].type, param_types[j].name,
DMUS_E_GET_UNSUPPORTED);
- } else
+ } else {
ok(hr == DMUS_E_TYPE_UNSUPPORTED,
"IsParamSupported(%s) failed: %08x, expected DMUS_E_TYPE_UNSUPPORTED\n",
param_types[j].name, hr);
+ expect_getparam(dmt, param_types[j].type, param_types[j].name,
+ DMUS_E_GET_UNSUPPORTED);
+ if (class[i].clsid == &CLSID_DirectMusicWaveTrack)
+ expect_setparam(dmt, param_types[j].type, param_types[j].name,
+ DMUS_E_TYPE_UNSUPPORTED);
+ else
+ expect_setparam(dmt, param_types[j].type, param_types[j].name,
+ DMUS_E_SET_UNSUPPORTED);
+ }
+
/* GetParam / SetParam for IsParamSupported supported types */
if (class[i].clsid == &CLSID_DirectMusicTimeSigTrack) {
expect_getparam(dmt, &GUID_DisableTimeSig, "GUID_DisableTimeSig",
--
2.23.0
Dec. 3, 2019
[PATCH 8/9] dmime: Tempo track supports only GUID_TempoParam in GetParam()
by Michael Stefaniuc
Signed-off-by: Michael Stefaniuc <mstefani(a)winehq.org>
---
dlls/dmime/tempotrack.c | 77 ++++++++++++++++++----------------------
dlls/dmime/tests/dmime.c | 5 +++
2 files changed, 39 insertions(+), 43 deletions(-)
diff --git a/dlls/dmime/tempotrack.c b/dlls/dmime/tempotrack.c
index 442aec37bf..94e4430b7f 100644
--- a/dlls/dmime/tempotrack.c
+++ b/dlls/dmime/tempotrack.c
@@ -153,52 +153,43 @@ static HRESULT WINAPI tempo_track_Play(IDirectMusicTrack8 *iface, void *pStateDa
return S_OK;
}
-static HRESULT WINAPI tempo_track_GetParam(IDirectMusicTrack8 *iface, REFGUID rguidType,
- MUSIC_TIME mtTime, MUSIC_TIME *pmtNext, void *pParam)
+static HRESULT WINAPI tempo_track_GetParam(IDirectMusicTrack8 *iface, REFGUID type, MUSIC_TIME time,
+ MUSIC_TIME *next, void *param)
{
- IDirectMusicTempoTrack *This = impl_from_IDirectMusicTrack8(iface);
-
- HRESULT hr = S_OK;
- struct list* pEntry = NULL;
- LPDMUS_PRIVATE_TEMPO_ITEM pIt = NULL;
- DMUS_TEMPO_PARAM* prm = pParam;
-
- FIXME("(%p, %s, %d, %p, %p): almost stub\n", This, debugstr_dmguid(rguidType), mtTime, pmtNext, pParam);
-
- if (NULL == pParam) {
- return E_POINTER;
- }
+ IDirectMusicTempoTrack *This = impl_from_IDirectMusicTrack8(iface);
+ DMUS_PRIVATE_TEMPO_ITEM *item = NULL;
+ DMUS_TEMPO_PARAM *prm = param;
+
+ TRACE("(%p, %s, %d, %p, %p)\n", This, debugstr_dmguid(type), time, next, param);
+
+ if (!param)
+ return E_POINTER;
+ if (!IsEqualGUID(type, &GUID_TempoParam))
+ return DMUS_E_GET_UNSUPPORTED;
+
+ FIXME("Partial support for GUID_TempoParam\n");
+
+ if (next)
+ *next = 0;
+ prm->mtTime = 0;
+ prm->dblTempo = 0.123456;
+
+ LIST_FOR_EACH_ENTRY(item, &This->Items, DMUS_PRIVATE_TEMPO_ITEM, entry) {
+ if (item->item.lTime <= time) {
+ MUSIC_TIME ofs = item->item.lTime - time;
+ if (ofs > prm->mtTime) {
+ prm->mtTime = ofs;
+ prm->dblTempo = item->item.dblTempo;
+ }
+ if (next && item->item.lTime > time && item->item.lTime < *next)
+ *next = item->item.lTime;
+ }
+ }
- hr = IDirectMusicTrack_IsParamSupported (iface, rguidType);
- if (FAILED(hr)) {
- return hr;
- }
+ if (0.123456 == prm->dblTempo)
+ return DMUS_E_NOT_FOUND;
- if (NULL != pmtNext) *pmtNext = 0;
- prm->mtTime = 0;
- prm->dblTempo = 0.123456;
-
- LIST_FOR_EACH (pEntry, &This->Items) {
- pIt = LIST_ENTRY(pEntry, DMUS_PRIVATE_TEMPO_ITEM, entry);
- /*TRACE(" - %p -> 0x%lx,%p\n", pIt, pIt->item.lTime, pIt->item.dblTempo);*/
- if (pIt->item.lTime <= mtTime) {
- MUSIC_TIME ofs = pIt->item.lTime - mtTime;
- if (ofs > prm->mtTime) {
- prm->mtTime = ofs;
- prm->dblTempo = pIt->item.dblTempo;
- }
- if (NULL != pmtNext && pIt->item.lTime > mtTime) {
- if (pIt->item.lTime < *pmtNext) {
- *pmtNext = pIt->item.lTime;
- }
- }
- }
- }
-
- if (0.123456 == prm->dblTempo) {
- return DMUS_E_NOT_FOUND;
- }
- return S_OK;
+ return S_OK;
}
static HRESULT WINAPI tempo_track_SetParam(IDirectMusicTrack8 *iface, REFGUID type, MUSIC_TIME time,
diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c
index 3e25328cd8..a35470b4cd 100644
--- a/dlls/dmime/tests/dmime.c
+++ b/dlls/dmime/tests/dmime.c
@@ -724,6 +724,11 @@ static void test_track(void)
DMUS_E_GET_UNSUPPORTED);
expect_setparam(dmt, &GUID_TimeSignature, "GUID_TimeSignature",
DMUS_E_SET_UNSUPPORTED);
+ } else if (class[i].clsid == &CLSID_DirectMusicTempoTrack) {
+ expect_getparam(dmt, &GUID_DisableTempo, "GUID_DisableTempo",
+ DMUS_E_GET_UNSUPPORTED);
+ expect_getparam(dmt, &GUID_EnableTempo, "GUID_EnableTempo",
+ DMUS_E_GET_UNSUPPORTED);
}
}
} else {
--
2.23.0
Dec. 3, 2019
[PATCH 7/9] dmime: Partial implementation of the Tempo track SetParam()
by Michael Stefaniuc
Signed-off-by: Michael Stefaniuc <mstefani(a)winehq.org>
---
dlls/dmime/tempotrack.c | 31 ++++++++++++++++++++++++++-----
1 file changed, 26 insertions(+), 5 deletions(-)
diff --git a/dlls/dmime/tempotrack.c b/dlls/dmime/tempotrack.c
index 6038f7ce9f..442aec37bf 100644
--- a/dlls/dmime/tempotrack.c
+++ b/dlls/dmime/tempotrack.c
@@ -201,12 +201,33 @@ static HRESULT WINAPI tempo_track_GetParam(IDirectMusicTrack8 *iface, REFGUID rg
return S_OK;
}
-static HRESULT WINAPI tempo_track_SetParam(IDirectMusicTrack8 *iface, REFGUID rguidType,
- MUSIC_TIME mtTime, void *pParam)
+static HRESULT WINAPI tempo_track_SetParam(IDirectMusicTrack8 *iface, REFGUID type, MUSIC_TIME time,
+ void *param)
{
- IDirectMusicTempoTrack *This = impl_from_IDirectMusicTrack8(iface);
- FIXME("(%p, %s, %d, %p): stub\n", This, debugstr_dmguid(rguidType), mtTime, pParam);
- return S_OK;
+ IDirectMusicTempoTrack *This = impl_from_IDirectMusicTrack8(iface);
+
+ TRACE("(%p, %s, %d, %p)\n", This, debugstr_dmguid(type), time, param);
+
+ if (IsEqualGUID(type, &GUID_DisableTempo)) {
+ if (!param)
+ return DMUS_E_TYPE_DISABLED;
+ FIXME("GUID_DisableTempo not handled yet\n");
+ return S_OK;
+ }
+ if (IsEqualGUID(type, &GUID_EnableTempo)) {
+ if (!param)
+ return DMUS_E_TYPE_DISABLED;
+ FIXME("GUID_EnableTempo not handled yet\n");
+ return S_OK;
+ }
+ if (IsEqualGUID(type, &GUID_TempoParam)) {
+ if (!param)
+ return E_POINTER;
+ FIXME("GUID_TempoParam not handled yet\n");
+ return S_OK;
+ }
+
+ return DMUS_E_SET_UNSUPPORTED;
}
static HRESULT WINAPI tempo_track_IsParamSupported(IDirectMusicTrack8 *iface, REFGUID rguidType)
--
2.23.0
Dec. 3, 2019
[PATCH 6/9] dmime: Partial implementation of the TimeSig track Get/SetParam
by Michael Stefaniuc
Signed-off-by: Michael Stefaniuc <mstefani(a)winehq.org>
---
dlls/dmime/tests/dmime.c | 9 +++++++++
dlls/dmime/timesigtrack.c | 38 ++++++++++++++++++++++++++++----------
2 files changed, 37 insertions(+), 10 deletions(-)
diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c
index a08fc19c22..3e25328cd8 100644
--- a/dlls/dmime/tests/dmime.c
+++ b/dlls/dmime/tests/dmime.c
@@ -716,6 +716,15 @@ static void test_track(void)
ok(hr == DMUS_E_TYPE_UNSUPPORTED,
"IsParamSupported(%s) failed: %08x, expected DMUS_E_TYPE_UNSUPPORTED\n",
param_types[j].name, hr);
+ /* GetParam / SetParam for IsParamSupported supported types */
+ if (class[i].clsid == &CLSID_DirectMusicTimeSigTrack) {
+ expect_getparam(dmt, &GUID_DisableTimeSig, "GUID_DisableTimeSig",
+ DMUS_E_GET_UNSUPPORTED);
+ expect_getparam(dmt, &GUID_EnableTimeSig, "GUID_EnableTimeSig",
+ DMUS_E_GET_UNSUPPORTED);
+ expect_setparam(dmt, &GUID_TimeSignature, "GUID_TimeSignature",
+ DMUS_E_SET_UNSUPPORTED);
+ }
}
} else {
hr = IDirectMusicTrack_GetParam(dmt, NULL, 0, NULL, NULL);
diff --git a/dlls/dmime/timesigtrack.c b/dlls/dmime/timesigtrack.c
index 74b2c3652d..697b3e3593 100644
--- a/dlls/dmime/timesigtrack.c
+++ b/dlls/dmime/timesigtrack.c
@@ -117,20 +117,38 @@ static HRESULT WINAPI IDirectMusicTrackImpl_Play(IDirectMusicTrack *iface, void
return S_OK;
}
-static HRESULT WINAPI IDirectMusicTrackImpl_GetParam(IDirectMusicTrack *iface, REFGUID rguidType,
- MUSIC_TIME mtTime, MUSIC_TIME *pmtNext, void *pParam)
+static HRESULT WINAPI IDirectMusicTrackImpl_GetParam(IDirectMusicTrack *iface, REFGUID type,
+ MUSIC_TIME time, MUSIC_TIME *next, void *param)
{
- IDirectMusicTimeSigTrack *This = impl_from_IDirectMusicTrack(iface);
- FIXME("(%p, %s, %d, %p, %p): stub\n", This, debugstr_dmguid(rguidType), mtTime, pmtNext, pParam);
- return S_OK;
+ IDirectMusicTimeSigTrack *This = impl_from_IDirectMusicTrack(iface);
+
+ TRACE("(%p, %s, %d, %p, %p)\n", This, debugstr_dmguid(type), time, next, param);
+
+ if (IsEqualGUID(type, &GUID_TimeSignature)) {
+ FIXME("GUID_TimeSignature not handled yet\n");
+ return DMUS_E_NOT_FOUND;
+ }
+
+ return DMUS_E_GET_UNSUPPORTED;
}
-static HRESULT WINAPI IDirectMusicTrackImpl_SetParam(IDirectMusicTrack *iface, REFGUID rguidType,
- MUSIC_TIME mtTime, void *pParam)
+static HRESULT WINAPI IDirectMusicTrackImpl_SetParam(IDirectMusicTrack *iface, REFGUID type,
+ MUSIC_TIME time, void *param)
{
- IDirectMusicTimeSigTrack *This = impl_from_IDirectMusicTrack(iface);
- FIXME("(%p, %s, %d, %p): stub\n", This, debugstr_dmguid(rguidType), mtTime, pParam);
- return S_OK;
+ IDirectMusicTimeSigTrack *This = impl_from_IDirectMusicTrack(iface);
+
+ TRACE("(%p, %s, %d, %p)\n", This, debugstr_dmguid(type), time, param);
+
+ if (IsEqualGUID(type, &GUID_DisableTimeSig)) {
+ FIXME("GUID_DisableTimeSig not handled yet\n");
+ return S_OK;
+ }
+ if (IsEqualGUID(type, &GUID_EnableTimeSig)) {
+ FIXME("GUID_EnableTimeSig not handled yet\n");
+ return S_OK;
+ }
+
+ return DMUS_E_SET_UNSUPPORTED;
}
static HRESULT WINAPI IDirectMusicTrackImpl_IsParamSupported(IDirectMusicTrack *iface,
--
2.23.0
Dec. 3, 2019
[PATCH 5/9] dmime: Partial implementation of the Wave track Get/SetParam
by Michael Stefaniuc
Signed-off-by: Michael Stefaniuc <mstefani(a)winehq.org>
---
dlls/dmime/tests/dmime.c | 3 +++
dlls/dmime/wavetrack.c | 48 +++++++++++++++++++++++++++++++---------
2 files changed, 41 insertions(+), 10 deletions(-)
diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c
index 723dc88399..a08fc19c22 100644
--- a/dlls/dmime/tests/dmime.c
+++ b/dlls/dmime/tests/dmime.c
@@ -709,6 +709,9 @@ static void test_track(void)
} else if (class[i].clsid == &CLSID_DirectMusicMarkerTrack)
expect_setparam(dmt, param_types[j].type, param_types[j].name,
DMUS_E_SET_UNSUPPORTED);
+ else if (class[i].clsid == &CLSID_DirectMusicWaveTrack)
+ expect_getparam(dmt, param_types[j].type, param_types[j].name,
+ DMUS_E_GET_UNSUPPORTED);
} else
ok(hr == DMUS_E_TYPE_UNSUPPORTED,
"IsParamSupported(%s) failed: %08x, expected DMUS_E_TYPE_UNSUPPORTED\n",
diff --git a/dlls/dmime/wavetrack.c b/dlls/dmime/wavetrack.c
index 34adca6065..1c6b08b523 100644
--- a/dlls/dmime/wavetrack.c
+++ b/dlls/dmime/wavetrack.c
@@ -120,20 +120,48 @@ static HRESULT WINAPI wave_track_Play(IDirectMusicTrack8 *iface, void *pStateDat
return S_OK;
}
-static HRESULT WINAPI wave_track_GetParam(IDirectMusicTrack8 *iface, REFGUID rguidType,
- MUSIC_TIME mtTime, MUSIC_TIME *pmtNext, void *pParam)
+static HRESULT WINAPI wave_track_GetParam(IDirectMusicTrack8 *iface, REFGUID type, MUSIC_TIME time,
+ MUSIC_TIME *next, void *param)
{
- IDirectMusicWaveTrack *This = impl_from_IDirectMusicTrack8(iface);
- FIXME("(%p, %s, %d, %p, %p): stub\n", This, debugstr_dmguid(rguidType), mtTime, pmtNext, pParam);
- return S_OK;
+ IDirectMusicWaveTrack *This = impl_from_IDirectMusicTrack8(iface);
+
+ TRACE("(%p, %s, %d, %p, %p): not supported\n", This, debugstr_dmguid(type), time, next, param);
+ return DMUS_E_GET_UNSUPPORTED;
}
-static HRESULT WINAPI wave_track_SetParam(IDirectMusicTrack8 *iface, REFGUID rguidType,
- MUSIC_TIME mtTime, void *pParam)
+static HRESULT WINAPI wave_track_SetParam(IDirectMusicTrack8 *iface, REFGUID type, MUSIC_TIME time,
+ void *param)
{
- IDirectMusicWaveTrack *This = impl_from_IDirectMusicTrack8(iface);
- FIXME("(%p, %s, %d, %p): stub\n", This, debugstr_dmguid(rguidType), mtTime, pParam);
- return S_OK;
+ IDirectMusicWaveTrack *This = impl_from_IDirectMusicTrack8(iface);
+
+ TRACE("(%p, %s, %d, %p)\n", This, debugstr_dmguid(type), time, param);
+
+ if (IsEqualGUID(type, &GUID_Disable_Auto_Download)) {
+ FIXME("GUID_Disable_Auto_Download not handled yet\n");
+ return S_OK;
+ }
+ if (IsEqualGUID(type, &GUID_Download)) {
+ FIXME("GUID_Download not handled yet\n");
+ return S_OK;
+ }
+ if (IsEqualGUID(type, &GUID_DownloadToAudioPath)) {
+ FIXME("GUID_DownloadToAudioPath not handled yet\n");
+ return S_OK;
+ }
+ if (IsEqualGUID(type, &GUID_Enable_Auto_Download)) {
+ FIXME("GUID_Enable_Auto_Download not handled yet\n");
+ return S_OK;
+ }
+ if (IsEqualGUID(type, &GUID_Unload)) {
+ FIXME("GUID_Unload not handled yet\n");
+ return S_OK;
+ }
+ if (IsEqualGUID(type, &GUID_UnloadFromAudioPath)) {
+ FIXME("GUID_UnloadFromAudioPath not handled yet\n");
+ return S_OK;
+ }
+
+ return DMUS_E_TYPE_UNSUPPORTED;
}
static HRESULT WINAPI wave_track_IsParamSupported(IDirectMusicTrack8 *iface, REFGUID type)
--
2.23.0
Dec. 3, 2019
[PATCH 4/9] dmime: Partial implementation of the Marker track Get/SetParam
by Michael Stefaniuc
Signed-off-by: Michael Stefaniuc <mstefani(a)winehq.org>
---
dlls/dmime/markertrack.c | 35 +++++++++++++++++++++++++----------
dlls/dmime/tests/dmime.c | 4 +++-
2 files changed, 28 insertions(+), 11 deletions(-)
diff --git a/dlls/dmime/markertrack.c b/dlls/dmime/markertrack.c
index 32dd025ed4..f41bed668a 100644
--- a/dlls/dmime/markertrack.c
+++ b/dlls/dmime/markertrack.c
@@ -117,20 +117,35 @@ static HRESULT WINAPI IDirectMusicTrackImpl_Play(IDirectMusicTrack *iface, void
return S_OK;
}
-static HRESULT WINAPI IDirectMusicTrackImpl_GetParam(IDirectMusicTrack *iface, REFGUID rguidType,
- MUSIC_TIME mtTime, MUSIC_TIME *pmtNext, void *pParam)
+static HRESULT WINAPI IDirectMusicTrackImpl_GetParam(IDirectMusicTrack *iface, REFGUID type,
+ MUSIC_TIME time, MUSIC_TIME *next, void *param)
{
- IDirectMusicMarkerTrack *This = impl_from_IDirectMusicTrack(iface);
- FIXME("(%p, %s, %d, %p, %p): stub\n", This, debugstr_dmguid(rguidType), mtTime, pmtNext, pParam);
- return S_OK;
+ IDirectMusicMarkerTrack *This = impl_from_IDirectMusicTrack(iface);
+
+ TRACE("(%p, %s, %d, %p, %p)\n", This, debugstr_dmguid(type), time, next, param);
+
+ if (!param)
+ return E_POINTER;
+
+ if (IsEqualGUID(type, &GUID_Play_Marker)) {
+ FIXME("GUID_Play_Marker not handled yet\n");
+ return S_FALSE;
+ }
+ if (IsEqualGUID(type, &GUID_Valid_Start_Time)) {
+ FIXME("GUID_Valid_Start_Time not handled yet\n");
+ return DMUS_E_NOT_FOUND;
+ }
+
+ return DMUS_E_GET_UNSUPPORTED;
}
-static HRESULT WINAPI IDirectMusicTrackImpl_SetParam(IDirectMusicTrack *iface, REFGUID rguidType,
- MUSIC_TIME mtTime, void *pParam)
+static HRESULT WINAPI IDirectMusicTrackImpl_SetParam(IDirectMusicTrack *iface, REFGUID type,
+ MUSIC_TIME time, void *param)
{
- IDirectMusicMarkerTrack *This = impl_from_IDirectMusicTrack(iface);
- FIXME("(%p, %s, %d, %p): stub\n", This, debugstr_dmguid(rguidType), mtTime, pParam);
- return S_OK;
+ IDirectMusicMarkerTrack *This = impl_from_IDirectMusicTrack(iface);
+
+ TRACE("(%p, %s, %d, %p): not supported\n", This, debugstr_dmguid(type), time, param);
+ return DMUS_E_SET_UNSUPPORTED;
}
static HRESULT WINAPI IDirectMusicTrackImpl_IsParamSupported(IDirectMusicTrack *iface,
diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c
index 0f4c43700c..723dc88399 100644
--- a/dlls/dmime/tests/dmime.c
+++ b/dlls/dmime/tests/dmime.c
@@ -706,7 +706,9 @@ static void test_track(void)
expect_getparam(dmt, param_types[j].type, param_types[j].name,
DMUS_E_GET_UNSUPPORTED);
expect_setparam(dmt, param_types[j].type, param_types[j].name, S_OK);
- }
+ } else if (class[i].clsid == &CLSID_DirectMusicMarkerTrack)
+ expect_setparam(dmt, param_types[j].type, param_types[j].name,
+ DMUS_E_SET_UNSUPPORTED);
} else
ok(hr == DMUS_E_TYPE_UNSUPPORTED,
"IsParamSupported(%s) failed: %08x, expected DMUS_E_TYPE_UNSUPPORTED\n",
--
2.23.0
Dec. 3, 2019
[PATCH 3/9] dmime: Implement the SegTrigger track Get/SetParam
by Michael Stefaniuc
Signed-off-by: Michael Stefaniuc <mstefani(a)winehq.org>
---
dlls/dmime/segtriggertrack.c | 23 ++++++++++++-----------
dlls/dmime/tests/dmime.c | 29 +++++++++++++++++++++++++++--
2 files changed, 39 insertions(+), 13 deletions(-)
diff --git a/dlls/dmime/segtriggertrack.c b/dlls/dmime/segtriggertrack.c
index 7e1d75aa05..d36412cfc3 100644
--- a/dlls/dmime/segtriggertrack.c
+++ b/dlls/dmime/segtriggertrack.c
@@ -134,20 +134,22 @@ static HRESULT WINAPI segment_track_Play(IDirectMusicTrack8 *iface, void *pState
return S_OK;
}
-static HRESULT WINAPI segment_track_GetParam(IDirectMusicTrack8 *iface, REFGUID rguidType,
- MUSIC_TIME mtTime, MUSIC_TIME *pmtNext, void *pParam)
+static HRESULT WINAPI segment_track_GetParam(IDirectMusicTrack8 *iface, REFGUID type,
+ MUSIC_TIME time, MUSIC_TIME *next, void *param)
{
- IDirectMusicSegTriggerTrack *This = impl_from_IDirectMusicTrack8(iface);
- FIXME("(%p, %s, %d, %p, %p): stub\n", This, debugstr_dmguid(rguidType), mtTime, pmtNext, pParam);
- return S_OK;
+ IDirectMusicSegTriggerTrack *This = impl_from_IDirectMusicTrack8(iface);
+
+ TRACE("(%p, %s, %d, %p, %p): not supported\n", This, debugstr_dmguid(type), time, next, param);
+ return DMUS_E_GET_UNSUPPORTED;
}
-static HRESULT WINAPI segment_track_SetParam(IDirectMusicTrack8 *iface, REFGUID rguidType,
- MUSIC_TIME mtTime, void *pParam)
+static HRESULT WINAPI segment_track_SetParam(IDirectMusicTrack8 *iface, REFGUID type,
+ MUSIC_TIME time, void *param)
{
- IDirectMusicSegTriggerTrack *This = impl_from_IDirectMusicTrack8(iface);
- FIXME("(%p, %s, %d, %p): stub\n", This, debugstr_dmguid(rguidType), mtTime, pParam);
- return S_OK;
+ IDirectMusicSegTriggerTrack *This = impl_from_IDirectMusicTrack8(iface);
+
+ TRACE("(%p, %s, %d, %p): not supported\n", This, debugstr_dmguid(type), time, param);
+ return S_OK;
}
static HRESULT WINAPI segment_track_IsParamSupported(IDirectMusicTrack8 *iface, REFGUID type)
@@ -155,7 +157,6 @@ static HRESULT WINAPI segment_track_IsParamSupported(IDirectMusicTrack8 *iface,
IDirectMusicSegTriggerTrack *This = impl_from_IDirectMusicTrack8(iface);
TRACE("(%p, %s)\n", This, debugstr_dmguid(type));
-
return S_OK;
}
diff --git a/dlls/dmime/tests/dmime.c b/dlls/dmime/tests/dmime.c
index f561eb391e..0f4c43700c 100644
--- a/dlls/dmime/tests/dmime.c
+++ b/dlls/dmime/tests/dmime.c
@@ -604,6 +604,26 @@ static void test_track_identify(void)
IDirectMusicSegment8_Release(seg);
}
+static void expect_getparam(IDirectMusicTrack *track, REFGUID type, const char *name,
+ HRESULT expect)
+{
+ HRESULT hr;
+ char buf[64] = { 0 };
+
+ hr = IDirectMusicTrack8_GetParam(track, type, 0, NULL, buf);
+ ok(hr == expect, "GetParam(%s) failed: %08x, expected %08x\n", name, hr, expect);
+}
+
+static void expect_setparam(IDirectMusicTrack *track, REFGUID type, const char *name,
+ HRESULT expect)
+{
+ HRESULT hr;
+ char buf[64] = { 0 };
+
+ hr = IDirectMusicTrack8_SetParam(track, type, 0, buf);
+ ok(hr == expect, "SetParam(%s) failed: %08x, expected %08x\n", name, hr, expect);
+}
+
static void test_track(void)
{
IDirectMusicTrack *dmt;
@@ -679,10 +699,15 @@ static void test_track(void)
if (class[i].has_params != ~0) {
for (j = 0; j < ARRAY_SIZE(param_types); j++) {
hr = IDirectMusicTrack8_IsParamSupported(dmt, param_types[j].type);
- if (class[i].has_params & (1 << j))
+ if (class[i].has_params & (1 << j)) {
ok(hr == S_OK, "IsParamSupported(%s) failed: %08x, expected S_OK\n",
param_types[j].name, hr);
- else
+ if (class[i].clsid == &CLSID_DirectMusicSegmentTriggerTrack) {
+ expect_getparam(dmt, param_types[j].type, param_types[j].name,
+ DMUS_E_GET_UNSUPPORTED);
+ expect_setparam(dmt, param_types[j].type, param_types[j].name, S_OK);
+ }
+ } else
ok(hr == DMUS_E_TYPE_UNSUPPORTED,
"IsParamSupported(%s) failed: %08x, expected DMUS_E_TYPE_UNSUPPORTED\n",
param_types[j].name, hr);
--
2.23.0
Dec. 3, 2019
[PATCH 2/9] dmime: Implement the ParamControl track param methods
by Michael Stefaniuc
Signed-off-by: Michael Stefaniuc <mstefani(a)winehq.org>
---
dlls/dmime/paramcontroltrack.c | 33 ++++++++++++++++-----------------
1 file changed, 16 insertions(+), 17 deletions(-)
diff --git a/dlls/dmime/paramcontroltrack.c b/dlls/dmime/paramcontroltrack.c
index 03d69d056c..8fb3d40332 100644
--- a/dlls/dmime/paramcontroltrack.c
+++ b/dlls/dmime/paramcontroltrack.c
@@ -118,31 +118,30 @@ static HRESULT WINAPI paramcontrol_track_Play(IDirectMusicTrack8 *iface, void *p
return S_OK;
}
-static HRESULT WINAPI paramcontrol_track_GetParam(IDirectMusicTrack8 *iface, REFGUID rguidType,
- MUSIC_TIME mtTime, MUSIC_TIME *pmtNext, void *pParam)
+static HRESULT WINAPI paramcontrol_track_GetParam(IDirectMusicTrack8 *iface, REFGUID type,
+ MUSIC_TIME time, MUSIC_TIME *next, void *param)
{
- IDirectMusicParamControlTrack *This = impl_from_IDirectMusicTrack8(iface);
- FIXME("(%p, %s, %d, %p, %p): stub\n", This, debugstr_dmguid(rguidType), mtTime, pmtNext, pParam);
- return S_OK;
+ IDirectMusicParamControlTrack *This = impl_from_IDirectMusicTrack8(iface);
+
+ TRACE("(%p, %s, %d, %p, %p): not supported\n", This, debugstr_dmguid(type), time, next, param);
+ return DMUS_E_GET_UNSUPPORTED;
}
-static HRESULT WINAPI paramcontrol_track_SetParam(IDirectMusicTrack8 *iface, REFGUID rguidType,
- MUSIC_TIME mtTime, void *pParam)
+static HRESULT WINAPI paramcontrol_track_SetParam(IDirectMusicTrack8 *iface, REFGUID type,
+ MUSIC_TIME time, void *param)
{
- IDirectMusicParamControlTrack *This = impl_from_IDirectMusicTrack8(iface);
- FIXME("(%p, %s, %d, %p): stub\n", This, debugstr_dmguid(rguidType), mtTime, pParam);
- return S_OK;
+ IDirectMusicParamControlTrack *This = impl_from_IDirectMusicTrack8(iface);
+
+ TRACE("(%p, %s, %d, %p): not supported\n", This, debugstr_dmguid(type), time, param);
+ return DMUS_E_SET_UNSUPPORTED;
}
-static HRESULT WINAPI paramcontrol_track_IsParamSupported(IDirectMusicTrack8 *iface,
- REFGUID rguidType)
+static HRESULT WINAPI paramcontrol_track_IsParamSupported(IDirectMusicTrack8 *iface, REFGUID type)
{
- IDirectMusicParamControlTrack *This = impl_from_IDirectMusicTrack8(iface);
+ IDirectMusicParamControlTrack *This = impl_from_IDirectMusicTrack8(iface);
- TRACE("(%p, %s)\n", This, debugstr_dmguid(rguidType));
- /* didn't find any params */
- TRACE("param unsupported\n");
- return DMUS_E_TYPE_UNSUPPORTED;
+ TRACE("(%p, %s): param type not supported\n", This, debugstr_dmguid(type));
+ return DMUS_E_TYPE_UNSUPPORTED;
}
static HRESULT WINAPI paramcontrol_track_AddNotificationType(IDirectMusicTrack8 *iface,
--
2.23.0
Dec. 3, 2019
[PATCH 1/9] dmime: Implement the Lyrics track param methods
by Michael Stefaniuc
Signed-off-by: Michael Stefaniuc <mstefani(a)winehq.org>
---
dlls/dmime/lyricstrack.c | 32 ++++++++++++++++----------------
1 file changed, 16 insertions(+), 16 deletions(-)
diff --git a/dlls/dmime/lyricstrack.c b/dlls/dmime/lyricstrack.c
index 7554356ba4..49f44f52a3 100644
--- a/dlls/dmime/lyricstrack.c
+++ b/dlls/dmime/lyricstrack.c
@@ -117,30 +117,30 @@ static HRESULT WINAPI lyrics_track_Play(IDirectMusicTrack8 *iface, void *pStateD
return S_OK;
}
-static HRESULT WINAPI lyrics_track_GetParam(IDirectMusicTrack8 *iface, REFGUID rguidType,
- MUSIC_TIME mtTime, MUSIC_TIME *pmtNext, void *pParam)
+static HRESULT WINAPI lyrics_track_GetParam(IDirectMusicTrack8 *iface, REFGUID type,
+ MUSIC_TIME time, MUSIC_TIME *next, void *param)
{
- IDirectMusicLyricsTrack *This = impl_from_IDirectMusicTrack8(iface);
- FIXME("(%p, %s, %d, %p, %p): stub\n", This, debugstr_dmguid(rguidType), mtTime, pmtNext, pParam);
- return S_OK;
+ IDirectMusicLyricsTrack *This = impl_from_IDirectMusicTrack8(iface);
+
+ TRACE("(%p, %s, %d, %p, %p): not supported\n", This, debugstr_dmguid(type), time, next, param);
+ return DMUS_E_GET_UNSUPPORTED;
}
-static HRESULT WINAPI lyrics_track_SetParam(IDirectMusicTrack8 *iface, REFGUID rguidType,
- MUSIC_TIME mtTime, void *pParam)
+static HRESULT WINAPI lyrics_track_SetParam(IDirectMusicTrack8 *iface, REFGUID type,
+ MUSIC_TIME time, void *param)
{
- IDirectMusicLyricsTrack *This = impl_from_IDirectMusicTrack8(iface);
- FIXME("(%p, %s, %d, %p): stub\n", This, debugstr_dmguid(rguidType), mtTime, pParam);
- return S_OK;
+ IDirectMusicLyricsTrack *This = impl_from_IDirectMusicTrack8(iface);
+
+ TRACE("(%p, %s, %d, %p): not supported\n", This, debugstr_dmguid(type), time, param);
+ return DMUS_E_SET_UNSUPPORTED;
}
-static HRESULT WINAPI lyrics_track_IsParamSupported(IDirectMusicTrack8 *iface, REFGUID rguidType)
+static HRESULT WINAPI lyrics_track_IsParamSupported(IDirectMusicTrack8 *iface, REFGUID type)
{
- IDirectMusicLyricsTrack *This = impl_from_IDirectMusicTrack8(iface);
+ IDirectMusicLyricsTrack *This = impl_from_IDirectMusicTrack8(iface);
- TRACE("(%p, %s)\n", This, debugstr_dmguid(rguidType));
- /* didn't find any params */
- TRACE("param unsupported\n");
- return DMUS_E_TYPE_UNSUPPORTED;
+ TRACE("(%p, %s): param type not supported\n", This, debugstr_dmguid(type));
+ return DMUS_E_TYPE_UNSUPPORTED;
}
static HRESULT WINAPI lyrics_track_AddNotificationType(IDirectMusicTrack8 *iface, REFGUID notiftype)
--
2.23.0
Dec. 3, 2019
[PATCH 4/4] ucrtbase: Correct behavior of strftime formats %c %r.
by Jeff Smith
Signed-off-by: Jeff Smith <whydoubt(a)gmail.com>
---
dlls/msvcrt/time.c | 14 +++++++++++++-
dlls/ucrtbase/tests/misc.c | 8 ++++----
2 files changed, 17 insertions(+), 5 deletions(-)
diff --git a/dlls/msvcrt/time.c b/dlls/msvcrt/time.c
index ef4f228554..4b8ad48c09 100644
--- a/dlls/msvcrt/time.c
+++ b/dlls/msvcrt/time.c
@@ -1234,6 +1234,18 @@ static MSVCRT_size_t strftime_impl(STRFTIME_CHAR *str, MSVCRT_size_t max,
switch(*format) {
case 'c':
+#if _MSVCR_VER>=140
+ if(time_data == &C_time_curr && !alternate)
+ {
+ static const WCHAR datetime_format[] =
+ { '%','a',' ','%','b',' ','%','e',' ','%','T',' ','%','Y',0 };
+ tmp = strftime_impl(str+ret, max-ret, datetime_format, mstm, time_data, loc);
+ if(!tmp)
+ return 0;
+ ret += tmp;
+ break;
+ }
+#endif
if(!strftime_format(str, &ret, max, mstm, time_data,
alternate ? STRFTIME_TD(time_data, date) : STRFTIME_TD(time_data, short_date)))
return 0;
@@ -1387,7 +1399,7 @@ static MSVCRT_size_t strftime_impl(STRFTIME_CHAR *str, MSVCRT_size_t max,
break;
#if _MSVCR_VER>=140
case 'r':
- if(time_data == MSVCRT_locale->locinfo->lc_time_curr)
+ if(time_data == &C_time_curr)
{
if(!MSVCRT_CHECK_PMT(mstm->tm_hour>=0 && mstm->tm_hour<=23))
goto einval_error;
diff --git a/dlls/ucrtbase/tests/misc.c b/dlls/ucrtbase/tests/misc.c
index d6fdf271e0..28832e9997 100644
--- a/dlls/ucrtbase/tests/misc.c
+++ b/dlls/ucrtbase/tests/misc.c
@@ -1004,8 +1004,8 @@ static void test_strftime(void)
{"%y", "00", { 0, 0, 0, 0, 0, -1900, 0, 0, 0 }},
{"%y", "99", { 0, 0, 0, 0, 0, 8099, 0, 0, 0 }},
{"%y", "", { 0, 0, 0, 0, 0, 8100, 0, 0, 0 }},
- {"%c", "Thu Jan 1 00:00:00 1970", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }, TRUE, TRUE},
- {"%c", "Thu Feb 30 00:00:00 1970", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }, TRUE, TRUE},
+ {"%c", "Thu Jan 1 00:00:00 1970", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }},
+ {"%c", "Thu Feb 30 00:00:00 1970", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }},
{"%#c", "Thursday, January 01, 1970 00:00:00", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }},
{"%#c", "Thursday, February 30, 1970 00:00:00", { 0, 0, 0, 30, 1, 70, 4, 0, 0 }},
{"%x", "01/01/70", { 0, 0, 0, 1, 0, 70, 4, 0, 0 }},
@@ -1197,8 +1197,8 @@ static void test_strftime(void)
ok(ret == 19, "ret = %d\n", ret);
ok(!strcmp(buf, "01/01/1970 00:00:00"), "buf = \"%s\", expected \"%s\"\n", buf, "01/01/1970 00:00:00");
ret = p_strftime(buf, sizeof(buf), "%r", &epoch);
- todo_wine ok(ret == 8, "ret = %d\n", ret);
- todo_wine ok(!strcmp(buf, "00:00:00"), "buf = \"%s\", expected \"%s\"\n", buf, "00:00:00");
+ ok(ret == 8, "ret = %d\n", ret);
+ ok(!strcmp(buf, "00:00:00"), "buf = \"%s\", expected \"%s\"\n", buf, "00:00:00");
p_setlocale(LC_ALL, "C");
}
--
2.23.0
Dec. 3, 2019
[PATCH 3/4] msvcrt: Create global lc_time_curr and use when possible.
by Jeff Smith
Signed-off-by: Jeff Smith <whydoubt(a)gmail.com>
---
dlls/msvcrt/locale.c | 156 ++++++++++++++++++++++++++-----------------
dlls/msvcrt/msvcrt.h | 1 +
2 files changed, 95 insertions(+), 62 deletions(-)
diff --git a/dlls/msvcrt/locale.c b/dlls/msvcrt/locale.c
index 6f2a83cc7c..2a5cc7fc02 100644
--- a/dlls/msvcrt/locale.c
+++ b/dlls/msvcrt/locale.c
@@ -53,6 +53,74 @@ BOOL initial_locale = TRUE;
#define MSVCRT_LEADBYTE 0x8000
#define MSVCRT_C1_DEFINED 0x200
+static MSVCRT_wchar_t C_time_curr_wstr[43][20] =
+{
+ {'S','u','n',0}, {'M','o','n',0}, {'T','u','e',0}, {'W','e','d',0},
+ {'T','h','u',0}, {'F','r','i',0}, {'S','a','t',0},
+ {'S','u','n','d','a','y',0}, {'M','o','n','d','a','y',0},
+ {'T','u','e','s','d','a','y',0}, {'W','e','d','n','e','s','d','a','y',0},
+ {'T','h','u','r','s','d','a','y',0}, {'F','r','i','d','a','y',0},
+ {'S','a','t','u','r','d','a','y',0},
+ {'J','a','n',0}, {'F','e','b',0}, {'M','a','r',0}, {'A','p','r',0},
+ {'M','a','y',0}, {'J','u','n',0}, {'J','u','l',0}, {'A','u','g',0},
+ {'S','e','p',0}, {'O','c','t',0}, {'N','o','v',0}, {'D','e','c',0},
+ {'J','a','n','u','a','r','y',0}, {'F','e','b','r','u','a','r','y',0},
+ {'M','a','r','c','h',0}, {'A','p','r','i','l',0}, {'M','a','y',0},
+ {'J','u','n','e',0}, {'J','u','l','y',0}, {'A','u','g','u','s','t',0},
+ {'S','e','p','t','e','m','b','e','r',0}, {'O','c','t','o','b','e','r',0},
+ {'N','o','v','e','m','b','e','r',0}, {'D','e','c','e','m','b','e','r',0},
+ {'A','M',0}, {'P','M',0},
+ {'M','M','/','d','d','/','y','y',0},
+ {'d','d','d','d',',',' ','M','M','M','M',' ','d','d',',',' ','y','y','y','y',0},
+ {'H','H',':','m','m',':','s','s',0}
+};
+
+static char C_time_curr_str[43][20] =
+{
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
+ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday",
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+ "January", "February", "March", "April", "May", "June", "July",
+ "August", "September", "October", "November", "December",
+ "AM", "PM", "MM/dd/yy", "dddd, MMMM dd, yyyy", "HH:mm:ss"
+};
+
+static MSVCRT_wchar_t C_time_curr_locname[] = {'e','n','-','U','S',0};
+
+MSVCRT___lc_time_data C_time_curr =
+{
+ {{C_time_curr_str[0], C_time_curr_str[1], C_time_curr_str[2], C_time_curr_str[3],
+ C_time_curr_str[4], C_time_curr_str[5], C_time_curr_str[6], C_time_curr_str[7],
+ C_time_curr_str[8], C_time_curr_str[9], C_time_curr_str[10], C_time_curr_str[11],
+ C_time_curr_str[12], C_time_curr_str[13], C_time_curr_str[14], C_time_curr_str[15],
+ C_time_curr_str[16], C_time_curr_str[17], C_time_curr_str[18], C_time_curr_str[19],
+ C_time_curr_str[20], C_time_curr_str[21], C_time_curr_str[22], C_time_curr_str[23],
+ C_time_curr_str[24], C_time_curr_str[25], C_time_curr_str[26], C_time_curr_str[27],
+ C_time_curr_str[28], C_time_curr_str[29], C_time_curr_str[30], C_time_curr_str[31],
+ C_time_curr_str[32], C_time_curr_str[33], C_time_curr_str[34], C_time_curr_str[35],
+ C_time_curr_str[36], C_time_curr_str[37], C_time_curr_str[38], C_time_curr_str[39],
+ C_time_curr_str[40], C_time_curr_str[41], C_time_curr_str[42]}},
+#if _MSVCR_VER < 110
+ MAKELCID(LANG_ENGLISH, SORT_DEFAULT),
+#endif
+ {0, 0},
+ {{C_time_curr_wstr[0], C_time_curr_wstr[1], C_time_curr_wstr[2], C_time_curr_wstr[3],
+ C_time_curr_wstr[4], C_time_curr_wstr[5], C_time_curr_wstr[6], C_time_curr_wstr[7],
+ C_time_curr_wstr[8], C_time_curr_wstr[9], C_time_curr_wstr[10], C_time_curr_wstr[11],
+ C_time_curr_wstr[12], C_time_curr_wstr[13], C_time_curr_wstr[14], C_time_curr_wstr[15],
+ C_time_curr_wstr[16], C_time_curr_wstr[17], C_time_curr_wstr[18], C_time_curr_wstr[19],
+ C_time_curr_wstr[20], C_time_curr_wstr[21], C_time_curr_wstr[22], C_time_curr_wstr[23],
+ C_time_curr_wstr[24], C_time_curr_wstr[25], C_time_curr_wstr[26], C_time_curr_wstr[27],
+ C_time_curr_wstr[28], C_time_curr_wstr[29], C_time_curr_wstr[30], C_time_curr_wstr[31],
+ C_time_curr_wstr[32], C_time_curr_wstr[33], C_time_curr_wstr[34], C_time_curr_wstr[35],
+ C_time_curr_wstr[36], C_time_curr_wstr[37], C_time_curr_wstr[38], C_time_curr_wstr[39],
+ C_time_curr_wstr[40], C_time_curr_wstr[41], C_time_curr_wstr[42]}},
+#if _MSVCR_VER >= 110
+ C_time_curr_locname,
+#endif
+ {}
+};
+
/* Friendly country strings & language names abbreviations. */
static const char * const _country_synonyms[] =
{
@@ -829,7 +897,8 @@ void free_locinfo(MSVCRT_pthreadlocinfo locinfo)
MSVCRT_free(locinfo->pclmap);
MSVCRT_free(locinfo->pcumap);
- MSVCRT_free(locinfo->lc_time_curr);
+ if(locinfo->lc_time_curr != &C_time_curr)
+ MSVCRT_free(locinfo->lc_time_curr);
MSVCRT_free(locinfo);
}
@@ -929,35 +998,21 @@ static MSVCRT___lc_time_data* create_time_data(LCID lcid)
LOCALE_SSHORTDATE, LOCALE_SLONGDATE,
LOCALE_STIMEFORMAT
};
- static const char cloc_short_date[] = "MM/dd/yy";
- static const MSVCRT_wchar_t cloc_short_dateW[] = {'M','M','/','d','d','/','y','y',0};
- static const char cloc_long_date[] = "dddd, MMMM dd, yyyy";
- static const MSVCRT_wchar_t cloc_long_dateW[] = {'d','d','d','d',',',' ','M','M','M','M',' ','d','d',',',' ','y','y','y','y',0};
- static const char cloc_time[] = "HH:mm:ss";
- static const MSVCRT_wchar_t cloc_timeW[] = {'H','H',':','m','m',':','s','s',0};
MSVCRT___lc_time_data *cur;
- const DWORD flags = lcid ? 0 : LOCALE_NOUSEROVERRIDE;
- const LCID lcid_tmp = lcid ? lcid : MAKELCID(LANG_ENGLISH, SORT_DEFAULT);
int i, ret, size;
size = sizeof(MSVCRT___lc_time_data);
for(i=0; i<ARRAY_SIZE(time_data); i++) {
- if(time_data[i]==LOCALE_SSHORTDATE && !lcid) {
- size += sizeof(cloc_short_date) + sizeof(cloc_short_dateW);
- }else if(time_data[i]==LOCALE_SLONGDATE && !lcid) {
- size += sizeof(cloc_long_date) + sizeof(cloc_long_dateW);
- }else {
- ret = GetLocaleInfoA(lcid_tmp, time_data[i]|flags, NULL, 0);
- if(!ret)
- return NULL;
- size += ret;
+ ret = GetLocaleInfoA(lcid, time_data[i], NULL, 0);
+ if(!ret)
+ return NULL;
+ size += ret;
- ret = GetLocaleInfoW(lcid_tmp, time_data[i]|flags, NULL, 0);
- if(!ret)
- return NULL;
- size += ret*sizeof(MSVCRT_wchar_t);
- }
+ ret = GetLocaleInfoW(lcid, time_data[i], NULL, 0);
+ if(!ret)
+ return NULL;
+ size += ret*sizeof(MSVCRT_wchar_t);
}
#if _MSVCR_VER >= 110
size += LCIDToLocaleName(lcid, NULL, 0, 0)*sizeof(MSVCRT_wchar_t);
@@ -970,35 +1025,12 @@ static MSVCRT___lc_time_data* create_time_data(LCID lcid)
ret = 0;
for(i=0; i<ARRAY_SIZE(time_data); i++) {
cur->str.str[i] = &cur->data[ret];
- if(time_data[i]==LOCALE_SSHORTDATE && !lcid) {
- memcpy(&cur->data[ret], cloc_short_date, sizeof(cloc_short_date));
- ret += sizeof(cloc_short_date);
- }else if(time_data[i]==LOCALE_SLONGDATE && !lcid) {
- memcpy(&cur->data[ret], cloc_long_date, sizeof(cloc_long_date));
- ret += sizeof(cloc_long_date);
- }else if(time_data[i]==LOCALE_STIMEFORMAT && !lcid) {
- memcpy(&cur->data[ret], cloc_time, sizeof(cloc_time));
- ret += sizeof(cloc_time);
- }else {
- ret += GetLocaleInfoA(lcid_tmp, time_data[i]|flags,
- &cur->data[ret], size-ret);
- }
+ ret += GetLocaleInfoA(lcid, time_data[i], &cur->data[ret], size-ret);
}
for(i=0; i<ARRAY_SIZE(time_data); i++) {
cur->wstr.wstr[i] = (MSVCRT_wchar_t*)&cur->data[ret];
- if(time_data[i]==LOCALE_SSHORTDATE && !lcid) {
- memcpy(&cur->data[ret], cloc_short_dateW, sizeof(cloc_short_dateW));
- ret += sizeof(cloc_short_dateW);
- }else if(time_data[i]==LOCALE_SLONGDATE && !lcid) {
- memcpy(&cur->data[ret], cloc_long_dateW, sizeof(cloc_long_dateW));
- ret += sizeof(cloc_long_dateW);
- }else if(time_data[i]==LOCALE_STIMEFORMAT && !lcid) {
- memcpy(&cur->data[ret], cloc_timeW, sizeof(cloc_timeW));
- ret += sizeof(cloc_timeW);
- }else {
- ret += GetLocaleInfoW(lcid_tmp, time_data[i]|flags,
- (MSVCRT_wchar_t*)&cur->data[ret], size-ret)*sizeof(MSVCRT_wchar_t);
- }
+ ret += GetLocaleInfoW(lcid, time_data[i],
+ (MSVCRT_wchar_t*)&cur->data[ret], size-ret)*sizeof(MSVCRT_wchar_t);
}
#if _MSVCR_VER >= 110
cur->locname = (MSVCRT_wchar_t*)&cur->data[ret];
@@ -1664,26 +1696,26 @@ static MSVCRT_pthreadlocinfo create_locinfo(int category,
lcid[MSVCRT_LC_TIME], cp[MSVCRT_LC_TIME])) {
locinfo->lc_handle[MSVCRT_LC_TIME] = old_locinfo->lc_handle[MSVCRT_LC_TIME];
locinfo->lc_id[MSVCRT_LC_TIME].wCodePage = old_locinfo->lc_id[MSVCRT_LC_TIME].wCodePage;
- } else {
- if(lcid[MSVCRT_LC_TIME] && (category==MSVCRT_LC_ALL || category==MSVCRT_LC_TIME)) {
- if(!update_threadlocinfo_category(lcid[MSVCRT_LC_TIME],
- cp[MSVCRT_LC_TIME], locinfo, MSVCRT_LC_TIME)) {
- free_locinfo(locinfo);
- return NULL;
- }
+ } else if(lcid[MSVCRT_LC_TIME] && (category==MSVCRT_LC_ALL || category==MSVCRT_LC_TIME)) {
+ if(!update_threadlocinfo_category(lcid[MSVCRT_LC_TIME],
+ cp[MSVCRT_LC_TIME], locinfo, MSVCRT_LC_TIME)) {
+ free_locinfo(locinfo);
+ return NULL;
+ }
- if(!set_lc_locale_name(locinfo, MSVCRT_LC_TIME)) {
- free_locinfo(locinfo);
- return NULL;
- }
- } else
- locinfo->lc_category[MSVCRT_LC_TIME].locale = MSVCRT__strdup("C");
+ if(!set_lc_locale_name(locinfo, MSVCRT_LC_TIME)) {
+ free_locinfo(locinfo);
+ return NULL;
+ }
locinfo->lc_time_curr = create_time_data(lcid[MSVCRT_LC_TIME]);
if(!locinfo->lc_time_curr) {
free_locinfo(locinfo);
return NULL;
}
+ } else {
+ locinfo->lc_category[MSVCRT_LC_TIME].locale = MSVCRT__strdup("C");
+ locinfo->lc_time_curr = &C_time_curr;
}
return locinfo;
diff --git a/dlls/msvcrt/msvcrt.h b/dlls/msvcrt/msvcrt.h
index 4a7e6f4219..cd7def3c27 100644
--- a/dlls/msvcrt/msvcrt.h
+++ b/dlls/msvcrt/msvcrt.h
@@ -305,6 +305,7 @@ extern thread_data_t *msvcrt_get_thread_data(void) DECLSPEC_HIDDEN;
LCID MSVCRT_locale_to_LCID(const char*, unsigned short*, BOOL*) DECLSPEC_HIDDEN;
extern MSVCRT__locale_t MSVCRT_locale DECLSPEC_HIDDEN;
+extern MSVCRT___lc_time_data C_time_curr DECLSPEC_HIDDEN;
extern unsigned int MSVCRT___lc_codepage;
extern int MSVCRT___lc_collate_cp;
extern WORD MSVCRT__ctype [257];
--
2.23.0
Dec. 3, 2019
[PATCH 2/4] msvcrt: Factor out code for creating lc_time_data structure.
by Jeff Smith
Signed-off-by: Jeff Smith <whydoubt(a)gmail.com>
---
dlls/msvcrt/locale.c | 167 ++++++++++++++++++++++---------------------
1 file changed, 87 insertions(+), 80 deletions(-)
diff --git a/dlls/msvcrt/locale.c b/dlls/msvcrt/locale.c
index fc41f05da2..6f2a83cc7c 100644
--- a/dlls/msvcrt/locale.c
+++ b/dlls/msvcrt/locale.c
@@ -910,8 +910,7 @@ static inline BOOL category_needs_update(int cat, int user_cat,
return lcid!=locinfo->lc_handle[cat] || cp!=locinfo->lc_id[cat].wCodePage;
}
-static MSVCRT_pthreadlocinfo create_locinfo(int category,
- const char *locale, MSVCRT_pthreadlocinfo old_locinfo)
+static MSVCRT___lc_time_data* create_time_data(LCID lcid)
{
static const DWORD time_data[] = {
LOCALE_SABBREVDAYNAME7, LOCALE_SABBREVDAYNAME1, LOCALE_SABBREVDAYNAME2,
@@ -930,11 +929,6 @@ static MSVCRT_pthreadlocinfo create_locinfo(int category,
LOCALE_SSHORTDATE, LOCALE_SLONGDATE,
LOCALE_STIMEFORMAT
};
- static const char collate[] = "COLLATE=";
- static const char ctype[] = "CTYPE=";
- static const char monetary[] = "MONETARY=";
- static const char numeric[] = "NUMERIC=";
- static const char time[] = "TIME=";
static const char cloc_short_date[] = "MM/dd/yy";
static const MSVCRT_wchar_t cloc_short_dateW[] = {'M','M','/','d','d','/','y','y',0};
static const char cloc_long_date[] = "dddd, MMMM dd, yyyy";
@@ -942,8 +936,91 @@ static MSVCRT_pthreadlocinfo create_locinfo(int category,
static const char cloc_time[] = "HH:mm:ss";
static const MSVCRT_wchar_t cloc_timeW[] = {'H','H',':','m','m',':','s','s',0};
+ MSVCRT___lc_time_data *cur;
+ const DWORD flags = lcid ? 0 : LOCALE_NOUSEROVERRIDE;
+ const LCID lcid_tmp = lcid ? lcid : MAKELCID(LANG_ENGLISH, SORT_DEFAULT);
+ int i, ret, size;
+
+ size = sizeof(MSVCRT___lc_time_data);
+ for(i=0; i<ARRAY_SIZE(time_data); i++) {
+ if(time_data[i]==LOCALE_SSHORTDATE && !lcid) {
+ size += sizeof(cloc_short_date) + sizeof(cloc_short_dateW);
+ }else if(time_data[i]==LOCALE_SLONGDATE && !lcid) {
+ size += sizeof(cloc_long_date) + sizeof(cloc_long_dateW);
+ }else {
+ ret = GetLocaleInfoA(lcid_tmp, time_data[i]|flags, NULL, 0);
+ if(!ret)
+ return NULL;
+ size += ret;
+
+ ret = GetLocaleInfoW(lcid_tmp, time_data[i]|flags, NULL, 0);
+ if(!ret)
+ return NULL;
+ size += ret*sizeof(MSVCRT_wchar_t);
+ }
+ }
+#if _MSVCR_VER >= 110
+ size += LCIDToLocaleName(lcid, NULL, 0, 0)*sizeof(MSVCRT_wchar_t);
+#endif
+
+ cur = MSVCRT_malloc(size);
+ if(!cur)
+ return NULL;
+
+ ret = 0;
+ for(i=0; i<ARRAY_SIZE(time_data); i++) {
+ cur->str.str[i] = &cur->data[ret];
+ if(time_data[i]==LOCALE_SSHORTDATE && !lcid) {
+ memcpy(&cur->data[ret], cloc_short_date, sizeof(cloc_short_date));
+ ret += sizeof(cloc_short_date);
+ }else if(time_data[i]==LOCALE_SLONGDATE && !lcid) {
+ memcpy(&cur->data[ret], cloc_long_date, sizeof(cloc_long_date));
+ ret += sizeof(cloc_long_date);
+ }else if(time_data[i]==LOCALE_STIMEFORMAT && !lcid) {
+ memcpy(&cur->data[ret], cloc_time, sizeof(cloc_time));
+ ret += sizeof(cloc_time);
+ }else {
+ ret += GetLocaleInfoA(lcid_tmp, time_data[i]|flags,
+ &cur->data[ret], size-ret);
+ }
+ }
+ for(i=0; i<ARRAY_SIZE(time_data); i++) {
+ cur->wstr.wstr[i] = (MSVCRT_wchar_t*)&cur->data[ret];
+ if(time_data[i]==LOCALE_SSHORTDATE && !lcid) {
+ memcpy(&cur->data[ret], cloc_short_dateW, sizeof(cloc_short_dateW));
+ ret += sizeof(cloc_short_dateW);
+ }else if(time_data[i]==LOCALE_SLONGDATE && !lcid) {
+ memcpy(&cur->data[ret], cloc_long_dateW, sizeof(cloc_long_dateW));
+ ret += sizeof(cloc_long_dateW);
+ }else if(time_data[i]==LOCALE_STIMEFORMAT && !lcid) {
+ memcpy(&cur->data[ret], cloc_timeW, sizeof(cloc_timeW));
+ ret += sizeof(cloc_timeW);
+ }else {
+ ret += GetLocaleInfoW(lcid_tmp, time_data[i]|flags,
+ (MSVCRT_wchar_t*)&cur->data[ret], size-ret)*sizeof(MSVCRT_wchar_t);
+ }
+ }
+#if _MSVCR_VER >= 110
+ cur->locname = (MSVCRT_wchar_t*)&cur->data[ret];
+ LCIDToLocaleName(lcid, cur->locname, (size-ret)/sizeof(MSVCRT_wchar_t), 0);
+#else
+ cur->lcid = lcid;
+#endif
+
+ return cur;
+}
+
+static MSVCRT_pthreadlocinfo create_locinfo(int category,
+ const char *locale, MSVCRT_pthreadlocinfo old_locinfo)
+{
+ static const char collate[] = "COLLATE=";
+ static const char ctype[] = "CTYPE=";
+ static const char monetary[] = "MONETARY=";
+ static const char numeric[] = "NUMERIC=";
+ static const char time[] = "TIME=";
+
MSVCRT_pthreadlocinfo locinfo;
- LCID lcid[6] = { 0 }, lcid_tmp;
+ LCID lcid[6] = { 0 };
unsigned short cp[6] = { 0 };
const char *locale_name[6] = { 0 };
int locale_len[6] = { 0 };
@@ -952,7 +1029,7 @@ static MSVCRT_pthreadlocinfo create_locinfo(int category,
#if _MSVCR_VER >= 100
MSVCRT_wchar_t wbuf[256];
#endif
- int i, ret, size;
+ int i;
TRACE("(%d %s)\n", category, locale);
@@ -1588,8 +1665,6 @@ static MSVCRT_pthreadlocinfo create_locinfo(int category,
locinfo->lc_handle[MSVCRT_LC_TIME] = old_locinfo->lc_handle[MSVCRT_LC_TIME];
locinfo->lc_id[MSVCRT_LC_TIME].wCodePage = old_locinfo->lc_id[MSVCRT_LC_TIME].wCodePage;
} else {
- DWORD flags = lcid[MSVCRT_LC_TIME] ? 0 : LOCALE_NOUSEROVERRIDE;
-
if(lcid[MSVCRT_LC_TIME] && (category==MSVCRT_LC_ALL || category==MSVCRT_LC_TIME)) {
if(!update_threadlocinfo_category(lcid[MSVCRT_LC_TIME],
cp[MSVCRT_LC_TIME], locinfo, MSVCRT_LC_TIME)) {
@@ -1604,79 +1679,11 @@ static MSVCRT_pthreadlocinfo create_locinfo(int category,
} else
locinfo->lc_category[MSVCRT_LC_TIME].locale = MSVCRT__strdup("C");
- size = sizeof(MSVCRT___lc_time_data);
- lcid_tmp = lcid[MSVCRT_LC_TIME] ? lcid[MSVCRT_LC_TIME] : MAKELCID(LANG_ENGLISH, SORT_DEFAULT);
- for(i=0; i<ARRAY_SIZE(time_data); i++) {
- if(time_data[i]==LOCALE_SSHORTDATE && !lcid[MSVCRT_LC_TIME]) {
- size += sizeof(cloc_short_date) + sizeof(cloc_short_dateW);
- }else if(time_data[i]==LOCALE_SLONGDATE && !lcid[MSVCRT_LC_TIME]) {
- size += sizeof(cloc_long_date) + sizeof(cloc_long_dateW);
- }else {
- ret = GetLocaleInfoA(lcid_tmp, time_data[i]|flags, NULL, 0);
- if(!ret) {
- free_locinfo(locinfo);
- return NULL;
- }
- size += ret;
-
- ret = GetLocaleInfoW(lcid_tmp, time_data[i]|flags, NULL, 0);
- if(!ret) {
- free_locinfo(locinfo);
- return NULL;
- }
- size += ret*sizeof(MSVCRT_wchar_t);
- }
- }
-#if _MSVCR_VER >= 110
- size += LCIDToLocaleName(lcid[MSVCRT_LC_TIME], NULL, 0, 0)*sizeof(MSVCRT_wchar_t);
-#endif
-
- locinfo->lc_time_curr = MSVCRT_malloc(size);
+ locinfo->lc_time_curr = create_time_data(lcid[MSVCRT_LC_TIME]);
if(!locinfo->lc_time_curr) {
free_locinfo(locinfo);
return NULL;
}
-
- ret = 0;
- for(i=0; i<ARRAY_SIZE(time_data); i++) {
- locinfo->lc_time_curr->str.str[i] = &locinfo->lc_time_curr->data[ret];
- if(time_data[i]==LOCALE_SSHORTDATE && !lcid[MSVCRT_LC_TIME]) {
- memcpy(&locinfo->lc_time_curr->data[ret], cloc_short_date, sizeof(cloc_short_date));
- ret += sizeof(cloc_short_date);
- }else if(time_data[i]==LOCALE_SLONGDATE && !lcid[MSVCRT_LC_TIME]) {
- memcpy(&locinfo->lc_time_curr->data[ret], cloc_long_date, sizeof(cloc_long_date));
- ret += sizeof(cloc_long_date);
- }else if(time_data[i]==LOCALE_STIMEFORMAT && !lcid[MSVCRT_LC_TIME]) {
- memcpy(&locinfo->lc_time_curr->data[ret], cloc_time, sizeof(cloc_time));
- ret += sizeof(cloc_time);
- }else {
- ret += GetLocaleInfoA(lcid_tmp, time_data[i]|flags,
- &locinfo->lc_time_curr->data[ret], size-ret);
- }
- }
- for(i=0; i<ARRAY_SIZE(time_data); i++) {
- locinfo->lc_time_curr->wstr.wstr[i] = (MSVCRT_wchar_t*)&locinfo->lc_time_curr->data[ret];
- if(time_data[i]==LOCALE_SSHORTDATE && !lcid[MSVCRT_LC_TIME]) {
- memcpy(&locinfo->lc_time_curr->data[ret], cloc_short_dateW, sizeof(cloc_short_dateW));
- ret += sizeof(cloc_short_dateW);
- }else if(time_data[i]==LOCALE_SLONGDATE && !lcid[MSVCRT_LC_TIME]) {
- memcpy(&locinfo->lc_time_curr->data[ret], cloc_long_dateW, sizeof(cloc_long_dateW));
- ret += sizeof(cloc_long_dateW);
- }else if(time_data[i]==LOCALE_STIMEFORMAT && !lcid[MSVCRT_LC_TIME]) {
- memcpy(&locinfo->lc_time_curr->data[ret], cloc_timeW, sizeof(cloc_timeW));
- ret += sizeof(cloc_timeW);
- }else {
- ret += GetLocaleInfoW(lcid_tmp, time_data[i]|flags,
- (MSVCRT_wchar_t*)&locinfo->lc_time_curr->data[ret], size-ret)*sizeof(MSVCRT_wchar_t);
- }
- }
-#if _MSVCR_VER >= 110
- locinfo->lc_time_curr->locname = (MSVCRT_wchar_t*)&locinfo->lc_time_curr->data[ret];
- LCIDToLocaleName(lcid[MSVCRT_LC_TIME], locinfo->lc_time_curr->locname,
- (size-ret)/sizeof(MSVCRT_wchar_t), 0);
-#else
- locinfo->lc_time_curr->lcid = lcid[MSVCRT_LC_TIME];
-#endif
}
return locinfo;
--
2.23.0
Dec. 3, 2019
[PATCH 1/4] ucrtbase: Improve strftime special test cases for %c %r formats.
by Jeff Smith
Signed-off-by: Jeff Smith <whydoubt(a)gmail.com>
---
dlls/ucrtbase/tests/misc.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/dlls/ucrtbase/tests/misc.c b/dlls/ucrtbase/tests/misc.c
index 083be42f42..d6fdf271e0 100644
--- a/dlls/ucrtbase/tests/misc.c
+++ b/dlls/ucrtbase/tests/misc.c
@@ -29,6 +29,7 @@
#include <fcntl.h>
#include <time.h>
#include <direct.h>
+#include <locale.h>
#include <windef.h>
#include <winbase.h>
@@ -162,6 +163,7 @@ static int* (CDECL *p_errno)(void);
static char* (CDECL *p_asctime)(const struct tm *);
static size_t (__cdecl *p_strftime)(char *, size_t, const char *, const struct tm *);
static size_t (__cdecl *p__Strftime)(char*, size_t, const char*, const struct tm*, void*);
+static char* (__cdecl *p_setlocale)(int, const char*);
static struct tm* (__cdecl *p__gmtime32)(const __time32_t*);
static void (CDECL *p_exit)(int);
static int (CDECL *p__crt_atexit)(void (CDECL*)(void));
@@ -538,6 +540,7 @@ static BOOL init(void)
p_asctime = (void*)GetProcAddress(module, "asctime");
p_strftime = (void*)GetProcAddress(module, "strftime");
p__Strftime = (void*)GetProcAddress(module, "_Strftime");
+ p_setlocale = (void*)GetProcAddress(module, "setlocale");
p__gmtime32 = (void*)GetProcAddress(module, "_gmtime32");
p__crt_atexit = (void*)GetProcAddress(module, "_crt_atexit");
p_exit = (void*)GetProcAddress(module, "exit");
@@ -1185,6 +1188,18 @@ static void test_strftime(void)
i, j, buf, tests_yweek[i].ret[j]);
}
}
+
+ if(!p_setlocale(LC_ALL, "fr-FR")) {
+ skip("fr-FR locale not available\n");
+ return;
+ }
+ ret = p_strftime(buf, sizeof(buf), "%c", &epoch);
+ ok(ret == 19, "ret = %d\n", ret);
+ ok(!strcmp(buf, "01/01/1970 00:00:00"), "buf = \"%s\", expected \"%s\"\n", buf, "01/01/1970 00:00:00");
+ ret = p_strftime(buf, sizeof(buf), "%r", &epoch);
+ todo_wine ok(ret == 8, "ret = %d\n", ret);
+ todo_wine ok(!strcmp(buf, "00:00:00"), "buf = \"%s\", expected \"%s\"\n", buf, "00:00:00");
+ p_setlocale(LC_ALL, "C");
}
static LONG* get_failures_counter(HANDLE *map)
--
2.23.0
Dec. 3, 2019
[PATCH 5/5] wined3d: Do not download textures to WINED3D_LOCATION_BUFFER in wined3d_texture_gl_unload().
by Henri Verbeet
Since we remove any potential PBO a few lines later.
Signed-off-by: Henri Verbeet <hverbeet(a)codeweavers.com>
---
dlls/wined3d/texture.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/dlls/wined3d/texture.c b/dlls/wined3d/texture.c
index a3bbf2585d8..1c315a1dd77 100644
--- a/dlls/wined3d/texture.c
+++ b/dlls/wined3d/texture.c
@@ -2942,6 +2942,7 @@ static void wined3d_texture_gl_unload(struct wined3d_resource *resource)
UINT sub_count = texture_gl->t.level_count * texture_gl->t.layer_count;
struct wined3d_renderbuffer_entry *entry, *entry2;
struct wined3d_device *device = resource->device;
+ unsigned int location = resource->map_binding;
const struct wined3d_gl_info *gl_info;
struct wined3d_context *context;
UINT i;
@@ -2951,14 +2952,17 @@ static void wined3d_texture_gl_unload(struct wined3d_resource *resource)
context = context_acquire(device, NULL, 0);
gl_info = wined3d_context_gl(context)->gl_info;
+ if (location == WINED3D_LOCATION_BUFFER)
+ location = WINED3D_LOCATION_SYSMEM;
+
for (i = 0; i < sub_count; ++i)
{
struct wined3d_texture_sub_resource *sub_resource = &texture_gl->t.sub_resources[i];
if (resource->access & WINED3D_RESOURCE_ACCESS_CPU
- && wined3d_texture_load_location(&texture_gl->t, i, context, resource->map_binding))
+ && wined3d_texture_load_location(&texture_gl->t, i, context, location))
{
- wined3d_texture_invalidate_location(&texture_gl->t, i, ~resource->map_binding);
+ wined3d_texture_invalidate_location(&texture_gl->t, i, ~location);
}
else
{
--
2.11.0
Dec. 3, 2019
[PATCH 4/5] wined3d: Destroy PBOs in wined3d_texture_gl_destroy_object().
by Henri Verbeet
Signed-off-by: Henri Verbeet <hverbeet(a)codeweavers.com>
---
dlls/wined3d/adapter_gl.c | 31 +++++++++++++++++++++++++++----
dlls/wined3d/texture.c | 24 ------------------------
2 files changed, 27 insertions(+), 28 deletions(-)
diff --git a/dlls/wined3d/adapter_gl.c b/dlls/wined3d/adapter_gl.c
index 7018c6da1d9..eb9965723a2 100644
--- a/dlls/wined3d/adapter_gl.c
+++ b/dlls/wined3d/adapter_gl.c
@@ -4765,17 +4765,39 @@ static void wined3d_texture_gl_destroy_object(void *object)
{
struct wined3d_renderbuffer_entry *entry, *entry2;
struct wined3d_texture_gl *texture_gl = object;
+ struct wined3d_context *context = NULL;
const struct wined3d_gl_info *gl_info;
- struct wined3d_context *context;
struct wined3d_device *device;
+ unsigned int sub_count, i;
+ GLuint buffer_object;
TRACE("texture_gl %p.\n", texture_gl);
+ sub_count = texture_gl->t.level_count * texture_gl->t.layer_count;
+ for (i = 0; i < sub_count; ++i)
+ {
+ if (!(buffer_object = texture_gl->t.sub_resources[i].buffer_object))
+ continue;
+
+ TRACE("Deleting buffer object %u.\n", buffer_object);
+
+ if (!context)
+ {
+ context = context_acquire(texture_gl->t.resource.device, NULL, 0);
+ gl_info = wined3d_context_gl(context)->gl_info;
+ }
+
+ GL_EXTCALL(glDeleteBuffers(1, &buffer_object));
+ }
+
if (!list_empty(&texture_gl->renderbuffers))
{
device = texture_gl->t.resource.device;
- context = context_acquire(device, NULL, 0);
- gl_info = wined3d_context_gl(context)->gl_info;
+ if (!context)
+ {
+ context = context_acquire(device, NULL, 0);
+ gl_info = wined3d_context_gl(context)->gl_info;
+ }
LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &texture_gl->renderbuffers, struct wined3d_renderbuffer_entry, entry)
{
@@ -4784,9 +4806,10 @@ static void wined3d_texture_gl_destroy_object(void *object)
gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
heap_free(entry);
}
+ }
+ if (context)
context_release(context);
- }
wined3d_texture_gl_unload_texture(texture_gl);
diff --git a/dlls/wined3d/texture.c b/dlls/wined3d/texture.c
index 260a0486302..a3bbf2585d8 100644
--- a/dlls/wined3d/texture.c
+++ b/dlls/wined3d/texture.c
@@ -1118,38 +1118,14 @@ ULONG CDECL wined3d_texture_incref(struct wined3d_texture *texture)
static void wined3d_texture_destroy_object(void *object)
{
- const struct wined3d_gl_info *gl_info = NULL;
struct wined3d_texture *texture = object;
- struct wined3d_context *context = NULL;
struct wined3d_dc_info *dc_info;
unsigned int sub_count;
- GLuint buffer_object;
unsigned int i;
TRACE("texture %p.\n", texture);
sub_count = texture->level_count * texture->layer_count;
- for (i = 0; i < sub_count; ++i)
- {
- if (!(buffer_object = texture->sub_resources[i].buffer_object))
- continue;
-
- TRACE("Deleting buffer object %u.\n", buffer_object);
-
- /* We may not be able to get a context in
- * wined3d_texture_destroy_object() in general, but if a buffer object
- * was previously created we can. */
- if (!context)
- {
- context = context_acquire(texture->resource.device, NULL, 0);
- gl_info = wined3d_context_gl(context)->gl_info;
- }
-
- GL_EXTCALL(glDeleteBuffers(1, &buffer_object));
- }
-
- if (context)
- context_release(context);
if ((dc_info = texture->dc_info))
{
--
2.11.0
Dec. 3, 2019
[PATCH 3/5] wined3d: Store texture sub-resource buffer objects as uintptr_t.
by Henri Verbeet
Signed-off-by: Henri Verbeet <hverbeet(a)codeweavers.com>
---
dlls/wined3d/texture.c | 17 ++++++++++-------
dlls/wined3d/wined3d_private.h | 2 +-
2 files changed, 11 insertions(+), 8 deletions(-)
diff --git a/dlls/wined3d/texture.c b/dlls/wined3d/texture.c
index 976235bf60f..260a0486302 100644
--- a/dlls/wined3d/texture.c
+++ b/dlls/wined3d/texture.c
@@ -477,13 +477,15 @@ void wined3d_texture_get_memory(struct wined3d_texture *texture, unsigned int su
static void wined3d_texture_remove_buffer_object(struct wined3d_texture *texture,
unsigned int sub_resource_idx, const struct wined3d_gl_info *gl_info)
{
- GLuint *buffer_object = &texture->sub_resources[sub_resource_idx].buffer_object;
+ uintptr_t *buffer_object = &texture->sub_resources[sub_resource_idx].buffer_object;
+ GLuint bo;
- GL_EXTCALL(glDeleteBuffers(1, buffer_object));
+ bo = *buffer_object;
+ GL_EXTCALL(glDeleteBuffers(1, &bo));
checkGLcall("glDeleteBuffers");
TRACE("Deleted buffer object %u for texture %p, sub-resource %u.\n",
- *buffer_object, texture, sub_resource_idx);
+ bo, texture, sub_resource_idx);
wined3d_texture_invalidate_location(texture, sub_resource_idx, WINED3D_LOCATION_BUFFER);
*buffer_object = 0;
@@ -1670,19 +1672,20 @@ static void wined3d_texture_prepare_buffer_object(struct wined3d_texture *textur
unsigned int sub_resource_idx, const struct wined3d_gl_info *gl_info)
{
struct wined3d_texture_sub_resource *sub_resource;
+ GLuint bo;
sub_resource = &texture->sub_resources[sub_resource_idx];
if (sub_resource->buffer_object)
return;
- GL_EXTCALL(glGenBuffers(1, &sub_resource->buffer_object));
- GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, sub_resource->buffer_object));
+ GL_EXTCALL(glGenBuffers(1, &bo));
+ GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, bo));
GL_EXTCALL(glBufferData(GL_PIXEL_UNPACK_BUFFER, sub_resource->size, NULL, GL_STREAM_DRAW));
GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
checkGLcall("Create buffer object");
- TRACE("Created buffer object %u for texture %p, sub-resource %u.\n",
- sub_resource->buffer_object, texture, sub_resource_idx);
+ sub_resource->buffer_object = bo;
+ TRACE("Created buffer object %u for texture %p, sub-resource %u.\n", bo, texture, sub_resource_idx);
}
static void wined3d_texture_force_reload(struct wined3d_texture *texture)
diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
index 67756a6bbfc..3754ed9e763 100644
--- a/dlls/wined3d/wined3d_private.h
+++ b/dlls/wined3d/wined3d_private.h
@@ -3563,7 +3563,7 @@ struct wined3d_texture
unsigned int map_count;
uint32_t map_flags;
DWORD locations;
- GLuint buffer_object;
+ uintptr_t buffer_object;
} *sub_resources;
};
--
2.11.0
Dec. 3, 2019
[PATCH 2/5] wined3d: Do not set depth/stencil sizes on colour formats.
by Henri Verbeet
Signed-off-by: Henri Verbeet <hverbeet(a)codeweavers.com>
---
dlls/wined3d/utils.c | 41 ++++++++++++++++++++++-------------------
dlls/wined3d/wined3d_private.h | 4 ++--
2 files changed, 24 insertions(+), 21 deletions(-)
diff --git a/dlls/wined3d/utils.c b/dlls/wined3d/utils.c
index ac821ce732a..453bee62565 100644
--- a/dlls/wined3d/utils.c
+++ b/dlls/wined3d/utils.c
@@ -151,13 +151,13 @@ static const struct wined3d_format_channels formats[] =
{WINED3DFMT_R32G32B32_TYPELESS, 32, 32, 32, 0, 0, 32, 64, 0, 12, 0, 0},
{WINED3DFMT_R16G16B16A16_TYPELESS, 16, 16, 16, 16, 0, 16, 32, 48, 8, 0, 0},
{WINED3DFMT_R32G32_TYPELESS, 32, 32, 0, 0, 0, 32, 0, 0, 8, 0, 0},
- {WINED3DFMT_R32G8X24_TYPELESS, 0, 0, 0, 0, 0, 0, 0, 0, 8, 32, 8},
+ {WINED3DFMT_R32G8X24_TYPELESS, 32, 8, 0, 0, 0, 0, 0, 0, 8, 0, 0},
{WINED3DFMT_R10G10B10A2_TYPELESS, 10, 10, 10, 2, 0, 10, 20, 30, 4, 0, 0},
{WINED3DFMT_R10G10B10X2_TYPELESS, 10, 10, 10, 0, 0, 10, 20, 0, 4, 0, 0},
{WINED3DFMT_R8G8B8A8_TYPELESS, 8, 8, 8, 8, 0, 8, 16, 24, 4, 0, 0},
{WINED3DFMT_R16G16_TYPELESS, 16, 16, 0, 0, 0, 16, 0, 0, 4, 0, 0},
{WINED3DFMT_R32_TYPELESS, 32, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0},
- {WINED3DFMT_R24G8_TYPELESS, 0, 0, 0, 0, 0, 0, 0, 0, 4, 24, 8},
+ {WINED3DFMT_R24G8_TYPELESS, 24, 8, 0, 0, 0, 0, 0, 0, 4, 0, 0},
{WINED3DFMT_R8G8_TYPELESS, 8, 8, 0, 0, 0, 8, 0, 0, 2, 0, 0},
{WINED3DFMT_R16_TYPELESS, 16, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0},
{WINED3DFMT_R8_TYPELESS, 8, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0},
@@ -220,9 +220,9 @@ static const struct wined3d_typed_format_info typed_formats[] =
{WINED3DFMT_R32G32_UINT, WINED3DFMT_R32G32_TYPELESS, "UU"},
{WINED3DFMT_R32G32_SINT, WINED3DFMT_R32G32_TYPELESS, "II"},
{WINED3DFMT_R32G32_FLOAT, WINED3DFMT_R32G32_TYPELESS, "FF"},
- {WINED3DFMT_R32_FLOAT_X8X24_TYPELESS, WINED3DFMT_R32G8X24_TYPELESS, "DX"},
- {WINED3DFMT_X32_TYPELESS_G8X24_UINT, WINED3DFMT_R32G8X24_TYPELESS, "XS"},
- {WINED3DFMT_D32_FLOAT_S8X24_UINT, WINED3DFMT_R32G8X24_TYPELESS, "DS"},
+ {WINED3DFMT_R32_FLOAT_X8X24_TYPELESS, WINED3DFMT_R32G8X24_TYPELESS, "FXX"},
+ {WINED3DFMT_X32_TYPELESS_G8X24_UINT, WINED3DFMT_R32G8X24_TYPELESS, "XUX"},
+ {WINED3DFMT_D32_FLOAT_S8X24_UINT, WINED3DFMT_R32G8X24_TYPELESS, "DSX"},
{WINED3DFMT_R10G10B10A2_SNORM, WINED3DFMT_R10G10B10A2_TYPELESS, "iiii"},
{WINED3DFMT_R10G10B10A2_UINT, WINED3DFMT_R10G10B10A2_TYPELESS, "UUUU"},
{WINED3DFMT_R10G10B10A2_UNORM, WINED3DFMT_R10G10B10A2_TYPELESS, "uuuu"},
@@ -242,8 +242,8 @@ static const struct wined3d_typed_format_info typed_formats[] =
{WINED3DFMT_R32_FLOAT, WINED3DFMT_R32_TYPELESS, "F"},
{WINED3DFMT_R32_UINT, WINED3DFMT_R32_TYPELESS, "U"},
{WINED3DFMT_R32_SINT, WINED3DFMT_R32_TYPELESS, "I"},
- {WINED3DFMT_R24_UNORM_X8_TYPELESS, WINED3DFMT_R24G8_TYPELESS, "DX"},
- {WINED3DFMT_X24_TYPELESS_G8_UINT, WINED3DFMT_R24G8_TYPELESS, "XS"},
+ {WINED3DFMT_R24_UNORM_X8_TYPELESS, WINED3DFMT_R24G8_TYPELESS, "uX"},
+ {WINED3DFMT_X24_TYPELESS_G8_UINT, WINED3DFMT_R24G8_TYPELESS, "XU"},
{WINED3DFMT_D24_UNORM_S8_UINT, WINED3DFMT_R24G8_TYPELESS, "DS"},
{WINED3DFMT_R8G8_SNORM, WINED3DFMT_R8G8_TYPELESS, "ii"},
{WINED3DFMT_R8G8_UNORM, WINED3DFMT_R8G8_TYPELESS, "uu"},
@@ -2099,6 +2099,12 @@ static BOOL init_format_base_info(struct wined3d_adapter *adapter)
format->depth_size = format->red_size;
format->red_size = format->red_offset = 0;
}
+
+ if (channel_type == WINED3D_CHANNEL_TYPE_STENCIL && !format->stencil_size)
+ {
+ format->stencil_size = format->green_size;
+ format->green_size = format->green_offset = 0;
+ }
}
format->component_count = component_count;
@@ -3785,27 +3791,24 @@ static BOOL init_typeless_formats(const struct wined3d_adapter *adapter)
typeless_format->flags[j] &= ~(WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
}
- if ((format_id = typeless_depth_stencil_formats[i].depth_view_id))
+ if ((format_id = typeless_depth_stencil_formats[i].depth_view_id)
+ && typeless_depth_stencil_formats[i].separate_depth_view_format)
{
if (!(depth_view_format = get_format_internal(adapter, format_id)))
return FALSE;
- if (typeless_depth_stencil_formats[i].separate_depth_view_format)
- {
- copy_format(adapter, depth_view_format, ds_format);
- depth_view_format->stencil_size = 0;
- }
- else
- {
- depth_view_format->depth_size = ds_format->depth_size;
- }
+ copy_format(adapter, depth_view_format, ds_format);
+ depth_view_format->red_size = depth_view_format->depth_size;
+ depth_view_format->depth_size = 0;
+ depth_view_format->stencil_size = 0;
}
if ((format_id = typeless_depth_stencil_formats[i].stencil_view_id))
{
if (!(stencil_view_format = get_format_internal(adapter, format_id)))
return FALSE;
copy_format(adapter, stencil_view_format, ds_format);
- if (typeless_depth_stencil_formats[i].separate_depth_view_format)
- stencil_view_format->depth_size = 0;
+ stencil_view_format->green_size = stencil_view_format->stencil_size;
+ stencil_view_format->depth_size = 0;
+ stencil_view_format->stencil_size = 0;
}
}
diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
index 1ee22808c1d..67756a6bbfc 100644
--- a/dlls/wined3d/wined3d_private.h
+++ b/dlls/wined3d/wined3d_private.h
@@ -5236,8 +5236,8 @@ static inline void wined3d_context_copy_bo_address(struct wined3d_context *conte
static inline BOOL wined3d_dsv_srv_conflict(const struct wined3d_rendertarget_view *dsv,
const struct wined3d_format *srv_format)
{
- return !srv_format || (srv_format->depth_size && !(dsv->desc.flags & WINED3D_VIEW_READ_ONLY_DEPTH))
- || (srv_format->stencil_size && !(dsv->desc.flags & WINED3D_VIEW_READ_ONLY_STENCIL));
+ return !srv_format || (srv_format->red_size && !(dsv->desc.flags & WINED3D_VIEW_READ_ONLY_DEPTH))
+ || (srv_format->green_size && !(dsv->desc.flags & WINED3D_VIEW_READ_ONLY_STENCIL));
}
static inline BOOL wined3d_resource_check_fbo_attached(const struct wined3d_state *state,
--
2.11.0
Dec. 3, 2019
[PATCH 1/5] wined3d: Finish the CS before removing the context in adapter_vk_uninit_3d().
by Henri Verbeet
Signed-off-by: Henri Verbeet <hverbeet(a)codeweavers.com>
---
dlls/wined3d/adapter_vk.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/dlls/wined3d/adapter_vk.c b/dlls/wined3d/adapter_vk.c
index 21163a20949..4a501292824 100644
--- a/dlls/wined3d/adapter_vk.c
+++ b/dlls/wined3d/adapter_vk.c
@@ -466,6 +466,7 @@ static void adapter_vk_uninit_3d(struct wined3d_device *device)
device->blitter->ops->blitter_destroy(device->blitter, NULL);
+ wined3d_cs_finish(device->cs, WINED3D_CS_QUEUE_DEFAULT);
device_context_remove(device, &context_vk->c);
device->shader_backend->shader_free_private(device, NULL);
wined3d_context_vk_cleanup(context_vk);
--
2.11.0
Dec. 3, 2019
Re: [PATCH v2 4/4] winegstreamer: Use strmbase state change methods.
by Zebediah Figura
On 12/3/19 2:38 AM, Alexandre Julliard wrote:
> Zebediah Figura <z.figura12(a)gmail.com> writes:
>
>> Signed-off-by: Zebediah Figura <z.figura12(a)gmail.com>
>> ---
>> dlls/winegstreamer/gstdemux.c | 255 ++++++++++++++++------------------
>> 1 file changed, 122 insertions(+), 133 deletions(-)
>
> This is still causing deadlocks:
>
> ../../../tools/runtest -q -P wine -T ../../.. -M winmm.dll -p winmm_test.exe mci && touch mci.ok
> 00e2:fixme:mci:MCI_SendCommand unhandled MCI_ALL_DEVICE_ID
> 00e2:fixme:mci:MCI_SysInfo Don't know how to get # of MCI devices of a given type
> 00e2:fixme:mci:MCI_SendCommand unhandled MCI_ALL_DEVICE_ID
> 00e2:fixme:mci:MCI_LoadMciDriver Couldn't load driver for type L"".
> 00e2:fixme:mci:MCI_LoadMciDriver Couldn't load driver for type L"".
> 00e2:fixme:mci:MCI_LoadMciDriver Couldn't load driver for type L"NOSUCHDEVICE".
> 00e2:fixme:mciwave:MCIWAVE_DriverProc Unsupported command [2134]
> 00e2:fixme:mci:mciSendStringW leaking auto-open device 1
> ALSA lib conf.c:3558:(snd_config_hooks_call) Cannot open shared library libasound_module_conf_pulse.so (/usr/lib/i386-linux-gnu/alsa-lib/libasound_module_conf_pulse.so: libasound_module_conf_pulse.so: cannot open shared object file: No such file or directory)
> ALSA lib seq.c:935:(snd_seq_open_noupdate) Unknown SEQ default
> 00e2:err:winediag:MIDIMAP_drvOpen No software synthesizer midi port found, Midi sound output probably won't work.
> 00e2:fixme:gstreamer:source_query_accept (0x6ea688) stub
> 00f9:err:ntdll:RtlpWaitForCriticalSection section 0x6ea1f0 "../strmbase/filter.c: strmbase_filter.csFilter" wait timed out in thread 00f9, blocked by 00fb, retrying (60 sec)
>
Thanks, it seems I misread the gstreamer code and mistakenly judged that
pad-removed was not sent from the streaming thread. I'll send a new
patch set that addresses this.
Dec. 3, 2019
[PATCH v3 4/4] winegstreamer: Use strmbase state change methods.
by Zebediah Figura
Signed-off-by: Zebediah Figura <z.figura12(a)gmail.com>
---
dlls/winegstreamer/gstdemux.c | 255 ++++++++++++++++------------------
1 file changed, 122 insertions(+), 133 deletions(-)
diff --git a/dlls/winegstreamer/gstdemux.c b/dlls/winegstreamer/gstdemux.c
index c9a32e35a9..da9bca6d1c 100644
--- a/dlls/winegstreamer/gstdemux.c
+++ b/dlls/winegstreamer/gstdemux.c
@@ -88,11 +88,6 @@ struct gstdemux_source
SourceSeeking seek;
};
-static inline struct gstdemux *impl_from_IBaseFilter(IBaseFilter *iface)
-{
- return CONTAINING_RECORD(iface, struct gstdemux, filter.IBaseFilter_iface);
-}
-
static inline struct gstdemux *impl_from_strmbase_filter(struct strmbase_filter *iface)
{
return CONTAINING_RECORD(iface, struct gstdemux, filter);
@@ -1247,10 +1242,123 @@ static void gstdemux_destroy(struct strmbase_filter *iface)
heap_free(filter);
}
+static HRESULT gstdemux_init_stream(struct strmbase_filter *iface)
+{
+ struct gstdemux *filter = impl_from_strmbase_filter(iface);
+ HRESULT hr = VFW_E_NOT_CONNECTED, pin_hr;
+ GstStateChangeReturn ret;
+ unsigned int i;
+
+ if (!filter->container)
+ return VFW_E_NOT_CONNECTED;
+
+ if (filter->no_more_pads_event)
+ ResetEvent(filter->no_more_pads_event);
+
+ if ((ret = gst_element_set_state(filter->container, GST_STATE_PAUSED)) == GST_STATE_CHANGE_FAILURE)
+ {
+ ERR("Failed to pause stream.\n");
+ return E_FAIL;
+ }
+
+ /* Make sure that all of our pads are connected before returning, lest we
+ * e.g. try to seek and fail. */
+ if (filter->no_more_pads_event)
+ WaitForSingleObject(filter->no_more_pads_event, INFINITE);
+
+ for (i = 0; i < filter->cStreams; ++i)
+ {
+ if (SUCCEEDED(pin_hr = BaseOutputPinImpl_Active(&filter->ppPins[i]->pin)))
+ hr = pin_hr;
+ }
+ return hr;
+}
+
+static HRESULT gstdemux_start_stream(struct strmbase_filter *iface, REFERENCE_TIME time)
+{
+ struct gstdemux *filter = impl_from_strmbase_filter(iface);
+ GstStateChangeReturn ret;
+
+ if (!filter->container)
+ return VFW_E_NOT_CONNECTED;
+
+ if ((ret = gst_element_set_state(filter->container, GST_STATE_PLAYING)) == GST_STATE_CHANGE_FAILURE)
+ {
+ ERR("Failed to play stream.\n");
+ return E_FAIL;
+ }
+ else if (ret == GST_STATE_CHANGE_ASYNC)
+ return S_FALSE;
+ return S_OK;
+}
+
+static HRESULT gstdemux_stop_stream(struct strmbase_filter *iface)
+{
+ struct gstdemux *filter = impl_from_strmbase_filter(iface);
+ GstStateChangeReturn ret;
+
+ if (!filter->container)
+ return VFW_E_NOT_CONNECTED;
+
+ if ((ret = gst_element_set_state(filter->container, GST_STATE_PAUSED)) == GST_STATE_CHANGE_FAILURE)
+ {
+ ERR("Failed to pause stream.\n");
+ return E_FAIL;
+ }
+ else if (ret == GST_STATE_CHANGE_ASYNC)
+ return S_FALSE;
+ return S_OK;
+}
+
+static HRESULT gstdemux_cleanup_stream(struct strmbase_filter *iface)
+{
+ struct gstdemux *filter = impl_from_strmbase_filter(iface);
+ GstStateChangeReturn ret;
+
+ if (!filter->container)
+ return S_OK;
+
+ filter->ignore_flush = TRUE;
+ if ((ret = gst_element_set_state(filter->container, GST_STATE_READY)) == GST_STATE_CHANGE_FAILURE)
+ {
+ ERR("Failed to pause stream.\n");
+ return E_FAIL;
+ }
+ gst_element_get_state(filter->container, NULL, NULL, GST_CLOCK_TIME_NONE);
+ filter->ignore_flush = FALSE;
+
+ return S_OK;
+}
+
+static HRESULT gstdemux_wait_state(struct strmbase_filter *iface, DWORD timeout)
+{
+ struct gstdemux *filter = impl_from_strmbase_filter(iface);
+ GstStateChangeReturn ret;
+
+ if (!filter->container)
+ return S_OK;
+
+ ret = gst_element_get_state(filter->container, NULL, NULL,
+ timeout == INFINITE ? GST_CLOCK_TIME_NONE : timeout * 1000);
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ {
+ ERR("Failed to get state.\n");
+ return E_FAIL;
+ }
+ else if (ret == GST_STATE_CHANGE_ASYNC)
+ return VFW_S_STATE_INTERMEDIATE;
+ return S_OK;
+}
+
static const struct strmbase_filter_ops filter_ops =
{
.filter_get_pin = gstdemux_get_pin,
.filter_destroy = gstdemux_destroy,
+ .filter_init_stream = gstdemux_init_stream,
+ .filter_start_stream = gstdemux_start_stream,
+ .filter_stop_stream = gstdemux_stop_stream,
+ .filter_cleanup_stream = gstdemux_cleanup_stream,
+ .filter_wait_state = gstdemux_wait_state,
};
static HRESULT sink_query_accept(struct strmbase_pin *iface, const AM_MEDIA_TYPE *mt)
@@ -1360,139 +1468,15 @@ IUnknown * CALLBACK Gstreamer_Splitter_create(IUnknown *outer, HRESULT *phr)
return &object->filter.IUnknown_inner;
}
-static HRESULT WINAPI GST_Stop(IBaseFilter *iface)
-{
- struct gstdemux *This = impl_from_IBaseFilter(iface);
-
- TRACE("(%p)\n", This);
-
- mark_wine_thread();
-
- if (This->container) {
- This->ignore_flush = TRUE;
- gst_element_set_state(This->container, GST_STATE_READY);
- gst_element_get_state(This->container, NULL, NULL, -1);
- This->ignore_flush = FALSE;
- }
- return S_OK;
-}
-
-static HRESULT WINAPI GST_Pause(IBaseFilter *iface)
-{
- struct gstdemux *This = impl_from_IBaseFilter(iface);
- HRESULT hr = S_OK;
- GstState now;
- GstStateChangeReturn ret;
-
- TRACE("(%p)\n", This);
-
- if (!This->container)
- return VFW_E_NOT_CONNECTED;
-
- mark_wine_thread();
-
- gst_element_get_state(This->container, &now, NULL, -1);
- if (now == GST_STATE_PAUSED)
- return S_OK;
- if (now != GST_STATE_PLAYING)
- hr = IBaseFilter_Run(iface, -1);
- if (FAILED(hr))
- return hr;
- ret = gst_element_set_state(This->container, GST_STATE_PAUSED);
- if (ret == GST_STATE_CHANGE_ASYNC)
- hr = S_FALSE;
- return hr;
-}
-
-static HRESULT WINAPI GST_Run(IBaseFilter *iface, REFERENCE_TIME tStart)
-{
- struct gstdemux *This = impl_from_IBaseFilter(iface);
- HRESULT hr = S_OK;
- ULONG i;
- GstState now;
- HRESULT hr_any = VFW_E_NOT_CONNECTED;
-
- TRACE("(%p)->(%s)\n", This, wine_dbgstr_longlong(tStart));
-
- mark_wine_thread();
-
- if (!This->container)
- return VFW_E_NOT_CONNECTED;
-
- gst_element_get_state(This->container, &now, NULL, -1);
- if (now == GST_STATE_PLAYING)
- return S_OK;
- if (now == GST_STATE_PAUSED) {
- GstStateChangeReturn ret;
- ret = gst_element_set_state(This->container, GST_STATE_PLAYING);
- if (ret == GST_STATE_CHANGE_ASYNC)
- return S_FALSE;
- return S_OK;
- }
-
- EnterCriticalSection(&This->filter.csFilter);
-
- if (This->no_more_pads_event)
- ResetEvent(This->no_more_pads_event);
-
- gst_element_set_state(This->container, GST_STATE_PLAYING);
-
- /* Make sure that all of our pads are connected before returning, lest we
- * e.g. try to seek and fail. */
- if (This->no_more_pads_event)
- WaitForSingleObject(This->no_more_pads_event, INFINITE);
-
- for (i = 0; i < This->cStreams; i++) {
- hr = BaseOutputPinImpl_Active(&This->ppPins[i]->pin);
- if (SUCCEEDED(hr)) {
- hr_any = hr;
- }
- }
- hr = hr_any;
- LeaveCriticalSection(&This->filter.csFilter);
-
- return hr;
-}
-
-static HRESULT WINAPI GST_GetState(IBaseFilter *iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
-{
- struct gstdemux *This = impl_from_IBaseFilter(iface);
- HRESULT hr = S_OK;
- GstState now, pending;
- GstStateChangeReturn ret;
-
- TRACE("(%p)->(%d, %p)\n", This, dwMilliSecsTimeout, pState);
-
- mark_wine_thread();
-
- if (!This->container) {
- *pState = State_Stopped;
- return S_OK;
- }
-
- ret = gst_element_get_state(This->container, &now, &pending, dwMilliSecsTimeout == INFINITE ? -1 : dwMilliSecsTimeout * 1000);
-
- if (ret == GST_STATE_CHANGE_ASYNC)
- hr = VFW_S_STATE_INTERMEDIATE;
- else
- pending = now;
-
- switch (pending) {
- case GST_STATE_PAUSED: *pState = State_Paused; return hr;
- case GST_STATE_PLAYING: *pState = State_Running; return hr;
- default: *pState = State_Stopped; return hr;
- }
-}
-
static const IBaseFilterVtbl GST_Vtbl = {
BaseFilterImpl_QueryInterface,
BaseFilterImpl_AddRef,
BaseFilterImpl_Release,
BaseFilterImpl_GetClassID,
- GST_Stop,
- GST_Pause,
- GST_Run,
- GST_GetState,
+ BaseFilterImpl_Stop,
+ BaseFilterImpl_Pause,
+ BaseFilterImpl_Run,
+ BaseFilterImpl_GetState,
BaseFilterImpl_SetSyncSource,
BaseFilterImpl_GetSyncSource,
BaseFilterImpl_EnumPins,
@@ -2632,6 +2616,11 @@ static const struct strmbase_filter_ops mpeg_splitter_ops =
.filter_query_interface = mpeg_splitter_query_interface,
.filter_get_pin = gstdemux_get_pin,
.filter_destroy = gstdemux_destroy,
+ .filter_init_stream = gstdemux_init_stream,
+ .filter_start_stream = gstdemux_start_stream,
+ .filter_stop_stream = gstdemux_stop_stream,
+ .filter_cleanup_stream = gstdemux_cleanup_stream,
+ .filter_wait_state = gstdemux_wait_state,
};
IUnknown * CALLBACK mpeg_splitter_create(IUnknown *outer, HRESULT *phr)
--
2.24.0
Dec. 3, 2019
[PATCH v3 3/4] winegstreamer: Also wait for the no-more-pads signal when pausing the filter.
by Zebediah Figura
Signed-off-by: Zebediah Figura <z.figura12(a)gmail.com>
---
dlls/winegstreamer/gstdemux.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/dlls/winegstreamer/gstdemux.c b/dlls/winegstreamer/gstdemux.c
index 9eddf78ced..c9a32e35a9 100644
--- a/dlls/winegstreamer/gstdemux.c
+++ b/dlls/winegstreamer/gstdemux.c
@@ -1431,8 +1431,17 @@ static HRESULT WINAPI GST_Run(IBaseFilter *iface, REFERENCE_TIME tStart)
}
EnterCriticalSection(&This->filter.csFilter);
+
+ if (This->no_more_pads_event)
+ ResetEvent(This->no_more_pads_event);
+
gst_element_set_state(This->container, GST_STATE_PLAYING);
+ /* Make sure that all of our pads are connected before returning, lest we
+ * e.g. try to seek and fail. */
+ if (This->no_more_pads_event)
+ WaitForSingleObject(This->no_more_pads_event, INFINITE);
+
for (i = 0; i < This->cStreams; i++) {
hr = BaseOutputPinImpl_Active(&This->ppPins[i]->pin);
if (SUCCEEDED(hr)) {
--
2.24.0
Dec. 3, 2019
[PATCH v3 2/4] winegstreamer: Don't hold the filter lock from the pad-added or pad-removed callbacks.
by Zebediah Figura
These may be called from the streaming thread, so it's not safe to do so.
Nor does it seem necessary. We expect that no streaming thread should ever
call methods on our pad or pin, and as long as we hold the filter lock and
wait for the no-more-pads signal when connecting or starting the stream, we
cannot race with application threads.
Signed-off-by: Zebediah Figura <z.figura12(a)gmail.com>
---
dlls/winegstreamer/gstdemux.c | 14 ++++++--------
1 file changed, 6 insertions(+), 8 deletions(-)
diff --git a/dlls/winegstreamer/gstdemux.c b/dlls/winegstreamer/gstdemux.c
index 5fa9d66c93..9eddf78ced 100644
--- a/dlls/winegstreamer/gstdemux.c
+++ b/dlls/winegstreamer/gstdemux.c
@@ -814,13 +814,17 @@ static void removed_decoded_pad(GstElement *bin, GstPad *pad, gpointer user)
TRACE("%p %p %p\n", This, bin, pad);
- EnterCriticalSection(&This->filter.csFilter);
for (x = 0; x < This->cStreams; ++x) {
if (This->ppPins[x]->their_src == pad)
break;
}
if (x == This->cStreams)
- goto out;
+ {
+ char *name = gst_pad_get_name(pad);
+ WARN("No pin matching pad %s found.\n", debugstr_a(name));
+ g_free(name);
+ return;
+ }
pin = This->ppPins[x];
@@ -831,9 +835,6 @@ static void removed_decoded_pad(GstElement *bin, GstPad *pad, gpointer user)
gst_object_unref(pin->their_src);
pin->their_src = NULL;
-out:
- TRACE("Removed %i/%i\n", x, This->cStreams);
- LeaveCriticalSection(&This->filter.csFilter);
}
static void init_new_decoded_pad(GstElement *bin, GstPad *pad, struct gstdemux *This)
@@ -971,7 +972,6 @@ static void existing_new_pad(GstElement *bin, GstPad *pad, gpointer user)
return;
}
- EnterCriticalSection(&This->filter.csFilter);
for (x = 0; x < This->cStreams; ++x) {
struct gstdemux_source *pin = This->ppPins[x];
if (!pin->their_src) {
@@ -986,13 +986,11 @@ static void existing_new_pad(GstElement *bin, GstPad *pad, gpointer user)
pin->their_src = pad;
gst_object_ref(pin->their_src);
TRACE("Relinked\n");
- LeaveCriticalSection(&This->filter.csFilter);
return;
}
}
}
init_new_decoded_pad(bin, pad, This);
- LeaveCriticalSection(&This->filter.csFilter);
}
static gboolean query_function(GstPad *pad, GstObject *parent, GstQuery *query)
--
2.24.0
Dec. 3, 2019
[PATCH v3 1/4] strmbase: Use a separate critical section for the SourceSeeking structure.
by Zebediah Figura
Signed-off-by: Zebediah Figura <z.figura12(a)gmail.com>
---
dlls/strmbase/seeking.c | 53 +++++++++++++++++++--------------
dlls/winegstreamer/gstdemux.c | 5 ++--
dlls/wineqtdecoder/qtsplitter.c | 3 +-
include/wine/strmbase.h | 7 +++--
4 files changed, 40 insertions(+), 28 deletions(-)
diff --git a/dlls/strmbase/seeking.c b/dlls/strmbase/seeking.c
index 8a773b51d5..d87e4f8516 100644
--- a/dlls/strmbase/seeking.c
+++ b/dlls/strmbase/seeking.c
@@ -18,7 +18,6 @@
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
-/* FIXME: critical sections */
#include "strmbase_private.h"
@@ -29,7 +28,9 @@ static inline SourceSeeking *impl_from_IMediaSeeking(IMediaSeeking *iface)
return CONTAINING_RECORD(iface, SourceSeeking, IMediaSeeking_iface);
}
-HRESULT SourceSeeking_Init(SourceSeeking *pSeeking, const IMediaSeekingVtbl *Vtbl, SourceSeeking_ChangeStop fnChangeStop, SourceSeeking_ChangeStart fnChangeStart, SourceSeeking_ChangeRate fnChangeRate, PCRITICAL_SECTION crit_sect)
+HRESULT strmbase_seeking_init(SourceSeeking *pSeeking, const IMediaSeekingVtbl *Vtbl,
+ SourceSeeking_ChangeStop fnChangeStop, SourceSeeking_ChangeStart fnChangeStart,
+ SourceSeeking_ChangeRate fnChangeRate)
{
assert(fnChangeStop && fnChangeStart && fnChangeRate);
@@ -48,10 +49,16 @@ HRESULT SourceSeeking_Init(SourceSeeking *pSeeking, const IMediaSeekingVtbl *Vtb
pSeeking->llDuration = pSeeking->llStop;
pSeeking->dRate = 1.0;
pSeeking->timeformat = TIME_FORMAT_MEDIA_TIME;
- pSeeking->crst = crit_sect;
+ InitializeCriticalSection(&pSeeking->cs);
+ pSeeking->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": SourceSeeking.cs");
return S_OK;
}
+void strmbase_seeking_cleanup(SourceSeeking *seeking)
+{
+ DeleteCriticalSection(&seeking->cs);
+}
+
HRESULT WINAPI SourceSeekingImpl_GetCapabilities(IMediaSeeking * iface, DWORD * pCapabilities)
{
SourceSeeking *This = impl_from_IMediaSeeking(iface);
@@ -104,9 +111,9 @@ HRESULT WINAPI SourceSeekingImpl_GetTimeFormat(IMediaSeeking * iface, GUID * pFo
SourceSeeking *This = impl_from_IMediaSeeking(iface);
TRACE("(%s)\n", debugstr_guid(pFormat));
- EnterCriticalSection(This->crst);
+ EnterCriticalSection(&This->cs);
*pFormat = This->timeformat;
- LeaveCriticalSection(This->crst);
+ LeaveCriticalSection(&This->cs);
return S_OK;
}
@@ -118,10 +125,10 @@ HRESULT WINAPI SourceSeekingImpl_IsUsingTimeFormat(IMediaSeeking * iface, const
TRACE("(%s)\n", debugstr_guid(pFormat));
- EnterCriticalSection(This->crst);
+ EnterCriticalSection(&This->cs);
if (!IsEqualIID(pFormat, &This->timeformat))
hr = S_FALSE;
- LeaveCriticalSection(This->crst);
+ LeaveCriticalSection(&This->cs);
return hr;
}
@@ -140,9 +147,9 @@ HRESULT WINAPI SourceSeekingImpl_GetDuration(IMediaSeeking * iface, LONGLONG * p
TRACE("(%p)\n", pDuration);
- EnterCriticalSection(This->crst);
+ EnterCriticalSection(&This->cs);
*pDuration = This->llDuration;
- LeaveCriticalSection(This->crst);
+ LeaveCriticalSection(&This->cs);
return S_OK;
}
@@ -153,9 +160,9 @@ HRESULT WINAPI SourceSeekingImpl_GetStopPosition(IMediaSeeking * iface, LONGLONG
TRACE("(%p)\n", pStop);
- EnterCriticalSection(This->crst);
+ EnterCriticalSection(&This->cs);
*pStop = This->llStop;
- LeaveCriticalSection(This->crst);
+ LeaveCriticalSection(&This->cs);
return S_OK;
}
@@ -167,9 +174,9 @@ HRESULT WINAPI SourceSeekingImpl_GetCurrentPosition(IMediaSeeking * iface, LONGL
TRACE("(%p)\n", pCurrent);
- EnterCriticalSection(This->crst);
+ EnterCriticalSection(&This->cs);
*pCurrent = This->llCurrent;
- LeaveCriticalSection(This->crst);
+ LeaveCriticalSection(&This->cs);
return S_OK;
}
@@ -214,7 +221,7 @@ HRESULT WINAPI SourceSeekingImpl_SetPositions(IMediaSeeking * iface, LONGLONG *
LONGLONG llNewCurrent, llNewStop;
TRACE("(%p, %x, %p, %x)\n", pCurrent, dwCurrentFlags, pStop, dwStopFlags);
- EnterCriticalSection(This->crst);
+ EnterCriticalSection(&This->cs);
llNewCurrent = Adjust(This->llCurrent, pCurrent, dwCurrentFlags);
llNewStop = Adjust(This->llStop, pStop, dwStopFlags);
@@ -233,7 +240,7 @@ HRESULT WINAPI SourceSeekingImpl_SetPositions(IMediaSeeking * iface, LONGLONG *
*pCurrent = llNewCurrent;
if (pStop && (dwStopFlags & AM_SEEKING_ReturnTime))
*pStop = llNewStop;
- LeaveCriticalSection(This->crst);
+ LeaveCriticalSection(&This->cs);
if (bChangeCurrent)
This->fnChangeStart(iface);
@@ -249,10 +256,10 @@ HRESULT WINAPI SourceSeekingImpl_GetPositions(IMediaSeeking * iface, LONGLONG *
TRACE("(%p, %p)\n", pCurrent, pStop);
- EnterCriticalSection(This->crst);
+ EnterCriticalSection(&This->cs);
IMediaSeeking_GetCurrentPosition(iface, pCurrent);
IMediaSeeking_GetStopPosition(iface, pStop);
- LeaveCriticalSection(This->crst);
+ LeaveCriticalSection(&This->cs);
return S_OK;
}
@@ -263,10 +270,10 @@ HRESULT WINAPI SourceSeekingImpl_GetAvailable(IMediaSeeking * iface, LONGLONG *
TRACE("(%p, %p)\n", pEarliest, pLatest);
- EnterCriticalSection(This->crst);
+ EnterCriticalSection(&This->cs);
*pEarliest = 0;
*pLatest = This->llDuration;
- LeaveCriticalSection(This->crst);
+ LeaveCriticalSection(&This->cs);
return S_OK;
}
@@ -285,11 +292,11 @@ HRESULT WINAPI SourceSeekingImpl_SetRate(IMediaSeeking * iface, double dRate)
return VFW_E_UNSUPPORTED_AUDIO;
}
- EnterCriticalSection(This->crst);
+ EnterCriticalSection(&This->cs);
This->dRate = dRate;
if (bChangeRate)
hr = This->fnChangeRate(iface);
- LeaveCriticalSection(This->crst);
+ LeaveCriticalSection(&This->cs);
return hr;
}
@@ -300,10 +307,10 @@ HRESULT WINAPI SourceSeekingImpl_GetRate(IMediaSeeking * iface, double * dRate)
TRACE("(%p)\n", dRate);
- EnterCriticalSection(This->crst);
+ EnterCriticalSection(&This->cs);
/* Forward? */
*dRate = This->dRate;
- LeaveCriticalSection(This->crst);
+ LeaveCriticalSection(&This->cs);
return S_OK;
}
diff --git a/dlls/winegstreamer/gstdemux.c b/dlls/winegstreamer/gstdemux.c
index 479077b48f..5fa9d66c93 100644
--- a/dlls/winegstreamer/gstdemux.c
+++ b/dlls/winegstreamer/gstdemux.c
@@ -1884,6 +1884,7 @@ static void free_source_pin(struct gstdemux_source *pin)
FreeMediaType(&pin->mt);
gst_segment_free(pin->segment);
+ strmbase_seeking_cleanup(&pin->seek);
strmbase_source_cleanup(&pin->pin);
heap_free(pin);
}
@@ -1936,8 +1937,8 @@ static struct gstdemux_source *create_pin(struct gstdemux *filter, const WCHAR *
pin->segment = gst_segment_new();
gst_segment_init(pin->segment, GST_FORMAT_TIME);
pin->IQualityControl_iface.lpVtbl = &GSTOutPin_QualityControl_Vtbl;
- SourceSeeking_Init(&pin->seek, &GST_Seeking_Vtbl, GST_ChangeStop,
- GST_ChangeCurrent, GST_ChangeRate, &filter->filter.csFilter);
+ strmbase_seeking_init(&pin->seek, &GST_Seeking_Vtbl, GST_ChangeStop,
+ GST_ChangeCurrent, GST_ChangeRate);
BaseFilterImpl_IncrementPinVersion(&filter->filter);
sprintf(pad_name, "qz_sink_%u", filter->cStreams);
diff --git a/dlls/wineqtdecoder/qtsplitter.c b/dlls/wineqtdecoder/qtsplitter.c
index fbec10a912..81e18d3420 100644
--- a/dlls/wineqtdecoder/qtsplitter.c
+++ b/dlls/wineqtdecoder/qtsplitter.c
@@ -340,7 +340,8 @@ IUnknown * CALLBACK QTSplitter_create(IUnknown *outer, HRESULT *phr)
This->pInputPin.pin.peer = NULL;
This->pInputPin.pin.pFuncsTable = &sink_ops;
- SourceSeeking_Init(&This->sourceSeeking, &QT_Seeking_Vtbl, QTSplitter_ChangeStop, QTSplitter_ChangeStart, QTSplitter_ChangeRate, &This->filter.csFilter);
+ strmbase_seeking_init(&This->sourceSeeking, &QT_Seeking_Vtbl,
+ QTSplitter_ChangeStop, QTSplitter_ChangeStart, QTSplitter_ChangeRate);
*phr = S_OK;
return &This->filter.IUnknown_inner;
diff --git a/include/wine/strmbase.h b/include/wine/strmbase.h
index b08303f93d..7949e6670a 100644
--- a/include/wine/strmbase.h
+++ b/include/wine/strmbase.h
@@ -272,10 +272,13 @@ typedef struct SourceSeeking
double dRate;
LONGLONG llCurrent, llStop, llDuration;
GUID timeformat;
- PCRITICAL_SECTION crst;
+ CRITICAL_SECTION cs;
} SourceSeeking;
-HRESULT SourceSeeking_Init(SourceSeeking *pSeeking, const IMediaSeekingVtbl *Vtbl, SourceSeeking_ChangeStop fnChangeStop, SourceSeeking_ChangeStart fnChangeStart, SourceSeeking_ChangeRate fnChangeRate, PCRITICAL_SECTION crit_sect);
+HRESULT strmbase_seeking_init(SourceSeeking *seeking, const IMediaSeekingVtbl *vtbl,
+ SourceSeeking_ChangeStop fnChangeStop, SourceSeeking_ChangeStart fnChangeStart,
+ SourceSeeking_ChangeRate fnChangeRate);
+void strmbase_seeking_cleanup(SourceSeeking *seeking);
HRESULT WINAPI SourceSeekingImpl_GetCapabilities(IMediaSeeking * iface, DWORD * pCapabilities);
HRESULT WINAPI SourceSeekingImpl_CheckCapabilities(IMediaSeeking * iface, DWORD * pCapabilities);
--
2.24.0
Dec. 3, 2019
[PATCH v5 resend] user32: Send a WM_ACTIVATE message after restoring a minimized window.
by Zhiyi Zhang
The WmShowRestoreMinimizedOverlappedSeq message sequence in tests
clearly show that there is a WM_ACTIVATE message at the end of
ShowWindow() calls after restoring a minimized window, and it's
not from SetFocus().
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=47507
Signed-off-by: Zhiyi Zhang <zzhang(a)codeweavers.com>
---
dlls/user32/tests/msg.c | 247 +++++++++++++++++++++++++++++++++++++++-
dlls/user32/winpos.c | 9 +-
2 files changed, 254 insertions(+), 2 deletions(-)
diff --git a/dlls/user32/tests/msg.c b/dlls/user32/tests/msg.c
index c0ba5834cf..78733f44c9 100644
--- a/dlls/user32/tests/msg.c
+++ b/dlls/user32/tests/msg.c
@@ -892,6 +892,145 @@ static const struct message WmShowVisMaxPopupSeq[] = {
{ EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
{ 0 }
};
+/* ShowWindow(hwnd, SW_RESTORE) to a minimized window */
+static const struct message WmShowRestoreMinimizedOverlappedSeq[] =
+{
+ { HCBT_MINMAX, hook },
+ { WM_QUERYOPEN, sent },
+ { WM_GETTEXT, sent|optional },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_STATECHANGED },
+ { WM_GETMINMAXINFO, sent|defwinproc },
+ { WM_NCCALCSIZE, sent },
+ { HCBT_ACTIVATE, hook },
+ { WM_WINDOWPOSCHANGING, sent|wparam|optional, SWP_NOSIZE|SWP_NOMOVE },
+ { WM_NCACTIVATE, sent },
+ { WM_GETTEXT, sent|defwinproc|optional },
+ { WM_ACTIVATE, sent|wparam, WA_ACTIVE },
+ { HCBT_SETFOCUS, hook },
+ { WM_SETFOCUS, sent|defwinproc },
+ { WM_NCPAINT, sent },
+ { WM_GETTEXT, sent|defwinproc|optional },
+ { WM_GETTEXT, sent|defwinproc|optional },
+ { WM_ERASEBKGND, sent },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_STATECHANGED },
+ { WM_MOVE, sent|defwinproc },
+ { WM_SIZE, sent|defwinproc },
+ { WM_NCCALCSIZE, sent|optional },
+ { WM_NCPAINT, sent|optional },
+ { WM_ERASEBKGND, sent|optional },
+ /* Note this WM_ACTIVATE message even if the window is already active and focused */
+ { WM_ACTIVATE, sent|wparam|lparam, WA_ACTIVE, 0 },
+ { WM_SYNCPAINT, sent|optional },
+ { WM_PAINT, sent },
+ { WM_GETMINMAXINFO, sent|optional },
+ { 0 }
+};
+/* ShowWindow(hwnd, SW_SHOWNOACTIVATE) to a minimized window */
+static const struct message WmShowNoActivateMinimizedOverlappedSeq[] =
+{
+ { HCBT_MINMAX, hook },
+ { WM_QUERYOPEN, sent },
+ { WM_GETTEXT, sent|optional },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOACTIVATE|SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_STATECHANGED },
+ { WM_GETMINMAXINFO, sent|defwinproc },
+ { WM_NCCALCSIZE, sent },
+ { WM_NCPAINT, sent },
+ { WM_GETTEXT, sent|defwinproc|optional },
+ { WM_ERASEBKGND, sent },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_STATECHANGED },
+ { WM_MOVE, sent|defwinproc },
+ { WM_SIZE, sent|defwinproc },
+ /* Following optional messages are on XP/2003 */
+ { WM_NCCALCSIZE, sent|optional },
+ { WM_NCPAINT, sent|optional },
+ { WM_ERASEBKGND, sent|optional },
+ { HCBT_SETFOCUS, hook|optional },
+ { WM_SETFOCUS, sent|optional },
+ { HCBT_ACTIVATE, hook|optional },
+ { WM_WINDOWPOSCHANGING, sent|wparam|optional, SWP_NOSIZE|SWP_NOMOVE },
+ { WM_NCACTIVATE, sent|optional },
+ { WM_GETTEXT, sent|defwinproc|optional },
+ { WM_ACTIVATE, sent|wparam|optional, WA_ACTIVE },
+ { HCBT_SETFOCUS, hook|optional },
+ { WM_SETFOCUS, sent|defwinproc|optional },
+ { WM_KILLFOCUS, sent|optional },
+ { WM_SETFOCUS, sent|optional },
+ /* Note this WM_ACTIVATE message on XP even if the window is already active and focused */
+ { WM_ACTIVATE, sent|wparam|lparam|optional, WA_ACTIVE, 0 },
+ { WM_SYNCPAINT, sent|optional },
+ { WM_PAINT, sent },
+ { WM_GETMINMAXINFO, sent|optional },
+ { 0 }
+};
+/* ShowWindow(hwnd, SW_RESTORE) to an active minimized window */
+static const struct message WmShowRestoreActiveMinimizedOverlappedSeq[] =
+{
+ { HCBT_MINMAX, hook },
+ { WM_QUERYOPEN, sent },
+ { WM_GETTEXT, sent|optional },
+ { WM_NCACTIVATE, sent },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOSIZE|SWP_NOMOVE },
+ { WM_WINDOWPOSCHANGED, sent|wparam|optional, SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { WM_NCCALCSIZE, sent|optional },
+ { WM_MOVE, sent|optional },
+ { WM_SIZE, sent|optional },
+ { WM_GETTEXT, sent|optional },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_STATECHANGED },
+ { WM_GETMINMAXINFO, sent|defwinproc },
+ { WM_NCCALCSIZE, sent },
+ { WM_NCPAINT, sent },
+ { WM_GETTEXT, sent|defwinproc|optional },
+ { WM_ERASEBKGND, sent },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_STATECHANGED },
+ { WM_MOVE, sent|defwinproc },
+ { WM_SIZE, sent|defwinproc },
+ { WM_NCCALCSIZE, sent|optional },
+ { WM_NCPAINT, sent|optional },
+ { WM_ERASEBKGND, sent|optional },
+ { HCBT_SETFOCUS, hook },
+ { WM_SETFOCUS, sent },
+ /* Note this WM_ACTIVATE message even if the window is already active */
+ { WM_ACTIVATE, sent|wparam|lparam, WA_ACTIVE, 0 },
+ { WM_SYNCPAINT, sent|optional },
+ { WM_PAINT, sent },
+ { WM_GETMINMAXINFO, sent|optional },
+ { 0 }
+};
+/* ShowWindow(hwnd, SW_SHOWNOACTIVATE) to an active minimized window */
+static const struct message WmShowNoActivateActiveMinimizedOverlappedSeq[] =
+{
+ { HCBT_MINMAX, hook },
+ { WM_QUERYOPEN, sent },
+ { WM_GETTEXT, sent|optional },
+ { WM_NCACTIVATE, sent },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOSIZE|SWP_NOMOVE },
+ { WM_WINDOWPOSCHANGED, sent|wparam|optional, SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { WM_NCCALCSIZE, sent|optional },
+ { WM_MOVE, sent|optional },
+ { WM_SIZE, sent|optional },
+ { WM_GETTEXT, sent|optional },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOACTIVATE|SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_STATECHANGED },
+ { WM_GETMINMAXINFO, sent|defwinproc },
+ { WM_NCCALCSIZE, sent },
+ { WM_NCPAINT, sent },
+ { WM_GETTEXT, sent|defwinproc|optional },
+ { WM_ERASEBKGND, sent },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_STATECHANGED },
+ { WM_MOVE, sent|defwinproc },
+ { WM_SIZE, sent|defwinproc },
+ { WM_NCCALCSIZE, sent|optional },
+ { WM_NCPAINT, sent|optional },
+ { WM_ERASEBKGND, sent|optional },
+ /* Following optional messages are present on XP */
+ { HCBT_SETFOCUS, hook|optional },
+ { WM_SETFOCUS, sent|optional },
+ /* Note this WM_ACTIVATE message even if the window is already active and with flag SW_SHOWNOACTIVATE */
+ { WM_ACTIVATE, sent|wparam|lparam|optional, WA_ACTIVE, 0 },
+ { WM_SYNCPAINT, sent|optional },
+ { WM_PAINT, sent },
+ { WM_GETMINMAXINFO, sent|optional },
+ { 0 }
+};
/* CreateWindow (for a child popup window, not initially visible) */
static const struct message WmCreateChildPopupSeq[] = {
{ HCBT_CREATEWND, hook },
@@ -4661,7 +4800,7 @@ static void test_scroll_messages(HWND hwnd)
static void test_showwindow(void)
{
- HWND hwnd, hchild;
+ HWND hwnd, hwnd2, hchild;
RECT rc;
hwnd = CreateWindowExA(0, "TestWindowClass", "Test overlapped", WS_OVERLAPPEDWINDOW,
@@ -4798,6 +4937,68 @@ static void test_showwindow(void)
ok_sequence(WmShowVisMaxPopupSeq, "ShowWindow(SW_SHOWMAXIMIZED):popup", FALSE);
DestroyWindow(hwnd);
flush_sequence();
+
+ /* Test 5:
+ * 1. Restoring a minimized window.
+ */
+ hwnd = CreateWindowA("TestWindowClass", "window1", WS_VISIBLE | WS_OVERLAPPEDWINDOW, 0, 0, 100, 100, 0, 0, 0, 0);
+ ok(hwnd != NULL, "Failed to create window\n");
+
+ hwnd2 = CreateWindowA("static", "window2", WS_VISIBLE | WS_OVERLAPPEDWINDOW, 0, 0, 100, 100, 0, 0, 0, 0);
+ ok(hwnd2 != NULL, "Failed to create window\n");
+
+ ShowWindow(hwnd, SW_MINIMIZE);
+ SetActiveWindow(hwnd2);
+ ok(GetActiveWindow() == hwnd2, "Unexpected active window\n");
+ flush_events();
+ flush_sequence();
+ ShowWindow(hwnd, SW_RESTORE);
+ flush_events();
+ ok_sequence(WmShowRestoreMinimizedOverlappedSeq,
+ "ShowWindow(hwnd, SW_RESTORE): minimized overlapped", TRUE);
+
+ ShowWindow(hwnd, SW_MINIMIZE);
+ SetActiveWindow(hwnd2);
+ ok(GetActiveWindow() == hwnd2, "Unexpected active window\n");
+ flush_events();
+ flush_sequence();
+ ShowWindow(hwnd, SW_SHOWNOACTIVATE);
+ flush_events();
+ ok_sequence(WmShowNoActivateMinimizedOverlappedSeq,
+ "ShowWindow(hwnd, SW_SHOWNOACTIVATE): minimized overlapped", TRUE);
+
+ DestroyWindow(hwnd2);
+ DestroyWindow(hwnd);
+ flush_sequence();
+
+ /* Test 6:
+ * 1. Restoring a minimized but active window.
+ */
+ hwnd = CreateWindowA("TestWindowClass", "parent", WS_VISIBLE | WS_OVERLAPPEDWINDOW, 0, 0, 100, 100, 0, 0, 0, 0);
+ ok(hwnd != NULL, "Failed to create window\n");
+
+ ShowWindow(hwnd, SW_MINIMIZE);
+ SetActiveWindow(hwnd);
+ ok(GetActiveWindow() == hwnd, "Unexpected active window\n");
+ flush_events();
+ flush_sequence();
+ ShowWindow(hwnd, SW_RESTORE);
+ flush_events();
+ ok_sequence(WmShowRestoreActiveMinimizedOverlappedSeq,
+ "ShowWindow(hwnd, SW_RESTORE): active minimized overlapped", TRUE);
+
+ ShowWindow(hwnd, SW_MINIMIZE);
+ SetActiveWindow(hwnd);
+ ok(GetActiveWindow() == hwnd, "Unexpected active window\n");
+ flush_events();
+ flush_sequence();
+ ShowWindow(hwnd, SW_SHOWNOACTIVATE);
+ flush_events();
+ ok_sequence(WmShowNoActivateActiveMinimizedOverlappedSeq,
+ "ShowWindow(hwnd, SW_SHOWNOACTIVATE): active minimized overlapped", TRUE);
+
+ DestroyWindow(hwnd);
+ flush_sequence();
}
static void test_sys_menu(void)
@@ -15521,6 +15722,42 @@ static const struct message WmRestoreMinimizedOverlappedSeq[] =
{ 0 }
};
+/* DefWindowProcA(hwnd, WM_SYSCOMMAND, SC_RESTORE, 0) to an active minimized window */
+static const struct message WmRestoreActiveMinimizedOverlappedSeq[] =
+{
+ { HCBT_SYSCOMMAND, hook|wparam|lparam, SC_RESTORE, 0 },
+ { HCBT_MINMAX, hook },
+ { WM_QUERYOPEN, sent },
+ { WM_GETTEXT, sent|optional },
+ { WM_NCACTIVATE, sent },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOSIZE|SWP_NOMOVE },
+ { WM_WINDOWPOSCHANGED, sent|wparam|optional, SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { WM_NCCALCSIZE, sent|optional },
+ { WM_MOVE, sent|optional },
+ { WM_SIZE, sent|optional },
+ { WM_GETTEXT, sent|optional },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_STATECHANGED },
+ { WM_GETMINMAXINFO, sent|defwinproc },
+ { WM_NCCALCSIZE, sent },
+ { WM_NCPAINT, sent },
+ { WM_GETTEXT, sent|defwinproc|optional },
+ { WM_ERASEBKGND, sent },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_STATECHANGED },
+ { WM_MOVE, sent|defwinproc },
+ { WM_SIZE, sent|defwinproc },
+ { WM_NCCALCSIZE, sent|optional },
+ { WM_NCPAINT, sent|optional },
+ { WM_ERASEBKGND, sent|optional },
+ { HCBT_SETFOCUS, hook },
+ { WM_SETFOCUS, sent },
+ /* Note this WM_ACTIVATE messages even if the window is already active */
+ { WM_ACTIVATE, sent|wparam|lparam, WA_ACTIVE, 0 },
+ { WM_SYNCPAINT, sent|optional },
+ { WM_PAINT, sent },
+ { WM_GETMINMAXINFO, sent|optional },
+ { 0 }
+};
+
struct rbuttonup_thread_data
{
HWND hwnd;
@@ -15582,7 +15819,15 @@ static void test_defwinproc(void)
DefWindowProcA(hwnd, WM_SYSCOMMAND, SC_RESTORE, 0);
flush_events();
ok_sequence(WmRestoreMinimizedOverlappedSeq, "DefWindowProcA(SC_RESTORE):overlapped", TRUE);
+
+ ShowWindow(hwnd, SW_MINIMIZE);
+ SetActiveWindow(hwnd);
+ ok(GetActiveWindow() == hwnd, "Unexpected active window\n");
+ flush_events();
flush_sequence();
+ DefWindowProcA(hwnd, WM_SYSCOMMAND, SC_RESTORE, 0);
+ flush_events();
+ ok_sequence(WmRestoreActiveMinimizedOverlappedSeq, "DefWindowProcA(SC_RESTORE):active minimized overlapped", TRUE);
GetCursorPos(&pos);
GetWindowRect(hwnd, &rect);
diff --git a/dlls/user32/winpos.c b/dlls/user32/winpos.c
index aad5274973..7aca94e8fe 100644
--- a/dlls/user32/winpos.c
+++ b/dlls/user32/winpos.c
@@ -1182,7 +1182,14 @@ static BOOL show_window( HWND hwnd, INT cmd )
else WIN_ReleasePtr( wndPtr );
/* if previous state was minimized Windows sets focus to the window */
- if (style & WS_MINIMIZE) SetFocus( hwnd );
+ if (style & WS_MINIMIZE)
+ {
+ SetFocus( hwnd );
+ /* Send a WM_ACTIVATE message for a top level window, even if the window is already active */
+ style = GetWindowLongW( hwnd, GWL_STYLE );
+ if (!(style & WS_CHILD) && !(swp & SWP_NOACTIVATE))
+ SendMessageW( hwnd, WM_ACTIVATE, WA_ACTIVE, 0 );
+ }
done:
SetThreadDpiAwarenessContext( context );
--
2.24.0
Dec. 3, 2019
[PATCH 3/3] user32/tests: Use a higher resolution in test_ChangeDisplaySettingsEx().
by Zhiyi Zhang
640x480 is not supported on some Windows 10 versions.
Signed-off-by: Zhiyi Zhang <zzhang(a)codeweavers.com>
---
Fix https://test.winehq.org/data/5536df1ee1042c6cf20a7d96c43520003a547092/win10…
dlls/user32/tests/monitor.c | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/dlls/user32/tests/monitor.c b/dlls/user32/tests/monitor.c
index 4268dba9c5..567870e3d1 100644
--- a/dlls/user32/tests/monitor.c
+++ b/dlls/user32/tests/monitor.c
@@ -234,20 +234,20 @@ struct vid_mode
};
static const struct vid_mode vid_modes_test[] = {
- {640, 480, 0, 0, DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY, 0},
- {640, 480, 0, 0, DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY, 1},
- {640, 480, 0, 0, DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL , 0},
- {640, 480, 0, 0, DM_PELSWIDTH | DM_PELSHEIGHT , 1},
- {640, 480, 0, 0, DM_BITSPERPEL , 0},
- {640, 480, 0, 0, DM_DISPLAYFREQUENCY, 0},
+ {1024, 768, 0, 0, DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY, 0},
+ {1024, 768, 0, 0, DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY, 1},
+ {1024, 768, 0, 0, DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL , 0},
+ {1024, 768, 0, 0, DM_PELSWIDTH | DM_PELSHEIGHT , 1},
+ {1024, 768, 0, 0, DM_BITSPERPEL , 0},
+ {1024, 768, 0, 0, DM_DISPLAYFREQUENCY, 0},
{0, 0, 0, 0, DM_PELSWIDTH, 0},
{0, 0, 0, 0, DM_PELSHEIGHT, 0},
- {640, 480, 0, 0, DM_PELSWIDTH, 0},
- {640, 480, 0, 0, DM_PELSHEIGHT, 0},
- { 0, 480, 0, 0, DM_PELSWIDTH | DM_PELSHEIGHT, 0},
- {640, 0, 0, 0, DM_PELSWIDTH | DM_PELSHEIGHT, 0},
+ {1024, 768, 0, 0, DM_PELSWIDTH, 0},
+ {1024, 768, 0, 0, DM_PELSHEIGHT, 0},
+ { 0, 768, 0, 0, DM_PELSWIDTH | DM_PELSHEIGHT, 0},
+ {1024, 0, 0, 0, DM_PELSWIDTH | DM_PELSHEIGHT, 0},
/* the following test succeeds under XP SP3
{0, 0, 0, 0, DM_DISPLAYFREQUENCY, 0}
--
2.24.0
Dec. 3, 2019
[PATCH 2/3] user32/tests: Test cursor clip rectangles in per-monitor aware context.
by Zhiyi Zhang
So the tests don't fail in HiDPI environment.
Signed-off-by: Zhiyi Zhang <zzhang(a)codeweavers.com>
---
dlls/user32/tests/monitor.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/dlls/user32/tests/monitor.c b/dlls/user32/tests/monitor.c
index ca87a89deb..4268dba9c5 100644
--- a/dlls/user32/tests/monitor.c
+++ b/dlls/user32/tests/monitor.c
@@ -28,6 +28,7 @@
static HMODULE hdll;
static LONG (WINAPI *pGetDisplayConfigBufferSizes)(UINT32,UINT32*,UINT32*);
+static DPI_AWARENESS_CONTEXT (WINAPI *pSetThreadDpiAwarenessContext)(DPI_AWARENESS_CONTEXT);
static void init_function_pointers(void)
{
@@ -39,6 +40,7 @@ static void init_function_pointers(void)
trace("GetProcAddress(%s) failed\n", #func);
GET_PROC(GetDisplayConfigBufferSizes)
+ GET_PROC(SetThreadDpiAwarenessContext)
#undef GET_PROC
}
@@ -254,6 +256,7 @@ static const struct vid_mode vid_modes_test[] = {
static void test_ChangeDisplaySettingsEx(void)
{
+ DPI_AWARENESS_CONTEXT context = NULL;
DEVMODEA dm;
DEVMODEW dmW;
DWORD width;
@@ -328,6 +331,11 @@ static void test_ChangeDisplaySettingsEx(void)
res == DISP_CHANGE_BADMODE /* XP SP3 */,
"ChangeDisplaySettingsExW returned %d\n", res);
+ /* Test clip rectangle after resolution changes */
+ /* GetClipCursor always returns result in physical pixels but GetSystemMetrics(SM_CX/CYVIRTUALSCREEN) are not.
+ * Set per-monitor aware context so that virtual screen rectangles are in physical pixels */
+ if (pSetThreadDpiAwarenessContext)
+ context = pSetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE);
memset(&dm, 0, sizeof(dm));
dm.dmSize = sizeof(dm);
@@ -384,6 +392,8 @@ static void test_ChangeDisplaySettingsEx(void)
ClipCursor(&virt);
}
}
+ if (pSetThreadDpiAwarenessContext && context)
+ pSetThreadDpiAwarenessContext(context);
res = ChangeDisplaySettingsExA(NULL, NULL, NULL, CDS_RESET, NULL);
ok(res == DISP_CHANGE_SUCCESSFUL, "Failed to reset default resolution: %d\n", res);
}
--
2.24.0
Dec. 3, 2019
[PATCH 1/3] user32/tests: Import functions if possible.
by Zhiyi Zhang
Signed-off-by: Zhiyi Zhang <zzhang(a)codeweavers.com>
---
dlls/user32/tests/monitor.c | 126 +++++++++++-------------------------
1 file changed, 36 insertions(+), 90 deletions(-)
diff --git a/dlls/user32/tests/monitor.c b/dlls/user32/tests/monitor.c
index 04e63dc38d..ca87a89deb 100644
--- a/dlls/user32/tests/monitor.c
+++ b/dlls/user32/tests/monitor.c
@@ -27,15 +27,6 @@
#include <stdio.h>
static HMODULE hdll;
-static LONG (WINAPI *pChangeDisplaySettingsExA)(LPCSTR, LPDEVMODEA, HWND, DWORD, LPVOID);
-static LONG (WINAPI *pChangeDisplaySettingsExW)(LPCWSTR, LPDEVMODEW, HWND, DWORD, LPVOID);
-static BOOL (WINAPI *pEnumDisplayDevicesA)(LPCSTR,DWORD,LPDISPLAY_DEVICEA,DWORD);
-static BOOL (WINAPI *pEnumDisplayMonitors)(HDC,LPRECT,MONITORENUMPROC,LPARAM);
-static BOOL (WINAPI *pGetMonitorInfoA)(HMONITOR,LPMONITORINFO);
-static BOOL (WINAPI *pGetMonitorInfoW)(HMONITOR,LPMONITORINFO);
-static HMONITOR (WINAPI *pMonitorFromPoint)(POINT,DWORD);
-static HMONITOR (WINAPI *pMonitorFromRect)(LPCRECT,DWORD);
-static HMONITOR (WINAPI *pMonitorFromWindow)(HWND,DWORD);
static LONG (WINAPI *pGetDisplayConfigBufferSizes)(UINT32,UINT32*,UINT32*);
static void init_function_pointers(void)
@@ -47,16 +38,7 @@ static void init_function_pointers(void)
if(!p ## func) \
trace("GetProcAddress(%s) failed\n", #func);
- GET_PROC(ChangeDisplaySettingsExA)
- GET_PROC(ChangeDisplaySettingsExW)
- GET_PROC(EnumDisplayDevicesA)
- GET_PROC(EnumDisplayMonitors)
GET_PROC(GetDisplayConfigBufferSizes)
- GET_PROC(GetMonitorInfoA)
- GET_PROC(GetMonitorInfoW)
- GET_PROC(MonitorFromPoint)
- GET_PROC(MonitorFromRect)
- GET_PROC(MonitorFromWindow)
#undef GET_PROC
}
@@ -69,7 +51,7 @@ static BOOL CALLBACK monitor_enum_proc(HMONITOR hmon, HDC hdc, LPRECT lprc,
mi.cbSize = sizeof(mi);
- ok(pGetMonitorInfoA(hmon, (MONITORINFO*)&mi), "GetMonitorInfo failed\n");
+ ok(GetMonitorInfoA(hmon, (MONITORINFO*)&mi), "GetMonitorInfo failed\n");
if (mi.dwFlags & MONITORINFOF_PRIMARY)
strcpy(primary, mi.szDevice);
@@ -203,20 +185,14 @@ static void test_enumdisplaydevices(void)
int monitor_index;
BOOL ret;
- if (!pEnumDisplayDevicesA)
- {
- win_skip("EnumDisplayDevicesA is not available\n");
- return;
- }
-
/* Doesn't accept \\.\DISPLAY */
dd.cb = sizeof(dd);
- ret = pEnumDisplayDevicesA("\\\\.\\DISPLAY", 0, &dd, 0);
+ ret = EnumDisplayDevicesA("\\\\.\\DISPLAY", 0, &dd, 0);
ok(!ret, "Expect failure\n");
/* Enumeration */
for (flag_index = 0; flag_index < ARRAY_SIZE(flags); flag_index++)
- for (adapter_index = 0; pEnumDisplayDevicesA(NULL, adapter_index, &dd, flags[flag_index]); adapter_index++)
+ for (adapter_index = 0; EnumDisplayDevicesA(NULL, adapter_index, &dd, flags[flag_index]); adapter_index++)
{
lstrcpyA(adapter_name, dd.DeviceName);
@@ -228,7 +204,7 @@ static void test_enumdisplaydevices(void)
test_enumdisplaydevices_adapter(adapter_index, &dd, flags[flag_index]);
- for (monitor_index = 0; pEnumDisplayDevicesA(adapter_name, monitor_index, &dd, flags[flag_index]);
+ for (monitor_index = 0; EnumDisplayDevicesA(adapter_name, monitor_index, &dd, flags[flag_index]);
monitor_index++)
test_enumdisplaydevices_monitor(adapter_index, monitor_index, adapter_name, &dd, flags[flag_index]);
}
@@ -237,18 +213,12 @@ static void test_enumdisplaydevices(void)
/* XP on Testbot doesn't report a monitor, whereas XP on real machine does */
ok(broken(monitor_count == 0) || monitor_count > 0, "Expect at least one monitor found\n");
- if (!pEnumDisplayMonitors || !pGetMonitorInfoA)
- {
- win_skip("EnumDisplayMonitors or GetMonitorInfoA are not available\n");
- return;
- }
-
- ret = pEnumDisplayDevicesA(NULL, 0, &dd, 0);
+ ret = EnumDisplayDevicesA(NULL, 0, &dd, 0);
ok(ret, "Expect success\n");
lstrcpyA(primary_device_name, dd.DeviceName);
primary_monitor_device_name[0] = 0;
- ret = pEnumDisplayMonitors(NULL, NULL, monitor_enum_proc, (LPARAM)primary_monitor_device_name);
+ ret = EnumDisplayMonitors(NULL, NULL, monitor_enum_proc, (LPARAM)primary_monitor_device_name);
ok(ret, "EnumDisplayMonitors failed\n");
ok(!strcmp(primary_monitor_device_name, primary_device_name),
"monitor device name %s, device name %s\n", primary_monitor_device_name,
@@ -290,12 +260,6 @@ static void test_ChangeDisplaySettingsEx(void)
LONG res;
int i;
- if (!pChangeDisplaySettingsExA)
- {
- win_skip("ChangeDisplaySettingsExA is not available\n");
- return;
- }
-
SetLastError(0xdeadbeef);
res = EnumDisplaySettingsA(NULL, ENUM_CURRENT_SETTINGS, &dm);
ok(res, "EnumDisplaySettings error %u\n", GetLastError());
@@ -311,7 +275,7 @@ static void test_ChangeDisplaySettingsEx(void)
/* crashes under XP SP3 for large dmDriverExtra values */
dm.dmDriverExtra = 1;
- res = pChangeDisplaySettingsExA(NULL, &dm, NULL, CDS_TEST, NULL);
+ res = ChangeDisplaySettingsExA(NULL, &dm, NULL, CDS_TEST, NULL);
ok(res == DISP_CHANGE_SUCCESSFUL,
"ChangeDisplaySettingsExW returned %d, expected DISP_CHANGE_SUCCESSFUL\n", res);
ok(dm.dmDriverExtra == 1, "ChangeDisplaySettingsExA shouldn't reset dmDriverExtra to 0\n");
@@ -333,7 +297,7 @@ static void test_ChangeDisplaySettingsEx(void)
/* Apparently XP treats dmDriverExtra being != 0 as an error */
dmW.dmDriverExtra = 1;
- res = pChangeDisplaySettingsExW(NULL, &dmW, NULL, CDS_TEST, NULL);
+ res = ChangeDisplaySettingsExW(NULL, &dmW, NULL, CDS_TEST, NULL);
if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
{
ok(res == DISP_CHANGE_SUCCESSFUL,
@@ -347,7 +311,7 @@ static void test_ChangeDisplaySettingsEx(void)
memset(&dm, 0, sizeof(dm));
dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
dm.dmPelsWidth = width;
- res = pChangeDisplaySettingsExA(NULL, &dm, NULL, CDS_TEST, NULL);
+ res = ChangeDisplaySettingsExA(NULL, &dm, NULL, CDS_TEST, NULL);
ok(res == DISP_CHANGE_SUCCESSFUL ||
res == DISP_CHANGE_BADMODE || /* Win98, WinMe */
res == DISP_CHANGE_FAILED, /* NT4 */
@@ -357,7 +321,7 @@ static void test_ChangeDisplaySettingsEx(void)
dmW.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
dmW.dmPelsWidth = width;
SetLastError(0xdeadbeef);
- res = pChangeDisplaySettingsExW(NULL, &dmW, NULL, CDS_TEST, NULL);
+ res = ChangeDisplaySettingsExW(NULL, &dmW, NULL, CDS_TEST, NULL);
if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
ok(res == DISP_CHANGE_FAILED ||
res == DISP_CHANGE_BADPARAM || /* NT4 */
@@ -374,7 +338,7 @@ static void test_ChangeDisplaySettingsEx(void)
dm.dmBitsPerPel = vid_modes_test[i].bpp;
dm.dmDisplayFrequency = vid_modes_test[i].freq;
dm.dmFields = vid_modes_test[i].fields;
- res = pChangeDisplaySettingsExA(NULL, &dm, NULL, CDS_TEST, NULL);
+ res = ChangeDisplaySettingsExA(NULL, &dm, NULL, CDS_TEST, NULL);
ok(vid_modes_test[i].must_succeed ?
(res == DISP_CHANGE_SUCCESSFUL || res == DISP_CHANGE_RESTART) :
(res == DISP_CHANGE_SUCCESSFUL || res == DISP_CHANGE_RESTART ||
@@ -420,7 +384,7 @@ static void test_ChangeDisplaySettingsEx(void)
ClipCursor(&virt);
}
}
- res = pChangeDisplaySettingsExA(NULL, NULL, NULL, CDS_RESET, NULL);
+ res = ChangeDisplaySettingsExA(NULL, NULL, NULL, CDS_RESET, NULL);
ok(res == DISP_CHANGE_SUCCESSFUL, "Failed to reset default resolution: %d\n", res);
}
@@ -464,76 +428,64 @@ static void test_monitors(void)
{0xdeadbeef, FALSE},
};
- if (!pMonitorFromPoint || !pMonitorFromWindow || !pMonitorFromRect)
- {
- win_skip("MonitorFromPoint, MonitorFromWindow, or MonitorFromRect is not available\n");
- return;
- }
-
pt.x = pt.y = 0;
- primary = pMonitorFromPoint( pt, MONITOR_DEFAULTTOPRIMARY );
+ primary = MonitorFromPoint( pt, MONITOR_DEFAULTTOPRIMARY );
ok( primary != 0, "couldn't get primary monitor\n" );
- monitor = pMonitorFromWindow( 0, MONITOR_DEFAULTTONULL );
+ monitor = MonitorFromWindow( 0, MONITOR_DEFAULTTONULL );
ok( !monitor, "got %p, should not get a monitor for an invalid window\n", monitor );
- monitor = pMonitorFromWindow( 0, MONITOR_DEFAULTTOPRIMARY );
+ monitor = MonitorFromWindow( 0, MONITOR_DEFAULTTOPRIMARY );
ok( monitor == primary, "got %p, should get primary %p for MONITOR_DEFAULTTOPRIMARY\n", monitor, primary );
- monitor = pMonitorFromWindow( 0, MONITOR_DEFAULTTONEAREST );
+ monitor = MonitorFromWindow( 0, MONITOR_DEFAULTTONEAREST );
ok( monitor == primary, "got %p, should get primary %p for MONITOR_DEFAULTTONEAREST\n", monitor, primary );
SetRect( &rc, 0, 0, 1, 1 );
- monitor = pMonitorFromRect( &rc, MONITOR_DEFAULTTONULL );
+ monitor = MonitorFromRect( &rc, MONITOR_DEFAULTTONULL );
ok( monitor == primary, "got %p, should get primary %p\n", monitor, primary );
- monitor = pMonitorFromRect( &rc, MONITOR_DEFAULTTOPRIMARY );
+ monitor = MonitorFromRect( &rc, MONITOR_DEFAULTTOPRIMARY );
ok( monitor == primary, "got %p, should get primary %p\n", monitor, primary );
- monitor = pMonitorFromRect( &rc, MONITOR_DEFAULTTONEAREST );
+ monitor = MonitorFromRect( &rc, MONITOR_DEFAULTTONEAREST );
ok( monitor == primary, "got %p, should get primary %p\n", monitor, primary );
/* Empty rect at 0,0 is considered inside the primary monitor */
SetRect( &rc, 0, 0, -1, -1 );
- monitor = pMonitorFromRect( &rc, MONITOR_DEFAULTTONULL );
+ monitor = MonitorFromRect( &rc, MONITOR_DEFAULTTONULL );
ok( monitor == primary, "got %p, should get primary %p\n", monitor, primary );
/* Even if there is a monitor left of the primary, the primary will have the most overlapping area */
SetRect( &rc, -1, 0, 2, 1 );
- monitor = pMonitorFromRect( &rc, MONITOR_DEFAULTTONULL );
+ monitor = MonitorFromRect( &rc, MONITOR_DEFAULTTONULL );
ok( monitor == primary, "got %p, should get primary %p\n", monitor, primary );
/* But the width of the rect doesn't matter if it's empty. */
SetRect( &rc, -1, 0, 2, -1 );
- monitor = pMonitorFromRect( &rc, MONITOR_DEFAULTTONULL );
+ monitor = MonitorFromRect( &rc, MONITOR_DEFAULTTONULL );
ok( monitor != primary, "got primary %p\n", monitor );
- if (!pGetMonitorInfoA)
- {
- win_skip("GetMonitorInfoA is not available\n");
- return;
- }
-
/* Search for a monitor that has no others equally near to (left, top-1) */
SetRect( &rc, -1, -2, 2, 0 );
- monitor = pMonitorFromRect( &rc, MONITOR_DEFAULTTONULL );
+ monitor = MonitorFromRect( &rc, MONITOR_DEFAULTTONULL );
nearest = primary;
while (monitor != NULL)
{
ok( monitor != primary, "got primary %p\n", monitor );
nearest = monitor;
mi.cbSize = sizeof(mi);
- ret = pGetMonitorInfoA( monitor, &mi );
+ ret = GetMonitorInfoA( monitor, &mi );
ok( ret, "GetMonitorInfo failed\n" );
SetRect( &rc, mi.rcMonitor.left-1, mi.rcMonitor.top-2, mi.rcMonitor.left+2, mi.rcMonitor.top );
- monitor = pMonitorFromRect( &rc, MONITOR_DEFAULTTONULL );
+ monitor = MonitorFromRect( &rc, MONITOR_DEFAULTTONULL );
}
/* tests for cbSize in MONITORINFO */
- monitor = pMonitorFromWindow( 0, MONITOR_DEFAULTTOPRIMARY );
+ monitor = MonitorFromWindow( 0, MONITOR_DEFAULTTOPRIMARY );
for (i = 0; i < ARRAY_SIZE(testdatami); i++)
{
memset( &mi, 0, sizeof(mi) );
mi.cbSize = testdatami[i].cbSize;
- ret = pGetMonitorInfoA( monitor, &mi );
+ ret = GetMonitorInfoA( monitor, &mi );
ok( ret == testdatami[i].ret, "GetMonitorInfo returned wrong value\n" );
if (ret)
ok( (mi.dwFlags & MONITORINFOF_PRIMARY), "MONITORINFOF_PRIMARY flag isn't set\n" );
@@ -542,7 +494,7 @@ static void test_monitors(void)
memset( &miexw, 0, sizeof(miexw) );
miexw.cbSize = testdatamiexw[i].cbSize;
- ret = pGetMonitorInfoW( monitor, (LPMONITORINFO)&miexw );
+ ret = GetMonitorInfoW( monitor, (LPMONITORINFO)&miexw );
ok( ret == testdatamiexw[i].ret, "GetMonitorInfo returned wrong value\n" );
if (ret)
ok( (miexw.dwFlags & MONITORINFOF_PRIMARY), "MONITORINFOF_PRIMARY flag isn't set\n" );
@@ -555,7 +507,7 @@ static void test_monitors(void)
{
memset( &miexa, 0, sizeof(miexa) );
miexa.cbSize = testdatamiexa[i].cbSize;
- ret = pGetMonitorInfoA( monitor, (LPMONITORINFO)&miexa );
+ ret = GetMonitorInfoA( monitor, (LPMONITORINFO)&miexa );
ok( ret == testdatamiexa[i].ret, "GetMonitorInfo returned wrong value\n" );
if (ret)
ok( (miexa.dwFlags & MONITORINFOF_PRIMARY), "MONITORINFOF_PRIMARY flag isn't set\n" );
@@ -568,7 +520,7 @@ static void test_monitors(void)
{
memset( &miexw, 0, sizeof(miexw) );
miexw.cbSize = testdatamiexw[i].cbSize;
- ret = pGetMonitorInfoW( monitor, (LPMONITORINFO)&miexw );
+ ret = GetMonitorInfoW( monitor, (LPMONITORINFO)&miexw );
ok( ret == testdatamiexw[i].ret, "GetMonitorInfo returned wrong value\n" );
if (ret)
ok( (miexw.dwFlags & MONITORINFOF_PRIMARY), "MONITORINFOF_PRIMARY flag isn't set\n" );
@@ -578,13 +530,13 @@ static void test_monitors(void)
SetRect( &rc, rc.left+1, rc.top+1, rc.left+2, rc.top+2 );
- monitor = pMonitorFromRect( &rc, MONITOR_DEFAULTTONULL );
+ monitor = MonitorFromRect( &rc, MONITOR_DEFAULTTONULL );
ok( monitor == NULL, "got %p\n", monitor );
- monitor = pMonitorFromRect( &rc, MONITOR_DEFAULTTOPRIMARY );
+ monitor = MonitorFromRect( &rc, MONITOR_DEFAULTTOPRIMARY );
ok( monitor == primary, "got %p, should get primary %p\n", monitor, primary );
- monitor = pMonitorFromRect( &rc, MONITOR_DEFAULTTONEAREST );
+ monitor = MonitorFromRect( &rc, MONITOR_DEFAULTTONEAREST );
ok( monitor == nearest, "got %p, should get nearest %p\n", monitor, nearest );
}
@@ -594,7 +546,7 @@ static BOOL CALLBACK find_primary_mon(HMONITOR hmon, HDC hdc, LPRECT rc, LPARAM
BOOL ret;
mi.cbSize = sizeof(mi);
- ret = pGetMonitorInfoA(hmon, &mi);
+ ret = GetMonitorInfoA(hmon, &mi);
ok(ret, "GetMonitorInfo failed\n");
if (mi.dwFlags & MONITORINFOF_PRIMARY)
{
@@ -613,19 +565,13 @@ static void test_work_area(void)
WINDOWPLACEMENT wp;
BOOL ret;
- if (!pEnumDisplayMonitors || !pGetMonitorInfoA)
- {
- win_skip("EnumDisplayMonitors or GetMonitorInfoA are not available\n");
- return;
- }
-
hmon = 0;
- ret = pEnumDisplayMonitors(NULL, NULL, find_primary_mon, (LPARAM)&hmon);
+ ret = EnumDisplayMonitors(NULL, NULL, find_primary_mon, (LPARAM)&hmon);
ok(!ret && hmon != 0, "Failed to find primary monitor\n");
mi.cbSize = sizeof(mi);
SetLastError(0xdeadbeef);
- ret = pGetMonitorInfoA(hmon, &mi);
+ ret = GetMonitorInfoA(hmon, &mi);
ok(ret, "GetMonitorInfo error %u\n", GetLastError());
ok(mi.dwFlags & MONITORINFOF_PRIMARY, "not a primary monitor\n");
trace("primary monitor %s\n", wine_dbgstr_rect(&mi.rcMonitor));
--
2.24.0
Dec. 3, 2019
Re: [PATCH 4/5] msvcrt: Create global lc_time_curr and use when possible.
by Piotr Caban
Hi Jeff,
Could you please rename the MSVCRT_time_curr to something like
time_data_c so the name shows it's for C locale?
It also needs to be freed on dll unload. On the other hand it may better
to initialize it statically.
Thanks,
Piotr
Dec. 3, 2019
Re: [PATCH 2/5] msvcrt: Refactor failure code for create_locinfo.
by Piotr Caban
Hi Jeff,
On 12/2/19 6:52 PM, Jeff Smith wrote:
> - if(!locinfo->lconv) {
> - free_locinfo(locinfo);
> - return NULL;
> - }
> + if(!locinfo->lconv)
> + goto failed;
I'm not sure if it's an improvement. I think I would prefer the code to
stay as it is.
Thanks,
Piotr
Dec. 3, 2019
Re: [PATCH 1/5] ucrtbase: Improve strftime special test cases for %c %r formats.
by Piotr Caban
Hi Jeff,
On 12/2/19 6:52 PM, Jeff Smith wrote:
> + locale = p_setlocale(LC_ALL, NULL);
> + p_setlocale(LC_ALL, "fr-FR");
setlocale may fail if the system is not supporting given locale. Please
skip the test in this case.
> + ret = p_strftime(buf, sizeof(buf), "%c", &epoch);
> + ok(ret == strlen(expected), "ret = %d\n", ret);
> + ok(!strcmp(buf, expected), "buf = \"%s\", expected \"%s\"\n", buf, expected);
> + ret = p_strftime(buf, sizeof(buf), "%x %r", &epoch);
> + todo_wine ok(ret == strlen(expected), "ret = %d\n", ret);
> + todo_wine ok(!strcmp(buf, expected), "buf = \"%s\", expected \"%s\"\n", buf, expected);
Please change it to:
ok(ret == strlen(buf), "ret = %d\n", ret);
todo_wine ok(!strcmp(buf, "01/01/1970 00:00:00"), ...);
It makes the expected variable not needed. I think the test is easier to
read this way.
> + p_setlocale(LC_ALL, locale);
Please restore C locale after the test (there's no need to store
previous locale).
Thanks,
Piotr
Dec. 3, 2019
Re: [PATCH v2 6/6] wined3d: Store the blend factor in the stateblock as a render state.
by Henri Verbeet
Signed-off-by: Henri Verbeet <hverbeet(a)codeweavers.com>
Dec. 3, 2019
[PATCH] mshtml/tests: Fix a typo in ok() message.
by Andrey Gusev
Signed-off-by: Andrey Gusev <andrey.goosev(a)gmail.com>
---
dlls/mshtml/tests/htmldoc.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/mshtml/tests/htmldoc.c b/dlls/mshtml/tests/htmldoc.c
index 4b55fdd0c8..141d2378db 100644
--- a/dlls/mshtml/tests/htmldoc.c
+++ b/dlls/mshtml/tests/htmldoc.c
@@ -2879,7 +2879,7 @@ static HRESULT WINAPI OleCommandTarget_Exec(IOleCommandTarget *iface, const GUID
readystate_set_interactive = (load_state != LD_INTERACTIVE);
break;
default:
- ok(0, "unexpevted V_I4(pvaIn)=%d\n", V_I4(pvaIn));
+ ok(0, "unexpected V_I4(pvaIn)=%d\n", V_I4(pvaIn));
}
return S_OK;
--
2.23.0
Dec. 3, 2019
[PATCH] ieframe/tests: Fix a typo in ok() message.
by Andrey Gusev
Signed-off-by: Andrey Gusev <andrey.goosev(a)gmail.com>
---
dlls/ieframe/tests/webbrowser.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/ieframe/tests/webbrowser.c b/dlls/ieframe/tests/webbrowser.c
index a99c68ac5e..f89b2c5b57 100644
--- a/dlls/ieframe/tests/webbrowser.c
+++ b/dlls/ieframe/tests/webbrowser.c
@@ -420,7 +420,7 @@ static HRESULT WINAPI OleCommandTarget_Exec(IOleCommandTarget *iface, const GUID
CHECK_EXPECT2(Exec_SETDOWNLOADSTATE_1);
break;
default:
- ok(0, "unexpevted V_I4(pvaIn)=%d\n", V_I4(pvaIn));
+ ok(0, "unexpected V_I4(pvaIn)=%d\n", V_I4(pvaIn));
}
return S_OK;
case OLECMDID_UPDATECOMMANDS:
--
2.23.0
Dec. 3, 2019
[PATCH 2/2] mfplat: Set origin name attribute for stream based bytestreams too.
by Nikolay Sivov
Signed-off-by: Nikolay Sivov <nsivov(a)codeweavers.com>
---
dlls/mfplat/main.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/dlls/mfplat/main.c b/dlls/mfplat/main.c
index fc656678ca..910b923542 100644
--- a/dlls/mfplat/main.c
+++ b/dlls/mfplat/main.c
@@ -3245,6 +3245,7 @@ HRESULT WINAPI MFCreateMFByteStreamOnStream(IStream *stream, IMFByteStream **byt
{
struct bytestream *object;
LARGE_INTEGER position;
+ STATSTG stat;
HRESULT hr;
TRACE("%p, %p.\n", stream, bytestream);
@@ -3271,6 +3272,16 @@ HRESULT WINAPI MFCreateMFByteStreamOnStream(IStream *stream, IMFByteStream **byt
position.QuadPart = 0;
IStream_Seek(object->stream, position, STREAM_SEEK_SET, NULL);
+ if (SUCCEEDED(IStream_Stat(object->stream, &stat, 0)))
+ {
+ if (stat.pwcsName)
+ {
+ IMFAttributes_SetString(&object->attributes.IMFAttributes_iface, &MF_BYTESTREAM_ORIGIN_NAME,
+ stat.pwcsName);
+ CoTaskMemFree(stat.pwcsName);
+ }
+ }
+
*bytestream = &object->IMFByteStream_iface;
return S_OK;
--
2.24.0
Dec. 3, 2019
[PATCH 1/2] mfplat: Use underlying stream access mode for bytestream capabilities.
by Nikolay Sivov
Signed-off-by: Nikolay Sivov <nsivov(a)codeweavers.com>
---
dlls/mfplat/main.c | 21 +++++++++++++++++++--
1 file changed, 19 insertions(+), 2 deletions(-)
diff --git a/dlls/mfplat/main.c b/dlls/mfplat/main.c
index e4ade7f202..fc656678ca 100644
--- a/dlls/mfplat/main.c
+++ b/dlls/mfplat/main.c
@@ -2700,6 +2700,24 @@ static ULONG WINAPI bytestream_Release(IMFByteStream *iface)
return refcount;
}
+static HRESULT WINAPI bytestream_stream_GetCapabilities(IMFByteStream *iface, DWORD *capabilities)
+{
+ struct bytestream *stream = impl_from_IMFByteStream(iface);
+ STATSTG stat;
+ HRESULT hr;
+
+ TRACE("%p, %p.\n", iface, capabilities);
+
+ if (FAILED(hr = IStream_Stat(stream->stream, &stat, STATFLAG_NONAME)))
+ return hr;
+
+ *capabilities = MFBYTESTREAM_IS_READABLE | MFBYTESTREAM_IS_SEEKABLE;
+ if (stat.grfMode & (STGM_WRITE | STGM_READWRITE))
+ *capabilities |= MFBYTESTREAM_IS_WRITABLE;
+
+ return S_OK;
+}
+
static HRESULT WINAPI bytestream_GetCapabilities(IMFByteStream *iface, DWORD *capabilities)
{
struct bytestream *stream = impl_from_IMFByteStream(iface);
@@ -3060,7 +3078,7 @@ static const IMFByteStreamVtbl bytestream_stream_vtbl =
bytestream_QueryInterface,
bytestream_AddRef,
bytestream_Release,
- bytestream_GetCapabilities,
+ bytestream_stream_GetCapabilities,
bytestream_stream_GetLength,
bytestream_stream_SetLength,
bytestream_stream_GetCurrentPosition,
@@ -3252,7 +3270,6 @@ HRESULT WINAPI MFCreateMFByteStreamOnStream(IStream *stream, IMFByteStream **byt
IStream_AddRef(object->stream);
position.QuadPart = 0;
IStream_Seek(object->stream, position, STREAM_SEEK_SET, NULL);
- object->capabilities = MFBYTESTREAM_IS_READABLE | MFBYTESTREAM_IS_SEEKABLE;
*bytestream = &object->IMFByteStream_iface;
--
2.24.0
Dec. 3, 2019
[PATCH 2/2] ntdll: Add support for querying thread suspend count.
by Nikolay Sivov
Signed-off-by: Nikolay Sivov <nsivov(a)codeweavers.com>
---
x64dbg debugger uses this info class, checking suspend status periodically.
When unavailable, it repeatedly suspends/resumes threads as a fallback to
get NtSuspendThread() returned counter, which is not ideal.
dlls/ntdll/tests/exception.c | 63 +++++++++++++++++++++++++++++++++-
dlls/ntdll/thread.c | 21 ++++++++++++
include/wine/server_protocol.h | 4 ++-
include/winternl.h | 1 +
server/protocol.def | 1 +
server/request.h | 5 +--
server/thread.c | 1 +
server/trace.c | 1 +
8 files changed, 93 insertions(+), 4 deletions(-)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c
index fb58c0ee7a..44df47466b 100644
--- a/dlls/ntdll/tests/exception.c
+++ b/dlls/ntdll/tests/exception.c
@@ -50,6 +50,7 @@ static ULONG (WINAPI *pRtlRemoveVectoredContinueHandler)(PVOID handler);
static NTSTATUS (WINAPI *pNtReadVirtualMemory)(HANDLE, const void*, void*, SIZE_T, SIZE_T*);
static NTSTATUS (WINAPI *pNtTerminateProcess)(HANDLE handle, LONG exit_code);
static NTSTATUS (WINAPI *pNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
+static NTSTATUS (WINAPI *pNtQueryInformationThread)(HANDLE, THREADINFOCLASS, PVOID, ULONG, PULONG);
static NTSTATUS (WINAPI *pNtSetInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG);
static BOOL (WINAPI *pIsWow64Process)(HANDLE, PBOOL);
static NTSTATUS (WINAPI *pNtClose)(HANDLE);
@@ -3163,11 +3164,34 @@ static DWORD WINAPI suspend_thread_test( void *arg )
return 0;
}
+static void test_suspend_count(HANDLE hthread, ULONG expected_count, int line)
+{
+ static BOOL supported = TRUE;
+ NTSTATUS status;
+ ULONG count;
+
+ if (!supported)
+ return;
+
+ count = ~0u;
+ status = pNtQueryInformationThread(hthread, ThreadSuspendCount, &count, sizeof(count), NULL);
+ if (status)
+ {
+ win_skip("ThreadSuspendCount is not supported.\n");
+ supported = FALSE;
+ return;
+ }
+
+ ok_(__FILE__, line)(!status, "Failed to get suspend count, status %#x.\n", status);
+ ok_(__FILE__, line)(count == expected_count, "Unexpected suspend count %u.\n", count);
+}
+
static void test_suspend_thread(void)
{
+#define TEST_SUSPEND_COUNT(thread, count) test_suspend_count((thread), (count), __LINE__)
HANDLE thread, event;
+ ULONG count, len;
NTSTATUS status;
- ULONG count;
DWORD ret;
status = NtSuspendThread(0, NULL);
@@ -3184,6 +3208,31 @@ static void test_suspend_thread(void)
ret = WaitForSingleObject(thread, 0);
ok(ret == WAIT_TIMEOUT, "Unexpected status %d.\n", ret);
+ status = pNtQueryInformationThread(thread, ThreadSuspendCount, &count, sizeof(count), NULL);
+ if (!status)
+ {
+ status = pNtQueryInformationThread(thread, ThreadSuspendCount, NULL, sizeof(count), NULL);
+ ok(status == STATUS_ACCESS_VIOLATION, "Unexpected status %#x.\n", status);
+
+ status = pNtQueryInformationThread(thread, ThreadSuspendCount, &count, sizeof(count) / 2, NULL);
+ ok(status == STATUS_INFO_LENGTH_MISMATCH, "Unexpected status %#x.\n", status);
+
+ len = 123;
+ status = pNtQueryInformationThread(thread, ThreadSuspendCount, &count, sizeof(count) / 2, &len);
+ ok(status == STATUS_INFO_LENGTH_MISMATCH, "Unexpected status %#x.\n", status);
+ ok(len == 123, "Unexpected info length %u.\n", len);
+
+ len = 123;
+ status = pNtQueryInformationThread(thread, ThreadSuspendCount, NULL, 0, &len);
+ ok(status == STATUS_INFO_LENGTH_MISMATCH, "Unexpected status %#x.\n", status);
+ ok(len == 123, "Unexpected info length %u.\n", len);
+
+ count = 10;
+ status = pNtQueryInformationThread(0, ThreadSuspendCount, &count, sizeof(count), NULL);
+ ok(status, "Unexpected status %#x.\n", status);
+ ok(count == 10, "Unexpected suspend count %u.\n", count);
+ }
+
status = NtResumeThread(thread, NULL);
ok(!status, "Unexpected status %#x.\n", status);
@@ -3191,24 +3240,35 @@ static void test_suspend_thread(void)
ok(!status, "Unexpected status %#x.\n", status);
ok(count == 0, "Unexpected suspended count %u.\n", count);
+ TEST_SUSPEND_COUNT(thread, 0);
+
status = NtSuspendThread(thread, NULL);
ok(!status, "Failed to suspend a thread, status %#x.\n", status);
+ TEST_SUSPEND_COUNT(thread, 1);
+
status = NtSuspendThread(thread, &count);
ok(!status, "Failed to suspend a thread, status %#x.\n", status);
ok(count == 1, "Unexpected suspended count %u.\n", count);
+ TEST_SUSPEND_COUNT(thread, 2);
+
status = NtResumeThread(thread, &count);
ok(!status, "Failed to resume a thread, status %#x.\n", status);
ok(count == 2, "Unexpected suspended count %u.\n", count);
+ TEST_SUSPEND_COUNT(thread, 1);
+
status = NtResumeThread(thread, NULL);
ok(!status, "Failed to resume a thread, status %#x.\n", status);
+ TEST_SUSPEND_COUNT(thread, 0);
+
SetEvent(event);
WaitForSingleObject(thread, INFINITE);
CloseHandle(thread);
+#undef TEST_SUSPEND_COUNT
}
static const char *suspend_process_event_name = "suspend_process_event";
@@ -3418,6 +3478,7 @@ START_TEST(exception)
X(RtlAddVectoredContinueHandler);
X(RtlRemoveVectoredContinueHandler);
X(NtQueryInformationProcess);
+ X(NtQueryInformationThread);
X(NtSetInformationProcess);
X(NtSuspendProcess);
X(NtResumeProcess);
diff --git a/dlls/ntdll/thread.c b/dlls/ntdll/thread.c
index fd8d388403..f993dd314c 100644
--- a/dlls/ntdll/thread.c
+++ b/dlls/ntdll/thread.c
@@ -1115,6 +1115,27 @@ NTSTATUS WINAPI NtQueryInformationThread( HANDLE handle, THREADINFOCLASS class,
*(BOOL*)data = FALSE;
if (ret_len) *ret_len = sizeof(BOOL);
return STATUS_SUCCESS;
+ case ThreadSuspendCount:
+ {
+ ULONG count = 0;
+
+ if (length != sizeof(ULONG)) return STATUS_INFO_LENGTH_MISMATCH;
+ if (!data) return STATUS_ACCESS_VIOLATION;
+
+ SERVER_START_REQ( get_thread_info )
+ {
+ req->handle = wine_server_obj_handle( handle );
+ req->tid_in = 0;
+ if (!(status = wine_server_call( req )))
+ count = reply->suspend_count;
+ }
+ SERVER_END_REQ;
+
+ if (!status)
+ *(ULONG *)data = count;
+
+ return status;
+ }
case ThreadDescription:
{
THREAD_DESCRIPTION_INFORMATION *info = data;
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index 108701b2bc..aaa5fd2e33 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -1006,8 +1006,10 @@ struct get_thread_info_reply
int exit_code;
int priority;
int last;
+ int suspend_count;
data_size_t desc_len;
/* VARARG(desc,unicode_str); */
+ char __pad_60[4];
};
@@ -6700,6 +6702,6 @@ union generic_reply
struct resume_process_reply resume_process_reply;
};
-#define SERVER_PROTOCOL_VERSION 592
+#define SERVER_PROTOCOL_VERSION 593
#endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/include/winternl.h b/include/winternl.h
index 311fba3ae9..83449b80c7 100644
--- a/include/winternl.h
+++ b/include/winternl.h
@@ -1008,6 +1008,7 @@ typedef enum _THREADINFOCLASS {
ThreadUmsInformation,
ThreadCounterProfiling,
ThreadIdealProcessorEx,
+ ThreadSuspendCount = 35,
ThreadDescription = 38,
MaxThreadInfoClass
} THREADINFOCLASS;
diff --git a/server/protocol.def b/server/protocol.def
index 566bc83bb2..1cb1fea602 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -944,6 +944,7 @@ struct rawinput_device
int exit_code; /* thread exit code */
int priority; /* thread priority level */
int last; /* last thread in process */
+ int suspend_count; /* thread suspend count */
data_size_t desc_len; /* description length in bytes */
VARARG(desc,unicode_str); /* description string */
@END
diff --git a/server/request.h b/server/request.h
index de7720ae68..2930b31d72 100644
--- a/server/request.h
+++ b/server/request.h
@@ -849,8 +849,9 @@ C_ASSERT( FIELD_OFFSET(struct get_thread_info_reply, affinity) == 32 );
C_ASSERT( FIELD_OFFSET(struct get_thread_info_reply, exit_code) == 40 );
C_ASSERT( FIELD_OFFSET(struct get_thread_info_reply, priority) == 44 );
C_ASSERT( FIELD_OFFSET(struct get_thread_info_reply, last) == 48 );
-C_ASSERT( FIELD_OFFSET(struct get_thread_info_reply, desc_len) == 52 );
-C_ASSERT( sizeof(struct get_thread_info_reply) == 56 );
+C_ASSERT( FIELD_OFFSET(struct get_thread_info_reply, suspend_count) == 52 );
+C_ASSERT( FIELD_OFFSET(struct get_thread_info_reply, desc_len) == 56 );
+C_ASSERT( sizeof(struct get_thread_info_reply) == 64 );
C_ASSERT( FIELD_OFFSET(struct get_thread_times_request, handle) == 12 );
C_ASSERT( sizeof(struct get_thread_times_request) == 16 );
C_ASSERT( FIELD_OFFSET(struct get_thread_times_reply, creation_time) == 8 );
diff --git a/server/thread.c b/server/thread.c
index 4fc6abf0ef..80db41b48d 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -1463,6 +1463,7 @@ DECL_HANDLER(get_thread_info)
reply->priority = thread->priority;
reply->affinity = thread->affinity;
reply->last = thread->process->running_threads == 1;
+ reply->suspend_count = thread->suspend;
reply->desc_len = thread->desc_len;
if (thread->desc && get_reply_max_size())
diff --git a/server/trace.c b/server/trace.c
index d44f67a021..411369a4f6 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -1423,6 +1423,7 @@ static void dump_get_thread_info_reply( const struct get_thread_info_reply *req
fprintf( stderr, ", exit_code=%d", req->exit_code );
fprintf( stderr, ", priority=%d", req->priority );
fprintf( stderr, ", last=%d", req->last );
+ fprintf( stderr, ", suspend_count=%d", req->suspend_count );
fprintf( stderr, ", desc_len=%u", req->desc_len );
dump_varargs_unicode_str( ", desc=", cur_size );
}
--
2.24.0
Dec. 3, 2019
[PATCH 1/2] ntdll: Use better type for thread description info structure.
by Nikolay Sivov
Signed-off-by: Nikolay Sivov <nsivov(a)codeweavers.com>
---
I didn't realize UNICODE_STRING lengths are 2-byte.
dlls/kernel32/tests/thread.c | 28 ++++++++++++++--------------
dlls/kernelbase/thread.c | 14 ++++++--------
dlls/ntdll/thread.c | 12 +++++-------
include/winternl.h | 3 +--
4 files changed, 26 insertions(+), 31 deletions(-)
diff --git a/dlls/kernel32/tests/thread.c b/dlls/kernel32/tests/thread.c
index 8ff34ba5f3..9a8ad9151c 100644
--- a/dlls/kernel32/tests/thread.c
+++ b/dlls/kernel32/tests/thread.c
@@ -2145,14 +2145,15 @@ static void test_thread_description(void)
ok(len == sizeof(*thread_desc), "Unexpected structure length %u.\n", len);
len2 = 0;
- thread_desc->Length = 1;
- thread_desc->Description = (WCHAR *)thread_desc;
+ thread_desc->Description.Length = 1;
+ thread_desc->Description.MaximumLength = 0;
+ thread_desc->Description.Buffer = (WCHAR *)thread_desc;
status = pNtQueryInformationThread(GetCurrentThread(), ThreadDescription, thread_desc, len, &len2);
ok(!status, "Failed to get thread info, status %#x.\n", status);
ok(len2 == sizeof(*thread_desc), "Unexpected structure length %u.\n", len);
- ok(!thread_desc->Length, "Unexpected description length %#x.\n", thread_desc->Length);
- ok(thread_desc->Description == (WCHAR *)(thread_desc + 1), "Unexpected description string pointer %p, %p.\n",
- thread_desc->Description, thread_desc);
+ ok(!thread_desc->Description.Length, "Unexpected description length %#x.\n", thread_desc->Description.Length);
+ ok(thread_desc->Description.Buffer == (WCHAR *)(thread_desc + 1),
+ "Unexpected description string pointer %p, %p.\n", thread_desc->Description.Buffer, thread_desc);
hr = pSetThreadDescription(GetCurrentThread(), NULL);
ok(hr == HRESULT_FROM_NT(STATUS_SUCCESS), "Failed to set thread description, hr %#x.\n", hr);
@@ -2176,11 +2177,11 @@ static void test_thread_description(void)
ok(!status, "Failed to get thread info.\n");
ok(len == sizeof(*thread_desc) + desc_len, "Unexpected structure length %u.\n", len);
- ok(thread_desc->Length == (desc_len << 16 | desc_len), "Unexpected description length %#x.\n",
- thread_desc->Length);
- ok(thread_desc->Description == (WCHAR *)(thread_desc + 1), "Unexpected description string pointer %p, %p.\n",
- thread_desc->Description, thread_desc);
- ok(!memcmp(thread_desc->Description, desc, desc_len), "Unexpected description string.\n");
+ ok(thread_desc->Description.Length == desc_len && thread_desc->Description.MaximumLength == desc_len,
+ "Unexpected description length %u.\n", thread_desc->Description.Length);
+ ok(thread_desc->Description.Buffer == (WCHAR *)(thread_desc + 1),
+ "Unexpected description string pointer %p, %p.\n", thread_desc->Description.Buffer, thread_desc);
+ ok(!memcmp(thread_desc->Description.Buffer, desc, desc_len), "Unexpected description string.\n");
/* Partial results. */
len = 0;
@@ -2193,7 +2194,7 @@ static void test_thread_description(void)
ok(len == sizeof(*thread_desc) + desc_len, "Unexpected structure length %u.\n", len);
/* Change description. */
- thread_desc->Length = 8 << 16 | 8;
+ thread_desc->Description.Length = thread_desc->Description.MaximumLength = 8;
lstrcpyW((WCHAR *)(thread_desc + 1), L"desc");
status = pNtSetInformationThread(GetCurrentThread(), ThreadDescription, thread_desc, sizeof(*thread_desc));
@@ -2211,7 +2212,7 @@ static void test_thread_description(void)
status = NtSetInformationThread(GetCurrentThread(), ThreadDescription, NULL, sizeof(*thread_desc));
ok(status == STATUS_ACCESS_VIOLATION, "Unexpected status %#x.\n", status);
- thread_desc->Description = NULL;
+ thread_desc->Description.Buffer = NULL;
status = pNtSetInformationThread(GetCurrentThread(), ThreadDescription, thread_desc, sizeof(*thread_desc));
ok(status == STATUS_ACCESS_VIOLATION, "Unexpected status %#x.\n", status);
@@ -2228,8 +2229,7 @@ static void test_thread_description(void)
hr = pSetThreadDescription(GetCurrentThread(), L"123");
ok(hr == HRESULT_FROM_NT(STATUS_SUCCESS), "Failed to set thread description, hr %#x.\n", hr);
- thread_desc->Length = 0;
- thread_desc->Description = NULL;
+ memset(thread_desc, 0, sizeof(*thread_desc));
status = pNtSetInformationThread(GetCurrentThread(), ThreadDescription, thread_desc, sizeof(*thread_desc));
ok(!status, "Failed to set thread description, status %#x.\n", status);
diff --git a/dlls/kernelbase/thread.c b/dlls/kernelbase/thread.c
index 521d3be079..2f0bcb7dde 100644
--- a/dlls/kernelbase/thread.c
+++ b/dlls/kernelbase/thread.c
@@ -404,8 +404,8 @@ HRESULT WINAPI DECLSPEC_HOTPATCH SetThreadDescription( HANDLE thread, PCWSTR des
if (length > USHRT_MAX)
return HRESULT_FROM_NT(STATUS_INVALID_PARAMETER);
- info.Length = length << 16 | length;
- info.Description = (WCHAR *)description;
+ info.Description.Length = info.Description.MaximumLength = length;
+ info.Description.Buffer = (WCHAR *)description;
return HRESULT_FROM_NT(NtSetInformationThread( thread, ThreadDescription, &info, sizeof(info) ));
}
@@ -434,15 +434,13 @@ HRESULT WINAPI DECLSPEC_HOTPATCH GetThreadDescription( HANDLE thread, WCHAR **de
status = NtQueryInformationThread( thread, ThreadDescription, info, length, &length );
if (!status)
{
- length = info->Length & 0xffff;
-
- if (!(*description = LocalAlloc( 0, length + sizeof(WCHAR))))
+ if (!(*description = LocalAlloc( 0, info->Description.Length + sizeof(WCHAR))))
status = STATUS_NO_MEMORY;
else
{
- if (length)
- memcpy(*description, info->Description, length);
- (*description)[length / sizeof(WCHAR)] = 0;
+ if (info->Description.Length)
+ memcpy(*description, info->Description.Buffer, info->Description.Length);
+ (*description)[info->Description.Length / sizeof(WCHAR)] = 0;
}
}
diff --git a/dlls/ntdll/thread.c b/dlls/ntdll/thread.c
index 6079a6c195..fd8d388403 100644
--- a/dlls/ntdll/thread.c
+++ b/dlls/ntdll/thread.c
@@ -1137,8 +1137,8 @@ NTSTATUS WINAPI NtQueryInformationThread( HANDLE handle, THREADINFOCLASS class,
status = STATUS_BUFFER_TOO_SMALL;
else if (status == STATUS_SUCCESS)
{
- info->Length = desc_len << 16 | desc_len;
- info->Description = ptr;
+ info->Description.Length = info->Description.MaximumLength = desc_len;
+ info->Description.Buffer = ptr;
}
if (ret_len && (status == STATUS_SUCCESS || status == STATUS_BUFFER_TOO_SMALL))
@@ -1303,20 +1303,18 @@ NTSTATUS WINAPI NtSetInformationThread( HANDLE handle, THREADINFOCLASS class,
case ThreadDescription:
{
const THREAD_DESCRIPTION_INFORMATION *info = data;
- data_size_t desc_len;
if (length != sizeof(*info)) return STATUS_INFO_LENGTH_MISMATCH;
if (!info) return STATUS_ACCESS_VIOLATION;
- desc_len = info->Length & 0xffff;
- if (info->Length >> 16 != desc_len) return STATUS_INVALID_PARAMETER;
- if (info->Length && !info->Description) return STATUS_ACCESS_VIOLATION;
+ if (info->Description.Length != info->Description.MaximumLength) return STATUS_INVALID_PARAMETER;
+ if (info->Description.Length && !info->Description.Buffer) return STATUS_ACCESS_VIOLATION;
SERVER_START_REQ( set_thread_info )
{
req->handle = wine_server_obj_handle( handle );
req->mask = SET_THREAD_INFO_DESCRIPTION;
- wine_server_add_data( req, info->Description, desc_len );
+ wine_server_add_data( req, info->Description.Buffer, info->Description.Length );
status = wine_server_call( req );
}
SERVER_END_REQ;
diff --git a/include/winternl.h b/include/winternl.h
index 1477932d10..311fba3ae9 100644
--- a/include/winternl.h
+++ b/include/winternl.h
@@ -1030,8 +1030,7 @@ typedef struct _THREAD_DESCRIPTOR_INFORMATION
typedef struct _THREAD_DESCRIPTION_INFORMATION
{
- DWORD Length;
- WCHAR *Description;
+ UNICODE_STRING Description;
} THREAD_DESCRIPTION_INFORMATION, *PTHREAD_DESCRIPTION_INFORMATION;
typedef struct _KERNEL_USER_TIMES {
--
2.24.0
Dec. 3, 2019
[PATCH] d3dcompiler/tests: Fix parameter desc test on d3d10.
by Matteo Bruni
Signed-off-by: Matteo Bruni <mbruni(a)codeweavers.com>
---
D3D11_SIGNATURE_PARAMETER_DESC has an additional "Stream" field
compared to D3D10_SIGNATURE_PARAMETER_DESC but they share the same
size on 64-bit because of padding (while they do not on 32-bit). Don't
check the contents of the padding area.
Inspired by a patch from Sven Baars.
dlls/d3dcompiler_43/tests/reflection.c | 38 +++++++++++++++++++-------
1 file changed, 28 insertions(+), 10 deletions(-)
diff --git a/dlls/d3dcompiler_43/tests/reflection.c b/dlls/d3dcompiler_43/tests/reflection.c
index f8c97230d61..44e66044ec1 100644
--- a/dlls/d3dcompiler_43/tests/reflection.c
+++ b/dlls/d3dcompiler_43/tests/reflection.c
@@ -433,8 +433,14 @@ static void test_reflection_desc_vs(void)
i, desc.Mask, pdesc->Mask);
ok(desc.ReadWriteMask == pdesc->ReadWriteMask, "GetInputParameterDesc(%u) ReadWriteMask failed, got %x, expected %x\n",
i, desc.ReadWriteMask, pdesc->ReadWriteMask);
- ok(desc.Stream == pdesc->Stream, "GetInputParameterDesc(%u) Stream failed, got %u, expected %u\n",
- i, desc.Stream, pdesc->Stream);
+ /* The Stream field of D3D11_SIGNATURE_PARAMETER_DESC is in the
+ * trailing padding of the D3D10_SIGNATURE_PARAMETER_DESC struct on
+ * 64-bits and thus undefined. Don't test it. */
+ if (D3D_COMPILER_VERSION)
+ ok(desc.Stream == pdesc->Stream, "(%u): got unexpected Stream %u, expected %u.\n",
+ i, desc.Stream, pdesc->Stream);
+ else if (sizeof(void *) == 4)
+ ok(!desc.Stream, "(%u): got unexpected Stream %u.\n", i, desc.Stream);
}
for (i = 0; i < ARRAY_SIZE(test_reflection_desc_vs_resultout); ++i)
@@ -458,8 +464,11 @@ static void test_reflection_desc_vs(void)
i, desc.Mask, pdesc->Mask);
ok(desc.ReadWriteMask == pdesc->ReadWriteMask, "GetOutputParameterDesc(%u) ReadWriteMask failed, got %x, expected %x\n",
i, desc.ReadWriteMask, pdesc->ReadWriteMask);
- ok(desc.Stream == pdesc->Stream, "GetOutputParameterDesc(%u) Stream failed, got %u, expected %u\n",
- i, desc.Stream, pdesc->Stream);
+ if (D3D_COMPILER_VERSION)
+ ok(desc.Stream == pdesc->Stream, "(%u): got unexpected Stream %u, expected %u.\n",
+ i, desc.Stream, pdesc->Stream);
+ else if (sizeof(void *) == 4)
+ ok(!desc.Stream, "(%u): got unexpected Stream %u.\n", i, desc.Stream);
}
count = ref11->lpVtbl->Release(ref11);
@@ -740,8 +749,11 @@ static void test_reflection_desc_ps(void)
i, desc.Mask, pdesc->Mask);
ok(desc.ReadWriteMask == pdesc->ReadWriteMask, "GetInputParameterDesc(%u) ReadWriteMask failed, got %x, expected %x\n",
i, desc.ReadWriteMask, pdesc->ReadWriteMask);
- ok(desc.Stream == pdesc->Stream, "GetInputParameterDesc(%u) Stream failed, got %u, expected %u\n",
- i, desc.Stream, pdesc->Stream);
+ if (D3D_COMPILER_VERSION)
+ ok(desc.Stream == pdesc->Stream, "(%u): got unexpected Stream %u, expected %u.\n",
+ i, desc.Stream, pdesc->Stream);
+ else if (sizeof(void *) == 4)
+ ok(!desc.Stream, "(%u): got unexpected Stream %u.\n", i, desc.Stream);
}
for (i = 0; i < ARRAY_SIZE(test_reflection_desc_ps_resultout); ++i)
@@ -771,8 +783,11 @@ static void test_reflection_desc_ps(void)
i, desc.Mask, pdesc->Mask);
ok(desc.ReadWriteMask == pdesc->ReadWriteMask, "GetOutputParameterDesc(%u) ReadWriteMask failed, got %x, expected %x\n",
i, desc.ReadWriteMask, pdesc->ReadWriteMask);
- ok(desc.Stream == pdesc->Stream, "GetOutputParameterDesc(%u) Stream failed, got %u, expected %u\n",
- i, desc.Stream, pdesc->Stream);
+ if (D3D_COMPILER_VERSION)
+ ok(desc.Stream == pdesc->Stream, "(%u): got unexpected Stream %u, expected %u.\n",
+ i, desc.Stream, pdesc->Stream);
+ else if (sizeof(void *) == 4)
+ ok(!desc.Stream, "(%u): got unexpected Stream %u.\n", i, desc.Stream);
}
count = ref11->lpVtbl->Release(ref11);
@@ -1008,8 +1023,11 @@ static void test_reflection_desc_ps_output(void)
i, desc.Mask, pdesc->Mask);
ok(desc.ReadWriteMask == pdesc->ReadWriteMask, "(%u): GetOutputParameterDesc ReadWriteMask failed, got %x, expected %x\n",
i, desc.ReadWriteMask, pdesc->ReadWriteMask);
- ok(desc.Stream == pdesc->Stream, "(%u): GetOutputParameterDesc Stream failed, got %u, expected %u\n",
- i, desc.Stream, pdesc->Stream);
+ if (D3D_COMPILER_VERSION)
+ ok(desc.Stream == pdesc->Stream, "(%u): got unexpected Stream %u, expected %u.\n",
+ i, desc.Stream, pdesc->Stream);
+ else if (sizeof(void *) == 4)
+ ok(!desc.Stream, "(%u): got unexpected Stream %u.\n", i, desc.Stream);
count = ref11->lpVtbl->Release(ref11);
ok(count == 0, "(%u): Release failed %u\n", i, count);
--
2.23.0
Dec. 3, 2019
Re: [PATCH] msiexec: Improve RegQueryValueExW() calls a bit.
by Alexandre Julliard
Serge Gautherie <winehq-git_serge_180711(a)gautherie.fr> writes:
> Signed-off-by: Serge Gautherie <winehq-git_serge_180711(a)gautherie.fr>
> ---
> programs/msiexec/msiexec.c | 8 ++++----
> 1 file changed, 4 insertions(+), 4 deletions(-)
>
> diff --git a/programs/msiexec/msiexec.c b/programs/msiexec/msiexec.c
> index 17d4bf8..dd83dac 100644
> --- a/programs/msiexec/msiexec.c
> +++ b/programs/msiexec/msiexec.c
> @@ -573,14 +573,14 @@ static BOOL process_args_from_reg( const WCHAR *ident, int *pargc, WCHAR ***parg
> {
> LONG r;
> HKEY hkey;
> - DWORD sz = 0, type = 0;
> + DWORD sz, type;
> WCHAR *buf;
> BOOL ret = FALSE;
>
> r = RegOpenKeyW(HKEY_LOCAL_MACHINE, InstallRunOnce, &hkey);
> if(r != ERROR_SUCCESS)
> return FALSE;
> - r = RegQueryValueExW(hkey, ident, 0, &type, 0, &sz);
> + r = RegQueryValueExW(hkey, ident, NULL, &type, NULL, &sz);
The indentation is messed up. Also please try to write better commit
messages, "improve a bit" doesn't tell us anything.
--
Alexandre Julliard
julliard(a)winehq.org
Dec. 3, 2019
Re: [PATCH] riched20/tests: Fix function names in two ok() messages.
by Huw Davies
Signed-off-by: Huw Davies <huw(a)codeweavers.com>
Dec. 3, 2019
Re: [PATCH v2 4/4] winegstreamer: Use strmbase state change methods.
by Alexandre Julliard
Zebediah Figura <z.figura12(a)gmail.com> writes:
> Signed-off-by: Zebediah Figura <z.figura12(a)gmail.com>
> ---
> dlls/winegstreamer/gstdemux.c | 255 ++++++++++++++++------------------
> 1 file changed, 122 insertions(+), 133 deletions(-)
This is still causing deadlocks:
../../../tools/runtest -q -P wine -T ../../.. -M winmm.dll -p winmm_test.exe mci && touch mci.ok
00e2:fixme:mci:MCI_SendCommand unhandled MCI_ALL_DEVICE_ID
00e2:fixme:mci:MCI_SysInfo Don't know how to get # of MCI devices of a given type
00e2:fixme:mci:MCI_SendCommand unhandled MCI_ALL_DEVICE_ID
00e2:fixme:mci:MCI_LoadMciDriver Couldn't load driver for type L"".
00e2:fixme:mci:MCI_LoadMciDriver Couldn't load driver for type L"".
00e2:fixme:mci:MCI_LoadMciDriver Couldn't load driver for type L"NOSUCHDEVICE".
00e2:fixme:mciwave:MCIWAVE_DriverProc Unsupported command [2134]
00e2:fixme:mci:mciSendStringW leaking auto-open device 1
ALSA lib conf.c:3558:(snd_config_hooks_call) Cannot open shared library libasound_module_conf_pulse.so (/usr/lib/i386-linux-gnu/alsa-lib/libasound_module_conf_pulse.so: libasound_module_conf_pulse.so: cannot open shared object file: No such file or directory)
ALSA lib seq.c:935:(snd_seq_open_noupdate) Unknown SEQ default
00e2:err:winediag:MIDIMAP_drvOpen No software synthesizer midi port found, Midi sound output probably won't work.
00e2:fixme:gstreamer:source_query_accept (0x6ea688) stub
00f9:err:ntdll:RtlpWaitForCriticalSection section 0x6ea1f0 "../strmbase/filter.c: strmbase_filter.csFilter" wait timed out in thread 00f9, blocked by 00fb, retrying (60 sec)
--
Alexandre Julliard
julliard(a)winehq.org
Dec. 3, 2019
Re: [PATCH 1/4] wusa: Add support for extracting MSU files.
by Hans Leidekker
> diff --git a/programs/wusa/wusa.h b/programs/wusa/wusa.h
> new file mode 100644
> index 0000000000..c6bf7eda11
> +
> +static void *heap_alloc(size_t len) __WINE_ALLOC_SIZE(1);
> +static inline void *heap_alloc(size_t len)
> +{
> + return HeapAlloc(GetProcessHeap(), 0, len);
> +}
> +
> +static inline BOOL heap_free(void *mem)
>
> Is there a reason why your not using the wine/heap.h ones?
I left it because we may as well switch to the C allocation functions,
now that we're building with msvcrt.
Dec. 3, 2019