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
June 2018
- 68 participants
- 1149 messages
[PATCH 2/3] user32: Add stub for GetPointerDevices
by Fabian Maurer
Fixes bug 45302.
Signed-off-by: Fabian Maurer <dark.shadow4(a)web.de>
---
...ms-win-rtcore-ntuser-wmpointer-l1-1-0.spec | 2 +-
dlls/user32/misc.c | 17 ++++++++++++++++
dlls/user32/user32.spec | 1 +
include/winuser.h | 20 +++++++++++++++++++
4 files changed, 39 insertions(+), 1 deletion(-)
diff --git a/dlls/api-ms-win-rtcore-ntuser-wmpointer-l1-1-0/api-ms-win-rtcore-ntuser-wmpointer-l1-1-0.spec b/dlls/api-ms-win-rtcore-ntuser-wmpointer-l1-1-0/api-ms-win-rtcore-ntuser-wmpointer-l1-1-0.spec
index 72cfe3d954..3a043dc9c2 100644
--- a/dlls/api-ms-win-rtcore-ntuser-wmpointer-l1-1-0/api-ms-win-rtcore-ntuser-wmpointer-l1-1-0.spec
+++ b/dlls/api-ms-win-rtcore-ntuser-wmpointer-l1-1-0/api-ms-win-rtcore-ntuser-wmpointer-l1-1-0.spec
@@ -4,7 +4,7 @@
@ stub GetPointerDevice
@ stub GetPointerDeviceProperties
@ stub GetPointerDeviceRects
-@ stub GetPointerDevices
+@ stdcall GetPointerDevices(ptr ptr) user32.GetPointerDevices
@ stub GetPointerFrameInfo
@ stub GetPointerFrameInfoHistory
@ stub GetPointerFramePenInfo
diff --git a/dlls/user32/misc.c b/dlls/user32/misc.c
index c3f4030a90..4cc54df412 100644
--- a/dlls/user32/misc.c
+++ b/dlls/user32/misc.c
@@ -846,6 +846,23 @@ BOOL WINAPI RegisterPointerDeviceNotifications(HWND hwnd, BOOL notifyrange)
return TRUE;
}
+/**********************************************************************
+ * GetPointerDevices [USER32.@]
+ */
+BOOL WINAPI GetPointerDevices(UINT32 *device_count, POINTER_DEVICE_INFO *devices)
+{
+ FIXME("(%p %p): stub\n", device_count, devices);
+
+ if (!device_count)
+ return FALSE;
+
+ if (!devices)
+ *device_count = 0;
+
+ return TRUE;
+}
+
+
static const WCHAR imeW[] = {'I','M','E',0};
const struct builtin_class_descr IME_builtin_class =
{
diff --git a/dlls/user32/user32.spec b/dlls/user32/user32.spec
index 2e5b203a85..7f652ee722 100644
--- a/dlls/user32/user32.spec
+++ b/dlls/user32/user32.spec
@@ -354,6 +354,7 @@
@ stdcall GetOpenClipboardWindow()
@ stdcall GetParent(long)
@ stdcall GetPhysicalCursorPos(ptr)
+@ stdcall GetPointerDevices(ptr ptr)
@ stdcall GetPriorityClipboardFormat(ptr long)
@ stdcall GetProcessDefaultLayout(ptr)
@ stdcall GetProcessDpiAwarenessInternal(long ptr)
diff --git a/include/winuser.h b/include/winuser.h
index 648efbe0e2..f546a4a1f3 100644
--- a/include/winuser.h
+++ b/include/winuser.h
@@ -3357,6 +3357,26 @@ typedef struct tagGESTUREINFO {
} GESTUREINFO, *PGESTUREINFO;
typedef GESTUREINFO const * PCGESTUREINFO;
+#define POINTER_DEVICE_PRODUCT_STRING_MAX 520
+
+typedef enum tagPOINTER_DEVICE_TYPE {
+ POINTER_DEVICE_TYPE_INTEGRATED_PEN = 0x00000001,
+ POINTER_DEVICE_TYPE_EXTERNAL_PEN = 0x00000002,
+ POINTER_DEVICE_TYPE_TOUCH = 0x00000003,
+ POINTER_DEVICE_TYPE_TOUCH_PAD = 0x00000004,
+ POINTER_DEVICE_TYPE_MAX = 0xFFFFFFFF
+} POINTER_DEVICE_TYPE;
+
+typedef struct tagPOINTER_DEVICE_INFO {
+ DWORD displayOrientation;
+ HANDLE device;
+ POINTER_DEVICE_TYPE pointerDeviceType;
+ HMONITOR monitor;
+ ULONG startingCursorId;
+ USHORT maxActiveContacts;
+ WCHAR productString[POINTER_DEVICE_PRODUCT_STRING_MAX];
+} POINTER_DEVICE_INFO;
+
#if defined(_WINGDI_) && !defined(NOGDI)
WINUSERAPI LONG WINAPI ChangeDisplaySettingsA(LPDEVMODEA,DWORD);
WINUSERAPI LONG WINAPI ChangeDisplaySettingsW(LPDEVMODEW,DWORD);
--
2.17.1
June 7, 2018
[PATCH 1/3] user32: Make RegisterPointerDeviceNotifications return TRUE to fake success
by Fabian Maurer
Fixes bug 45284.
Signed-off-by: Fabian Maurer <dark.shadow4(a)web.de>
---
dlls/user32/misc.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/user32/misc.c b/dlls/user32/misc.c
index be4b71a4bc..c3f4030a90 100644
--- a/dlls/user32/misc.c
+++ b/dlls/user32/misc.c
@@ -843,7 +843,7 @@ LONG WINAPI GetDisplayConfigBufferSizes(UINT32 flags, UINT32 *num_path_info, UIN
BOOL WINAPI RegisterPointerDeviceNotifications(HWND hwnd, BOOL notifyrange)
{
FIXME("(%p %d): stub\n", hwnd, notifyrange);
- return FALSE;
+ return TRUE;
}
static const WCHAR imeW[] = {'I','M','E',0};
--
2.17.1
June 7, 2018
Re: [PATCH 4/4] wsdapi: Add initial support for reading messages; identify Probe message.
by Owen Rudge
On 07/06/2018 08:56, Huw Davies wrote:
>> +static BOOL move_to_element(WS_XML_READER *reader, const char
*element_name, WS_XML_STRING *uri)
>
> return HRESULT
>
>> +static BOOL ws_element_to_wsdxml_element(WS_XML_READER *reader,
IWSDXMLContext *context, WSDXML_ELEMENT *parent_element)
>
> and here.
>
>> int read_message(const char *xml, int xml_length, WSD_SOAP_MESSAGE
**out_msg)
>
> This is also looking like a good contender to return HRESULT and
> take an [out] int *type parameter.
As per the previous patch, the HRESULT is not ultimately used, and I
can't see that there's a great benefit in passing it along. I do
understand it for the other functions that ultimately return a value to
the caller, but these functions are all called by the listener thread,
and a simple identification of the message type is all we require.
If you think it would be beneficial, I can modify these functions to
return HRESULT, but it would be good to understand the benefits of doing so.
>> - ok(any != NULL, "%s: any == NULL\n", debug_prefix);
>> + todo_wine ok(any != NULL, "%s: any == NULL\n", debug_prefix);
>
> You'll need to have a really good reason for doing this. Can you
> re-order things so you don't break the tests?
This will be fixed in the next patchset; I didn't want to include any
more code in what's already quite a large patch. There's probably an
extra 40 or so lines of code needed to avoid this breakage. There's not
really a way I can re-order this that I can think of.
If preferred though, I can submit the patch that will fix this as part
of this patchset, so the net result once the set is committed involves
no extra todos.
Thanks,
Owen
June 7, 2018
Re: [PATCH 3/4] wsdapi: Add stub for Probe message parsing, prepare notification sink message.
by Owen Rudge
On 07/06/2018 08:45, Huw Davies wrote:
>> +static void process_received_message(listener_thread_params
*params, char *message, int message_len,
>> + SOCKADDR_STORAGE *source_addr)
>
> This should return HRESULT.
There's not really anything that can be done with that result. If an
unknown message is received, I can add a trace for it, but an error
reading the message is not a reason to, for instance, stop listening on
the socket, so I'm not sure what purpose having a return value here
would serve.
Cheers,
Owen
June 7, 2018
[PATCH] po: Update Finnish translation.
by Lauri Kenttä
Signed-off-by: Lauri Kenttä <lauri.kentta(a)gmail.com>
---
po/fi.po | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/po/fi.po b/po/fi.po
index fce90dcccf..d654e904f2 100644
--- a/po/fi.po
+++ b/po/fi.po
@@ -12782,10 +12782,8 @@ msgid "Confirm Value Delete"
msgstr "Vahvista arvon tuhoaminen"
#: regedit.rc:210
-#, fuzzy
-#| msgid "Are you sure you want to delete the registry value '%1'?"
msgid "Are you sure you want to delete the selected registry value?"
-msgstr "Haluatko varmasti tuhota rekisteristä arvon '%1'?"
+msgstr "Haluatko varmasti tuhota rekisteristä valitun arvon?"
#: regedit.rc:216
msgid "Search complete. The string '%1' was not found."
--
2.17.1
June 7, 2018
Re: [PATCH] shellpath.c: Fix creation of HOME directory symbolic links
by Huw Davies
On 7 Jun 2018, at 16:11, Bob Wya <bob.mt.wya(a)gmail.com> wrote:
> Thanks for the valuable feedback! I think I spent quite a bit of time fiddling with this,
> so I've probably "lost the wood for the trees" a bit... :-)
>
> Just to clarify a style point, for the re-factored patches...
>
> If I still want to split the _SHCreateSymbolicLinks() function into a re-factored
> implementation that has an iterative wrapper function, is it OK to call them:
>
> _SHCreateSymbolicLink()
> _SHCreateSymbolicLinks()
>
> ??
> This would introduce a new "Camel Case" style function name... But would obviously
> be more consistent...
For new functions you’d avoid CamelCase and use something like
create_symbolic_link(). If might be ok to rename existing
functions if the patch is small enough. The problem at the
moment is that the patch is much too large.
Huw.
June 7, 2018
Re: [PATCH] shellpath.c: Fix creation of HOME directory symbolic links
by Bob Wya
On 7 June 2018 at 11:57, Huw Davies <huw(a)codeweavers.com> wrote:
> On Wed, Jun 06, 2018 at 08:46:49AM +0100, Rob Walker wrote:
> > Fixes: https://bugs.winehq.org/show_bug.cgi?id=41668 (1)
> > https://bugs.winehq.org/show_bug.cgi?id=28216 (2)
> >
> > (1) Introduces a "WINESYMLINK" env variable that allows a user to
> > control what shell folder links are created to the HOME folder, when
> > a WINEPREFIX is initially created.
> > The Wineprefix Shell Folders, that Wine symlinks to the user's HOME
> directory,
> > are only (re-)created on each Wine boot if they: do not pre-exist or
> are broken
> > symbolic links.
> >
> > (2) The XDG Directory specification automatically falls back to the user
> HOME
> > directory for any XDG_*_DIR that does not exist
> (xdg-user-dirs-update).
> > The implemented solution is to block Wine from symlinking directly
> > to HOME directory.
>
> Hi Rob,
>
> There's an awful lot going on in this patch which makes it very
> difficult to review. Please avoid re-naming and re-indenting
> existing functions so that we can actually see the real changes.
>
> Then please split the changes into separate patches. A clue
> that you're not doing this is that you have a list of two
> items in the commit message. If the patch is doing two
> things, it should be two patches.
>
> Huw.
>
>
Hi Huw,
Thanks for the valuable feedback! I think I spent quite a bit of time
fiddling with this,
so I've probably "lost the wood for the trees" a bit... :-)
Just to clarify a style point, for the re-factored patches...
If I still want to split the _SHCreateSymbolicLinks() function into a
re-factored
implementation that has an iterative wrapper function, is it OK to call
them:
_SHCreateSymbolicLink()
_SHCreateSymbolicLinks()
??
This would introduce a new "Camel Case" style function name... But would
obviously
be more consistent...
Thanks
Rob
> >
> > Test on Gentoo GNU/Linux.
> >
> > Signed-off-by: Rob Walker <bob.mt.wya(a)gmail.com>
> > ---
> > dlls/shell32/shellpath.c | 554 ++++++++++++++++++++++++---------------
> > 1 file changed, 343 insertions(+), 211 deletions(-)
> >
> > diff --git a/dlls/shell32/shellpath.c b/dlls/shell32/shellpath.c
> > index a551e93aa8..b8c74273c6 100644
> > --- a/dlls/shell32/shellpath.c
> > +++ b/dlls/shell32/shellpath.c
> > @@ -4326,252 +4326,259 @@ static HRESULT _SHRegisterCommonShellFolders(
> void)
> > }
> >
> > /***********************************************************
> *******************
> > - * _SHAppendToUnixPath [Internal]
> > + * append_to_unix_path [Internal]
> > *
> > - * Helper function for _SHCreateSymbolicLinks. Appends pwszSubPath (or
> the
> > - * corresponding resource, if IS_INTRESOURCE) to the unix base path
> 'szBasePath'
> > - * and replaces backslashes with slashes.
> > + * Helper function for create_home_subdir_symbolic_link.
> > + * Appends 'subpath', or the corresponding resource (if it is a
> > + * resource identifier), to the Unix base path 'base_dir'.
> > + * Replace backslashes with forward slashes.
> > *
> > * PARAMS
> > - * szBasePath [IO] The unix base path, which will be appended to
> (CP_UNXICP).
> > - * pwszSubPath [I] Sub-path or resource id (use MAKEINTRESOURCEW).
> > + * base_dir [IO] The Unix base path, which will be appended to
> (CP_UNXICP).
> > + * subpath [I] Sub-path or resource id (use MAKEINTRESOURCEW,
> pre-call).
> > *
> > * RETURNS
> > - * Success: TRUE,
> > + * Success: TRUE
> > * Failure: FALSE
> > */
> > -static inline BOOL _SHAppendToUnixPath(char *szBasePath, LPCWSTR
> pwszSubPath) {
> > - WCHAR wszSubPath[MAX_PATH];
> > - int cLen = strlen(szBasePath);
> > - char *pBackslash;
> > -
> > - if (IS_INTRESOURCE(pwszSubPath)) {
> > - if (!LoadStringW(shell32_hInstance, LOWORD(pwszSubPath),
> wszSubPath, MAX_PATH)) {
> > - /* Fall back to hard coded defaults. */
> > - switch (LOWORD(pwszSubPath)) {
> > - case IDS_PERSONAL:
> > - lstrcpyW(wszSubPath, DocumentsW);
> > - break;
> > - case IDS_MYMUSIC:
> > - lstrcpyW(wszSubPath, My_MusicW);
> > - break;
> > - case IDS_MYPICTURES:
> > - lstrcpyW(wszSubPath, My_PicturesW);
> > - break;
> > - case IDS_MYVIDEOS:
> > - lstrcpyW(wszSubPath, My_VideosW);
> > - break;
> > - default:
> > - ERR("LoadString(%d) failed!\n",
> LOWORD(pwszSubPath));
> > - return FALSE;
> > - }
> > +static inline BOOL append_to_unix_path(char *base_dir, LPCWSTR subpath)
> > +{
> > + WCHAR ws_subpath[MAX_PATH];
> > + int cLen = strlen(base_dir);
> > + char *back_slash;
> > +
> > + if (!IS_INTRESOURCE(subpath))
> > + {
> > + lstrcpyW(ws_subpath, subpath);
> > + }
> > + else if (!LoadStringW(shell32_hInstance, LOWORD(subpath),
> ws_subpath, MAX_PATH))
> > + {
> > + /* Fall back to hard coded defaults. */
> > + switch (LOWORD(subpath)) {
> > + case IDS_PERSONAL:
> > + lstrcpyW(ws_subpath, DocumentsW);
> > + break;
> > + case IDS_MYMUSIC:
> > + lstrcpyW(ws_subpath, My_MusicW);
> > + break;
> > + case IDS_MYPICTURES:
> > + lstrcpyW(ws_subpath, My_PicturesW);
> > + break;
> > + case IDS_MYVIDEOS:
> > + lstrcpyW(ws_subpath, My_VideosW);
> > + break;
> > + case IDS_DESKTOP:
> > + lstrcpyW(ws_subpath, DesktopW);
> > + break;
> > + default:
> > + ERR("LoadString(%d) failed!\n", LOWORD(subpath));
> > + return FALSE;
> > }
> > - } else {
> > - lstrcpyW(wszSubPath, pwszSubPath);
> > }
> > -
> > - if (szBasePath[cLen-1] != '/') szBasePath[cLen++] = '/';
> > -
> > - if (!WideCharToMultiByte(CP_UNIXCP, 0, wszSubPath, -1, szBasePath
> + cLen,
> > +
> > + if (!cLen || (base_dir[cLen-1] != '/')) base_dir[cLen++] = '/';
> > +
> > + if (!WideCharToMultiByte(CP_UNIXCP, 0, ws_subpath, -1, base_dir +
> cLen,
> > FILENAME_MAX - cLen, NULL, NULL))
> > {
> > return FALSE;
> > }
> > -
> > - pBackslash = szBasePath + cLen;
> > - while ((pBackslash = strchr(pBackslash, '\\'))) *pBackslash = '/';
> > -
> > +
> > + back_slash = base_dir + cLen;
> > + while ((back_slash = strchr(back_slash, '\\'))) *back_slash = '/';
> > +
> > return TRUE;
> > }
> >
> > /***********************************************************
> *******************
> > - * _SHCreateSymbolicLinks [Internal]
> > + * compare_pathft_to_bootft [Internal]
> > *
> > - * Sets up symbol links for various shell folders to point into the
> users home
> > - * directory. We do an educated guess about what the user would
> probably want:
> > - * - If there is a 'My Documents' directory in $HOME, the user probably
> wants
> > - * wine's 'My Documents' to point there. Furthermore, we imply that
> the user
> > - * is a Windows lover and has no problem with wine creating 'My
> Pictures',
> > - * 'My Music' and 'My Videos' subfolders under '$HOME/My Documents',
> if those
> > - * do not already exits. We put appropriate symbolic links in place
> for those,
> > - * too.
> > - * - If there is no 'My Documents' directory in $HOME, we let 'My
> Documents'
> > - * point directly to $HOME. We assume the user to be a unix hacker
> who does not
> > - * want wine to create anything anywhere besides the .wine directory.
> So, if
> > - * there already is a 'My Music' directory in $HOME, we symlink the
> 'My Music'
> > - * shell folder to it. But if not, then we check XDG_MUSIC_DIR -
> "well known"
> > - * directory, and try to link to that. If that fails, then we symlink
> to
> > - * $HOME directly. The same holds fo 'My Pictures' and 'My Videos'.
> > - * - The Desktop shell folder is symlinked to XDG_DESKTOP_DIR. If that
> does not
> > - * exist, then we try '$HOME/Desktop'. If that does not exist, then
> we leave
> > - * it alone.
> > - * ('My Music',... above in fact means LoadString(IDS_MYMUSIC))
> > + * Function to compare the FILETIME of ws_path (a Windows path) to the
> (estimated)
> > + * Wine System boot time.
> > + *
> > + * PARAMS
> > + * ws_path [I] The Windows path, for which provides the FS
> write time.
> > + * time_difference [O] -1 Windows path last written before time of
> Wine boot.
> > + * 0 Windows path last written at time of Wine
> boot.
> > + * +1 Windows path last written after time of
> Wine boot.
> > + * RETURNS
> > + * Success: S_OK
> > + * Failure: E_FAIL
> > + *
> > + * NOTES
> > + * Pre-allocate storage for time_difference pointer variable
> externally.
> > */
> > -static void _SHCreateSymbolicLinks(void)
> > -{
> > - UINT aidsMyStuff[] = { IDS_MYPICTURES, IDS_MYVIDEOS, IDS_MYMUSIC },
> i;
> > - const WCHAR* MyOSXStuffW[] = { PicturesW, MoviesW, MusicW };
> > - int acsidlMyStuff[] = { CSIDL_MYPICTURES, CSIDL_MYVIDEO,
> CSIDL_MYMUSIC };
> > - static const char * const xdg_dirs[] = { "PICTURES", "VIDEOS",
> "MUSIC", "DOCUMENTS", "DESKTOP" };
> > - static const unsigned int num = ARRAY_SIZE(xdg_dirs);
> > - WCHAR wszTempPath[MAX_PATH];
> > - char szPersonalTarget[FILENAME_MAX], *pszPersonal;
> > - char szMyStuffTarget[FILENAME_MAX], *pszMyStuff;
> > - char szDesktopTarget[FILENAME_MAX], *pszDesktop;
> > - struct stat statFolder;
> > - const char *pszHome;
> > - HRESULT hr;
> > - char ** xdg_results;
> > - char * xdg_desktop_dir;
> > -
> > - /* Create all necessary profile sub-dirs up to 'My Documents' and
> get the unix path. */
> > - hr = SHGetFolderPathW(NULL, CSIDL_PERSONAL|CSIDL_FLAG_CREATE, NULL,
> > - SHGFP_TYPE_DEFAULT, wszTempPath);
> > - if (FAILED(hr)) return;
> > - pszPersonal = wine_get_unix_file_name(wszTempPath);
> > - if (!pszPersonal) return;
> > +static HRESULT compare_pathft_to_bootft(const WCHAR * ws_path, int *
> time_difference)
> > +{
> > + static ULONG64 time_system_start = 0;
> > + SYSTEMTIME system_time_st;
> > + FILETIME dummy_ft, system_time_ft, last_write_ft;
> > + ULONG64 tick_count, time_last_write;
> > + HANDLE fhandle;
> > + DWORD ft_errorc = 0;
> >
> > - hr = XDG_UserDirLookup(xdg_dirs, num, &xdg_results);
> > - if (FAILED(hr)) xdg_results = NULL;
> > + if (!time_difference) return E_FAIL;
> >
> > - pszHome = getenv("HOME");
> > - if (pszHome && !stat(pszHome, &statFolder) &&
> S_ISDIR(statFolder.st_mode))
> > + if (!time_system_start)
> > {
> > - while (1)
> > + tick_count = (ULONG64) GetTickCount64();
> > + GetSystemTime(&system_time_st);
> > + if (!SystemTimeToFileTime( &system_time_st, &system_time_ft))
> > {
> > - /* Check if there's already a Wine-specific 'My Documents'
> folder */
> > - strcpy(szPersonalTarget, pszHome);
> > - if (_SHAppendToUnixPath(szPersonalTarget,
> MAKEINTRESOURCEW(IDS_PERSONAL)) &&
> > - !stat(szPersonalTarget, &statFolder) &&
> S_ISDIR(statFolder.st_mode))
> > - {
> > - /* '$HOME/My Documents' exists. Create 'My Pictures',
> > - * 'My Videos' and 'My Music' subfolders or fail
> silently if
> > - * they already exist.
> > - */
> > - for (i = 0; i < ARRAY_SIZE(aidsMyStuff); i++)
> > - {
> > - strcpy(szMyStuffTarget, szPersonalTarget);
> > - if (_SHAppendToUnixPath(szMyStuffTarget,
> MAKEINTRESOURCEW(aidsMyStuff[i])))
> > - mkdir(szMyStuffTarget, 0777);
> > - }
> > - break;
> > - }
> > -
> > - /* Try to point to the XDG Documents folder */
> > - if (xdg_results && xdg_results[num-2] &&
> > - !stat(xdg_results[num-2], &statFolder) &&
> > - S_ISDIR(statFolder.st_mode))
> > - {
> > - strcpy(szPersonalTarget, xdg_results[num-2]);
> > - break;
> > - }
> > -
> > - /* Or the hardcoded / OS X Documents folder */
> > - strcpy(szPersonalTarget, pszHome);
> > - if (_SHAppendToUnixPath(szPersonalTarget, DocumentsW) &&
> > - !stat(szPersonalTarget, &statFolder) &&
> > - S_ISDIR(statFolder.st_mode))
> > - break;
> > -
> > - /* As a last resort point to $HOME. */
> > - strcpy(szPersonalTarget, pszHome);
> > - break;
> > + ERR("SystemTimeToFileTime call failed (%d)\n",
> GetLastError());
> > + return E_FAIL;
> > }
> > -
> > - /* Replace 'My Documents' directory with a symlink or fail
> silently if not empty. */
> > - remove(pszPersonal);
> > - symlink(szPersonalTarget, pszPersonal);
> > + time_system_start = (((ULONG64)system_time_ft.dwHighDateTime)
> << 32)
> > + + system_time_ft.dwLowDateTime
> > + - (tick_count * 10000);
> > }
> > - else
> > + fhandle = CreateFileW( ws_path, 0, FILE_SHARE_READ, NULL,
> > + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS,
> NULL);
> > + if (fhandle == INVALID_HANDLE_VALUE)
> > {
> > - /* '$HOME' doesn't exist. Create 'My Pictures', 'My Videos' and
> 'My Music' subdirs
> > - * in '%USERPROFILE%\\My Documents' or fail silently if they
> already exist. */
> > - pszHome = NULL;
> > - strcpy(szPersonalTarget, pszPersonal);
> > - for (i = 0; i < ARRAY_SIZE(aidsMyStuff); i++) {
> > - strcpy(szMyStuffTarget, szPersonalTarget);
> > - if (_SHAppendToUnixPath(szMyStuffTarget,
> MAKEINTRESOURCEW(aidsMyStuff[i])))
> > - mkdir(szMyStuffTarget, 0777);
> > - }
> > + ERR("Open file handle failed for path: %s (%d)\n",
> > + debugstr_w(ws_path), GetLastError());
> > + return E_FAIL;
> > }
> > -
> > - /* Create symbolic links for 'My Pictures', 'My Videos' and 'My
> Music'. */
> > - for (i=0; i < ARRAY_SIZE(aidsMyStuff); i++)
> > + if (!GetFileTime(fhandle, &dummy_ft, &dummy_ft, &last_write_ft))
> > + ft_errorc = GetLastError();
> > + if (!CloseHandle(fhandle))
> > {
> > - /* Create the current 'My Whatever' folder and get its unix
> path. */
> > - hr = SHGetFolderPathW(NULL, acsidlMyStuff[i]|CSIDL_FLAG_CREATE,
> NULL,
> > - SHGFP_TYPE_DEFAULT, wszTempPath);
> > - if (FAILED(hr)) continue;
> > -
> > - pszMyStuff = wine_get_unix_file_name(wszTempPath);
> > - if (!pszMyStuff) continue;
> > -
> > - while (1)
> > - {
> > - /* Check for the Wine-specific '$HOME/My Documents'
> subfolder */
> > - strcpy(szMyStuffTarget, szPersonalTarget);
> > - if (_SHAppendToUnixPath(szMyStuffTarget,
> MAKEINTRESOURCEW(aidsMyStuff[i])) &&
> > - !stat(szMyStuffTarget, &statFolder) &&
> S_ISDIR(statFolder.st_mode))
> > - break;
> > + ERR("Close file handle failed: %p (%d)\n",
> > + fhandle, GetLastError());
> > + return E_FAIL;
> > + }
> > + if (ft_errorc)
> > + {
> > + ERR("Get file time failed: %p (%d)\n",
> > + fhandle, ft_errorc);
> > + return E_FAIL;
> > + }
> > + time_last_write = (((ULONG64)last_write_ft.dwHighDateTime) << 32)
> > + + last_write_ft.dwLowDateTime;
> > + *time_difference = ((time_last_write > time_system_start) ? 1 :
> > + ((time_last_write < time_system_start) ? -1 :
> > + 0));
> >
> > - /* Try the XDG_XXX_DIR folder */
> > - if (xdg_results && xdg_results[i])
> > - {
> > - strcpy(szMyStuffTarget, xdg_results[i]);
> > - break;
> > - }
> > + return S_OK;
> > +}
> >
> > - /* Or the OS X folder (these are never localized) */
> > - if (pszHome)
> > - {
> > - strcpy(szMyStuffTarget, pszHome);
> > - if (_SHAppendToUnixPath(szMyStuffTarget,
> MyOSXStuffW[i]) &&
> > - !stat(szMyStuffTarget, &statFolder) &&
> > - S_ISDIR(statFolder.st_mode))
> > - break;
> > - }
> > +/**********************************************************
> ********************
> > + * create_homedir_symbolic_link [Internal]
> > + *
> > + * Creates a symbolic link from the current Wineprefix to an appropriate
> > + * HOME subdirectory (if one is found).
> > + *
> > + * Creates 'XXXX' directory in Wineprefix.
> > + * Then create a 'My XXXX' symbolic link in Wineprefix:
> > + * 1) If '$HOME/XXXX' (IDS directory) exists then target this.
> > + * 2) If '$HOME/XXXX' (XDG_XXXX_DIR) exists then target this.
> > + * 3) If '$HOME/XXXX' (MacOS XXXX media directory) exists then target
> this.
> > + *
> > + * PARAMS
> > + * env_enabled [I]
> > + * TRUE The user has enabled this Shell Folder to be
> symlinked (default).
> > + * FALSE The user has chosen to disable symlinking this
> Shell Folder.
> > + * The Shell Folder will be created, in the current
> Wineprefix (if not pre-existing).
> > + * ids_dir [I] Windows Resource Identifier code for current Shell
> Folder.
> > + * csidl_dir [I] Constant Special Item ID List identifier for
> current Shell Folder.
> > + * xdg_dir [I] Full path of external Unix XDG directory
> corresponding to current Shell Folder.
> > + * ws_osx_dir [I] Fallback directory name to use, corresponding to
> current Shell Folder (OSX specific).
> > + *
> > + */
> > +void create_homedir_symbolic_link(BOOL env_enabled,
> > + UINT ids_dir,
> > + int csidl_dir,
> > + const char * xdg_dir,
> > + const WCHAR * ws_osx_dir)
> > +{
> > + static const char * env_homedir = NULL;
> > + WCHAR ws_temp_path[MAX_PATH];
> > + char home_target[FILENAME_MAX], * prefix_dir;
> > + struct stat stat_folder, stat_home_folder;
> > + HRESULT hr;
> > + BOOL target_ok;
> > + int time_difference;
> >
> > - /* As a last resort point to the same location as 'My
> Documents' */
> > - strcpy(szMyStuffTarget, szPersonalTarget);
> > - break;
> > + hr = SHGetFolderPathW(NULL, csidl_dir, NULL,
> > + SHGFP_TYPE_DEFAULT, ws_temp_path);
> > + if (SUCCEEDED(hr))
> > + {
> > + if (ids_dir != IDS_DESKTOPDIRECTORY) return;
> > +
> > + if ((compare_pathft_to_bootft(ws_temp_path, &time_difference)
> == S_OK)
> > + && (time_difference < 0))
> > + {
> > + TRACE("%s directory created before wineboot\n",
> > + debugstr_w(ws_temp_path));
> > + return;
> > }
> > - remove(pszMyStuff);
> > - symlink(szMyStuffTarget, pszMyStuff);
> > - heap_free(pszMyStuff);
> > }
> > -
> > - /* Last but not least, the Desktop folder */
> > - if (pszHome)
> > - strcpy(szDesktopTarget, pszHome);
> > + else if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
> > + {
> > + hr = SHGetFolderPathW(NULL, csidl_dir|CSIDL_FLAG_CREATE, NULL,
> > + SHGFP_TYPE_DEFAULT, ws_temp_path);
> > + if (FAILED(hr)) return;
> > + }
> > else
> > - strcpy(szDesktopTarget, pszPersonal);
> > - heap_free(pszPersonal);
> > + {
> > + ERR("Failed to get Wineprefix path corresponding to %d CSIDL\n",
> > + csidl_dir);
> > + return;
> > + }
> > + if (!env_enabled) return;
> > + prefix_dir = wine_get_unix_file_name(ws_temp_path);
> > + if (!prefix_dir)
> > + {
> > + ERR("Failed to get Unix Wineprefix directory for: %s\n",
> > + debugstr_w(ws_temp_path));
> > + return;
> > + }
> > + if (!env_homedir) env_homedir = getenv("HOME");
> > + if (!(env_homedir
> > + && (stat(env_homedir, &stat_home_folder) != 1)
> > + && S_ISDIR(stat_home_folder.st_mode)))
> > + {
> > + if (prefix_dir) heap_free(prefix_dir);
> > + return;
> > + }
> >
> > - xdg_desktop_dir = xdg_results ? xdg_results[num - 1] : NULL;
> > - if (xdg_desktop_dir ||
> > - (_SHAppendToUnixPath(szDesktopTarget, DesktopW) &&
> > - !stat(szDesktopTarget, &statFolder) &&
> S_ISDIR(statFolder.st_mode)))
> > + /* Check for:
> > + * '$HOME/XXXX' (IDS directory)
> > + * or '$HOME/XXXX' (XDG directory)
> > + * or '$HOME/XXXX' (MacOS directory)
> > + */
> > + target_ok = FALSE;
> > + strcpy(home_target, env_homedir);
> > + if (append_to_unix_path(home_target, MAKEINTRESOURCEW(ids_dir))
> > + && (stat(home_target, &stat_folder) != -1)
> > + && S_ISDIR(stat_folder.st_mode))
> > {
> > - hr = SHGetFolderPathW(NULL, CSIDL_DESKTOPDIRECTORY|CSIDL_FLAG_CREATE,
> NULL,
> > - SHGFP_TYPE_DEFAULT, wszTempPath);
> > - if (SUCCEEDED(hr) && (pszDesktop =
> wine_get_unix_file_name(wszTempPath)))
> > - {
> > - remove(pszDesktop);
> > - if (xdg_desktop_dir)
> > - symlink(xdg_desktop_dir, pszDesktop);
> > - else
> > - symlink(szDesktopTarget, pszDesktop);
> > - heap_free(pszDesktop);
> > - }
> > + target_ok = TRUE;
> > + }
> > + else if (xdg_dir && stat(xdg_dir, &stat_folder))
> > + {
> > + strcpy(home_target, xdg_dir);
> > + /* Only link to the XDG directory, if it does not point directly
> > + * to the user's HOME directory (XDG specification fallback
> path). */
> > + target_ok = (stat_folder.st_ino != stat_home_folder.st_ino);
> > + }
> > + else
> > + {
> > + strcpy(home_target, env_homedir);
> > + target_ok = append_to_unix_path(home_target, ws_osx_dir)
> > + && (stat(home_target, &stat_folder) != -1)
> > + && S_ISDIR(stat_folder.st_mode);
> > }
> >
> > - /* Free resources allocated by XDG_UserDirLookup() */
> > - if (xdg_results)
> > + if (target_ok)
> > {
> > - for (i = 0; i < num; i++)
> > - heap_free(xdg_results[i]);
> > - heap_free(xdg_results);
> > + remove(prefix_dir);
> > + symlink(home_target, prefix_dir);
> > }
> > +
> > +
> > + if (prefix_dir) heap_free(prefix_dir);
> > }
> >
> > /***********************************************************
> *******************
> > @@ -6126,15 +6133,139 @@ static void register_system_knownfolders(void)
> > }
> > }
> >
> > +/**********************************************************
> ********************
> > + * parse_symlinks_env_variable [Internal]
> > + *
> > + * PARAMS
> > + * xdg_dir [I] Unix XDG directory name (without "XDG_" prefix and
> "_DIR" suffix).
> > + *
> > + * RETURNS
> > + * TRUE "WINESYMLINK" contains the specified XDG directory string.
> > + * FALSE "WINESYMLINK" does not contain the specified XDG directory
> string.
> > + * Any error condition.
> > + *
> > + * Note: "WINESYMLINK", defaults to "ALL", when unset. This enables all
> symlinks.
> > + * "WINESYMLINK" can also be manually set to "ALL", enabling all
> symlinks.
> > + *
> > + */
> > +static BOOL parse_symlinks_env_variable(const char * xdg_dir)
> > +{
> > + static const char * matchall_env_var = "ALL";
> > + static char env_var[MAX_PATH+1];
> > + static char * env_winesymlink = NULL;
> > + char * sstr, * sstr_stop, * tstr, * word_start = NULL;
> > + char seperator_ch = '\0';
> > + BOOL end_string, in_word = FALSE, matched = FALSE;
> > + size_t slength = 0, matchall_length, xdg_dir_slength;
> > +
> > + if (!xdg_dir) return matched;
> > +
> > + if (!env_winesymlink)
> > + {
> > + env_winesymlink = getenv("WINESYMLINK");
> > + if (!env_winesymlink) env_winesymlink = (char*)
> matchall_env_var;
> > + tstr = env_var;
> > + sstr_stop = env_winesymlink + MAX_PATH;
> > + for (sstr = env_winesymlink; *sstr && (sstr != sstr_stop);
> ++sstr)
> > + {
> > + if (!seperator_ch && (ispunct(*sstr) || isspace(*sstr)))
> > + seperator_ch = *sstr;
> > + if (!isalpha(*sstr)) continue;
> > +
> > + if (seperator_ch && in_word)
> > + {
> > + *tstr++ = seperator_ch;
> > + ++slength;
> > + }
> > + in_word = TRUE;
> > + seperator_ch = '\0';
> > + *tstr++ = toupper(*sstr);
> > + ++slength;
> > + }
> > + env_var[slength] = '\0';
> > + }
> > +
> > + TRACE("processed env variable: %s\n", debugstr_a(&env_var[0]));
> > + matchall_length = strlen(matchall_env_var);
> > + xdg_dir_slength = strlen(xdg_dir);
> > + end_string = !(env_var[0]);
> > + for (sstr = &env_var[0]; !end_string; ++sstr)
> > + {
> > + end_string = !(*(sstr+1));
> > + in_word = isalpha(*sstr);
> > + if (in_word && !word_start) word_start = sstr;
> > + in_word = in_word && !end_string;
> > + if (in_word || !word_start) continue;
> > +
> > + slength = ((size_t) (sstr-word_start))+(end_string ? 1 : 0);
> > + matched = ( (slength == xdg_dir_slength) &&
> (strncmp(word_start, xdg_dir, slength) == 0) )
> > + || ( (slength == matchall_length) &&
> (strncmp(word_start, "ALL", slength) == 0) );
> > + if (matched) break;
> > +
> > + word_start = NULL;
> > + }
> > + TRACE("%s symlinking for %s\n", matched ? "Enabled" : "Disabled",
> debugstr_a(xdg_dir));
> > +
> > + return matched;
> > +}
> > +
> > +/**********************************************************
> ********************
> > + * create_homedir_symbolic_links [Internal]
> > + *
> > + * Parse WINESYMLINK env variable, for each XDG directory argument. To
> test if symlinking is
> > + * enabled for that XDG directory / Wine Profile Folder.
> > + * Then calls the function create_homedir_symbolic_link to potentially
> symlink from a Shell Folder,
> > + * in the current Wineprefix, to a subdirectory of the current user's
> HOME directory.
> > + *
> > + * PARAMS
> > + * xdg_dirnames [I] Pointer to an array of Unix XDG directory names
> > + * (without "XDG_" prefix and "_DIR" suffix).
> > + * xdg_dir_count [I] Item count of array (above).
> > + *
> > + */
> > +static void create_homedir_symbolic_links(const char * const
> xdg_dirnames[], const UINT xdg_dir_count)
> > +{
> > + char ** xdg_dirs_array;
> > + char * xdg_dir;
> > + HRESULT hr;
> > + UINT i;
> > + BOOL env_enabled;
> > +
> > + if (!xdg_dirnames) return;
> > +
> > + hr = XDG_UserDirLookup(xdg_dirnames, xdg_dir_count,
> &xdg_dirs_array);
> > + if (FAILED(hr)) xdg_dirs_array = NULL;
> > +
> > + for (i = 0; i < xdg_dir_count; ++i)
> > + {
> > + env_enabled = parse_symlinks_env_variable(xdg_dirnames[i]);
> > + xdg_dir = xdg_dirs_array ? xdg_dirs_array[i] : NULL;
> > + if (!strcmp(xdg_dirnames[i],"DOCUMENTS"))
> > + create_homedir_symbolic_link(env_enabled, IDS_PERSONAL,
> CSIDL_PERSONAL, xdg_dir, DocumentsW);
> > + else if (!strcmp(xdg_dirnames[i],"PICTURES"))
> > + create_homedir_symbolic_link(env_enabled, IDS_MYPICTURES,
> CSIDL_MYPICTURES, xdg_dir, PicturesW);
> > + else if (!strcmp(xdg_dirnames[i],"VIDEOS"))
> > + create_homedir_symbolic_link(env_enabled, IDS_MYVIDEOS,
> CSIDL_MYVIDEO, xdg_dir, MoviesW);
> > + else if (!strcmp(xdg_dirnames[i],"MUSIC"))
> > + create_homedir_symbolic_link(env_enabled, IDS_MYMUSIC,
> CSIDL_MYMUSIC, xdg_dir, MusicW);
> > + else if (!strcmp(xdg_dirnames[i],"DESKTOP"))
> > + create_homedir_symbolic_link(env_enabled,
> IDS_DESKTOPDIRECTORY, CSIDL_DESKTOPDIRECTORY, xdg_dir, DesktopW);
> > + else
> > + ERR("XDG directory name specifier invalid: %s\n",
> debugstr_a(xdg_dirnames[i]));
> > + if (xdg_dir) heap_free(xdg_dirs_array[i]);
> > + }
> > + if (xdg_dirs_array) heap_free(xdg_dirs_array);
> > +}
> > +
> > HRESULT SHELL_RegisterShellFolders(void)
> > {
> > + static const char * const xdg_dirnames[] = { "DOCUMENTS",
> "PICTURES", "VIDEOS", "MUSIC", "DESKTOP" };
> > + const UINT xdg_dir_count = 5;
> > HRESULT hr;
> >
> > - /* Set up '$HOME' targeted symlinks for 'My Documents', 'My
> Pictures',
> > - * 'My Videos', 'My Music' and 'Desktop' in advance, so that the
> > - * _SHRegister*ShellFolders() functions will find everything nice
> and clean
> > - * and thus will not attempt to create them in the profile
> directory. */
> > - _SHCreateSymbolicLinks();
> > + /* Early setup of symlinks from specific User Shell Folders, in
> > + * current Wineprefix, to subdirecties of the user's HOME
> directory. */
> > + create_homedir_symbolic_links(xdg_dirnames, xdg_dir_count);
> >
> > hr = _SHRegisterUserShellFolders(TRUE);
> > if (SUCCEEDED(hr))
> > @@ -6147,5 +6278,6 @@ HRESULT SHELL_RegisterShellFolders(void)
> > hr = set_folder_attributes();
> > if (SUCCEEDED(hr))
> > register_system_knownfolders();
> > +
> > return hr;
> > }
> > --
> > 2.17.1
> >
> >
> >
>
June 7, 2018
Re: [PATCH 1/5] wined3d: Declare the correct number and type of fragment shader color outputs.
by Henri Verbeet
On 6 June 2018 at 00:32, Matteo Bruni <mbruni(a)codeweavers.com> wrote:
> Signed-off-by: Matteo Bruni <mbruni(a)codeweavers.com>
> ---
> dlls/wined3d/glsl_shader.c | 116 ++++++++++++++++++++++++++++++++++++++++++---
> 1 file changed, 109 insertions(+), 7 deletions(-)
>
This fails the tests here:
../../../../../src/wine-git/tools/runtest -q -P wine -T ../../.. -M
d3d10core.dll -p d3d10core_test.exe.so device && touch device.ok
device.c:11300: Test failed: Got 0xff0000ff, expected 0xff00ff00 at
(0, 0), sub-resource 0.
device.c:11307: Test failed: Got 0xff0000ff, expected 0xff00ff00 at
(0, 0), sub-resource 0.
../../../../../src/wine-git/tools/runtest -q -P wine -T ../../.. -M
d3d11.dll -p d3d11_test.exe.so d3d11 && touch d3d11.ok
d3d11.c:14777: Test failed: Got 0xff0000ff, expected 0xff00ff00 at (0,
0, 0), sub-resource 0.
d3d11.c:14784: Test failed: Got 0xff0000ff, expected 0xff00ff00 at (0,
0, 0), sub-resource 0.
d3d11.c:20644: Tests skipped: Raw buffers are not supported.
d3d11.c:20644: Tests skipped: Raw buffers are not supported.
June 7, 2018
Re: [PATCH vkd3d] libs/vkd3d: Use VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT only if array size is >= 6.
by Henri Verbeet
Signed-off-by: Henri Verbeet <hverbeet(a)codeweavers.com>
June 7, 2018
Re: [PATCH vkd3d] libs/vkd3d: Use VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT only if array size is >= 6.
by Józef Kucia
On Thu, Jun 7, 2018 at 4:03 PM, Henri Verbeet <hverbeet(a)gmail.com> wrote:
> On 7 June 2018 at 15:52, Józef Kucia <joseph.kucia(a)gmail.com> wrote:
>> + if (desc->Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D
>> + && desc->Width == desc->Height && desc->DepthOrArraySize >= 6)
>> image_info.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
> Should that be a multiple of 6? I.e., are partial cubemap arrays a
> thing in Direct3D 12?
I think you can create a cube shader resource view for 2D textures,
even if the array size is not a multiple of 6. At least, it works that
way in Direct3D 11. We have some tests in
test_create_shader_resource_view().
June 7, 2018
Re: [PATCH vkd3d] libs/vkd3d: Use VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT only if array size is >= 6.
by Henri Verbeet
On 7 June 2018 at 15:52, Józef Kucia <joseph.kucia(a)gmail.com> wrote:
> + if (desc->Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D
> + && desc->Width == desc->Height && desc->DepthOrArraySize >= 6)
> image_info.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
Should that be a multiple of 6? I.e., are partial cubemap arrays a
thing in Direct3D 12?
June 7, 2018
[PATCH vkd3d] libs/vkd3d: Use VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT only if array size is >= 6.
by Józef Kucia
From: Józef Kucia <jkucia(a)codeweavers.com>
Signed-off-by: Józef Kucia <jkucia(a)codeweavers.com>
---
libs/vkd3d/resource.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/libs/vkd3d/resource.c b/libs/vkd3d/resource.c
index acd3bada63ec..2ccb84797514 100644
--- a/libs/vkd3d/resource.c
+++ b/libs/vkd3d/resource.c
@@ -132,7 +132,8 @@ static HRESULT vkd3d_create_image(struct d3d12_resource *resource, struct d3d12_
image_info.flags = 0;
if (!(desc->Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) && dxgi_format_is_typeless(desc->Format))
image_info.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
- if (desc->Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D && desc->Width == desc->Height)
+ if (desc->Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D
+ && desc->Width == desc->Height && desc->DepthOrArraySize >= 6)
image_info.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
image_info.imageType = vk_image_type_from_d3d12_resource_dimension(desc->Dimension);
--
2.16.4
June 7, 2018
Re: [PATCH v2 1/4] kernel32: Return BIOS info from GetSystemFirmwareTable on Linux
by Huw Davies
On Mon, May 28, 2018 at 10:05:26PM -0600, Alex Henrie wrote:
> /***********************************************************************
> * GetSystemFirmwareTable (KERNEL32.@)
> */
> UINT WINAPI GetSystemFirmwareTable(DWORD provider, DWORD id, PVOID buffer, DWORD size)
> {
> - FIXME("(%d %d %p %d):stub\n", provider, id, buffer, size);
> + FIXME("(%08x %08x %p %d): semi-stub\n", provider, id, buffer, size);
This should probably be forwarded to
NtQuerySystemInformation(SystemFirmwareTableInformation, ...).
Huw.
June 7, 2018
Re: [PATCH] shellpath.c: Fix creation of HOME directory symbolic links
by Huw Davies
On Wed, Jun 06, 2018 at 08:46:49AM +0100, Rob Walker wrote:
> Fixes: https://bugs.winehq.org/show_bug.cgi?id=41668 (1)
> https://bugs.winehq.org/show_bug.cgi?id=28216 (2)
>
> (1) Introduces a "WINESYMLINK" env variable that allows a user to
> control what shell folder links are created to the HOME folder, when
> a WINEPREFIX is initially created.
> The Wineprefix Shell Folders, that Wine symlinks to the user's HOME directory,
> are only (re-)created on each Wine boot if they: do not pre-exist or are broken
> symbolic links.
>
> (2) The XDG Directory specification automatically falls back to the user HOME
> directory for any XDG_*_DIR that does not exist (xdg-user-dirs-update).
> The implemented solution is to block Wine from symlinking directly
> to HOME directory.
Hi Rob,
There's an awful lot going on in this patch which makes it very
difficult to review. Please avoid re-naming and re-indenting
existing functions so that we can actually see the real changes.
Then please split the changes into separate patches. A clue
that you're not doing this is that you have a list of two
items in the commit message. If the patch is doing two
things, it should be two patches.
Huw.
>
> Test on Gentoo GNU/Linux.
>
> Signed-off-by: Rob Walker <bob.mt.wya(a)gmail.com>
> ---
> dlls/shell32/shellpath.c | 554 ++++++++++++++++++++++++---------------
> 1 file changed, 343 insertions(+), 211 deletions(-)
>
> diff --git a/dlls/shell32/shellpath.c b/dlls/shell32/shellpath.c
> index a551e93aa8..b8c74273c6 100644
> --- a/dlls/shell32/shellpath.c
> +++ b/dlls/shell32/shellpath.c
> @@ -4326,252 +4326,259 @@ static HRESULT _SHRegisterCommonShellFolders(void)
> }
>
> /******************************************************************************
> - * _SHAppendToUnixPath [Internal]
> + * append_to_unix_path [Internal]
> *
> - * Helper function for _SHCreateSymbolicLinks. Appends pwszSubPath (or the
> - * corresponding resource, if IS_INTRESOURCE) to the unix base path 'szBasePath'
> - * and replaces backslashes with slashes.
> + * Helper function for create_home_subdir_symbolic_link.
> + * Appends 'subpath', or the corresponding resource (if it is a
> + * resource identifier), to the Unix base path 'base_dir'.
> + * Replace backslashes with forward slashes.
> *
> * PARAMS
> - * szBasePath [IO] The unix base path, which will be appended to (CP_UNXICP).
> - * pwszSubPath [I] Sub-path or resource id (use MAKEINTRESOURCEW).
> + * base_dir [IO] The Unix base path, which will be appended to (CP_UNXICP).
> + * subpath [I] Sub-path or resource id (use MAKEINTRESOURCEW, pre-call).
> *
> * RETURNS
> - * Success: TRUE,
> + * Success: TRUE
> * Failure: FALSE
> */
> -static inline BOOL _SHAppendToUnixPath(char *szBasePath, LPCWSTR pwszSubPath) {
> - WCHAR wszSubPath[MAX_PATH];
> - int cLen = strlen(szBasePath);
> - char *pBackslash;
> -
> - if (IS_INTRESOURCE(pwszSubPath)) {
> - if (!LoadStringW(shell32_hInstance, LOWORD(pwszSubPath), wszSubPath, MAX_PATH)) {
> - /* Fall back to hard coded defaults. */
> - switch (LOWORD(pwszSubPath)) {
> - case IDS_PERSONAL:
> - lstrcpyW(wszSubPath, DocumentsW);
> - break;
> - case IDS_MYMUSIC:
> - lstrcpyW(wszSubPath, My_MusicW);
> - break;
> - case IDS_MYPICTURES:
> - lstrcpyW(wszSubPath, My_PicturesW);
> - break;
> - case IDS_MYVIDEOS:
> - lstrcpyW(wszSubPath, My_VideosW);
> - break;
> - default:
> - ERR("LoadString(%d) failed!\n", LOWORD(pwszSubPath));
> - return FALSE;
> - }
> +static inline BOOL append_to_unix_path(char *base_dir, LPCWSTR subpath)
> +{
> + WCHAR ws_subpath[MAX_PATH];
> + int cLen = strlen(base_dir);
> + char *back_slash;
> +
> + if (!IS_INTRESOURCE(subpath))
> + {
> + lstrcpyW(ws_subpath, subpath);
> + }
> + else if (!LoadStringW(shell32_hInstance, LOWORD(subpath), ws_subpath, MAX_PATH))
> + {
> + /* Fall back to hard coded defaults. */
> + switch (LOWORD(subpath)) {
> + case IDS_PERSONAL:
> + lstrcpyW(ws_subpath, DocumentsW);
> + break;
> + case IDS_MYMUSIC:
> + lstrcpyW(ws_subpath, My_MusicW);
> + break;
> + case IDS_MYPICTURES:
> + lstrcpyW(ws_subpath, My_PicturesW);
> + break;
> + case IDS_MYVIDEOS:
> + lstrcpyW(ws_subpath, My_VideosW);
> + break;
> + case IDS_DESKTOP:
> + lstrcpyW(ws_subpath, DesktopW);
> + break;
> + default:
> + ERR("LoadString(%d) failed!\n", LOWORD(subpath));
> + return FALSE;
> }
> - } else {
> - lstrcpyW(wszSubPath, pwszSubPath);
> }
> -
> - if (szBasePath[cLen-1] != '/') szBasePath[cLen++] = '/';
> -
> - if (!WideCharToMultiByte(CP_UNIXCP, 0, wszSubPath, -1, szBasePath + cLen,
> +
> + if (!cLen || (base_dir[cLen-1] != '/')) base_dir[cLen++] = '/';
> +
> + if (!WideCharToMultiByte(CP_UNIXCP, 0, ws_subpath, -1, base_dir + cLen,
> FILENAME_MAX - cLen, NULL, NULL))
> {
> return FALSE;
> }
> -
> - pBackslash = szBasePath + cLen;
> - while ((pBackslash = strchr(pBackslash, '\\'))) *pBackslash = '/';
> -
> +
> + back_slash = base_dir + cLen;
> + while ((back_slash = strchr(back_slash, '\\'))) *back_slash = '/';
> +
> return TRUE;
> }
>
> /******************************************************************************
> - * _SHCreateSymbolicLinks [Internal]
> + * compare_pathft_to_bootft [Internal]
> *
> - * Sets up symbol links for various shell folders to point into the users home
> - * directory. We do an educated guess about what the user would probably want:
> - * - If there is a 'My Documents' directory in $HOME, the user probably wants
> - * wine's 'My Documents' to point there. Furthermore, we imply that the user
> - * is a Windows lover and has no problem with wine creating 'My Pictures',
> - * 'My Music' and 'My Videos' subfolders under '$HOME/My Documents', if those
> - * do not already exits. We put appropriate symbolic links in place for those,
> - * too.
> - * - If there is no 'My Documents' directory in $HOME, we let 'My Documents'
> - * point directly to $HOME. We assume the user to be a unix hacker who does not
> - * want wine to create anything anywhere besides the .wine directory. So, if
> - * there already is a 'My Music' directory in $HOME, we symlink the 'My Music'
> - * shell folder to it. But if not, then we check XDG_MUSIC_DIR - "well known"
> - * directory, and try to link to that. If that fails, then we symlink to
> - * $HOME directly. The same holds fo 'My Pictures' and 'My Videos'.
> - * - The Desktop shell folder is symlinked to XDG_DESKTOP_DIR. If that does not
> - * exist, then we try '$HOME/Desktop'. If that does not exist, then we leave
> - * it alone.
> - * ('My Music',... above in fact means LoadString(IDS_MYMUSIC))
> + * Function to compare the FILETIME of ws_path (a Windows path) to the (estimated)
> + * Wine System boot time.
> + *
> + * PARAMS
> + * ws_path [I] The Windows path, for which provides the FS write time.
> + * time_difference [O] -1 Windows path last written before time of Wine boot.
> + * 0 Windows path last written at time of Wine boot.
> + * +1 Windows path last written after time of Wine boot.
> + * RETURNS
> + * Success: S_OK
> + * Failure: E_FAIL
> + *
> + * NOTES
> + * Pre-allocate storage for time_difference pointer variable externally.
> */
> -static void _SHCreateSymbolicLinks(void)
> -{
> - UINT aidsMyStuff[] = { IDS_MYPICTURES, IDS_MYVIDEOS, IDS_MYMUSIC }, i;
> - const WCHAR* MyOSXStuffW[] = { PicturesW, MoviesW, MusicW };
> - int acsidlMyStuff[] = { CSIDL_MYPICTURES, CSIDL_MYVIDEO, CSIDL_MYMUSIC };
> - static const char * const xdg_dirs[] = { "PICTURES", "VIDEOS", "MUSIC", "DOCUMENTS", "DESKTOP" };
> - static const unsigned int num = ARRAY_SIZE(xdg_dirs);
> - WCHAR wszTempPath[MAX_PATH];
> - char szPersonalTarget[FILENAME_MAX], *pszPersonal;
> - char szMyStuffTarget[FILENAME_MAX], *pszMyStuff;
> - char szDesktopTarget[FILENAME_MAX], *pszDesktop;
> - struct stat statFolder;
> - const char *pszHome;
> - HRESULT hr;
> - char ** xdg_results;
> - char * xdg_desktop_dir;
> -
> - /* Create all necessary profile sub-dirs up to 'My Documents' and get the unix path. */
> - hr = SHGetFolderPathW(NULL, CSIDL_PERSONAL|CSIDL_FLAG_CREATE, NULL,
> - SHGFP_TYPE_DEFAULT, wszTempPath);
> - if (FAILED(hr)) return;
> - pszPersonal = wine_get_unix_file_name(wszTempPath);
> - if (!pszPersonal) return;
> +static HRESULT compare_pathft_to_bootft(const WCHAR * ws_path, int * time_difference)
> +{
> + static ULONG64 time_system_start = 0;
> + SYSTEMTIME system_time_st;
> + FILETIME dummy_ft, system_time_ft, last_write_ft;
> + ULONG64 tick_count, time_last_write;
> + HANDLE fhandle;
> + DWORD ft_errorc = 0;
>
> - hr = XDG_UserDirLookup(xdg_dirs, num, &xdg_results);
> - if (FAILED(hr)) xdg_results = NULL;
> + if (!time_difference) return E_FAIL;
>
> - pszHome = getenv("HOME");
> - if (pszHome && !stat(pszHome, &statFolder) && S_ISDIR(statFolder.st_mode))
> + if (!time_system_start)
> {
> - while (1)
> + tick_count = (ULONG64) GetTickCount64();
> + GetSystemTime(&system_time_st);
> + if (!SystemTimeToFileTime( &system_time_st, &system_time_ft))
> {
> - /* Check if there's already a Wine-specific 'My Documents' folder */
> - strcpy(szPersonalTarget, pszHome);
> - if (_SHAppendToUnixPath(szPersonalTarget, MAKEINTRESOURCEW(IDS_PERSONAL)) &&
> - !stat(szPersonalTarget, &statFolder) && S_ISDIR(statFolder.st_mode))
> - {
> - /* '$HOME/My Documents' exists. Create 'My Pictures',
> - * 'My Videos' and 'My Music' subfolders or fail silently if
> - * they already exist.
> - */
> - for (i = 0; i < ARRAY_SIZE(aidsMyStuff); i++)
> - {
> - strcpy(szMyStuffTarget, szPersonalTarget);
> - if (_SHAppendToUnixPath(szMyStuffTarget, MAKEINTRESOURCEW(aidsMyStuff[i])))
> - mkdir(szMyStuffTarget, 0777);
> - }
> - break;
> - }
> -
> - /* Try to point to the XDG Documents folder */
> - if (xdg_results && xdg_results[num-2] &&
> - !stat(xdg_results[num-2], &statFolder) &&
> - S_ISDIR(statFolder.st_mode))
> - {
> - strcpy(szPersonalTarget, xdg_results[num-2]);
> - break;
> - }
> -
> - /* Or the hardcoded / OS X Documents folder */
> - strcpy(szPersonalTarget, pszHome);
> - if (_SHAppendToUnixPath(szPersonalTarget, DocumentsW) &&
> - !stat(szPersonalTarget, &statFolder) &&
> - S_ISDIR(statFolder.st_mode))
> - break;
> -
> - /* As a last resort point to $HOME. */
> - strcpy(szPersonalTarget, pszHome);
> - break;
> + ERR("SystemTimeToFileTime call failed (%d)\n", GetLastError());
> + return E_FAIL;
> }
> -
> - /* Replace 'My Documents' directory with a symlink or fail silently if not empty. */
> - remove(pszPersonal);
> - symlink(szPersonalTarget, pszPersonal);
> + time_system_start = (((ULONG64)system_time_ft.dwHighDateTime) << 32)
> + + system_time_ft.dwLowDateTime
> + - (tick_count * 10000);
> }
> - else
> + fhandle = CreateFileW( ws_path, 0, FILE_SHARE_READ, NULL,
> + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
> + if (fhandle == INVALID_HANDLE_VALUE)
> {
> - /* '$HOME' doesn't exist. Create 'My Pictures', 'My Videos' and 'My Music' subdirs
> - * in '%USERPROFILE%\\My Documents' or fail silently if they already exist. */
> - pszHome = NULL;
> - strcpy(szPersonalTarget, pszPersonal);
> - for (i = 0; i < ARRAY_SIZE(aidsMyStuff); i++) {
> - strcpy(szMyStuffTarget, szPersonalTarget);
> - if (_SHAppendToUnixPath(szMyStuffTarget, MAKEINTRESOURCEW(aidsMyStuff[i])))
> - mkdir(szMyStuffTarget, 0777);
> - }
> + ERR("Open file handle failed for path: %s (%d)\n",
> + debugstr_w(ws_path), GetLastError());
> + return E_FAIL;
> }
> -
> - /* Create symbolic links for 'My Pictures', 'My Videos' and 'My Music'. */
> - for (i=0; i < ARRAY_SIZE(aidsMyStuff); i++)
> + if (!GetFileTime(fhandle, &dummy_ft, &dummy_ft, &last_write_ft))
> + ft_errorc = GetLastError();
> + if (!CloseHandle(fhandle))
> {
> - /* Create the current 'My Whatever' folder and get its unix path. */
> - hr = SHGetFolderPathW(NULL, acsidlMyStuff[i]|CSIDL_FLAG_CREATE, NULL,
> - SHGFP_TYPE_DEFAULT, wszTempPath);
> - if (FAILED(hr)) continue;
> -
> - pszMyStuff = wine_get_unix_file_name(wszTempPath);
> - if (!pszMyStuff) continue;
> -
> - while (1)
> - {
> - /* Check for the Wine-specific '$HOME/My Documents' subfolder */
> - strcpy(szMyStuffTarget, szPersonalTarget);
> - if (_SHAppendToUnixPath(szMyStuffTarget, MAKEINTRESOURCEW(aidsMyStuff[i])) &&
> - !stat(szMyStuffTarget, &statFolder) && S_ISDIR(statFolder.st_mode))
> - break;
> + ERR("Close file handle failed: %p (%d)\n",
> + fhandle, GetLastError());
> + return E_FAIL;
> + }
> + if (ft_errorc)
> + {
> + ERR("Get file time failed: %p (%d)\n",
> + fhandle, ft_errorc);
> + return E_FAIL;
> + }
> + time_last_write = (((ULONG64)last_write_ft.dwHighDateTime) << 32)
> + + last_write_ft.dwLowDateTime;
> + *time_difference = ((time_last_write > time_system_start) ? 1 :
> + ((time_last_write < time_system_start) ? -1 :
> + 0));
>
> - /* Try the XDG_XXX_DIR folder */
> - if (xdg_results && xdg_results[i])
> - {
> - strcpy(szMyStuffTarget, xdg_results[i]);
> - break;
> - }
> + return S_OK;
> +}
>
> - /* Or the OS X folder (these are never localized) */
> - if (pszHome)
> - {
> - strcpy(szMyStuffTarget, pszHome);
> - if (_SHAppendToUnixPath(szMyStuffTarget, MyOSXStuffW[i]) &&
> - !stat(szMyStuffTarget, &statFolder) &&
> - S_ISDIR(statFolder.st_mode))
> - break;
> - }
> +/******************************************************************************
> + * create_homedir_symbolic_link [Internal]
> + *
> + * Creates a symbolic link from the current Wineprefix to an appropriate
> + * HOME subdirectory (if one is found).
> + *
> + * Creates 'XXXX' directory in Wineprefix.
> + * Then create a 'My XXXX' symbolic link in Wineprefix:
> + * 1) If '$HOME/XXXX' (IDS directory) exists then target this.
> + * 2) If '$HOME/XXXX' (XDG_XXXX_DIR) exists then target this.
> + * 3) If '$HOME/XXXX' (MacOS XXXX media directory) exists then target this.
> + *
> + * PARAMS
> + * env_enabled [I]
> + * TRUE The user has enabled this Shell Folder to be symlinked (default).
> + * FALSE The user has chosen to disable symlinking this Shell Folder.
> + * The Shell Folder will be created, in the current Wineprefix (if not pre-existing).
> + * ids_dir [I] Windows Resource Identifier code for current Shell Folder.
> + * csidl_dir [I] Constant Special Item ID List identifier for current Shell Folder.
> + * xdg_dir [I] Full path of external Unix XDG directory corresponding to current Shell Folder.
> + * ws_osx_dir [I] Fallback directory name to use, corresponding to current Shell Folder (OSX specific).
> + *
> + */
> +void create_homedir_symbolic_link(BOOL env_enabled,
> + UINT ids_dir,
> + int csidl_dir,
> + const char * xdg_dir,
> + const WCHAR * ws_osx_dir)
> +{
> + static const char * env_homedir = NULL;
> + WCHAR ws_temp_path[MAX_PATH];
> + char home_target[FILENAME_MAX], * prefix_dir;
> + struct stat stat_folder, stat_home_folder;
> + HRESULT hr;
> + BOOL target_ok;
> + int time_difference;
>
> - /* As a last resort point to the same location as 'My Documents' */
> - strcpy(szMyStuffTarget, szPersonalTarget);
> - break;
> + hr = SHGetFolderPathW(NULL, csidl_dir, NULL,
> + SHGFP_TYPE_DEFAULT, ws_temp_path);
> + if (SUCCEEDED(hr))
> + {
> + if (ids_dir != IDS_DESKTOPDIRECTORY) return;
> +
> + if ((compare_pathft_to_bootft(ws_temp_path, &time_difference) == S_OK)
> + && (time_difference < 0))
> + {
> + TRACE("%s directory created before wineboot\n",
> + debugstr_w(ws_temp_path));
> + return;
> }
> - remove(pszMyStuff);
> - symlink(szMyStuffTarget, pszMyStuff);
> - heap_free(pszMyStuff);
> }
> -
> - /* Last but not least, the Desktop folder */
> - if (pszHome)
> - strcpy(szDesktopTarget, pszHome);
> + else if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
> + {
> + hr = SHGetFolderPathW(NULL, csidl_dir|CSIDL_FLAG_CREATE, NULL,
> + SHGFP_TYPE_DEFAULT, ws_temp_path);
> + if (FAILED(hr)) return;
> + }
> else
> - strcpy(szDesktopTarget, pszPersonal);
> - heap_free(pszPersonal);
> + {
> + ERR("Failed to get Wineprefix path corresponding to %d CSIDL\n",
> + csidl_dir);
> + return;
> + }
> + if (!env_enabled) return;
> + prefix_dir = wine_get_unix_file_name(ws_temp_path);
> + if (!prefix_dir)
> + {
> + ERR("Failed to get Unix Wineprefix directory for: %s\n",
> + debugstr_w(ws_temp_path));
> + return;
> + }
> + if (!env_homedir) env_homedir = getenv("HOME");
> + if (!(env_homedir
> + && (stat(env_homedir, &stat_home_folder) != 1)
> + && S_ISDIR(stat_home_folder.st_mode)))
> + {
> + if (prefix_dir) heap_free(prefix_dir);
> + return;
> + }
>
> - xdg_desktop_dir = xdg_results ? xdg_results[num - 1] : NULL;
> - if (xdg_desktop_dir ||
> - (_SHAppendToUnixPath(szDesktopTarget, DesktopW) &&
> - !stat(szDesktopTarget, &statFolder) && S_ISDIR(statFolder.st_mode)))
> + /* Check for:
> + * '$HOME/XXXX' (IDS directory)
> + * or '$HOME/XXXX' (XDG directory)
> + * or '$HOME/XXXX' (MacOS directory)
> + */
> + target_ok = FALSE;
> + strcpy(home_target, env_homedir);
> + if (append_to_unix_path(home_target, MAKEINTRESOURCEW(ids_dir))
> + && (stat(home_target, &stat_folder) != -1)
> + && S_ISDIR(stat_folder.st_mode))
> {
> - hr = SHGetFolderPathW(NULL, CSIDL_DESKTOPDIRECTORY|CSIDL_FLAG_CREATE, NULL,
> - SHGFP_TYPE_DEFAULT, wszTempPath);
> - if (SUCCEEDED(hr) && (pszDesktop = wine_get_unix_file_name(wszTempPath)))
> - {
> - remove(pszDesktop);
> - if (xdg_desktop_dir)
> - symlink(xdg_desktop_dir, pszDesktop);
> - else
> - symlink(szDesktopTarget, pszDesktop);
> - heap_free(pszDesktop);
> - }
> + target_ok = TRUE;
> + }
> + else if (xdg_dir && stat(xdg_dir, &stat_folder))
> + {
> + strcpy(home_target, xdg_dir);
> + /* Only link to the XDG directory, if it does not point directly
> + * to the user's HOME directory (XDG specification fallback path). */
> + target_ok = (stat_folder.st_ino != stat_home_folder.st_ino);
> + }
> + else
> + {
> + strcpy(home_target, env_homedir);
> + target_ok = append_to_unix_path(home_target, ws_osx_dir)
> + && (stat(home_target, &stat_folder) != -1)
> + && S_ISDIR(stat_folder.st_mode);
> }
>
> - /* Free resources allocated by XDG_UserDirLookup() */
> - if (xdg_results)
> + if (target_ok)
> {
> - for (i = 0; i < num; i++)
> - heap_free(xdg_results[i]);
> - heap_free(xdg_results);
> + remove(prefix_dir);
> + symlink(home_target, prefix_dir);
> }
> +
> +
> + if (prefix_dir) heap_free(prefix_dir);
> }
>
> /******************************************************************************
> @@ -6126,15 +6133,139 @@ static void register_system_knownfolders(void)
> }
> }
>
> +/******************************************************************************
> + * parse_symlinks_env_variable [Internal]
> + *
> + * PARAMS
> + * xdg_dir [I] Unix XDG directory name (without "XDG_" prefix and "_DIR" suffix).
> + *
> + * RETURNS
> + * TRUE "WINESYMLINK" contains the specified XDG directory string.
> + * FALSE "WINESYMLINK" does not contain the specified XDG directory string.
> + * Any error condition.
> + *
> + * Note: "WINESYMLINK", defaults to "ALL", when unset. This enables all symlinks.
> + * "WINESYMLINK" can also be manually set to "ALL", enabling all symlinks.
> + *
> + */
> +static BOOL parse_symlinks_env_variable(const char * xdg_dir)
> +{
> + static const char * matchall_env_var = "ALL";
> + static char env_var[MAX_PATH+1];
> + static char * env_winesymlink = NULL;
> + char * sstr, * sstr_stop, * tstr, * word_start = NULL;
> + char seperator_ch = '\0';
> + BOOL end_string, in_word = FALSE, matched = FALSE;
> + size_t slength = 0, matchall_length, xdg_dir_slength;
> +
> + if (!xdg_dir) return matched;
> +
> + if (!env_winesymlink)
> + {
> + env_winesymlink = getenv("WINESYMLINK");
> + if (!env_winesymlink) env_winesymlink = (char*) matchall_env_var;
> + tstr = env_var;
> + sstr_stop = env_winesymlink + MAX_PATH;
> + for (sstr = env_winesymlink; *sstr && (sstr != sstr_stop); ++sstr)
> + {
> + if (!seperator_ch && (ispunct(*sstr) || isspace(*sstr)))
> + seperator_ch = *sstr;
> + if (!isalpha(*sstr)) continue;
> +
> + if (seperator_ch && in_word)
> + {
> + *tstr++ = seperator_ch;
> + ++slength;
> + }
> + in_word = TRUE;
> + seperator_ch = '\0';
> + *tstr++ = toupper(*sstr);
> + ++slength;
> + }
> + env_var[slength] = '\0';
> + }
> +
> + TRACE("processed env variable: %s\n", debugstr_a(&env_var[0]));
> + matchall_length = strlen(matchall_env_var);
> + xdg_dir_slength = strlen(xdg_dir);
> + end_string = !(env_var[0]);
> + for (sstr = &env_var[0]; !end_string; ++sstr)
> + {
> + end_string = !(*(sstr+1));
> + in_word = isalpha(*sstr);
> + if (in_word && !word_start) word_start = sstr;
> + in_word = in_word && !end_string;
> + if (in_word || !word_start) continue;
> +
> + slength = ((size_t) (sstr-word_start))+(end_string ? 1 : 0);
> + matched = ( (slength == xdg_dir_slength) && (strncmp(word_start, xdg_dir, slength) == 0) )
> + || ( (slength == matchall_length) && (strncmp(word_start, "ALL", slength) == 0) );
> + if (matched) break;
> +
> + word_start = NULL;
> + }
> + TRACE("%s symlinking for %s\n", matched ? "Enabled" : "Disabled", debugstr_a(xdg_dir));
> +
> + return matched;
> +}
> +
> +/******************************************************************************
> + * create_homedir_symbolic_links [Internal]
> + *
> + * Parse WINESYMLINK env variable, for each XDG directory argument. To test if symlinking is
> + * enabled for that XDG directory / Wine Profile Folder.
> + * Then calls the function create_homedir_symbolic_link to potentially symlink from a Shell Folder,
> + * in the current Wineprefix, to a subdirectory of the current user's HOME directory.
> + *
> + * PARAMS
> + * xdg_dirnames [I] Pointer to an array of Unix XDG directory names
> + * (without "XDG_" prefix and "_DIR" suffix).
> + * xdg_dir_count [I] Item count of array (above).
> + *
> + */
> +static void create_homedir_symbolic_links(const char * const xdg_dirnames[], const UINT xdg_dir_count)
> +{
> + char ** xdg_dirs_array;
> + char * xdg_dir;
> + HRESULT hr;
> + UINT i;
> + BOOL env_enabled;
> +
> + if (!xdg_dirnames) return;
> +
> + hr = XDG_UserDirLookup(xdg_dirnames, xdg_dir_count, &xdg_dirs_array);
> + if (FAILED(hr)) xdg_dirs_array = NULL;
> +
> + for (i = 0; i < xdg_dir_count; ++i)
> + {
> + env_enabled = parse_symlinks_env_variable(xdg_dirnames[i]);
> + xdg_dir = xdg_dirs_array ? xdg_dirs_array[i] : NULL;
> + if (!strcmp(xdg_dirnames[i],"DOCUMENTS"))
> + create_homedir_symbolic_link(env_enabled, IDS_PERSONAL, CSIDL_PERSONAL, xdg_dir, DocumentsW);
> + else if (!strcmp(xdg_dirnames[i],"PICTURES"))
> + create_homedir_symbolic_link(env_enabled, IDS_MYPICTURES, CSIDL_MYPICTURES, xdg_dir, PicturesW);
> + else if (!strcmp(xdg_dirnames[i],"VIDEOS"))
> + create_homedir_symbolic_link(env_enabled, IDS_MYVIDEOS, CSIDL_MYVIDEO, xdg_dir, MoviesW);
> + else if (!strcmp(xdg_dirnames[i],"MUSIC"))
> + create_homedir_symbolic_link(env_enabled, IDS_MYMUSIC, CSIDL_MYMUSIC, xdg_dir, MusicW);
> + else if (!strcmp(xdg_dirnames[i],"DESKTOP"))
> + create_homedir_symbolic_link(env_enabled, IDS_DESKTOPDIRECTORY, CSIDL_DESKTOPDIRECTORY, xdg_dir, DesktopW);
> + else
> + ERR("XDG directory name specifier invalid: %s\n", debugstr_a(xdg_dirnames[i]));
> + if (xdg_dir) heap_free(xdg_dirs_array[i]);
> + }
> + if (xdg_dirs_array) heap_free(xdg_dirs_array);
> +}
> +
> HRESULT SHELL_RegisterShellFolders(void)
> {
> + static const char * const xdg_dirnames[] = { "DOCUMENTS", "PICTURES", "VIDEOS", "MUSIC", "DESKTOP" };
> + const UINT xdg_dir_count = 5;
> HRESULT hr;
>
> - /* Set up '$HOME' targeted symlinks for 'My Documents', 'My Pictures',
> - * 'My Videos', 'My Music' and 'Desktop' in advance, so that the
> - * _SHRegister*ShellFolders() functions will find everything nice and clean
> - * and thus will not attempt to create them in the profile directory. */
> - _SHCreateSymbolicLinks();
> + /* Early setup of symlinks from specific User Shell Folders, in
> + * current Wineprefix, to subdirecties of the user's HOME directory. */
> + create_homedir_symbolic_links(xdg_dirnames, xdg_dir_count);
>
> hr = _SHRegisterUserShellFolders(TRUE);
> if (SUCCEEDED(hr))
> @@ -6147,5 +6278,6 @@ HRESULT SHELL_RegisterShellFolders(void)
> hr = set_folder_attributes();
> if (SUCCEEDED(hr))
> register_system_knownfolders();
> +
> return hr;
> }
> --
> 2.17.1
>
>
>
June 7, 2018
Re: [PATCH] ntoskrnl: Have MmIsAddressValid use IsBadReadPtr.
by Thomas Faber
On 2018-06-06 17:11, Derek Lesho wrote:
> Hi Thomas, in your example, does &Base refer to the base of
> ntoskrnl.exe? If so, why are you allocating a buffer there, wouldn't
> that corrupt the memory of ntoskrnl, or am I misunderstanding what
> your code is doing. On windows, do drivers have access to change the
> memory of ntoskrnl?
My intention was to simply make a user mode memory allocation, so there
should be a "Base = NULL;" before the call to ZwAllocateVirtualMemory.
This function cannot allocate kernel address space (or overwrite loaded
modules), so it wouldn't do anything unexpected. I was just suggesting
it to demonstrate behavior with NOACCESS/READONLY/READWRITE pages.
As for your actual use case, I'd assume ntoskrnl's code is most likely
write-protected on Windows as well, much like you encountered on Wine,
but I haven't verified.
Since your patch was committed I don't know if you're still interested
in adding the test. If you are, I'd suggest to go with the simpler
second version, since like I said the "full test" won't be very useful
in Wine.
> On Wed, Jun 6, 2018 at 3:51 AM, Thomas Faber <thomas.faber(a)reactos.org> wrote:
>> void test_function()
>> {
>> ok(MmIsAddressValid((void*)test_function), ...); // fails in master, succeeds with patch?
>> ptr = ExAllocatePoolWithTag(NonPagedPool, ...)
>> ok(MmIsAddressValid(ptr), ...);
>> ExFreePoolWithTag...
>> ok(!MmIsAddressValid(NULL), ...);
>> }
Perhaps also
ok(MmIsAddressValid((void*)MmIsAddressValid), ...);
to specifically test ntoskrnl instead of the test driver's binary.
-Thomas
June 7, 2018
Re: [PATCH 3/4 v5] riched20: Support external HDC in ME_WrapMarkedParagraphs().
by Huw Davies
On Wed, Jun 06, 2018 at 05:24:31PM +0800, Jactry Zeng wrote:
> Superseded patch 146572.
>
> Signed-off-by: Jactry Zeng <jzeng(a)codeweavers.com>
> ---
> dlls/riched20/editor.c | 13 +++++++------
> dlls/riched20/editor.h | 2 +-
> dlls/riched20/editstr.h | 1 +
> dlls/riched20/paint.c | 10 +++++-----
> dlls/riched20/table.c | 4 ++--
> dlls/riched20/wrap.c | 11 +++++++++--
> 6 files changed, 25 insertions(+), 16 deletions(-)
>
> diff --git a/dlls/riched20/editor.c b/dlls/riched20/editor.c
> index 820b0380c7..82132761f0 100644
> --- a/dlls/riched20/editor.c
> +++ b/dlls/riched20/editor.c
> @@ -3028,6 +3028,7 @@ ME_TextEditor *ME_MakeEditor(ITextHost *texthost, BOOL bEmulateVersion10)
>
> ed->hWnd = NULL;
> ed->hwndParent = NULL;
> + ed->hdcDraw = NULL;
> ed->sizeWindow.cx = ed->sizeWindow.cy = 0;
> ed->texthost = texthost;
> ed->reOle = NULL;
> @@ -3494,7 +3495,7 @@ static LRESULT ME_WmCreate(ME_TextEditor *editor, LPARAM lParam, BOOL unicode)
> }
>
> ME_CommitUndo(editor);
> - ME_WrapMarkedParagraphs(editor);
> + ME_WrapMarkedParagraphs(editor, NULL);
> ME_MoveCaret(editor);
> return 0;
> }
> @@ -3645,7 +3646,7 @@ LRESULT ME_HandleMessage(ME_TextEditor *editor, UINT msg, WPARAM wParam,
> cf.yHeight = size * 20; /* convert twips to points */
> ME_SetSelectionCharFormat(editor, &cf);
> ME_CommitUndo(editor);
> - ME_WrapMarkedParagraphs(editor);
> + ME_WrapMarkedParagraphs(editor, NULL);
> ME_UpdateScrollBar(editor);
> ME_Repaint(editor);
>
> @@ -3934,7 +3935,7 @@ LRESULT ME_HandleMessage(ME_TextEditor *editor, UINT msg, WPARAM wParam,
> ME_CommitUndo(editor);
> if (bRepaint)
> {
> - ME_WrapMarkedParagraphs(editor);
> + ME_WrapMarkedParagraphs(editor, NULL);
> ME_UpdateScrollBar(editor);
> ME_Repaint(editor);
> }
> @@ -3959,7 +3960,7 @@ LRESULT ME_HandleMessage(ME_TextEditor *editor, UINT msg, WPARAM wParam,
> case EM_SETPARAFORMAT:
> {
> BOOL result = ME_SetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam);
> - ME_WrapMarkedParagraphs(editor);
> + ME_WrapMarkedParagraphs(editor, NULL);
> ME_UpdateScrollBar(editor);
> ME_Repaint(editor);
> ME_CommitUndo(editor);
> @@ -4054,7 +4055,7 @@ LRESULT ME_HandleMessage(ME_TextEditor *editor, UINT msg, WPARAM wParam,
>
> ME_CommitUndo(editor);
> ME_MarkAllForWrapping(editor);
> - ME_WrapMarkedParagraphs(editor);
> + ME_WrapMarkedParagraphs(editor, NULL);
> ME_UpdateScrollBar(editor);
> if (bRepaint)
> ME_Repaint(editor);
> @@ -4714,7 +4715,7 @@ LRESULT ME_HandleMessage(ME_TextEditor *editor, UINT msg, WPARAM wParam,
> editor->bDefaultFormatRect = TRUE;
> }
> ME_MarkAllForWrapping(editor);
> - ME_WrapMarkedParagraphs(editor);
> + ME_WrapMarkedParagraphs(editor, NULL);
> ME_UpdateScrollBar(editor);
> if (msg != EM_SETRECTNP)
> ME_Repaint(editor);
> diff --git a/dlls/riched20/editor.h b/dlls/riched20/editor.h
> index 0f0e56ecd1..425a34f0c2 100644
> --- a/dlls/riched20/editor.h
> +++ b/dlls/riched20/editor.h
> @@ -182,7 +182,7 @@ void ME_InitContext(ME_Context *c, ME_TextEditor *editor, HDC hDC) DECLSPEC_HIDD
> void ME_DestroyContext(ME_Context *c) DECLSPEC_HIDDEN;
>
> /* wrap.c */
> -BOOL ME_WrapMarkedParagraphs(ME_TextEditor *editor) DECLSPEC_HIDDEN;
> +BOOL ME_WrapMarkedParagraphs(ME_TextEditor *editor, HDC hdc) DECLSPEC_HIDDEN;
> void ME_InvalidateParagraphRange(ME_TextEditor *editor, ME_DisplayItem *start_para, ME_DisplayItem *last_para) DECLSPEC_HIDDEN;
> void ME_SendRequestResize(ME_TextEditor *editor, BOOL force) DECLSPEC_HIDDEN;
>
> diff --git a/dlls/riched20/editstr.h b/dlls/riched20/editstr.h
> index 206ce85287..1d811e1c2a 100644
> --- a/dlls/riched20/editstr.h
> +++ b/dlls/riched20/editstr.h
> @@ -383,6 +383,7 @@ typedef struct tagME_InStream ME_InStream;
> typedef struct tagME_TextEditor
> {
> HWND hWnd, hwndParent;
> + HDC hdcDraw;
> ITextHost *texthost;
> IRichEditOle *reOle;
> BOOL bEmulateVersion10;
> diff --git a/dlls/riched20/paint.c b/dlls/riched20/paint.c
> index fcc4e105a3..f2429081fc 100644
> --- a/dlls/riched20/paint.c
> +++ b/dlls/riched20/paint.c
> @@ -105,7 +105,7 @@ void ME_PaintContent(ME_TextEditor *editor, HDC hDC, const RECT *rcUpdate)
>
> void ME_Repaint(ME_TextEditor *editor)
> {
> - if (ME_WrapMarkedParagraphs(editor))
> + if (ME_WrapMarkedParagraphs(editor, NULL))
> {
> ME_UpdateScrollBar(editor);
> FIXME("ME_Repaint had to call ME_WrapMarkedParagraphs\n");
> @@ -118,7 +118,7 @@ void ME_UpdateRepaint(ME_TextEditor *editor, BOOL update_now)
> /* Should be called whenever the contents of the control have changed */
> BOOL wrappedParagraphs;
>
> - wrappedParagraphs = ME_WrapMarkedParagraphs(editor);
> + wrappedParagraphs = ME_WrapMarkedParagraphs(editor, NULL);
> if (wrappedParagraphs)
> ME_UpdateScrollBar(editor);
>
> @@ -145,7 +145,7 @@ ME_RewrapRepaint(ME_TextEditor *editor)
> * looks, but not content. Like resizing. */
>
> ME_MarkAllForWrapping(editor);
> - ME_WrapMarkedParagraphs(editor);
> + ME_WrapMarkedParagraphs(editor, NULL);
> ME_UpdateScrollBar(editor);
> ME_Repaint(editor);
> }
> @@ -1163,7 +1163,7 @@ void ME_UpdateScrollBar(ME_TextEditor *editor)
> SCROLLINFO si;
> BOOL bScrollBarWasVisible, bScrollBarWillBeVisible;
>
> - if (ME_WrapMarkedParagraphs(editor))
> + if (ME_WrapMarkedParagraphs(editor, NULL))
> FIXME("ME_UpdateScrollBar had to call ME_WrapMarkedParagraphs\n");
>
> si.cbSize = sizeof(si);
> @@ -1336,7 +1336,7 @@ ME_InvalidateSelection(ME_TextEditor *editor)
> there's no (inverted) area to be repainted, neither old nor new */
> if (nStart == nEnd && editor->nLastSelStart == editor->nLastSelEnd)
> return;
> - ME_WrapMarkedParagraphs(editor);
> + ME_WrapMarkedParagraphs(editor, NULL);
> ME_GetSelectionParas(editor, &sel_start, &sel_end);
> assert(sel_start->type == diParagraph);
> assert(sel_end->type == diParagraph);
> diff --git a/dlls/riched20/table.c b/dlls/riched20/table.c
> index 4cd77eb399..5457935dfa 100644
> --- a/dlls/riched20/table.c
> +++ b/dlls/riched20/table.c
> @@ -499,7 +499,7 @@ static void ME_SelectOrInsertNextCell(ME_TextEditor *editor,
> editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
> editor->pCursors[0].nOffset = 0;
> editor->pCursors[1] = editor->pCursors[0];
> - ME_WrapMarkedParagraphs(editor);
> + ME_WrapMarkedParagraphs(editor, NULL);
> return;
> }
> }
> @@ -544,7 +544,7 @@ static void ME_SelectOrInsertNextCell(ME_TextEditor *editor,
> editor->pCursors[0].pRun = ME_FindItemFwd(para, diRun);
> editor->pCursors[0].nOffset = 0;
> editor->pCursors[1] = editor->pCursors[0];
> - ME_WrapMarkedParagraphs(editor);
> + ME_WrapMarkedParagraphs(editor, NULL);
> return;
> }
> }
> diff --git a/dlls/riched20/wrap.c b/dlls/riched20/wrap.c
> index e3a70f07d1..4c92252547 100644
> --- a/dlls/riched20/wrap.c
> +++ b/dlls/riched20/wrap.c
> @@ -980,14 +980,21 @@ static void ME_MarkRepaintEnd(ME_DisplayItem *para,
> *repaint_end = para;
> }
>
> -BOOL ME_WrapMarkedParagraphs(ME_TextEditor *editor)
> +BOOL ME_WrapMarkedParagraphs(ME_TextEditor *editor, HDC hdc)
> {
> ME_DisplayItem *item;
> ME_Context c;
> int totalWidth = 0;
> ME_DisplayItem *repaint_start = NULL, *repaint_end = NULL;
>
> - ME_InitContext(&c, editor, ITextHost_TxGetDC(editor->texthost));
> + if (!hdc)
> + hdc = ITextHost_TxGetDC(editor->texthost);
> + if (hdc != editor->hdcDraw)
> + {
> + ME_MarkAllForWrapping(editor);
> + editor->hdcDraw = hdc;
> + }
> + ME_InitContext(&c, editor, hdc);
> c.pt.x = 0;
> item = editor->pBuffer->pFirst->next;
> while(item != editor->pBuffer->pLast) {
Unfortunately it's more complicated than this because
the styles cache font metrics and uniscribe data. These
will need to be recalculated for a new hdc.
We'll probably also want to re-think how the windowed and window-less
controls handle hdcs in general.
Huw.
June 7, 2018
Re: [PATCH 1/4 v5] riched20/tests: Rewrite tests for ITextServices_TxGetNaturalSize().
by Huw Davies
On Thu, Jun 07, 2018 at 05:21:25PM +0800, Jactry Zeng wrote:
> Huw Davies <huw(a)codeweavers.com> 于2018年6月7日周四 下午4:54写道:
> >
> > On Wed, Jun 06, 2018 at 05:24:13PM +0800, Jactry Zeng wrote:
> > > Superseded patch 146570.
> > >
>
> > > + LONG expected_width, expected_height;
> > > +
> > > + GetClientRect(WindowFromDC(hdc), &rect);
> > > + DrawTextW(hdc, string, -1, &rect, DT_LEFT | DT_CALCRECT | DT_NOCLIP | DT_EDITCONTROL | DT_WORDBREAK);
> >
> > Why can't you use GetTextExtentPointW() here? This would avoid the
> > GetClientRect() call too.
> >
>
> In 0003 there are some tests about multiline support. It seems that
> using DrawText can make it more simple or we will need to compute
> a max width of it by ourselves.
Ok, then please pass the client rect to CHECK_TXGETNATURALSIZE so that
it doesn't need to be queried from there.
Also, this patch series seems out-of-order. Perhaps move 0001 after
0002 and 0003 (or put 0004 before 0002 and 0003).
Huw.
June 7, 2018
Re: [PATCH 2/4 v5] riched20: Call ITextHost_TxGetCharFormat() for setting default charformat.
by Huw Davies
On Wed, Jun 06, 2018 at 05:24:22PM +0800, Jactry Zeng wrote:
> Superseded patch 146571.
>
> ChangeLog:
> v5:
> - Remove an unnecessary newline.
>
> Signed-off-by: Jactry Zeng <jzeng(a)codeweavers.com>
> ---
> dlls/riched20/para.c | 5 ++++
> dlls/riched20/tests/txtsrv.c | 58 +++++++++++++++++++++++++++++++++++-
> 2 files changed, 62 insertions(+), 1 deletion(-)
>
> diff --git a/dlls/riched20/para.c b/dlls/riched20/para.c
> index f2a70e5746..ffb017c123 100644
> --- a/dlls/riched20/para.c
> +++ b/dlls/riched20/para.c
> @@ -36,6 +36,7 @@ void ME_MakeFirstParagraph(ME_TextEditor *editor)
> {
> ME_Context c;
> CHARFORMAT2W cf;
> + const CHARFORMATW *host_cf;
> LOGFONTW lf;
> HFONT hf;
> ME_TextBuffer *text = editor->pBuffer;
> @@ -44,6 +45,7 @@ void ME_MakeFirstParagraph(ME_TextEditor *editor)
> ME_Style *style;
> int eol_len;
> WCHAR cr_lf[] = {'\r','\n',0};
> + HRESULT hr;
>
> ME_InitContext(&c, editor, ITextHost_TxGetDC(editor->texthost));
>
> @@ -73,6 +75,9 @@ void ME_MakeFirstParagraph(ME_TextEditor *editor)
> cf.bCharSet = lf.lfCharSet;
> cf.lcid = GetSystemDefaultLCID();
>
> + hr = ITextHost_TxGetCharFormat(editor->texthost, (const CHARFORMATW **)&host_cf);
> + if (hr == S_OK && (host_cf->cbSize >= sizeof(CHARFORMATW)))
> + ME_ToCF2W(&cf, (CHARFORMAT2W *)host_cf);
Hmm, you need to use the return value of ME_ToCF2W() is this case.
However, ME_ToCF2W() and ME_ToCFAny() should be cleaned up. They
should both do the copy even if the from argument is of the
correct type (basically what ME_CopyToCFAny() does).
Something like:
BOOL cfany_to_cf2w(CHARFORMAT2W *dst, const *CHARFORMAT2W *src);
BOOL cf2w_to_cfany(CHARFORMAT2W *dst, const *CHARFORMAT2W *src);
> style = ME_MakeStyle(&cf);
> text->pDefaultStyle = style;
>
> diff --git a/dlls/riched20/tests/txtsrv.c b/dlls/riched20/tests/txtsrv.c
> index 6e61392dc5..ac06aa868e 100644
> --- a/dlls/riched20/tests/txtsrv.c
> +++ b/dlls/riched20/tests/txtsrv.c
> @@ -85,6 +85,7 @@ typedef struct ITextHostTestImpl
> {
> ITextHost ITextHost_iface;
> LONG refCount;
> + CHARFORMATW char_format;
> } ITextHostTestImpl;
>
> static inline ITextHostTestImpl *impl_from_ITextHost(ITextHost *iface)
> @@ -330,7 +331,8 @@ static HRESULT WINAPI ITextHostImpl_TxGetCharFormat(ITextHost *iface,
> {
> ITextHostTestImpl *This = impl_from_ITextHost(iface);
> TRACECALL("Call to TxGetCharFormat(%p, ppCF=%p)\n", This, ppCF);
> - return E_NOTIMPL;
> + *ppCF = &This->char_format;
> + return S_OK;
> }
>
> static HRESULT WINAPI ITextHostImpl_TxGetParaFormat(ITextHost *iface,
> @@ -609,6 +611,8 @@ static BOOL init_texthost(ITextServices **txtserv, ITextHost **ret)
> ITextHostTestImpl *dummyTextHost;
> IUnknown *init;
> HRESULT result;
> + LOGFONTW lf;
> + HFONT hf;
>
> dummyTextHost = CoTaskMemAlloc(sizeof(*dummyTextHost));
> if (dummyTextHost == NULL) {
> @@ -617,6 +621,19 @@ static BOOL init_texthost(ITextServices **txtserv, ITextHost **ret)
> }
> dummyTextHost->ITextHost_iface.lpVtbl = &itextHostVtbl;
> dummyTextHost->refCount = 1;
> + memset(&dummyTextHost->char_format, 0, sizeof(dummyTextHost->char_format));
> + dummyTextHost->char_format.cbSize = sizeof(dummyTextHost->char_format);
> + dummyTextHost->char_format.dwMask = CFM_FACE | CFM_SIZE | CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_STRIKEOUT;
> + hf = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
No cast.
> + GetObjectW(hf, sizeof(LOGFONTW), &lf);
> + lstrcpyW(dummyTextHost->char_format.szFaceName, lf.lfFaceName);
> + dummyTextHost->char_format.yHeight = MulDiv(abs(lf.lfHeight), 1440, GetDeviceCaps(GetDC(NULL), LOGPIXELSY));
> + if (lf.lfWeight > FW_NORMAL) dummyTextHost->char_format.dwEffects |= CFE_BOLD;
> + if (lf.lfItalic) dummyTextHost->char_format.dwEffects |= CFE_ITALIC;
> + if (lf.lfUnderline) dummyTextHost->char_format.dwEffects |= CFE_UNDERLINE;
> + if (lf.lfStrikeOut) dummyTextHost->char_format.dwEffects |= CFE_SUBSCRIPT;
> + dummyTextHost->char_format.bPitchAndFamily = lf.lfPitchAndFamily;
> + dummyTextHost->char_format.bCharSet = lf.lfCharSet;
You do the same thing in the previous patch, so this should be a
helper function.
> /* MSDN states that an IUnknown object is returned by
> CreateTextServices which is then queried to obtain a
> @@ -947,6 +964,44 @@ static void test_QueryInterface(void)
> ITextHost_Release(host);
> }
>
> +static void test_default_format(void)
> +{
> + ITextServices *txtserv;
> + ITextHost *host;
> + HRESULT result;
> + LRESULT lresult;
> + CHARFORMAT2W cf2;
> + HFONT hf;
> + LOGFONTW lf;
> + LONG expected_height;
> +
> + if (!init_texthost(&txtserv, &host))
> + return;
> +
> + cf2.cbSize = sizeof(CHARFORMAT2W);
> + result = ITextServices_TxSendMessage(txtserv, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2, &lresult);
> + ok(result == S_OK, "ITextServices_TxSendMessage failed: 0x%08x.\n", result);
> + hf = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
> + GetObjectW(hf, sizeof(LOGFONTW), &lf);
Rather than getting the font here, why not call ITextHost_GetCharFormat() ?
> + expected_height = MulDiv(abs(lf.lfHeight), 1440, GetDeviceCaps(GetDC(NULL), LOGPIXELSY));
> + ok(!lstrcmpW(lf.lfFaceName, cf2.szFaceName), "got wrong font name: %s.\n", wine_dbgstr_w(cf2.szFaceName));
> + ok(cf2.yHeight == expected_height, "got wrong yHeight: %d, expetced %d.\n", cf2.yHeight, expected_height);
> + if (lf.lfWeight > FW_NORMAL)
> + ok(cf2.dwEffects & CFE_BOLD, "got dwEffects without CFE_BOLD: %x.\n", cf2.dwEffects);
> + if (lf.lfItalic)
> + ok(cf2.dwEffects & CFE_ITALIC, "got dwEffects without CFE_ITALIC: %x.\n", cf2.dwEffects);
> + if (lf.lfUnderline)
> + ok(cf2.dwEffects & CFE_UNDERLINE, "got dwEffects without CFE_UNDERLINE: %x.\n", cf2.dwEffects);
> + if (lf.lfStrikeOut)
> + ok(cf2.dwEffects & CFE_STRIKEOUT, "got dwEffects without CFE_STRIKEOUT: %x.\n", cf2.dwEffects);
> + ok(cf2.bPitchAndFamily == lf.lfPitchAndFamily, "got wrong bPitchAndFamily: %x, expected %x.\n",
> + cf2.bPitchAndFamily, lf.lfPitchAndFamily);
> + ok(cf2.bCharSet == lf.lfCharSet, "got wrong bCharSet: %x, expected %x.\n", cf2.bCharSet, lf.lfCharSet);
> +
> + ITextServices_Release(txtserv);
> + ITextHost_Release(host);
> +}
> +
> START_TEST( txtsrv )
> {
> ITextServices *txtserv;
> @@ -977,6 +1032,7 @@ START_TEST( txtsrv )
> test_TxGetNaturalSize();
> test_TxDraw();
> test_QueryInterface();
> + test_default_format();
> }
> if (wrapperCodeMem) VirtualFree(wrapperCodeMem, 0, MEM_RELEASE);
> }
> --
> 2.17.1
>
>
>
>
June 7, 2018
Re: [PATCH 1/4 v5] riched20/tests: Rewrite tests for ITextServices_TxGetNaturalSize().
by Jactry Zeng
Jactry Zeng <jactry92(a)gmail.com> 于2018年6月7日周四 下午5:21写道:
>
>
> In 0003 there are some tests about multiline support. It seems that
> using DrawText can make it more simple or we will need to compute
> a max width of it by ourselves.
Sorry, it is in 0004.
> --
> Regards,
> Jactry Zeng
--
Regards,
Jactry Zeng
June 7, 2018
Re: [PATCH 1/4 v5] riched20/tests: Rewrite tests for ITextServices_TxGetNaturalSize().
by Jactry Zeng
Huw Davies <huw(a)codeweavers.com> 于2018年6月7日周四 下午4:54写道:
>
> On Wed, Jun 06, 2018 at 05:24:13PM +0800, Jactry Zeng wrote:
> > Superseded patch 146570.
> >
> > + LONG expected_width, expected_height;
> > +
> > + GetClientRect(WindowFromDC(hdc), &rect);
> > + DrawTextW(hdc, string, -1, &rect, DT_LEFT | DT_CALCRECT | DT_NOCLIP | DT_EDITCONTROL | DT_WORDBREAK);
>
> Why can't you use GetTextExtentPointW() here? This would avoid the
> GetClientRect() call too.
>
In 0003 there are some tests about multiline support. It seems that
using DrawText can make it more simple or we will need to compute
a max width of it by ourselves.
--
Regards,
Jactry Zeng
June 7, 2018
Re: [PATCH 1/2] schedsvc: Load job files at the service start up.
by Dmitry Timoshkov
There is a problem with this patchset, please ignore it for now.
--
Dmitry.
June 7, 2018
Re: [PATCH 1/4 v5] riched20/tests: Rewrite tests for ITextServices_TxGetNaturalSize().
by Huw Davies
On Wed, Jun 06, 2018 at 05:24:13PM +0800, Jactry Zeng wrote:
> Superseded patch 146570.
>
> ChangeLog:
> v5:
> - Use DrawTextW for calculating expected values;
> - Simplify format setting with EM_SETCHARFORMAT.
>
> Signed-off-by: Jactry Zeng <jzeng(a)codeweavers.com>
> ---
> dlls/riched20/tests/txtsrv.c | 125 +++++++++++++++++------------------
> 1 file changed, 61 insertions(+), 64 deletions(-)
>
> diff --git a/dlls/riched20/tests/txtsrv.c b/dlls/riched20/tests/txtsrv.c
> index 88b5adf13c..6e61392dc5 100644
> --- a/dlls/riched20/tests/txtsrv.c
> +++ b/dlls/riched20/tests/txtsrv.c
> @@ -697,81 +697,78 @@ static void test_TxSetText(void)
> ITextHost_Release(host);
> }
>
> +#define CHECK_TXGETNATURALSIZE(res,width,height,hdc,string) \
> + _check_txgetnaturalsize(res, width, height, hdc, string, __LINE__)
> +static void _check_txgetnaturalsize(HRESULT res, LONG width, LONG height, HDC hdc, LPCWSTR string, int line)
> +{
> + RECT rect;
> + LONG expected_width, expected_height;
> +
> + GetClientRect(WindowFromDC(hdc), &rect);
> + DrawTextW(hdc, string, -1, &rect, DT_LEFT | DT_CALCRECT | DT_NOCLIP | DT_EDITCONTROL | DT_WORDBREAK);
Why can't you use GetTextExtentPointW() here? This would avoid the
GetClientRect() call too.
> + expected_width = rect.right - rect.left;
> + expected_height = rect.bottom - rect.top;
> + ok_(__FILE__,line)(res == S_OK, "ITextServices_TxGetNaturalSize failed: 0x%08x.\n", res);
> + ok_(__FILE__,line)(width >= expected_width && width <= expected_width + 1,
> + "got wrong width: %d, expected: %d {+1}.\n", width, expected_width);
> + ok_(__FILE__,line)(height == expected_height, "got wrong height: %d, expected: %d.\n",
> + height, expected_height);
> +}
> +
> static void test_TxGetNaturalSize(void)
> {
> ITextServices *txtserv;
> ITextHost *host;
> HRESULT result;
> - BOOL ret;
> -
> - /* This value is used when calling TxGetNaturalSize. MSDN says
> - that this is not supported however a null pointer cannot be
> - used as it will cause a segmentation violation. The values in
> - the structure being pointed to are required to be INT_MAX
> - otherwise calculations can give wrong values. */
> - const SIZEL psizelExtent = {INT_MAX,INT_MAX};
> -
> - static const WCHAR oneA[] = {'A',0};
> -
> - /* Results of measurements */
> - LONG xdim, ydim;
> -
> - /* The device context to do the tests in */
> + SIZEL psizelExtent = {-1,-1};
Let's just call this 'extent';
> + static const WCHAR test_text[] = {'T','e','s','t','S','o','m','e','T','e','x','t',0};
> + LONG width, height;
> HDC hdcDraw;
> -
> - /* Variables with the text metric information */
> - INT charwidth_caps_text[26];
> - TEXTMETRICA tmInfo_text;
> + HWND hwnd;
> + RECT rect;
> + CHARFORMAT2W cf;
> + LRESULT lresult;
> + HFONT hf;
> + LOGFONTW lf;
>
> if (!init_texthost(&txtserv, &host))
> return;
>
> - hdcDraw = GetDC(NULL);
> - SaveDC(hdcDraw);
> -
> - /* Populate the metric strucs */
> - SetMapMode(hdcDraw,MM_TEXT);
> - GetTextMetricsA(hdcDraw, &tmInfo_text);
> - SetLastError(0xdeadbeef);
> - ret = GetCharWidth32A(hdcDraw,'A','Z',charwidth_caps_text);
> - if (!ret && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) {
> - win_skip("GetCharWidth32 is not available\n");
> - goto cleanup;
> - }
> -
> - /* Make measurements in MM_TEXT */
> + hwnd = CreateWindowExA(0, "static", NULL, WS_POPUP | WS_VISIBLE,
> + 0, 0, 100, 100, 0, 0, 0, NULL);
> + hdcDraw = GetDC(hwnd);
> SetMapMode(hdcDraw,MM_TEXT);
> - xdim = 0; ydim = 0;
> -
> - result = ITextServices_TxSetText(txtserv, oneA);
> - ok(result == S_OK, "ITextServices_TxSetText failed (result = %x)\n", result);
> - if (result != S_OK) {
> - skip("Could not set text\n");
> - goto cleanup;
> - }
> -
> - SetLastError(0xdeadbeef);
> - result = ITextServices_TxGetNaturalSize(txtserv, DVASPECT_CONTENT,
> - hdcDraw, NULL, NULL,
> - TXTNS_FITTOCONTENT, &psizelExtent,
> - &xdim, &ydim);
> - todo_wine ok(result == S_OK || broken(result == E_FAIL), /* WINXP Arabic Language */
> - "TxGetNaturalSize gave unexpected return value (result = %x)\n", result);
> - if (result == S_OK) {
> - todo_wine ok(ydim == tmInfo_text.tmHeight,
> - "Height calculated incorrectly (expected %d, got %d)\n",
> - tmInfo_text.tmHeight, ydim);
> - /* The native DLL adds one pixel extra when calculating widths. */
> - todo_wine ok(xdim >= charwidth_caps_text[0] && xdim <= charwidth_caps_text[0] + 1,
> - "Width calculated incorrectly (expected %d {+1}, got %d)\n",
> - charwidth_caps_text[0], xdim);
> - } else
> - skip("TxGetNaturalSize measurements not performed (xdim = %d, ydim = %d, result = %x, error = %x)\n",
> - xdim, ydim, result, GetLastError());
> -
> -cleanup:
> - RestoreDC(hdcDraw,1);
> - ReleaseDC(NULL,hdcDraw);
> + GetClientRect(hwnd, &rect);
> +
> + memset(&cf, 0, sizeof(cf));
> + cf.cbSize = sizeof(cf);
> + cf.dwMask = CFM_ALL2;
> + hf = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
No need for cast.
> + GetObjectW(hf, sizeof(LOGFONTW), &lf);
> + lstrcpyW(cf.szFaceName, lf.lfFaceName);
> + cf.yHeight = MulDiv(abs(lf.lfHeight), 1440, GetDeviceCaps(GetDC(NULL), LOGPIXELSY));
> + if (lf.lfWeight > FW_NORMAL) cf.dwEffects |= CFE_BOLD;
> + if (lf.lfItalic) cf.dwEffects |= CFE_ITALIC;
> + if (lf.lfUnderline) cf.dwEffects |= CFE_UNDERLINE;
> + if (lf.lfStrikeOut) cf.dwEffects |= CFE_SUBSCRIPT;
> + cf.bPitchAndFamily = lf.lfPitchAndFamily;
> + cf.bCharSet = lf.lfCharSet;
> + result = ITextServices_TxSendMessage(txtserv, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf, &lresult);
> + ok(result == S_OK, "ITextServices_TxSendMessage failed: 0x%08x.\n", result);
> + SelectObject(hdcDraw, hf);
> +
> + result = ITextServices_TxSetText(txtserv, test_text);
> + ok(result == S_OK, "ITextServices_TxSetText failed: 0x%08x.\n", result);
> +
> + psizelExtent.cx = -1; psizelExtent.cy = -1;
This is already initialised.
> + width = rect.right - rect.left;
> + height = 0;
> + result = ITextServices_TxGetNaturalSize(txtserv, DVASPECT_CONTENT, hdcDraw, NULL, NULL,
> + TXTNS_FITTOCONTENT, &psizelExtent, &width, &height);
> + todo_wine CHECK_TXGETNATURALSIZE(result, width, height, hdcDraw, test_text);
> +
> + ReleaseDC(hwnd, hdcDraw);
> + DestroyWindow(hwnd);
> ITextServices_Release(txtserv);
> ITextHost_Release(host);
> }
> --
> 2.17.1
>
>
>
>
June 7, 2018
Re: [PATCH 4/4] wsdapi: Add initial support for reading messages; identify Probe message.
by Huw Davies
On Wed, Jun 06, 2018 at 09:58:52PM +0100, Owen Rudge wrote:
> Signed-off-by: Owen Rudge <orudge(a)codeweavers.com>
> ---
> dlls/wsdapi/soap.c | 340
> +++++++++++++++++++++++++++++++++++++++++-
> dlls/wsdapi/tests/discovery.c | 6 +-
> 2 files changed, 341 insertions(+), 5 deletions(-)
>
>
> diff --git a/dlls/wsdapi/soap.c b/dlls/wsdapi/soap.c
> index 4e191a5c82..2bc974f111 100644
> --- a/dlls/wsdapi/soap.c
> +++ b/dlls/wsdapi/soap.c
> @@ -45,6 +45,13 @@ static const WCHAR actionHello[] = {
> 'd','i','s','c','o','v','e','r','y','/',
> 'H','e','l','l','o', 0 };
>
> +static const WCHAR actionProbe[] = {
> + 'h','t','t','p',':','/','/',
> + 's','c','h','e','m','a','s','.','x','m','l','s','o','a','p','.','o','r','g','/',
> + 'w','s','/','2','0','0','5','/','0','4','/',
> + 'd','i','s','c','o','v','e','r','y','/',
> + 'P','r','o','b','e', 0 };
> +
> static const WCHAR actionBye[] = {
> 'h','t','t','p',':','/','/',
> 's','c','h','e','m','a','s','.','x','m','l','s','o','a','p','.','o','r','g','/',
> @@ -98,6 +105,27 @@ struct discovered_namespace
> LPCWSTR uri;
> };
>
> +static LPWSTR utf8_to_wide(void *parent, const char *utf8_str, int length)
> +{
> + int utf8_str_len = 0, chars_needed = 0, bytes_needed = 0;
> + LPWSTR new_str = NULL;
> +
> + if (utf8_str == NULL) return NULL;
> +
> + utf8_str_len = (length < 0) ? lstrlenA(utf8_str) : length;
> + chars_needed = MultiByteToWideChar(CP_UTF8, 0, utf8_str, utf8_str_len, NULL, 0);
> +
> + if (chars_needed <= 0) return NULL;
> +
> + bytes_needed = sizeof(WCHAR) * (chars_needed + 1);
> + new_str = WSDAllocateLinkedMemory(parent, bytes_needed);
> +
> + MultiByteToWideChar(CP_UTF8, 0, utf8_str, utf8_str_len, new_str, chars_needed);
> + new_str[chars_needed] = 0;
> +
> + return new_str;
> +}
> +
> static char *wide_to_utf8(LPCWSTR wide_string, int *length)
> {
> char *new_string = NULL;
> @@ -1081,8 +1109,316 @@ cleanup:
> return ret;
> }
>
> +static LPWSTR xml_text_to_wide_string(void *parent_memory, WS_XML_TEXT *text)
> +{
> + if (text->textType == WS_XML_TEXT_TYPE_UTF8)
> + {
> + WS_XML_UTF8_TEXT *utf8_text = (WS_XML_UTF8_TEXT *) text;
> + return utf8_to_wide(parent_memory, (const char *) utf8_text->value.bytes, utf8_text->value.length);
> + }
> + else if (text->textType == WS_XML_TEXT_TYPE_UTF16)
> + {
> + WS_XML_UTF16_TEXT *utf_16_text = (WS_XML_UTF16_TEXT *) text;
> + return duplicate_string(parent_memory, (LPCWSTR) utf_16_text->bytes);
> + }
> +
> + FIXME("Support for text type %d not implemented.\n", text->textType);
> + return NULL;
> +}
> +
> +static BOOL move_to_element(WS_XML_READER *reader, const char *element_name, WS_XML_STRING *uri)
return HRESULT
> +{
> + WS_XML_STRING envelope;
> + BOOL found = FALSE;
> +
> + envelope.bytes = (BYTE *) element_name;
> + envelope.length = strlen(element_name);
> + envelope.dictionary = NULL;
> + envelope.id = 0;
> +
> + return SUCCEEDED(WsReadToStartElement(reader, &envelope, uri, &found, NULL)) && found;
> +}
> +
> +static BOOL ws_element_to_wsdxml_element(WS_XML_READER *reader, IWSDXMLContext *context, WSDXML_ELEMENT *parent_element)
and here.
> +{
> + WSDXML_ATTRIBUTE *cur_wsd_attrib = NULL, *new_wsd_attrib = NULL;
> + const WS_XML_ELEMENT_NODE *element_node = NULL;
> + WSDXML_ELEMENT *cur_element = parent_element;
> + const WS_XML_TEXT_NODE *text_node = NULL;
> + LPWSTR uri = NULL, element_name = NULL;
> + WS_XML_STRING *ns_string = NULL;
> + WS_XML_ATTRIBUTE *attrib = NULL;
> + WSDXML_ELEMENT *element = NULL;
> + const WS_XML_NODE *node = NULL;
> + WSDXML_NAME *name = NULL;
> + WSDXML_TEXT *text = NULL;
> + HRESULT ret;
> + int i;
> +
> + for (;;)
> + {
> + if (cur_element == NULL) break;
> +
> + ret = WsReadNode(reader, NULL);
> + if (FAILED(ret)) goto cleanup;
> +
> + ret = WsGetReaderNode(reader, &node, NULL);
> + if (FAILED(ret)) goto cleanup;
> +
> + switch (node->nodeType)
> + {
> + case WS_XML_NODE_TYPE_ELEMENT:
> + element_node = (const WS_XML_ELEMENT_NODE *) node;
> +
> + uri = utf8_to_wide(NULL, (const char *) element_node->ns->bytes, element_node->ns->length);
> + if (uri == NULL) goto cleanup;
> +
> + /* Link element_name to uri so they will be freed at the same time */
> + element_name = utf8_to_wide(uri, (const char *) element_node->localName->bytes,
> + element_node->localName->length);
> + if (element_name == NULL) goto cleanup;
> +
> + if (FAILED(IWSDXMLContext_AddNameToNamespace(context, uri, element_name, &name))) goto cleanup;
> +
> + WSDFreeLinkedMemory(uri);
> + uri = NULL;
> +
> + if (FAILED(WSDXMLBuildAnyForSingleElement(name, NULL, &element))) goto cleanup;
> + WSDXMLAddChild(cur_element, element);
> +
> + cur_wsd_attrib = NULL;
> +
> + /* Add attributes */
> + for (i = 0; i < element_node->attributeCount; i++)
> + {
> + attrib = element_node->attributes[i];
> + if (attrib->isXmlNs) continue;
> +
> + new_wsd_attrib = WSDAllocateLinkedMemory(element, sizeof(WSDXML_ATTRIBUTE));
> + if (new_wsd_attrib == NULL) goto cleanup;
> +
> + ns_string = attrib->ns;
> + if (ns_string->length == 0) ns_string = element_node->ns;
> +
> + uri = utf8_to_wide(NULL, (const char *) ns_string->bytes, ns_string->length);
> + if (uri == NULL) goto cleanup;
> +
> + /* Link element_name to uri so they will be freed at the same time */
> + element_name = utf8_to_wide(uri, (const char *) attrib->localName->bytes, attrib->localName->length);
> + if (element_name == NULL) goto cleanup;
> +
> + if (FAILED(IWSDXMLContext_AddNameToNamespace(context, uri, element_name, &name))) goto cleanup;
> +
> + WSDFreeLinkedMemory(uri);
> + uri = NULL;
> +
> + new_wsd_attrib->Value = xml_text_to_wide_string(new_wsd_attrib, attrib->value);
> + if (new_wsd_attrib->Value == NULL) goto cleanup;
> +
> + new_wsd_attrib->Name = name;
> + new_wsd_attrib->Element = cur_element;
> + new_wsd_attrib->Next = NULL;
> +
> + if (cur_wsd_attrib == NULL)
> + element->FirstAttribute = new_wsd_attrib;
> + else
> + cur_wsd_attrib->Next = new_wsd_attrib;
> +
> + cur_wsd_attrib = new_wsd_attrib;
> + }
> +
> + cur_element = element;
> + break;
> +
> + case WS_XML_NODE_TYPE_TEXT:
> + text_node = (const WS_XML_TEXT_NODE *) node;
> +
> + if (cur_element == NULL)
> + {
> + WARN("No parent element open but encountered text element!\n");
> + continue;
> + }
> +
> + if (cur_element->FirstChild != NULL)
> + {
> + WARN("Text node encountered but parent already has child!\n");
> + continue;
> + }
> +
> + text = WSDAllocateLinkedMemory(element, sizeof(WSDXML_TEXT));
> + if (text == NULL) goto cleanup;
> +
> + text->Node.Parent = element;
> + text->Node.Next = NULL;
> + text->Node.Type = TextType;
> + text->Text = xml_text_to_wide_string(text, text_node->text);
> +
> + if (text->Text == NULL)
> + {
> + WARN("Text node returned null string.\n");
> + WSDFreeLinkedMemory(text);
> + continue;
> + }
> +
> + cur_element->FirstChild = (WSDXML_NODE *) text;
> + break;
> +
> + case WS_XML_NODE_TYPE_END_ELEMENT:
> + /* Go up a level to the parent element */
> + cur_element = cur_element->Node.Parent;
> + break;
> +
> + default:
> + break;
> + }
> + }
> +
> + return TRUE;
> +
> +cleanup:
> + /* Free uri and element_name if applicable */
> + WSDFreeLinkedMemory(uri);
> + return FALSE;
> +}
> +
> +static WSDXML_ELEMENT *find_element(WSDXML_ELEMENT *parent, LPCWSTR name, LPCWSTR ns_uri)
> +{
> + WSDXML_ELEMENT *cur = (WSDXML_ELEMENT *) parent->FirstChild;
> +
> + while (cur != NULL)
> + {
> + if ((lstrcmpW(cur->Name->LocalName, name) == 0) && (lstrcmpW(cur->Name->Space->Uri, ns_uri) == 0))
> + return cur;
> +
> + cur = (WSDXML_ELEMENT *) cur->Node.Next;
> + }
> +
> + return NULL;
> +}
> +
> int read_message(const char *xml, int xml_length, WSD_SOAP_MESSAGE **out_msg)
This is also looking like a good contender to return HRESULT and
take an [out] int *type parameter.
> {
> - /* TODO: Parse and read message */
> - return MSGTYPE_UNKNOWN;
> + WSDXML_ELEMENT *envelope = NULL, *header_element, *body_element;
> + WS_XML_READER_TEXT_ENCODING encoding;
> + WS_XML_ELEMENT_NODE *envelope_node;
> + WSD_SOAP_MESSAGE *soap_msg = NULL;
> + WS_XML_READER_BUFFER_INPUT input;
> + WS_XML_ATTRIBUTE *attrib = NULL;
> + IWSDXMLContext *context = NULL;
> + WS_XML_STRING *soap_uri = NULL;
> + const WS_XML_NODE *node;
> + WS_XML_READER *reader;
> + LPCWSTR value = NULL;
> + int i, message_type;
> + LPWSTR uri, prefix;
> + WS_HEAP *heap;
> + HRESULT ret;
> +
> + message_type = MSGTYPE_UNKNOWN;
> +
> + ret = WsCreateHeap(16384, 4096, NULL, 0, &heap, NULL);
> + if (FAILED(ret)) goto cleanup;
> +
> + ret = WsCreateReader(NULL, 0, &reader, NULL);
> + if (FAILED(ret)) goto cleanup;
> +
> + encoding.encoding.encodingType = WS_XML_READER_ENCODING_TYPE_TEXT;
> + encoding.charSet = WS_CHARSET_AUTO;
> +
> + input.input.inputType = WS_XML_READER_INPUT_TYPE_BUFFER;
> + input.encodedData = (char *) xml;
> + input.encodedDataSize = xml_length;
> +
> + ret = WsSetInput(reader, (WS_XML_READER_ENCODING *) &encoding, (WS_XML_READER_INPUT *) &input, NULL, 0, NULL);
> + if (FAILED(ret)) goto cleanup;
> +
> + soap_uri = populate_xml_string(envelopeNsUri);
> + if (soap_uri == NULL) goto cleanup;
> +
> + if (!move_to_element(reader, "Envelope", soap_uri)) goto cleanup;
> +
> + ret = WsGetReaderNode(reader, &node, NULL);
> + if (FAILED(ret)) goto cleanup;
> +
> + if (node->nodeType != WS_XML_NODE_TYPE_ELEMENT) goto cleanup;
> +
> + envelope_node = (WS_XML_ELEMENT_NODE *) node;
> +
> + if (FAILED(WSDXMLCreateContext(&context))) goto cleanup;
> +
> + /* Find XML namespaces from the envelope element's attributes */
> + for (i = 0; i < envelope_node->attributeCount; i++)
> + {
> + attrib = envelope_node->attributes[i];
> +
> + if (attrib->isXmlNs)
> + {
> + uri = utf8_to_wide(NULL, (const char *) attrib->ns->bytes, attrib->ns->length);
> + if (uri == NULL) continue;
> +
> + prefix = utf8_to_wide(uri, (const char *) attrib->localName->bytes, attrib->localName->length);
> +
> + if (prefix == NULL)
> + {
> + WSDFreeLinkedMemory(uri);
> + continue;
> + }
> +
> + IWSDXMLContext_AddNamespace(context, uri, prefix, NULL);
> + WSDFreeLinkedMemory(uri);
> + }
> + }
> +
> + /* Create the SOAP message to return to the caller */
> + soap_msg = WSDAllocateLinkedMemory(NULL, sizeof(WSD_SOAP_MESSAGE));
> + if (soap_msg == NULL) goto cleanup;
> +
> + ZeroMemory(soap_msg, sizeof(WSD_SOAP_MESSAGE));
> +
> + envelope = WSDAllocateLinkedMemory(soap_msg, sizeof(WSDXML_ELEMENT));
> + if (envelope == NULL) goto cleanup;
> +
> + ZeroMemory(envelope, sizeof(WSDXML_ELEMENT));
> +
> + if (!ws_element_to_wsdxml_element(reader, context, envelope)) goto cleanup;
> +
> + /* Find the header element */
> + header_element = find_element(envelope, headerString, envelopeNsUri);
> + if (header_element == NULL) goto cleanup;
> +
> + if (FAILED(WSDXMLGetValueFromAny(addressingNsUri, actionString, (WSDXML_ELEMENT *) header_element->FirstChild,
> + &value))) goto cleanup;
> + soap_msg->Header.Action = duplicate_string(soap_msg, value);
> + if (soap_msg->Header.Action == NULL) goto cleanup;
> +
> + if (FAILED(WSDXMLGetValueFromAny(addressingNsUri, toString, (WSDXML_ELEMENT *) header_element->FirstChild,
> + &value))) goto cleanup;
> + soap_msg->Header.To = duplicate_string(soap_msg, value);
> + if (soap_msg->Header.To == NULL) goto cleanup;
> +
> + if (FAILED(WSDXMLGetValueFromAny(addressingNsUri, messageIdString, (WSDXML_ELEMENT *) header_element->FirstChild,
> + &value))) goto cleanup;
> + soap_msg->Header.MessageID = duplicate_string(soap_msg, value);
> + if (soap_msg->Header.MessageID == NULL) goto cleanup;
> +
> + /* Find the body element */
> + body_element = find_element(envelope, bodyString, envelopeNsUri);
> + if (body_element == NULL) goto cleanup;
> +
> + /* Now figure out which message we've been sent */
> + if (lstrcmpW(soap_msg->Header.Action, actionProbe) == 0)
> + {
> + /* TODO: Parse the Probe message */
> +
> + *out_msg = soap_msg;
> + soap_msg = NULL; /* caller will clean this up */
> + message_type = MSGTYPE_PROBE;
> + }
> +
> +cleanup:
> + free_xml_string(soap_uri);
> + WSDFreeLinkedMemory(soap_msg);
> + if (context != NULL) IWSDXMLContext_Release(context);
> +
> + return message_type;
> }
> diff --git a/dlls/wsdapi/tests/discovery.c b/dlls/wsdapi/tests/discovery.c
> index 96c2ead7f9..0599123a46 100644
> --- a/dlls/wsdapi/tests/discovery.c
> +++ b/dlls/wsdapi/tests/discovery.c
> @@ -491,7 +491,7 @@ static void verify_wsdxml_any_text(const char *debug_prefix, WSDXML_ELEMENT *any
> {
> WSDXML_TEXT *child;
>
> - ok(any != NULL, "%s: any == NULL\n", debug_prefix);
> + todo_wine ok(any != NULL, "%s: any == NULL\n", debug_prefix);
You'll need to have a really good reason for doing this. Can you
re-order things so you don't break the tests?
> if (any == NULL) return;
>
> child = (WSDXML_TEXT *) any->FirstChild;
> @@ -534,7 +534,7 @@ static HRESULT WINAPI IWSDiscoveryPublisherNotifyImpl_ProbeHandler(IWSDiscoveryP
> static const WCHAR extra_info[] = {'E','x','t','r','a','I','n','f','o',0};
> WSD_PROBE *probe_msg = (WSD_PROBE *) pSoap->Body;
>
> - ok(pSoap->Body != NULL, "pSoap->Body == NULL\n");
> + todo_wine ok(pSoap->Body != NULL, "pSoap->Body == NULL\n");
> ok(pSoap->Header.To != NULL && lstrcmpW(pSoap->Header.To, discoveryTo) == 0,
> "pSoap->Header.To == '%s'\n", wine_dbgstr_w(pSoap->Header.To));
> ok(pSoap->Header.Action != NULL && lstrcmpW(pSoap->Header.Action, actionProbe) == 0,
> @@ -939,7 +939,7 @@ after_publish_test:
> sprintf(probe_message, testProbeMessage, probe_uuid_str);
>
> ok(send_udp_multicast_of_type(probe_message, strlen(probe_message), AF_INET) == TRUE, "Sending Probe message failed\n");
> - todo_wine ok(WaitForSingleObject(probe_event, 2000) == WAIT_OBJECT_0, "Probe message not received\n");
> + ok(WaitForSingleObject(probe_event, 2000) == WAIT_OBJECT_0, "Probe message not received\n");
>
> RpcStringFreeA(&probe_uuid_str);
> }
>
>
June 7, 2018
Re: [PATCH 3/4] wsdapi: Add stub for Probe message parsing, prepare notification sink message.
by Huw Davies
On Wed, Jun 06, 2018 at 09:58:49PM +0100, Owen Rudge wrote:
> Signed-off-by: Owen Rudge <orudge(a)codeweavers.com>
> ---
> dlls/wsdapi/network.c | 52
> ++++++++++++++++++++++++++++++++++++++++++-
> dlls/wsdapi/soap.c | 6 +++++
> dlls/wsdapi/wsdapi_internal.h | 5 +++++
> 3 files changed, 62 insertions(+), 1 deletion(-)
>
>
> diff --git a/dlls/wsdapi/network.c b/dlls/wsdapi/network.c
> index 3e7509bfa3..7f6cee3ab0 100644
> --- a/dlls/wsdapi/network.c
> +++ b/dlls/wsdapi/network.c
> @@ -292,6 +292,56 @@ typedef struct listener_thread_params
> BOOL ipv6;
> } listener_thread_params;
>
> +static void process_received_message(listener_thread_params *params, char *message, int message_len,
> + SOCKADDR_STORAGE *source_addr)
This should return HRESULT.
> +{
> + IWSDUdpMessageParameters *msg_params = NULL;
> + IWSDUdpAddress *remote_addr = NULL;
> + struct notificationSink *sink;
> + WSD_SOAP_MESSAGE *msg = NULL;
> + int msg_type;
> +
> + msg_type = read_message(message, message_len, &msg);
> +
> + switch (msg_type)
> + {
> + case MSGTYPE_PROBE:
> + TRACE("Received probe message\n");
> +
> + if (FAILED(WSDCreateUdpMessageParameters(&msg_params)))
> + {
> + ERR("Unable to create IWSDUdpMessageParameters, not processing message.\n");
> + goto cleanup;
> + }
> +
> + if (FAILED(WSDCreateUdpAddress(&remote_addr)))
> + {
> + ERR("Unable to create IWSDUdpAddress, not processing message.\n");
> + goto cleanup;
> + }
> +
> + IWSDUdpAddress_SetSockaddr(remote_addr, source_addr);
> + IWSDUdpMessageParameters_SetRemoteAddress(msg_params, (IWSDAddress *)remote_addr);
> +
> + EnterCriticalSection(¶ms->impl->notification_sink_critical_section);
> +
> + LIST_FOR_EACH_ENTRY(sink, ¶ms->impl->notificationSinks, struct notificationSink, entry)
> + {
> + IWSDiscoveryPublisherNotify_ProbeHandler(sink->notificationSink, msg, (IWSDMessageParameters *)msg_params);
> + }
> +
> + LeaveCriticalSection(¶ms->impl->notification_sink_critical_section);
> +
> + break;
> + }
> +
> +cleanup:
> + WSDFreeLinkedMemory(msg);
> +
> + if (remote_addr != NULL) IWSDUdpAddress_Release(remote_addr);
> + if (msg_params != NULL) IWSDUdpMessageParameters_Release(msg_params);
> +}
> +
> #define RECEIVE_BUFFER_SIZE 65536
>
> static DWORD WINAPI listening_thread(LPVOID params)
> @@ -321,7 +371,7 @@ static DWORD WINAPI listening_thread(LPVOID params)
> }
> else
> {
> - /* TODO: Process received message */
> + process_received_message(parameter, buffer, bytes_received, &source_addr);
> }
> }
>
> diff --git a/dlls/wsdapi/soap.c b/dlls/wsdapi/soap.c
> index d0fb0afb0c..4e191a5c82 100644
> --- a/dlls/wsdapi/soap.c
> +++ b/dlls/wsdapi/soap.c
> @@ -1080,3 +1080,9 @@ cleanup:
>
> return ret;
> }
> +
> +int read_message(const char *xml, int xml_length, WSD_SOAP_MESSAGE **out_msg)
> +{
> + /* TODO: Parse and read message */
> + return MSGTYPE_UNKNOWN;
> +}
> diff --git a/dlls/wsdapi/wsdapi_internal.h b/dlls/wsdapi/wsdapi_internal.h
> index 3e5e163f3a..5c62627bba 100644
> --- a/dlls/wsdapi/wsdapi_internal.h
> +++ b/dlls/wsdapi/wsdapi_internal.h
> @@ -72,6 +72,11 @@ HRESULT send_bye_message(IWSDiscoveryPublisherImpl *impl, LPCWSTR id, ULONGLONG
>
> HRESULT register_namespaces(IWSDXMLContext *xml_context);
>
> +int read_message(const char *xml, int xml_length, WSD_SOAP_MESSAGE **out_msg);
> +
> +#define MSGTYPE_UNKNOWN 0
> +#define MSGTYPE_PROBE 1
> +
> /* xml.c */
>
> LPWSTR duplicate_string(void *parentMemoryBlock, LPCWSTR value);
>
>
June 7, 2018
Re: [PATCH 1/4] wsdapi: Add support for listening for UDP multicast broadcasts.
by Huw Davies
On Wed, Jun 06, 2018 at 09:58:45PM +0100, Owen Rudge wrote:
> Signed-off-by: Owen Rudge <orudge(a)codeweavers.com>
> ---
> dlls/wsdapi/network.c | 278
> +++++++++++++++++++++++++++++++++++++++++-
> dlls/wsdapi/wsdapi_internal.h | 4 +
> 2 files changed, 281 insertions(+), 1 deletion(-)
>
>
> diff --git a/dlls/wsdapi/network.c b/dlls/wsdapi/network.c
> index f86b3b4a3e..3e7509bfa3 100644
> --- a/dlls/wsdapi/network.c
> +++ b/dlls/wsdapi/network.c
> @@ -231,11 +231,278 @@ BOOL send_udp_multicast(IWSDiscoveryPublisherImpl *impl, char *data, int length,
> return TRUE;
> }
>
> +static int join_multicast_group(SOCKET s, SOCKADDR_STORAGE *group, SOCKADDR_STORAGE *iface)
> +{
> + int level, optname, optlen;
> + struct ipv6_mreq mreqv6;
> + struct ip_mreq mreqv4;
> + char *optval;
> +
> + if (iface->ss_family == AF_INET6)
> + {
> + level = IPPROTO_IPV6;
> + optname = IPV6_ADD_MEMBERSHIP;
> + optval = (char *)&mreqv6;
> + optlen = sizeof(mreqv6);
> +
> + mreqv6.ipv6mr_multiaddr = ((SOCKADDR_IN6 *)group)->sin6_addr;
> + mreqv6.ipv6mr_interface = ((SOCKADDR_IN6 *)iface)->sin6_scope_id;
> + }
> + else
> + {
> + level = IPPROTO_IP;
> + optname = IP_ADD_MEMBERSHIP;
> + optval = (char *)&mreqv4;
> + optlen = sizeof(mreqv4);
> +
> + mreqv4.imr_multiaddr.s_addr = ((SOCKADDR_IN *)group)->sin_addr.s_addr;
> + mreqv4.imr_interface.s_addr = ((SOCKADDR_IN *)iface)->sin_addr.s_addr;
> + }
> +
> + return setsockopt(s, level, optname, optval, optlen);
> +}
> +
> +static int set_send_interface(SOCKET s, SOCKADDR_STORAGE *iface)
> +{
> + int level, optname, optlen;
> + char *optval = NULL;
> +
> + if (iface->ss_family == AF_INET6)
> + {
> + level = IPPROTO_IPV6;
> + optname = IPV6_MULTICAST_IF;
> + optval = (char *) &((SOCKADDR_IN6 *)iface)->sin6_scope_id;
> + optlen = sizeof(((SOCKADDR_IN6 *)iface)->sin6_scope_id);
> + }
> + else
> + {
> + level = IPPROTO_IP;
> + optname = IP_MULTICAST_IF;
> + optval = (char *) &((SOCKADDR_IN *)iface)->sin_addr.s_addr;
> + optlen = sizeof(((SOCKADDR_IN *)iface)->sin_addr.s_addr);
> + }
> +
> + return setsockopt(s, level, optname, optval, optlen);
> +}
> +
> +typedef struct listener_thread_params
> +{
> + IWSDiscoveryPublisherImpl *impl;
> + SOCKET listening_socket;
> + BOOL ipv6;
> +} listener_thread_params;
> +
> +#define RECEIVE_BUFFER_SIZE 65536
> +
> +static DWORD WINAPI listening_thread(LPVOID params)
> +{
> + listener_thread_params *parameter = (listener_thread_params *)params;
> + int bytes_received, address_len, err;
> + SOCKADDR_STORAGE source_addr;
> + char *buffer;
> +
> + buffer = heap_alloc(RECEIVE_BUFFER_SIZE);
> + address_len = parameter->ipv6 ? sizeof(SOCKADDR_IN6) : sizeof(SOCKADDR_IN);
> +
> + while (parameter->impl->publisherStarted)
> + {
> + bytes_received = recvfrom(parameter->listening_socket, buffer, RECEIVE_BUFFER_SIZE, 0,
> + (LPSOCKADDR) &source_addr, &address_len);
> +
> + if (bytes_received == SOCKET_ERROR)
> + {
> + err = WSAGetLastError();
> +
> + if (err != WSAETIMEDOUT)
> + {
> + WARN("Received error when trying to read from socket: %d. Stopping listener.\n", err);
> + return 0;
> + }
> + }
> + else
> + {
> + /* TODO: Process received message */
> + }
> + }
> +
> + /* The publisher has been stopped */
> + closesocket(parameter->listening_socket);
> +
> + heap_free(buffer);
> + heap_free(parameter);
> +
> + return 0;
> +}
> +
> +static void start_listening(IWSDiscoveryPublisherImpl *impl, SOCKADDR_STORAGE *bind_address)
This should probably return an indication of success (even if it's not
possible for the caller to do something sensible with the result).
> +{
> + SOCKADDR_STORAGE multicast_addr, bind_addr, interface_addr;
> + listener_thread_params *parameter = NULL;
> + const DWORD receive_timeout = 5000;
> + const UINT reuse_addr = 1;
> + HANDLE thread_handle;
> + int address_length;
> + SOCKET s = 0;
> +
> + TRACE("(%p, %p) family %d\n", impl, bind_address, bind_address->ss_family);
> +
> + /* Populate the multicast address */
> + ZeroMemory(&multicast_addr, sizeof(SOCKADDR_STORAGE));
> +
> + if (bind_address->ss_family == AF_INET)
> + {
> + SOCKADDR_IN *sockaddr4 = (SOCKADDR_IN *)&multicast_addr;
> +
> + sockaddr4->sin_port = htons(SEND_PORT);
> + sockaddr4->sin_addr.S_un.S_addr = htonl(SEND_ADDRESS_IPV4);
> + address_length = sizeof(SOCKADDR_IN);
> + }
> + else
> + {
> + SOCKADDR_IN6 *sockaddr6 = (SOCKADDR_IN6 *)&multicast_addr;
> +
> + sockaddr6->sin6_port = htons(SEND_PORT);
> + memcpy(&sockaddr6->sin6_addr, &send_address_ipv6, sizeof(send_address_ipv6));
> + address_length = sizeof(SOCKADDR_IN6);
> + }
> +
> + /* Update the port for the binding address */
> + memcpy(&bind_addr, bind_address, address_length);
> + ((SOCKADDR_IN *)&bind_addr)->sin_port = htons(SEND_PORT);
> +
> + /* Update the port for the interface address */
> + memcpy(&interface_addr, bind_address, address_length);
> + ((SOCKADDR_IN *)&interface_addr)->sin_port = htons(0);
> +
> + /* Create the socket */
> + s = socket(bind_address->ss_family, SOCK_DGRAM, IPPROTO_UDP);
> +
> + if (s == INVALID_SOCKET)
> + {
> + WARN("socket() failed (error %d)\n", WSAGetLastError());
> + goto cleanup;
> + }
> +
> + /* Ensure the socket can be reused */
> + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&reuse_addr, sizeof(reuse_addr)) == SOCKET_ERROR)
> + {
> + WARN("setsockopt(SO_REUSEADDR) failed (error %d)\n", WSAGetLastError());
> + goto cleanup;
> + }
> +
> + /* Bind the socket to the local interface so we can receive data */
> + if (bind(s, (struct sockaddr *)&bind_addr, address_length) == SOCKET_ERROR)
> + {
> + WARN("bind() failed (error %d)\n", WSAGetLastError());
> + goto cleanup;
> + }
> +
> + /* Join the multicast group */
> + if (join_multicast_group(s, &multicast_addr, &interface_addr) == SOCKET_ERROR)
> + {
> + WARN("Unable to join multicast group (error %d)\n", WSAGetLastError());
> + goto cleanup;
> + }
> +
> + /* Set the outgoing interface */
> + if (set_send_interface(s, &interface_addr) == SOCKET_ERROR)
> + {
> + WARN("Unable to set outgoing interface (error %d)\n", WSAGetLastError());
> + goto cleanup;
> + }
> +
> + /* Set a 5-second receive timeout */
> + if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (const char *)&receive_timeout, sizeof(receive_timeout)) == SOCKET_ERROR)
> + {
> + WARN("setsockopt(SO_RCVTIME0) failed (error %d)\n", WSAGetLastError());
> + goto cleanup;
> + }
> +
> + /* Allocate memory for thread parameters */
> + parameter = heap_alloc(sizeof(listener_thread_params));
> +
> + parameter->impl = impl;
> + parameter->listening_socket = s;
> + parameter->ipv6 = (bind_address->ss_family == AF_INET6);
> +
> + thread_handle = CreateThread(NULL, 0, listening_thread, parameter, 0, NULL);
> +
> + if (thread_handle == NULL)
> + {
> + WARN("CreateThread failed (error %d)\n", GetLastError());
> + goto cleanup;
> + }
> +
> + impl->thread_handles[impl->num_thread_handles] = thread_handle;
> + impl->num_thread_handles++;
> +
> + return;
> +
> +cleanup:
> + closesocket(s);
> + heap_free(parameter);
> +}
> +
> +static BOOL start_listening_on_all_addresses(IWSDiscoveryPublisherImpl *impl, ULONG family)
> +{
> + IP_ADAPTER_ADDRESSES *adapter_addresses = NULL, *adapter_address;
> + ULONG bufferSize = 0;
> + ULONG ret;
> +
> + ret = GetAdaptersAddresses(family, 0, NULL, NULL, &bufferSize); /* family should be AF_INET or AF_INET6 */
> +
> + if (ret != ERROR_BUFFER_OVERFLOW)
> + {
> + WARN("GetAdaptorsAddresses failed with error %08x\n", ret);
> + return FALSE;
> + }
> +
> + /* Get size of buffer for adapters */
> + adapter_addresses = (IP_ADAPTER_ADDRESSES *)heap_alloc(bufferSize);
> +
> + if (adapter_addresses == NULL)
> + {
> + WARN("Out of memory allocating space for adapter information\n");
> + return FALSE;
> + }
> +
> + /* Get list of adapters */
> + ret = GetAdaptersAddresses(family, 0, NULL, adapter_addresses, &bufferSize);
> +
> + if (ret != ERROR_SUCCESS)
> + {
> + WARN("GetAdaptorsAddresses failed with error %08x\n", ret);
> + goto cleanup;
> + }
> +
> + for (adapter_address = adapter_addresses; adapter_address != NULL; adapter_address = adapter_address->Next)
> + {
> + if (impl->num_thread_handles >= MAX_WSD_THREADS)
> + {
> + WARN("Exceeded maximum number of supported listener threads; too many network interfaces.");
> + goto cleanup;
> + }
> +
> + if (adapter_address->FirstUnicastAddress == NULL)
> + {
> + TRACE("No address found for adaptor '%s' (%p)\n", adapter_address->AdapterName, adapter_address);
> + continue;
> + }
> +
> + start_listening(impl, (SOCKADDR_STORAGE *)adapter_address->FirstUnicastAddress->Address.lpSockaddr);
> + }
> +
> +cleanup:
> + heap_free(adapter_addresses);
> + return ret == ERROR_SUCCESS;
> +}
> +
> void terminate_networking(IWSDiscoveryPublisherImpl *impl)
> {
> BOOL needsCleanup = impl->publisherStarted;
>
> impl->publisherStarted = FALSE;
> + WaitForMultipleObjects(impl->num_thread_handles, impl->thread_handles, TRUE, INFINITE);
>
> if (needsCleanup)
> WSACleanup();
> @@ -254,6 +521,15 @@ BOOL init_networking(IWSDiscoveryPublisherImpl *impl)
>
> impl->publisherStarted = TRUE;
>
> - /* TODO: Start listening */
> + if ((impl->addressFamily & WSDAPI_ADDRESSFAMILY_IPV4) && (!start_listening_on_all_addresses(impl, AF_INET)))
> + goto cleanup;
> +
> + if ((impl->addressFamily & WSDAPI_ADDRESSFAMILY_IPV6) && (!start_listening_on_all_addresses(impl, AF_INET6)))
> + goto cleanup;
> +
> return TRUE;
> +
> +cleanup:
> + terminate_networking(impl);
> + return FALSE;
> }
> diff --git a/dlls/wsdapi/wsdapi_internal.h b/dlls/wsdapi/wsdapi_internal.h
> index 45d54c2ef1..f88822d5ef 100644
> --- a/dlls/wsdapi/wsdapi_internal.h
> +++ b/dlls/wsdapi/wsdapi_internal.h
> @@ -40,6 +40,8 @@ struct notificationSink
> IWSDiscoveryPublisherNotify *notificationSink;
> };
>
> +#define MAX_WSD_THREADS 20
> +
> typedef struct IWSDiscoveryPublisherImpl {
> IWSDiscoveryPublisher IWSDiscoveryPublisher_iface;
> LONG ref;
> @@ -47,6 +49,8 @@ typedef struct IWSDiscoveryPublisherImpl {
> DWORD addressFamily;
> struct list notificationSinks;
> BOOL publisherStarted;
> + HANDLE thread_handles[MAX_WSD_THREADS];
> + int num_thread_handles;
> } IWSDiscoveryPublisherImpl;
>
> /* network.c */
>
>
June 7, 2018