Wine-Devel
By thread
wine-devel@list.winehq.org
By month
Messages by month
- ----- 2026 -----
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2004 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2003 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2002 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2001 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
April 2022
- 87 participants
- 3124 messages
Re: MR9v1 - ntdll: Implement __fastfail().
by Biswapriyo Nath (@Biswa96)
Just wondering. Is it possible to add comment or names beside those constant values?
--
https://gitlab.winehq.org/wine/wine/-/merge_requests/9#note_519
April 30, 2022
[PATCH v3 9/9] loader: Switch stack if the old stack address is in reserved range.
by Jinoh Kang
From: Jinoh Kang <jinoh.kang.kr(a)gmail.com>
Today, the preloader abandons reserved address ranges that conflict with
the call stack area.
Fix this by attempting to copy the stack somewhere else, and switching
to it before entering the ld.so entry point. This way, the preloader
does not have to give up the address reservation.
Since this is a potentially risky change, this behaviour is hidden
behind the "WINEPRELOADREMAPSTACK" environment variable. To activate
the behaviour, the user needs to set
"WINEPRELOADREMAPSTACK=on-conflict". After sufficient testing has
been done via staging process, the new behaviour could be the default
and the environment variables removed.
Note that changes to argv and envp is *not* visible in
/proc/PID/{environ,cmdline} after the stack has been switched, since
kernel mm pointer fields are still pointing to the old stack.
Signed-off-by: Jinoh Kang <jinoh.kang.kr(a)gmail.com>
---
loader/preloader.c | 153 ++++++++++++++++++++++++++++++++++++++-------
1 file changed, 132 insertions(+), 21 deletions(-)
diff --git a/loader/preloader.c b/loader/preloader.c
index f5a3470d81f..9b57430bf26 100644
--- a/loader/preloader.c
+++ b/loader/preloader.c
@@ -266,6 +266,7 @@ enum vma_type_flags
#ifdef __arm__
VMA_SIGPAGE = 0x08,
#endif
+ VMA_STACK = 0x10,
};
struct vma_area
@@ -302,6 +303,7 @@ enum remap_policy
#ifdef __arm__
REMAP_POLICY_DEFAULT_SIGPAGE = REMAP_POLICY_SKIP,
#endif
+ REMAP_POLICY_DEFAULT_STACK = REMAP_POLICY_SKIP,
};
/*
@@ -1265,6 +1267,82 @@ static void stackargs_shift_args( struct stackarg_info *info, int num_args )
*(int *)info->stack = info->argc;
}
+/*
+ * relocate_argvec
+ *
+ * Copy argument / environment vector from src to dest, fixing up addresses so
+ * that addresses relative to src are now relative to dest.
+ */
+static size_t relocate_argvec( char **dest, char **src, size_t count )
+{
+ size_t i;
+ unsigned long delta = (unsigned long)dest - (unsigned long)src;
+
+ for (i = 0; i < count && src[i]; i++)
+ dest[i] = src[i] + delta;
+
+ dest[i] = 0;
+ return i;
+}
+
+/*
+ * relocate_auxvec
+ *
+ * Copy auxiliary vector from src to dest, fixing up addresses so that addresses
+ * relative to src are now relative to dest.
+ */
+static void relocate_auxvec( struct wld_auxv *dest, struct wld_auxv *src, size_t count )
+{
+ size_t i;
+ unsigned long delta = (unsigned long)dest - (unsigned long)src;
+
+ for (i = 0; i < count; i++)
+ {
+ dest[i].a_type = src[i].a_type;
+ switch (dest[i].a_type)
+ {
+ case AT_RANDOM:
+ case AT_PLATFORM:
+ case AT_BASE_PLATFORM:
+ case AT_EXECFN:
+ if (src[i].a_un.a_val >= (unsigned long)src)
+ {
+ dest[i].a_un.a_val = src[i].a_un.a_val + delta;
+ break;
+ }
+ /* fallthrough */
+ default:
+ dest[i].a_un.a_val = src[i].a_un.a_val;
+ break;
+ }
+ }
+}
+
+/*
+ * copy_stackargs
+ *
+ * Copy the initial stack containing program arguments to newstack, fixing up
+ * addresses as appropriate.
+ */
+static void copy_stackargs( struct stackarg_info *newinfo, struct stackarg_info *oldinfo, void *newstack, void *newstackend )
+{
+ unsigned long delta = (unsigned long)newstack - (unsigned long)oldinfo->stack;
+
+ newinfo->stack = newstack;
+ newinfo->argc = oldinfo->argc;
+ newinfo->argv = (void *)((unsigned long)oldinfo->argv + delta);
+ newinfo->envp = (void *)((unsigned long)oldinfo->envp + delta);
+ newinfo->auxv = (void *)((unsigned long)oldinfo->auxv + delta);
+ newinfo->auxv_end = (void *)((unsigned long)oldinfo->auxv_end + delta);
+
+ *(int *)newstack = *(int *)oldinfo->stack; /* Copy argc */
+ relocate_argvec( newinfo->argv, oldinfo->argv, newinfo->envp - newinfo->argv );
+ relocate_argvec( newinfo->envp, oldinfo->envp, (char **)newinfo->auxv - newinfo->envp );
+ relocate_auxvec( newinfo->auxv, oldinfo->auxv, newinfo->auxv_end - newinfo->auxv );
+ wld_memmove( newinfo->auxv_end, oldinfo->auxv_end,
+ (unsigned long)newstackend - (unsigned long)newinfo->auxv_end );
+}
+
/*
* set_auxiliary_values
*
@@ -2143,7 +2221,7 @@ static int remap_multiple_vmas( struct vma_area_list *list, unsigned long delta,
*
* Parse /proc/self/maps into the given VMA area list.
*/
-static void scan_vma( struct vma_area_list *list, size_t *real_count )
+static void scan_vma( struct vma_area_list *list, size_t *real_count, void *stack_ptr )
{
int fd;
size_t n = 0;
@@ -2167,6 +2245,9 @@ static void scan_vma( struct vma_area_list *list, size_t *real_count )
{
if (parse_maps_line( &item, line ) >= 0)
{
+ if (item.start <= (unsigned long)stack_ptr &&
+ item.end > (unsigned long)stack_ptr)
+ item.type_flags |= VMA_STACK;
if (list->list_end < list->alloc_end) insert_vma_entry( list, &item );
n++;
}
@@ -2197,7 +2278,7 @@ static void free_vma_list( struct vma_area_list *list )
*
* Parse /proc/self/maps into a newly allocated VMA area list.
*/
-static void alloc_scan_vma( struct vma_area_list *listp )
+static void alloc_scan_vma( struct vma_area_list *listp, void *stack_ptr )
{
size_t max_count = page_size / sizeof(struct vma_area);
struct vma_area_list vma_list;
@@ -2212,7 +2293,7 @@ static void alloc_scan_vma( struct vma_area_list *listp )
vma_list.list_end = vma_list.base;
vma_list.alloc_end = vma_list.base + max_count;
- scan_vma( &vma_list, &max_count );
+ scan_vma( &vma_list, &max_count, stack_ptr );
if (vma_list.list_end - vma_list.base == max_count)
{
wld_memmove(listp, &vma_list, sizeof(*listp));
@@ -2482,7 +2563,7 @@ static int remap_vdso( struct vma_area_list *vma_list, struct preloader_state *s
/* Refresh VMA list */
free_vma_list( vma_list );
- alloc_scan_vma( vma_list );
+ alloc_scan_vma( vma_list, state->s.stack );
return 1;
remap_restore:
@@ -2530,7 +2611,7 @@ static int remap_sigpage( struct vma_area_list *vma_list, struct preloader_state
/* Refresh VMA list */
free_vma_list( vma_list );
- alloc_scan_vma( vma_list );
+ alloc_scan_vma( vma_list, state->s.stack );
return 1;
remap_restore:
@@ -2541,29 +2622,58 @@ remap_restore:
}
#endif
+/*
+ * remap_stack
+ *
+ * Perform stack remapping if it conflicts with one of the reserved address ranges.
+ */
+static int remap_stack( struct vma_area_list *vma_list, struct preloader_state *state )
+{
+ unsigned long stack_start, stack_size;
+ struct stackarg_info newinfo;
+ void *new_stack, *new_stack_base;
+ int result, i;
+
+ if (find_vma_envelope_range( vma_list, VMA_STACK,
+ &stack_start, &stack_size ) < 0) return 0;
+
+ result = check_remap_policy( state, "WINEPRELOADREMAPSTACK",
+ REMAP_POLICY_DEFAULT_STACK,
+ stack_start, stack_size );
+ if (result < 0) goto remove_from_reserve;
+ if (result == 0) return 0;
+
+ new_stack_base = wld_mmap( NULL, stack_size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0 );
+ if (new_stack_base == (void *)-1) goto remove_from_reserve;
+
+ new_stack = (void *)((unsigned long)new_stack_base + ((unsigned long)state->s.stack - stack_start));
+ copy_stackargs( &newinfo, &state->s, new_stack, (void *)((unsigned long)new_stack_base + stack_size) );
+
+ wld_memmove( &state->s, &newinfo, sizeof(state->s) );
+
+ free_vma_list( vma_list );
+ alloc_scan_vma( vma_list, state->s.stack );
+ return 1;
+
+remove_from_reserve:
+ while ((i = find_preload_reserved_area( (void *)stack_start, stack_size )) >= 0)
+ remove_preload_range( i );
+ return -1;
+}
+
/*
* map_reserve_preload_ranges
*
* Attempt to reserve memory ranges into preload_info.
- * If any preload_info entry overlaps with stack, remove the entry instead of
- * reserving.
*/
-static void map_reserve_preload_ranges( const struct vma_area_list *vma_list,
- const struct stackarg_info *stackinfo )
+static void map_reserve_preload_ranges( const struct vma_area_list *vma_list )
{
size_t i;
- unsigned long exclude_start = (unsigned long)stackinfo->stack - 1;
- unsigned long exclude_end = (unsigned long)stackinfo->auxv + 1;
for (i = 0; preload_info[i].size; i++)
{
- if (exclude_end > (unsigned long)preload_info[i].addr &&
- exclude_start <= (unsigned long)preload_info[i].addr + preload_info[i].size - 1)
- {
- remove_preload_range( i );
- i--;
- }
- else if (map_reserve_unmapped_range( vma_list, preload_info[i].addr, preload_info[i].size ) < 0)
+ if (map_reserve_unmapped_range( vma_list, preload_info[i].addr, preload_info[i].size ) < 0)
{
/* don't warn for low 64k */
if (preload_info[i].addr >= (void *)0x10000
@@ -2626,15 +2736,16 @@ void* wld_start( void **stack )
reserve = stackargs_getenv( &state.s, "WINEPRELOADRESERVE" );
if (reserve) preload_reserve( reserve );
- alloc_scan_vma( &vma_list );
- map_reserve_preload_ranges( &vma_list, &state.s );
+ alloc_scan_vma( &vma_list, state.s.stack );
+ map_reserve_preload_ranges( &vma_list );
remap_done = 0;
remap_done |= remap_vdso( &vma_list, &state ) > 0;
#ifdef __arm__
remap_done |= remap_sigpage( &vma_list, &state ) > 0;
#endif
- if (remap_done) map_reserve_preload_ranges( &vma_list, &state.s );
+ remap_done |= remap_stack( &vma_list, &state ) > 0;
+ if (remap_done) map_reserve_preload_ranges( &vma_list );
/* add an executable page at the top of the address space to defeat
* broken no-exec protections that play with the code selector limit */
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/6
April 30, 2022
[PATCH v3 8/9] loader: Relocate sigpage on conflict with reserved ranges in ARM.
by Jinoh Kang
From: Jinoh Kang <jinoh.kang.kr(a)gmail.com>
Today, the preloader makes no attempt to remap the sigpage when it
conflicts with reserved addresses. If libc doesn't have its own signal
restorer, this results in inability to return from signal handlers.
Fix this by relocating sigpage to another address whenever possible.
Since this is a potentially risky change, this behaviour is hidden
behind the "WINEPRELOADREMAPSIGPAGE" environment variable. To activate
the behaviour, the user needs to set
"WINEPRELOADREMAPSIGPAGE=on-conflict". After sufficient testing has
been done via staging process, the new behaviour could be the default
and the environment variables removed.
Signed-off-by: Jinoh Kang <jinoh.kang.kr(a)gmail.com>
---
loader/preloader.c | 75 ++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 70 insertions(+), 5 deletions(-)
diff --git a/loader/preloader.c b/loader/preloader.c
index adedb6dcae0..f5a3470d81f 100644
--- a/loader/preloader.c
+++ b/loader/preloader.c
@@ -260,9 +260,12 @@ struct linebuffer
*/
enum vma_type_flags
{
- VMA_NORMAL = 0x01,
- VMA_VDSO = 0x02,
- VMA_VVAR = 0x04,
+ VMA_NORMAL = 0x01,
+ VMA_VDSO = 0x02,
+ VMA_VVAR = 0x04,
+#ifdef __arm__
+ VMA_SIGPAGE = 0x08,
+#endif
};
struct vma_area
@@ -295,7 +298,10 @@ enum remap_policy
REMAP_POLICY_SKIP = 2,
LAST_REMAP_POLICY,
- REMAP_POLICY_DEFAULT_VDSO = REMAP_POLICY_SKIP,
+ REMAP_POLICY_DEFAULT_VDSO = REMAP_POLICY_SKIP,
+#ifdef __arm__
+ REMAP_POLICY_DEFAULT_SIGPAGE = REMAP_POLICY_SKIP,
+#endif
};
/*
@@ -1957,6 +1963,10 @@ static int parse_maps_line( struct vma_area *entry, const char *line )
item.type_flags |= VMA_VDSO;
else if (wld_strcmp(ptr, "[vvar]") == 0)
item.type_flags |= VMA_VVAR;
+#ifdef __arm__
+ else if (wld_strcmp(ptr, "[sigpage]") == 0)
+ item.type_flags |= VMA_SIGPAGE;
+#endif
}
*entry = item;
@@ -2482,6 +2492,55 @@ remap_restore:
return -1;
}
+#ifdef __arm__
+/*
+ * remap_sigpage
+ *
+ * Perform sigpage remapping if it conflicts with one of the reserved address ranges.
+ *
+ * sigpage remapping shouldn't really be necessary, since modern libcs
+ * use their own signal restorer anyway. But better be safe than sorry...
+ */
+static int remap_sigpage( struct vma_area_list *vma_list, struct preloader_state *state )
+{
+ int result;
+ unsigned long sigpage_start, sigpage_size, delta;
+ void *new_sigpage;
+
+ if (find_vma_envelope_range( vma_list, VMA_SIGPAGE,
+ &sigpage_start, &sigpage_size ) < 0) return 0;
+
+ result = check_remap_policy( state, "WINEPRELOADREMAPSIGPAGE",
+ REMAP_POLICY_DEFAULT_SIGPAGE,
+ sigpage_start, sigpage_size );
+ if (result <= 0) return result;
+
+ new_sigpage = wld_mmap( NULL, sigpage_size, PROT_NONE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0 );
+ if (new_sigpage == (void *)-1) return -1;
+
+ delta = (unsigned long)new_sigpage - sigpage_start;
+ if (remap_multiple_vmas( vma_list, delta, VMA_SIGPAGE, 0 ) < 0) goto remap_restore;
+
+ if (test_remap_successful( vma_list, state, sigpage_start, sigpage_size, delta ) < 0)
+ {
+ /* mapping restore done by test_remap_successful */
+ return -1;
+ }
+
+ /* Refresh VMA list */
+ free_vma_list( vma_list );
+ alloc_scan_vma( vma_list );
+ return 1;
+
+remap_restore:
+ if (remap_multiple_vmas( vma_list, delta, -1, 1 ) < 0)
+ fatal_error( "Cannot restore remapped VMAs\n" );
+
+ return -1;
+}
+#endif
+
/*
* map_reserve_preload_ranges
*
@@ -2537,6 +2596,7 @@ void* wld_start( void **stack )
struct wine_preload_info **wine_main_preload_info;
struct preloader_state state = { 0 };
struct vma_area_list vma_list = { NULL };
+ int remap_done;
parse_stackargs( &state.s, *stack );
@@ -2569,7 +2629,12 @@ void* wld_start( void **stack )
alloc_scan_vma( &vma_list );
map_reserve_preload_ranges( &vma_list, &state.s );
- if (remap_vdso( &vma_list, &state ) > 0) map_reserve_preload_ranges( &vma_list, &state.s );
+ remap_done = 0;
+ remap_done |= remap_vdso( &vma_list, &state ) > 0;
+#ifdef __arm__
+ remap_done |= remap_sigpage( &vma_list, &state ) > 0;
+#endif
+ if (remap_done) map_reserve_preload_ranges( &vma_list, &state.s );
/* add an executable page at the top of the address space to defeat
* broken no-exec protections that play with the code selector limit */
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/6
April 30, 2022
[PATCH v3 7/9] loader: Relocate vDSO on conflict with reserved ranges.
by Jinoh Kang
From: Jinoh Kang <jinoh.kang.kr(a)gmail.com>
Today, the preloader removes the vDSO entries (AT_SYSINFO*) from the
auxiliary vector when it conflicts with one of the predefined reserved
ranges.
vDSO is a shared object provided by the kernel. Among other things, it
provides a mechanism to issue certain system calls without the overhead
of switching to the kernel mode.
Without vDSO, libc still works; however, it is expected that some system
call functions (e.g. gettimeofday, clock_gettime) will show degraded
performance.
Fix this by relocating vDSO to another address (if supported by the
kernel) instead of erasing it from auxv entirely.
Since this is a potentially risky change, this behaviour is hidden
behind the "WINEPRELOADREMAPVDSO" environment variable. To activate the
behaviour, the user needs to set "WINEPRELOADREMAPVDSO=on-conflict".
After sufficient testing has been done via staging process, the new
behaviour could be the default and the environment variables removed.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=52313
Signed-off-by: Jinoh Kang <jinoh.kang.kr(a)gmail.com>
---
loader/preloader.c | 607 ++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 604 insertions(+), 3 deletions(-)
diff --git a/loader/preloader.c b/loader/preloader.c
index 763cf6bdbfc..adedb6dcae0 100644
--- a/loader/preloader.c
+++ b/loader/preloader.c
@@ -72,6 +72,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
+#include <signal.h>
#include <sys/mman.h>
#ifdef HAVE_SYS_SYSCALL_H
# include <sys/syscall.h>
@@ -86,6 +87,9 @@
#ifdef HAVE_SYS_LINK_H
# include <sys/link.h>
#endif
+#ifdef HAVE_SYS_UCONTEXT_H
+# include <sys/ucontext.h>
+#endif
#include "wine/asm.h"
#include "main.h"
@@ -102,6 +106,11 @@
#ifndef MAP_NORESERVE
#define MAP_NORESERVE 0
#endif
+#ifndef MREMAP_FIXED
+#define MREMAP_FIXED 2
+#endif
+
+#define REMAP_TEST_SIG SIGIO /* Any signal GDB doesn't stop on */
static struct wine_preload_info preload_info[] =
{
@@ -165,6 +174,19 @@ struct wld_auxv
} a_un;
};
+typedef unsigned long wld_sigset_t[8 / sizeof(unsigned long)];
+
+struct wld_sigaction
+{
+ /* Prefix all fields since they may collide with macros from libc headers */
+ void (*wld_sa_sigaction)(int, siginfo_t *, void *);
+ unsigned long wld_sa_flags;
+ void (*wld_sa_restorer)(void);
+ wld_sigset_t wld_sa_mask;
+};
+
+#define WLD_SA_SIGINFO 4
+
/* Aggregates information about initial program stack and variables
* (e.g. argv and envp) that reside in it.
*/
@@ -194,10 +216,61 @@ struct linebuffer
int truncated; /* line truncated? (if true, skip until next line) */
};
+/*
+ * Flags that specify the kind of each VMA entry read from /proc/self/maps.
+ *
+ * On Linux, vDSO hard-codes vvar's address relative to vDSO. Therefore, it is
+ * necessary to maintain vvar's position relative to vDSO when they are
+ * remapped. We cannot just remap one of them and leave the other one behind;
+ * they have to be moved as a single unit. Doing so requires identifying the
+ * *exact* size and boundaries of *both* mappings. This is met by a few
+ * challenges:
+ *
+ * 1. vvar's size *and* its location relative to vDSO is *not* guaranteed by
+ * Linux userspace ABI, and has changed all the time.
+ *
+ * - x86: [vvar] orginally resided at a fixed address 0xffffffffff5ff000
+ * (64-bit) [1], but was later changed so that it precedes [vdso] [2].
+ * There, sym_vvar_start is a negative value [3]. text_start is the base
+ * address of vDSO, and addr becomes the address of vvar.
+ *
+ * - AArch32: [vvar] is a single page and precedes [vdso] [4].
+ *
+ * - AArch64: [vvar] is two pages long and precedes [vdso] [5].
+ * Before v5.9, however, [vvar] was a single page [6].
+ *
+ * 2. It's very difficult to infer vDSO and vvar's size and offset relative to
+ * each other just from vDSO data. Since vvar's symbol does not exist in
+ * vDSO's symtab, determining the layout would require parsing vDSO's code.
+ *
+ * 3. Determining the size of both mappings is not a trivial task. Even if we
+ * parse vDSO's ELF header, we cannot still measure the size of vvar.
+ *
+ * Therefore, the only reliable method to identify the range of the mappings is
+ * to read from /proc/self/maps. This is also what the CRIU (Checkpoint
+ * Restore In Userspace) project uses for relocating vDSO [7].
+ *
+ * [1] https://lwn.net/Articles/615809/
+ * [2] https://elixir.bootlin.com/linux/v5.16.3/source/arch/x86/entry/vdso/vma.c#L…
+ * [3] https://elixir.bootlin.com/linux/v5.16.3/source/arch/x86/include/asm/vdso.h…
+ * [4] https://elixir.bootlin.com/linux/v5.16.3/source/arch/arm/kernel/vdso.c#L236
+ * [5] https://elixir.bootlin.com/linux/v5.16.3/source/arch/arm64/kernel/vdso.c#L2…
+ * [6] https://elixir.bootlin.com/linux/v5.8/source/arch/arm64/kernel/vdso.c#L161
+ * [7] https://github.com/checkpoint-restore/criu/blob/2f0f12839673c7d82cfc18e99d7…
+ */
+enum vma_type_flags
+{
+ VMA_NORMAL = 0x01,
+ VMA_VDSO = 0x02,
+ VMA_VVAR = 0x04,
+};
+
struct vma_area
{
unsigned long start;
unsigned long end;
+ unsigned char type_flags; /* enum vma_type_flags */
+ unsigned char moved; /* has been mremap()'d? */
};
struct vma_area_list
@@ -210,6 +283,60 @@ struct vma_area_list
#define FOREACH_VMA(list, item) \
for ((item) = (list)->base; (item) != (list)->list_end; (item)++)
+/*
+ * Allow the user to configure the remapping behaviour if it causes trouble.
+ * The "force" (REMAP_POLICY_FORCE) value can be used to test the remapping
+ * code path unconditionally.
+ */
+enum remap_policy
+{
+ REMAP_POLICY_ON_CONFLICT = 0,
+ REMAP_POLICY_FORCE = 1,
+ REMAP_POLICY_SKIP = 2,
+ LAST_REMAP_POLICY,
+
+ REMAP_POLICY_DEFAULT_VDSO = REMAP_POLICY_SKIP,
+};
+
+/*
+ * Used in the signal handler that tests if mremap() on vDSO works on the
+ * current kernel.
+ */
+struct remap_test_block
+{
+ /*
+ * The old address range of vDSO or sigpage. Used to test if pages are
+ * remapped properly.
+ */
+ unsigned long old_mapping_start;
+ unsigned long old_mapping_size;
+
+ /*
+ * A snapshot of the VMA area list of the current process. Used to restore
+ * vDSO mappings on remapping failure from the signal handler.
+ */
+ struct vma_area_list *vma_list;
+
+ /*
+ * The difference between the new mapping's address and the old mapping's
+ * address. Set to 0 if the handler reverted mappings to old state before
+ * returning.
+ */
+ unsigned long delta;
+
+ /*
+ * Set to 1 by the signal handler if it determines that the remapping was
+ * successfully recognised by the kernel.
+ */
+ unsigned char is_successful;
+
+ /*
+ * Set to 1 by the signal handler if it determines that the remapping was
+ * not recognised by the kernel.
+ */
+ unsigned char is_failed;
+} remap_test;
+
/*
* The __bb_init_func is an empty function only called when file is
* compiled with gcc flags "-fprofile-arcs -ftest-coverage". This
@@ -245,6 +372,16 @@ struct
unsigned int garbage : 25;
} thread_ldt = { -1, (unsigned long)thread_data, 0xfffff, 1, 0, 0, 1, 0, 1, 0 };
+typedef unsigned long wld_old_sigset_t;
+
+struct wld_old_sigaction
+{
+ /* Prefix all fields since they may collide with macros from libc headers */
+ void (*wld_sa_sigaction)(int, siginfo_t *, void *);
+ wld_old_sigset_t wld_sa_mask;
+ unsigned long wld_sa_flags;
+ void (*wld_sa_restorer)(void);
+};
/*
* The _start function is the entry and exit point of this program
@@ -382,6 +519,16 @@ static inline int wld_munmap( void *addr, size_t len )
return SYSCALL_RET(ret);
}
+static inline void *wld_mremap( void *old_addr, size_t old_len, size_t new_size, int flags, void *new_addr )
+{
+ int ret;
+ __asm__ __volatile__( "pushl %%ebx; movl %2,%%ebx; int $0x80; popl %%ebx"
+ : "=a" (ret) : "0" (163 /* SYS_mremap */), "r" (old_addr), "c" (old_len),
+ "d" (new_size), "S" (flags), "D" (new_addr)
+ : "memory" );
+ return (void *)SYSCALL_RET(ret);
+}
+
static inline int wld_prctl( int code, long arg )
{
int ret;
@@ -390,6 +537,67 @@ static inline int wld_prctl( int code, long arg )
return SYSCALL_RET(ret);
}
+static void copy_old_sigset( void *dest, const void *src )
+{
+ /* Avoid aliasing */
+ size_t i;
+ for (i = 0; i < sizeof(wld_old_sigset_t); i++)
+ *((unsigned char *)dest + i) = *((const unsigned char *)src + i);
+}
+
+static inline int wld_sigaction( int signum, const struct wld_sigaction *act, struct wld_sigaction *old_act )
+{
+ int ret;
+ __asm__ __volatile__( "pushl %%ebx; movl %2,%%ebx; int $0x80; popl %%ebx"
+ : "=a" (ret) : "0" (174 /* SYS_rt_sigaction */), "r" (signum), "c" (act), "d" (old_act), "S" (sizeof(act->wld_sa_mask))
+ : "memory" );
+ if (ret == -38 /* ENOSYS */)
+ {
+ struct wld_old_sigaction act_buf, old_act_buf, *act_real, *old_act_real;
+
+ if (act)
+ {
+ act_real = &act_buf;
+ act_buf.wld_sa_sigaction = act->wld_sa_sigaction;
+ copy_old_sigset(&act_buf.wld_sa_mask, &act->wld_sa_mask);
+ act_buf.wld_sa_flags = act->wld_sa_flags;
+ act_buf.wld_sa_restorer = act->wld_sa_restorer;
+ }
+
+ if (old_act) old_act_real = &old_act_buf;
+
+ __asm__ __volatile__( "pushl %%ebx; movl %2,%%ebx; int $0x80; popl %%ebx"
+ : "=a" (ret) : "0" (67 /* SYS_sigaction */), "r" (signum), "c" (act_real), "d" (old_act_real)
+ : "memory" );
+
+ if (old_act && ret >= 0)
+ {
+ old_act->wld_sa_sigaction = old_act_buf.wld_sa_sigaction;
+ old_act->wld_sa_flags = old_act_buf.wld_sa_flags;
+ old_act->wld_sa_restorer = old_act_buf.wld_sa_restorer;
+ copy_old_sigset(&old_act->wld_sa_mask, &old_act_buf.wld_sa_mask);
+ }
+ }
+ return SYSCALL_RET(ret);
+}
+
+static inline int wld_kill( pid_t pid, int sig )
+{
+ int ret;
+ __asm__ __volatile__( "pushl %%ebx; movl %2,%%ebx; int $0x80; popl %%ebx"
+ : "=a" (ret) : "0" (37 /* SYS_kill */), "r" (pid), "c" (sig)
+ : "memory" /* clobber: signal handler side effects on raise() */ );
+ return SYSCALL_RET(ret);
+}
+
+static inline pid_t wld_getpid( void )
+{
+ int ret;
+ __asm__ __volatile__( "int $0x80"
+ : "=a" (ret) : "0" (20 /* SYS_getpid */) );
+ return ret;
+}
+
#elif defined(__x86_64__)
void *thread_data[256];
@@ -468,9 +676,15 @@ SYSCALL_FUNC( wld_mprotect, 10 /* SYS_mprotect */ );
int wld_munmap( void *addr, size_t len );
SYSCALL_FUNC( wld_munmap, 11 /* SYS_munmap */ );
+void *wld_mremap( void *old_addr, size_t old_len, size_t new_size, int flags, void *new_addr );
+SYSCALL_FUNC( wld_mremap, 25 /* SYS_mremap */ );
+
int wld_prctl( int code, long arg );
SYSCALL_FUNC( wld_prctl, 157 /* SYS_prctl */ );
+pid_t wld_getpid(void);
+SYSCALL_NOERR( wld_getpid, 39 /* SYS_getpid */ );
+
uid_t wld_getuid(void);
SYSCALL_NOERR( wld_getuid, 102 /* SYS_getuid */ );
@@ -578,9 +792,26 @@ SYSCALL_FUNC( wld_mprotect, 226 /* SYS_mprotect */ );
int wld_munmap( void *addr, size_t len );
SYSCALL_FUNC( wld_munmap, 215 /* SYS_munmap */ );
+void *wld_mremap( void *old_addr, size_t old_len, size_t new_size, int flags, void *new_addr );
+SYSCALL_FUNC( wld_mremap, 216 /* SYS_mremap */ );
+
int wld_prctl( int code, long arg );
SYSCALL_FUNC( wld_prctl, 167 /* SYS_prctl */ );
+int wld_rt_sigaction( int signum, const struct wld_sigaction *act, struct wld_sigaction *old_act, size_t sigsetsize );
+SYSCALL_FUNC( wld_rt_sigaction, 134 /* SYS_rt_sigaction */ );
+
+static inline int wld_sigaction( int signum, const struct wld_sigaction *act, struct wld_sigaction *old_act )
+{
+ return wld_rt_sigaction( signum, act, old_act, sizeof(act->wld_sa_mask) );
+}
+
+int wld_kill( pid_t pid, int sig );
+SYSCALL_FUNC( wld_kill, 129 /* SYS_kill */ );
+
+pid_t wld_getpid(void);
+SYSCALL_NOERR( wld_getpid, 172 /* SYS_getpid */ );
+
uid_t wld_getuid(void);
SYSCALL_NOERR( wld_getuid, 174 /* SYS_getuid */ );
@@ -680,9 +911,26 @@ SYSCALL_FUNC( wld_mprotect, 125 /* SYS_mprotect */ );
int wld_munmap( void *addr, size_t len );
SYSCALL_FUNC( wld_munmap, 91 /* SYS_munmap */ );
+void *wld_mremap( void *old_addr, size_t old_len, size_t new_size, int flags, void *new_addr );
+SYSCALL_FUNC( wld_mremap, 163 /* SYS_mremap */ );
+
int wld_prctl( int code, long arg );
SYSCALL_FUNC( wld_prctl, 172 /* SYS_prctl */ );
+int wld_rt_sigaction( int signum, const struct wld_sigaction *act, struct wld_sigaction *old_act, size_t sigsetsize );
+SYSCALL_FUNC( wld_rt_sigaction, 174 /* SYS_rt_sigaction */ );
+
+static inline int wld_sigaction( int signum, const struct wld_sigaction *act, struct wld_sigaction *old_act )
+{
+ return wld_rt_sigaction( signum, act, old_act, sizeof(act->wld_sa_mask) );
+}
+
+int wld_kill( pid_t pid, int sig );
+SYSCALL_FUNC( wld_kill, 37 /* SYS_kill */ );
+
+pid_t wld_getpid(void);
+SYSCALL_NOERR( wld_getpid, 20 /* SYS_getpid */ );
+
uid_t wld_getuid(void);
SYSCALL_NOERR( wld_getuid, 24 /* SYS_getuid */ );
@@ -1657,6 +1905,7 @@ static char *linebuffer_getline( struct linebuffer *lbuf )
static int parse_maps_line( struct vma_area *entry, const char *line )
{
struct vma_area item = { 0 };
+ unsigned long dev_maj, dev_min;
char *ptr = (char *)line;
int overflow;
@@ -1687,11 +1936,11 @@ static int parse_maps_line( struct vma_area *entry, const char *line )
if (*ptr != ' ') fatal_error( "parse error in /proc/self/maps\n" );
ptr++;
- parse_ul( ptr, &ptr, 16, NULL );
+ dev_maj = parse_ul( ptr, &ptr, 16, NULL );
if (*ptr != ':') fatal_error( "parse error in /proc/self/maps\n" );
ptr++;
- parse_ul( ptr, &ptr, 16, NULL );
+ dev_min = parse_ul( ptr, &ptr, 16, NULL );
if (*ptr != ' ') fatal_error( "parse error in /proc/self/maps\n" );
ptr++;
@@ -1699,6 +1948,17 @@ static int parse_maps_line( struct vma_area *entry, const char *line )
if (*ptr != ' ') fatal_error( "parse error in /proc/self/maps\n" );
ptr++;
+ while (*ptr == ' ')
+ ptr++;
+
+ if (dev_maj == 0 && dev_min == 0)
+ {
+ if (wld_strcmp(ptr, "[vdso]") == 0)
+ item.type_flags |= VMA_VDSO;
+ else if (wld_strcmp(ptr, "[vvar]") == 0)
+ item.type_flags |= VMA_VVAR;
+ }
+
*entry = item;
return 0;
}
@@ -1798,6 +2058,76 @@ static void insert_vma_entry( struct vma_area_list *list, const struct vma_area
return;
}
+/*
+ * find_vma_envelope_range
+ *
+ * Compute the smallest range that contains all VMAs with any of the given
+ * type flags.
+ */
+static int find_vma_envelope_range( const struct vma_area_list *list, int type_mask, unsigned long *startp, unsigned long *sizep )
+{
+ const struct vma_area *item;
+ unsigned long start = ULONG_MAX;
+ unsigned long end = 0;
+
+ FOREACH_VMA(list, item)
+ {
+ if (item->type_flags & type_mask)
+ {
+ if (start > item->start) start = item->start;
+ if (end < item->end) end = item->end;
+ }
+ }
+
+ if (start >= end) return -1;
+
+ *startp = start;
+ *sizep = end - start;
+ return 0;
+}
+
+/*
+ * remap_multiple_vmas
+ *
+ * Relocate all VMAs with the given type flags.
+ * This function can also be used to reverse the effects of previous
+ * remap_multiple_vmas().
+ */
+static int remap_multiple_vmas( struct vma_area_list *list, unsigned long delta, int type_mask, unsigned char revert )
+{
+ struct vma_area *item;
+ void *old_addr, *desired_addr, *mapped_addr;
+ size_t size;
+
+ FOREACH_VMA(list, item)
+ {
+ if ((item->type_flags & type_mask) && item->moved == revert)
+ {
+ if (revert)
+ {
+ old_addr = (void *)(item->start + delta);
+ desired_addr = (void *)item->start;
+ }
+ else
+ {
+ old_addr = (void *)item->start;
+ desired_addr = (void *)(item->start + delta);
+ }
+ size = item->end - item->start;
+ mapped_addr = wld_mremap( old_addr, size, size, MREMAP_FIXED | MREMAP_MAYMOVE, desired_addr );
+ if (mapped_addr == (void *)-1) return -1;
+ if (mapped_addr != desired_addr)
+ {
+ if (mapped_addr == old_addr) return -1; /* kernel deoesn't support MREMAP_FIXED */
+ fatal_error( "mremap() returned different address\n" );
+ }
+ item->moved = !revert;
+ }
+ }
+
+ return 0;
+}
+
/*
* scan_vma
*
@@ -1883,6 +2213,275 @@ static void alloc_scan_vma( struct vma_area_list *listp )
}
}
+/*
+ * stackargs_get_remap_policy
+ *
+ * Parse the remap policy value from the given environment variable.
+ */
+static enum remap_policy stackargs_get_remap_policy( const struct stackarg_info *info, const char *name,
+ enum remap_policy default_policy )
+{
+ char *valstr = stackargs_getenv( info, name ), *endptr;
+ unsigned long valnum;
+
+ if (valstr)
+ {
+ if (wld_strcmp(valstr, "auto") == 0 || wld_strcmp(valstr, "on-conflict") == 0)
+ return REMAP_POLICY_ON_CONFLICT;
+ if (wld_strcmp(valstr, "always") == 0 || wld_strcmp(valstr, "force") == 0)
+ return REMAP_POLICY_FORCE;
+ if (wld_strcmp(valstr, "never") == 0 || wld_strcmp(valstr, "skip") == 0)
+ return REMAP_POLICY_SKIP;
+ valnum = parse_ul( valstr, &endptr, 10, NULL );
+ if (!*endptr && valnum < LAST_REMAP_POLICY) return valnum;
+ }
+
+ return default_policy;
+}
+
+/*
+ * check_remap_policy
+ *
+ * Check remap policy against the given range and determine the action to take.
+ *
+ * -1: fail
+ * 0: do nothing
+ * 1: proceed with remapping
+ */
+static int check_remap_policy( struct preloader_state *state,
+ const char *policy_envname, enum remap_policy default_policy,
+ unsigned long start, unsigned long size )
+{
+ switch (stackargs_get_remap_policy( &state->s, policy_envname, default_policy ))
+ {
+ case REMAP_POLICY_SKIP:
+ return -1;
+ case REMAP_POLICY_ON_CONFLICT:
+ if (find_preload_reserved_area( (void *)start, size ) < 0)
+ return 0;
+ /* fallthrough */
+ case REMAP_POLICY_FORCE:
+ default:
+ return 1;
+ }
+}
+
+#ifndef __x86_64__
+/*
+ * remap_test_in_old_address_range
+ *
+ * Determine whether the address falls in the old mapping address range
+ * (i.e. before mremap).
+ */
+static int remap_test_in_old_address_range( unsigned long address )
+{
+ return address - remap_test.old_mapping_start < remap_test.old_mapping_size;
+}
+
+/*
+ * remap_test_signal_handler
+ *
+ * A signal handler that detects whether the kernel has acknowledged the new
+ * addresss for the remapped vDSO.
+ */
+static void remap_test_signal_handler( int signum, siginfo_t *sinfo, void *context )
+{
+ (void)signum;
+ (void)sinfo;
+ (void)context;
+
+ if (remap_test_in_old_address_range((unsigned long)__builtin_return_address(0))) goto fail;
+
+#ifdef __i386__
+ /* test for SYSENTER/SYSEXIT return address (int80_landing_pad) */
+ if (remap_test_in_old_address_range(((ucontext_t *)context)->uc_mcontext.gregs[REG_EIP])) goto fail;
+#endif
+
+ remap_test.is_successful = 1;
+ return;
+
+fail:
+ /* Kernel too old to support remapping. Restore vDSO/sigpage to return safely. */
+ if (remap_test.delta) {
+ if (remap_multiple_vmas( remap_test.vma_list, remap_test.delta, -1, 1 ) < 0)
+ fatal_error( "Cannot restore remapped VMAs\n" );
+ remap_test.delta = 0;
+ }
+
+ /* The signal handler might be called several times due to externally
+ * originated spurious signals, so overwrite with the latest status just to
+ * be safe.
+ */
+ remap_test.is_failed = 1;
+}
+#endif
+
+/*
+ * test_remap_successful
+ *
+ * Test if the kernel has acknowledged the remapped vDSO.
+ *
+ * Remapping vDSO requires explicit kernel support for most architectures, but
+ * the support is missing in old Linux kernels (pre-4.8). Among other things,
+ * vDSO contains the default signal restorer (sigreturn trampoline) and the
+ * fast syscall gate (SYSENTER) on Intel IA-32. The kernel keeps track of
+ * their addresses per process, and they need to be updated accordingly if the
+ * vDSO address changes. Without proper support, mremap() on vDSO does not
+ * indicate failure, but the kernel still uses old addresses for the vDSO
+ * components, resulting in crashes or other unpredictable behaviour if any of
+ * those addresses are used.
+ *
+ * We attempt to detect this condition by installing a signal handler and
+ * sending a signal to ourselves. The signal handler will test if the restorer
+ * address (plus the syscall gate on i386) falls in the old address range; if
+ * this is the case, we remap the vDSO to its old address and report failure
+ * (i.e. no support from kernel). If the addresses do not overlap with the old
+ * address range, the kernel is new enough to support vDSO remapping and we can
+ * proceed as normal.
+ */
+static int test_remap_successful( struct vma_area_list *vma_list, struct preloader_state *state,
+ unsigned long old_mapping_start, unsigned long old_mapping_size,
+ unsigned long delta )
+{
+#ifdef __x86_64__
+ (void)vma_list;
+ (void)state;
+ (void)old_mapping_start;
+ (void)old_mapping_size;
+ (void)delta;
+
+ /* x86-64 doesn't use SYSENTER for syscalls, and requires sa_restorer for
+ * signal handlers. We can safely relocate vDSO without kernel support
+ * (vdso_mremap).
+ */
+ return 0;
+#else
+ struct wld_sigaction sigact;
+ pid_t pid;
+ int result = -1;
+ unsigned long syscall_addr = 0;
+
+ pid = wld_getpid();
+ if (pid < 0) fatal_error( "failed to get PID\n" );
+
+#ifdef __i386__
+ syscall_addr = get_auxiliary( state->s.auxv, AT_SYSINFO, 0 );
+ if (syscall_addr - old_mapping_start < old_mapping_size) syscall_addr += delta;
+#endif
+
+ remap_test.old_mapping_start = old_mapping_start;
+ remap_test.old_mapping_size = old_mapping_size;
+ remap_test.vma_list = vma_list;
+ remap_test.delta = delta;
+ remap_test.is_successful = 0;
+ remap_test.is_failed = 0;
+
+ wld_memset( &sigact, 0, sizeof(sigact) );
+ sigact.wld_sa_sigaction = remap_test_signal_handler;
+ sigact.wld_sa_flags = WLD_SA_SIGINFO;
+ /* We deliberately skip sa_restorer, since we're trying to get the address
+ * of the kernel's built-in restorer function. */
+
+ if (wld_sigaction( REMAP_TEST_SIG, &sigact, &sigact ) < 0) fatal_error( "cannot register test signal handler\n" );
+
+ /* Unsafe region below - may race with signal handler */
+#ifdef __i386__
+ if (syscall_addr) {
+ /* Also test __kernel_vsyscall return as well */
+ __asm__ __volatile__( "call *%1"
+ : "=a" (result) : "r" (syscall_addr), "0" (37 /* SYS_kill */), "b" (pid), "c" (REMAP_TEST_SIG) );
+ result = SYSCALL_RET(result);
+ }
+#else
+ syscall_addr = 0;
+#endif
+ if (!syscall_addr) result = wld_kill( pid, REMAP_TEST_SIG );
+ /* Unsafe region above - may race with signal handler */
+
+ if (wld_sigaction( REMAP_TEST_SIG, &sigact, &sigact ) < 0) fatal_error( "cannot unregister test signal handler\n" );
+ if (result == -1) fatal_error( "cannot raise test signal\n" );
+
+ /* Now that the signal handler invocation is no longer possible, we can
+ * safely access the result.
+ *
+ * If neither is_successful nor is_failed is set, it signifies that the
+ * signal handler was not called or did not return properly. In this case,
+ * failure is assumed.
+ *
+ * If both is_successful and is_failed are set, it signifies that the
+ * signal handler was called successively multiple times. This may be due
+ * to externally originated spurious signals. In this case, is_failed
+ * takes precedence.
+ */
+ if (remap_test.is_failed || !remap_test.is_successful) {
+ if (remap_test.delta && remap_multiple_vmas( remap_test.vma_list, remap_test.delta, -1, 1 ) < 0)
+ fatal_error( "Cannot restore remapped VMAs\n" );
+ return -1;
+ }
+
+ return 0;
+#endif
+}
+
+/*
+ * remap_vdso
+ *
+ * Perform vDSO remapping if it conflicts with one of the reserved address ranges.
+ */
+static int remap_vdso( struct vma_area_list *vma_list, struct preloader_state *state )
+{
+ int result;
+ unsigned long vdso_start, vdso_size, delta;
+ void *new_vdso;
+ struct wld_auxv *auxv;
+
+ if (find_vma_envelope_range( vma_list, VMA_VDSO | VMA_VVAR, &vdso_start, &vdso_size ) < 0) return 0;
+
+ result = check_remap_policy( state, "WINEPRELOADREMAPVDSO",
+ REMAP_POLICY_DEFAULT_VDSO,
+ vdso_start, vdso_size );
+ if (result <= 0) return result;
+
+ new_vdso = wld_mmap( NULL, vdso_size, PROT_NONE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0 );
+ if (new_vdso == (void *)-1) return -1;
+
+ delta = (unsigned long)new_vdso - vdso_start;
+ /* It's easier to undo vvar remapping, so we remap it first. */
+ if (remap_multiple_vmas( vma_list, delta, VMA_VVAR, 0 ) < 0 ||
+ remap_multiple_vmas( vma_list, delta, VMA_VDSO, 0 ) < 0) goto remap_restore;
+
+ /* NOTE: AArch32 may have restorer in vDSO if we're running on an old ARM64 kernel. */
+ if (test_remap_successful( vma_list, state, vdso_start, vdso_size, delta ) < 0)
+ {
+ /* mapping restore done by test_remap_successful */
+ return -1;
+ }
+
+ for (auxv = state->s.auxv; auxv->a_type != AT_NULL; auxv++)
+ {
+ switch (auxv->a_type)
+ {
+ case AT_SYSINFO:
+ case AT_SYSINFO_EHDR:
+ if ((unsigned long)auxv->a_un.a_val - vdso_start < vdso_size)
+ auxv->a_un.a_val += delta;
+ break;
+ }
+ }
+
+ /* Refresh VMA list */
+ free_vma_list( vma_list );
+ alloc_scan_vma( vma_list );
+ return 1;
+
+remap_restore:
+ if (remap_multiple_vmas( vma_list, delta, -1, 1 ) < 0)
+ fatal_error( "Cannot restore remapped VMAs\n" );
+
+ return -1;
+}
+
/*
* map_reserve_preload_ranges
*
@@ -1970,6 +2569,8 @@ void* wld_start( void **stack )
alloc_scan_vma( &vma_list );
map_reserve_preload_ranges( &vma_list, &state.s );
+ if (remap_vdso( &vma_list, &state ) > 0) map_reserve_preload_ranges( &vma_list, &state.s );
+
/* add an executable page at the top of the address space to defeat
* broken no-exec protections that play with the code selector limit */
if (find_preload_reserved_area( (char *)0x80000000 - page_size, page_size ) >= 0)
@@ -1999,7 +2600,7 @@ void* wld_start( void **stack )
#undef SET_NEW_AV
i = 0;
- /* delete sysinfo values if addresses conflict */
+ /* delete sysinfo values if addresses conflict and remap failed */
if (is_in_preload_range( state.s.auxv, AT_SYSINFO ) || is_in_preload_range( state.s.auxv, AT_SYSINFO_EHDR ))
{
delete_av[i++].a_type = AT_SYSINFO;
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/6
April 30, 2022
[PATCH v3 6/9] loader: Fix return type of get_auxiliary().
by Jinoh Kang
From: Jinoh Kang <jinoh.kang.kr(a)gmail.com>
This is required for fetching pointer-valued vectors (e.g.
AT_SYSINFO_EHDR).
Signed-off-by: Jinoh Kang <jinoh.kang.kr(a)gmail.com>
---
loader/preloader.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/loader/preloader.c b/loader/preloader.c
index 785b6153eaf..763cf6bdbfc 100644
--- a/loader/preloader.c
+++ b/loader/preloader.c
@@ -1085,7 +1085,7 @@ static void set_auxiliary_values( struct wld_auxv *av, const struct wld_auxv *ne
*
* Get a field of the auxiliary structure
*/
-static int get_auxiliary( struct wld_auxv *av, int type, int def_val )
+static ElfW(Addr) get_auxiliary( struct wld_auxv *av, int type, ElfW(Addr) def_val )
{
for ( ; av->a_type != AT_NULL; av++)
if( av->a_type == type ) return av->a_un.a_val;
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/6
April 30, 2022
[PATCH v3 5/9] loader: Don't clobber existing memory mappings when reserving addresses.
by Jinoh Kang
From: Jinoh Kang <jinoh.kang.kr(a)gmail.com>
The main role of the preloader is to reserve specific virtual memory
address ranges used for special purposes on Windows, before Wine could
be loaded.
It achieves this goal via the following process:
(1) It eliminates future allocations of addresses in the reserved
ranges. Specifically, it issues a series of mmap() calls with
PROT_NONE protection to reserve those ranges, so that the OS won't
allocate any of the reserved addresses for other users (i.e. Unix
system libraries).
(2) It eliminates current references to addresses in the reserved
ranges. Specifically, if the vDSO had occupied one of the reserved
ranges, the preloader removes it from the auxiliary vector
(AT_SYSINFO*).
(3) If (2) is not possible because the address is in use (e.g. current
thread stack), it gives up reservation and removes the reserved
range from preload_info.
Today, each virtual memory area (VMA) is treated as follows when it
overlaps with Wine's reserved address ranges.
- Preloader code/data. Preloader should leave no trace of itself after
Wine has been loaded. Thus, no current references to preloader code
or data remain. (2) is a no-op in this case.
Meanwhile, if any part of the preloader overlaps with a reserved
range, that part is overwritten. This could lead to crash if it ever
touched any part of code or data that are still being used.
- vDSO/vvar. These are overwritten and ignored completely when they
overlap with any reserved range. In other words, both (1) and (2)
are performed.
- Stack. Since the stack is always in use, (2) is not possible.
Therefore, (3) is performed.
There are a few issues with this approach:
1. Existing VMAs that overlap with any reserved ranges are forcibly
overwritten during (1). There is actually no need to overwrite them,
since existing VMAs themselves automatically act as reservations by
nature (i.e. no future allocations would overlap any existing VMAs).
Furthermore, arbitrarily overwriting any memory in use would cause
the preloader to crash. The only treatment required for existing
VMAs is either (2) or (3), not (1).
2. (1) irrevocably overwrites some useful preexisting VMAs such as vDSO
if they overlap with any reserved ranges. Newer versions of Linux
kernel supports relocating vDSO, which can be used to move it outside
of reserved address ranges instead of discarding it. To do so,
however, we first have to allocate a _new_ address for such VMAs
before the overlapping address range could be reserved. Notice a
chicken-egg problem here:
- If we perform (1) before allocating a new address for vDSO, the
vDSO goes away even before we get a chance to relocate it.
- If we allocate a new address for vDSO before performing (1),
the new address allocated by the OS might end up overlapping with
one of the reserved ranges.
What we need here is a way to mmap()-fill all unallocated regions
inside the reserved ranges, *while* still keeping existing VMAs
intact. In this way, we can perform (1) foremost to avoid allocating
a reserved address for relocated vDSO. After the vDSO is relocated
to a safe address, we can perform (1) once more to finalise the
reservation.
3. Only the stack receives the special treatment of not being
overwritten by PROT_NONE allocation from (1). Theoretically other
VMAs that are in use such as the preloader code and data shall
receive the equal treatment anyway.
Fix this by reading /proc/self/maps for existing VMAs, and splitting
mmap() calls to avoid erasing existing memory mappings.
Note that MAP_FIXED_NOREPLACE is not suitable for this kind of job:
it fails entirely if there exist *any* overlapping memory mappings.
Signed-off-by: Jinoh Kang <jinoh.kang.kr(a)gmail.com>
---
loader/preloader.c | 434 ++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 411 insertions(+), 23 deletions(-)
diff --git a/loader/preloader.c b/loader/preloader.c
index 88865587975..785b6153eaf 100644
--- a/loader/preloader.c
+++ b/loader/preloader.c
@@ -184,6 +184,32 @@ struct preloader_state
struct stackarg_info s;
};
+/* Buffer for line-buffered I/O read. */
+struct linebuffer
+{
+ char *base; /* start of the buffer */
+ char *limit; /* last byte of the buffer (for NULL terminator) */
+ char *head; /* next byte to write to */
+ char *tail; /* next byte to read from */
+ int truncated; /* line truncated? (if true, skip until next line) */
+};
+
+struct vma_area
+{
+ unsigned long start;
+ unsigned long end;
+};
+
+struct vma_area_list
+{
+ struct vma_area *base;
+ struct vma_area *list_end;
+ struct vma_area *alloc_end;
+};
+
+#define FOREACH_VMA(list, item) \
+ for ((item) = (list)->base; (item) != (list)->list_end; (item)++)
+
/*
* The __bb_init_func is an empty function only called when file is
* compiled with gcc flags "-fprofile-arcs -ftest-coverage". This
@@ -723,6 +749,44 @@ static size_t wld_strlen( const char *str )
return ptr - str;
}
+static inline void *wld_memmove( void *dest, const void *src, size_t len )
+{
+ unsigned char *destp = dest;
+ const unsigned char *srcp = src;
+
+ /* Two area overlaps and src precedes dest?
+ *
+ * Note: comparing pointers to different objects leads to undefined
+ * behavior in C; therefore, we cast them to unsigned long for comparison
+ * (which is implementation-defined instead). This also allows us to rely
+ * on unsigned overflow on dest < src (forward copy case) in which case the
+ * LHS exceeds len and makes the condition false.
+ */
+ if ((unsigned long)dest - (unsigned long)src < len)
+ {
+ destp += len;
+ srcp += len;
+ while (len--) *--destp = *--srcp;
+ }
+ else
+ {
+ while (len--) *destp++ = *srcp++;
+ }
+
+ return dest;
+}
+
+static inline void *wld_memchr( const void *mem, int val, size_t len )
+{
+ const unsigned char *ptr = mem, *end = (const unsigned char *)ptr + len;
+
+ for (ptr = mem; ptr != end; ptr++)
+ if (*ptr == (unsigned char)val)
+ return (void *)ptr;
+
+ return NULL;
+}
+
/*
* parse_ul - parse an unsigned long number with given radix
*
@@ -1516,6 +1580,347 @@ static void set_process_name( int argc, char *argv[] )
for (i = 1; i < argc; i++) argv[i] -= off;
}
+/*
+ * linebuffer_init
+ *
+ * Initialise a linebuffer with the given buffer.
+ */
+static void linebuffer_init( struct linebuffer *lbuf, char *base, size_t len )
+{
+ lbuf->base = base;
+ lbuf->limit = base + (len - 1); /* NULL terminator */
+ lbuf->head = base;
+ lbuf->tail = base;
+ lbuf->truncated = 0;
+}
+
+/*
+ * linebuffer_getline
+ *
+ * Retrieve a line from the linebuffer.
+ * If a line is longer than the allocated buffer, then the line is truncated;
+ * the truncated flag is set to indicate this condition.
+ */
+static char *linebuffer_getline( struct linebuffer *lbuf )
+{
+ char *lnp, *line;
+
+ while ((lnp = wld_memchr( lbuf->tail, '\n', lbuf->head - lbuf->tail )))
+ {
+ /* Consume the current line from the buffer. */
+ line = lbuf->tail;
+ lbuf->tail = lnp + 1;
+
+ if (!lbuf->truncated)
+ {
+ *lnp = '\0';
+ return line;
+ }
+
+ /* Remainder of a previously truncated line; ignore it. */
+ lbuf->truncated = 0;
+ }
+
+ if (lbuf->tail == lbuf->base && lbuf->head == lbuf->limit)
+ {
+ /* We have not encountered the end of the current line yet; however,
+ * the buffer is full and cannot be compacted to accept more
+ * characters. Truncate the line here, and consume it from the buffer.
+ */
+ line = lbuf->tail;
+ lbuf->tail = lbuf->head;
+
+ /* Ignore any further characters until the start of the next line. */
+ lbuf->truncated = 1;
+ *lbuf->head = '\0';
+ return line;
+ }
+
+ if (lbuf->tail != lbuf->base)
+ {
+ /* Compact the buffer. Make room for reading more data by zapping the
+ * leading gap in the buffer.
+ */
+ wld_memmove( lbuf->base, lbuf->tail, lbuf->head - lbuf->tail);
+ lbuf->head -= lbuf->tail - lbuf->base;
+ lbuf->tail = lbuf->base;
+ }
+
+ return NULL;
+}
+
+/*
+ * parse_maps_line
+ *
+ * Parse an entry from /proc/self/maps file into a vma_area structure.
+ */
+static int parse_maps_line( struct vma_area *entry, const char *line )
+{
+ struct vma_area item = { 0 };
+ char *ptr = (char *)line;
+ int overflow;
+
+ item.start = parse_ul( ptr, &ptr, 16, &overflow );
+ if (overflow) return -1;
+ if (*ptr != '-') fatal_error( "parse error in /proc/self/maps\n" );
+ ptr++;
+
+ item.end = parse_ul( ptr, &ptr, 16, &overflow );
+ if (overflow) item.end = -page_size;
+ if (*ptr != ' ') fatal_error( "parse error in /proc/self/maps\n" );
+ ptr++;
+
+ if (item.start >= item.end) return -1;
+
+ if (*ptr != 'r' && *ptr != '-') fatal_error( "parse error in /proc/self/maps\n" );
+ ptr++;
+ if (*ptr != 'w' && *ptr != '-') fatal_error( "parse error in /proc/self/maps\n" );
+ ptr++;
+ if (*ptr != 'x' && *ptr != '-') fatal_error( "parse error in /proc/self/maps\n" );
+ ptr++;
+ if (*ptr != 's' && *ptr != 'p') fatal_error( "parse error in /proc/self/maps\n" );
+ ptr++;
+ if (*ptr != ' ') fatal_error( "parse error in /proc/self/maps\n" );
+ ptr++;
+
+ parse_ul( ptr, &ptr, 16, NULL );
+ if (*ptr != ' ') fatal_error( "parse error in /proc/self/maps\n" );
+ ptr++;
+
+ parse_ul( ptr, &ptr, 16, NULL );
+ if (*ptr != ':') fatal_error( "parse error in /proc/self/maps\n" );
+ ptr++;
+
+ parse_ul( ptr, &ptr, 16, NULL );
+ if (*ptr != ' ') fatal_error( "parse error in /proc/self/maps\n" );
+ ptr++;
+
+ parse_ul( ptr, &ptr, 10, NULL );
+ if (*ptr != ' ') fatal_error( "parse error in /proc/self/maps\n" );
+ ptr++;
+
+ *entry = item;
+ return 0;
+}
+
+/*
+ * lookup_vma_entry
+ *
+ * Find the first VMA of which end address is greater than the given address.
+ */
+static struct vma_area *lookup_vma_entry( const struct vma_area_list *list, unsigned long address )
+{
+ const struct vma_area *left = list->base, *right = list->list_end, *mid;
+ while (left < right)
+ {
+ mid = left + (right - left) / 2;
+ if (mid->end <= address) left = mid + 1;
+ else right = mid;
+ }
+ return (struct vma_area *)left;
+}
+
+/*
+ * map_reserve_range
+ *
+ * Reserve the specified address range.
+ * If there are any existing VMAs in the range, they are replaced.
+ */
+static int map_reserve_range( void *addr, size_t size )
+{
+ if (addr == (void *)-1 ||
+ wld_mmap( addr, size, PROT_NONE,
+ MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0) != addr)
+ return -1;
+ return 0;
+}
+
+/*
+ * map_reserve_unmapped_range
+ *
+ * Reserve the specified address range excluding already mapped areas.
+ */
+static int map_reserve_unmapped_range( const struct vma_area_list *list, void *addr, size_t size )
+{
+ unsigned long range_start = (unsigned long)addr,
+ range_end = (unsigned long)addr + size;
+ const struct vma_area *start, *item;
+ unsigned long last_addr = range_start;
+
+ start = lookup_vma_entry( list, range_start );
+ for (item = start; item != list->list_end && item->start < range_end; item++)
+ {
+ if (item->start > last_addr &&
+ map_reserve_range( (void *)last_addr, item->start - last_addr ) < 0)
+ goto fail;
+ last_addr = item->end;
+ }
+
+ if (range_end > last_addr &&
+ map_reserve_range( (void *)last_addr, range_end - last_addr ) < 0)
+ goto fail;
+ return 0;
+
+fail:
+ while (item != start)
+ {
+ item--;
+ last_addr = item == start ? range_start : item[-1].end;
+ if (item->start > last_addr)
+ wld_munmap( (void *)last_addr, item->start - last_addr );
+ }
+ return -1;
+}
+
+/*
+ * insert_vma_entry
+ *
+ * Insert the given VMA into the list.
+ */
+static void insert_vma_entry( struct vma_area_list *list, const struct vma_area *item )
+{
+ struct vma_area *left = list->base, *right = list->list_end, *mid;
+
+ if (left < right)
+ {
+ mid = right - 1; /* optimisation: start search from end */
+ for (;;)
+ {
+ if (mid->end < item->end) left = mid + 1;
+ else right = mid;
+ if (left >= right) break;
+ mid = left + (right - left) / 2;
+ }
+ }
+ wld_memmove(left + 1, left, list->list_end - left);
+ wld_memmove(left, item, sizeof(*item));
+ list->list_end++;
+ return;
+}
+
+/*
+ * scan_vma
+ *
+ * Parse /proc/self/maps into the given VMA area list.
+ */
+static void scan_vma( struct vma_area_list *list, size_t *real_count )
+{
+ int fd;
+ size_t n = 0;
+ ssize_t nread;
+ struct linebuffer lbuf;
+ char buffer[80 + PATH_MAX], *line;
+ struct vma_area item;
+
+ fd = wld_open( "/proc/self/maps", O_RDONLY );
+ if (fd == -1) fatal_error( "could not open /proc/self/maps\n" );
+
+ linebuffer_init(&lbuf, buffer, sizeof(buffer));
+ for (;;)
+ {
+ nread = wld_read( fd, lbuf.head, lbuf.limit - lbuf.head );
+ if (nread < 0) fatal_error( "could not read /proc/self/maps\n" );
+ if (nread == 0) break;
+ lbuf.head += nread;
+
+ while ((line = linebuffer_getline( &lbuf )))
+ {
+ if (parse_maps_line( &item, line ) >= 0)
+ {
+ if (list->list_end < list->alloc_end) insert_vma_entry( list, &item );
+ n++;
+ }
+ }
+ }
+
+ wld_close(fd);
+ *real_count = n;
+}
+
+/*
+ * free_vma_list
+ *
+ * Free the buffer in the given VMA list.
+ */
+static void free_vma_list( struct vma_area_list *list )
+{
+ if (list->base)
+ wld_munmap( list->base,
+ (unsigned char *)list->alloc_end - (unsigned char *)list->base );
+ list->base = NULL;
+ list->list_end = NULL;
+ list->alloc_end = NULL;
+}
+
+/*
+ * alloc_scan_vma
+ *
+ * Parse /proc/self/maps into a newly allocated VMA area list.
+ */
+static void alloc_scan_vma( struct vma_area_list *listp )
+{
+ size_t max_count = page_size / sizeof(struct vma_area);
+ struct vma_area_list vma_list;
+
+ for (;;)
+ {
+ vma_list.base = wld_mmap( NULL, sizeof(struct vma_area) * max_count,
+ PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS,
+ -1, 0 );
+ if (vma_list.base == (struct vma_area *)-1)
+ fatal_error( "could not allocate memory for VMA list\n");
+ vma_list.list_end = vma_list.base;
+ vma_list.alloc_end = vma_list.base + max_count;
+
+ scan_vma( &vma_list, &max_count );
+ if (vma_list.list_end - vma_list.base == max_count)
+ {
+ wld_memmove(listp, &vma_list, sizeof(*listp));
+ break;
+ }
+
+ free_vma_list( &vma_list );
+ }
+}
+
+/*
+ * map_reserve_preload_ranges
+ *
+ * Attempt to reserve memory ranges into preload_info.
+ * If any preload_info entry overlaps with stack, remove the entry instead of
+ * reserving.
+ */
+static void map_reserve_preload_ranges( const struct vma_area_list *vma_list,
+ const struct stackarg_info *stackinfo )
+{
+ size_t i;
+ unsigned long exclude_start = (unsigned long)stackinfo->stack - 1;
+ unsigned long exclude_end = (unsigned long)stackinfo->auxv + 1;
+
+ for (i = 0; preload_info[i].size; i++)
+ {
+ if (exclude_end > (unsigned long)preload_info[i].addr &&
+ exclude_start <= (unsigned long)preload_info[i].addr + preload_info[i].size - 1)
+ {
+ remove_preload_range( i );
+ i--;
+ }
+ else if (map_reserve_unmapped_range( vma_list, preload_info[i].addr, preload_info[i].size ) < 0)
+ {
+ /* don't warn for low 64k */
+ if (preload_info[i].addr >= (void *)0x10000
+#ifdef __aarch64__
+ && preload_info[i].addr < (void *)0x7fffffffff /* ARM64 address space might end here*/
+#endif
+ )
+ wld_printf( "preloader: Warning: failed to reserve range %p-%p\n",
+ preload_info[i].addr, (char *)preload_info[i].addr + preload_info[i].size );
+ remove_preload_range( i );
+ i--;
+ }
+ }
+}
+
/*
* wld_start
@@ -1532,6 +1937,7 @@ void* wld_start( void **stack )
struct wld_link_map main_binary_map, ld_so_map;
struct wine_preload_info **wine_main_preload_info;
struct preloader_state state = { 0 };
+ struct vma_area_list vma_list = { NULL };
parse_stackargs( &state.s, *stack );
@@ -1560,29 +1966,9 @@ void* wld_start( void **stack )
/* reserve memory that Wine needs */
reserve = stackargs_getenv( &state.s, "WINEPRELOADRESERVE" );
if (reserve) preload_reserve( reserve );
- for (i = 0; preload_info[i].size; i++)
- {
- if ((char *)state.s.auxv >= (char *)preload_info[i].addr &&
- (char *)state.s.stack <= (char *)preload_info[i].addr + preload_info[i].size)
- {
- remove_preload_range( i );
- i--;
- }
- else if (wld_mmap( preload_info[i].addr, preload_info[i].size, PROT_NONE,
- MAP_FIXED | MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, -1, 0 ) == (void *)-1)
- {
- /* don't warn for low 64k */
- if (preload_info[i].addr >= (void *)0x10000
-#ifdef __aarch64__
- && preload_info[i].addr < (void *)0x7fffffffff /* ARM64 address space might end here*/
-#endif
- )
- wld_printf( "preloader: Warning: failed to reserve range %p-%p\n",
- preload_info[i].addr, (char *)preload_info[i].addr + preload_info[i].size );
- remove_preload_range( i );
- i--;
- }
- }
+
+ alloc_scan_vma( &vma_list );
+ map_reserve_preload_ranges( &vma_list, &state.s );
/* add an executable page at the top of the address space to defeat
* broken no-exec protections that play with the code selector limit */
@@ -1645,6 +2031,8 @@ void* wld_start( void **stack )
}
#endif
+ free_vma_list( &vma_list );
+
return (void *)ld_so_map.l_entry;
}
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/6
April 30, 2022
[PATCH v3 4/9] loader: Explicitly munmap() the preloader's ELF EHDR.
by Jinoh Kang
From: Jinoh Kang <jinoh.kang.kr(a)gmail.com>
Today, the preloader reserves some predefined address ranges without
checking if there are any overlapping virtual memory mappings.
One side effect of this behaviour is that the preloader's ELF EHDR gets
unmapped. Note the following overlapping address ranges:
- 0x00110000 - 0x68000000: low memory area (preload_info)
- 0x08040000 - 0x08041000: preloader ELF EHDR (x86)
- 0x00400000 - 0x00401000: preloader ELF EHDR (AMD64)
In practice, unmapping the preloader ELF EHDR is harmless; this is
because the dynamic linker does not recognise the preloader binary.
Make the unmapping behaviour explicit by calling munmap() on the
preloader's ELF EHDR.
Signed-off-by: Jinoh Kang <jinoh.kang.kr(a)gmail.com>
---
loader/preloader.c | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/loader/preloader.c b/loader/preloader.c
index cce9353bdb4..88865587975 100644
--- a/loader/preloader.c
+++ b/loader/preloader.c
@@ -227,6 +227,7 @@ struct
* then jumps to the address wld_start returns.
*/
void _start(void);
+extern char __executable_start[];
extern char _end[];
__ASM_GLOBAL_FUNC(_start,
__ASM_CFI("\t.cfi_undefined %eip\n")
@@ -346,6 +347,15 @@ __ASM_GLOBAL_FUNC(wld_mmap,
__ASM_CFI(".cfi_adjust_cfa_offset -4\n\t")
"\tret\n" )
+static inline int wld_munmap( void *addr, size_t len )
+{
+ int ret;
+ __asm__ __volatile__( "pushl %%ebx; movl %2,%%ebx; int $0x80; popl %%ebx"
+ : "=a" (ret) : "0" (91 /* SYS_munmap */), "r" (addr), "c" (len)
+ : "memory" );
+ return SYSCALL_RET(ret);
+}
+
static inline int wld_prctl( int code, long arg )
{
int ret;
@@ -365,6 +375,7 @@ void *thread_data[256];
* then jumps to the address wld_start returns.
*/
void _start(void);
+extern char __executable_start[];
extern char _end[];
__ASM_GLOBAL_FUNC(_start,
__ASM_CFI(".cfi_undefined %rip\n\t")
@@ -428,6 +439,9 @@ SYSCALL_FUNC( wld_mmap, 9 /* SYS_mmap */ );
int wld_mprotect( const void *addr, size_t len, int prot );
SYSCALL_FUNC( wld_mprotect, 10 /* SYS_mprotect */ );
+int wld_munmap( void *addr, size_t len );
+SYSCALL_FUNC( wld_munmap, 11 /* SYS_munmap */ );
+
int wld_prctl( int code, long arg );
SYSCALL_FUNC( wld_prctl, 157 /* SYS_prctl */ );
@@ -454,6 +468,7 @@ void *thread_data[256];
* then jumps to the address wld_start returns.
*/
void _start(void);
+extern char __executable_start[];
extern char _end[];
__ASM_GLOBAL_FUNC(_start,
"mov x0, SP\n\t"
@@ -534,6 +549,9 @@ SYSCALL_FUNC( wld_mmap, 222 /* SYS_mmap */ );
int wld_mprotect( const void *addr, size_t len, int prot );
SYSCALL_FUNC( wld_mprotect, 226 /* SYS_mprotect */ );
+int wld_munmap( void *addr, size_t len );
+SYSCALL_FUNC( wld_munmap, 215 /* SYS_munmap */ );
+
int wld_prctl( int code, long arg );
SYSCALL_FUNC( wld_prctl, 167 /* SYS_prctl */ );
@@ -560,6 +578,7 @@ void *thread_data[256];
* then jumps to the address wld_start returns.
*/
void _start(void);
+extern char __executable_start[];
extern char _end[];
__ASM_GLOBAL_FUNC(_start,
"mov r0, sp\n\t"
@@ -632,6 +651,9 @@ void *wld_mmap( void *start, size_t len, int prot, int flags, int fd, off_t offs
int wld_mprotect( const void *addr, size_t len, int prot );
SYSCALL_FUNC( wld_mprotect, 125 /* SYS_mprotect */ );
+int wld_munmap( void *addr, size_t len );
+SYSCALL_FUNC( wld_munmap, 91 /* SYS_munmap */ );
+
int wld_prctl( int code, long arg );
SYSCALL_FUNC( wld_prctl, 172 /* SYS_prctl */ );
@@ -1521,6 +1543,14 @@ void* wld_start( void **stack )
preloader_start = (char *)_start - ((unsigned long)_start & page_mask);
preloader_end = (char *)((unsigned long)(_end + page_mask) & ~page_mask);
+ if ((unsigned long)preloader_start >= (unsigned long)__executable_start + page_size)
+ {
+ /* Unmap preloader's ELF EHDR */
+ wld_munmap( __executable_start,
+ ((unsigned long)preloader_start -
+ (unsigned long)__executable_start) & ~page_mask );
+ }
+
#ifdef DUMP_AUX_INFO
wld_printf( "stack = %p\n", state.s.stack );
for( i = 0; i < state.s.argc; i++ ) wld_printf("argv[%lx] = %s\n", i, state.s.argv[i]);
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/6
April 30, 2022
[PATCH v3 3/9] loader: Generalise is_addr_reserved to find overlapping address ranges.
by Jinoh Kang
From: Jinoh Kang <jinoh.kang.kr(a)gmail.com>
Rename is_addr_reserved to find_preload_reserved_area, with the
following changes:
- Accept second argument "size" which specifies the size of the address
range to test.
- Return the index of the matching entry, or -1 if none found.
Signed-off-by: Jinoh Kang <jinoh.kang.kr(a)gmail.com>
---
loader/preloader.c | 27 +++++++++++++++++++--------
1 file changed, 19 insertions(+), 8 deletions(-)
diff --git a/loader/preloader.c b/loader/preloader.c
index c964e2aeeb2..cce9353bdb4 100644
--- a/loader/preloader.c
+++ b/loader/preloader.c
@@ -1423,18 +1423,29 @@ error:
fatal_error( "invalid WINEPRELOADRESERVE value '%s'\n", str );
}
-/* check if address is in one of the reserved ranges */
-static int is_addr_reserved( const void *addr )
+/*
+ * find_preload_reserved_area
+ *
+ * Check if the given address range overlaps with one of the reserved ranges.
+ */
+static int find_preload_reserved_area( const void *addr, size_t size )
{
+ /* Make the interval inclusive to avoid integer overflow. */
+ unsigned long start = (unsigned long)addr;
+ unsigned long end = (unsigned long)addr + size - 1;
int i;
+ /* Handle size == 0 specifically since "end" may overflow otherwise. */
+ if (!size)
+ return -1;
+
for (i = 0; preload_info[i].size; i++)
{
- if ((const char *)addr >= (const char *)preload_info[i].addr &&
- (const char *)addr < (const char *)preload_info[i].addr + preload_info[i].size)
- return 1;
+ if (end >= (unsigned long)preload_info[i].addr &&
+ start < (unsigned long)preload_info[i].addr + preload_info[i].size)
+ return i;
}
- return 0;
+ return -1;
}
/* remove a range from the preload list */
@@ -1457,7 +1468,7 @@ static int is_in_preload_range( const struct wld_auxv *av, int type )
{
while (av->a_type != AT_NULL)
{
- if (av->a_type == type) return is_addr_reserved( (const void *)av->a_un.a_val );
+ if (av->a_type == type) return find_preload_reserved_area( (const void *)av->a_un.a_val, 1 ) >= 0;
av++;
}
return 0;
@@ -1545,7 +1556,7 @@ void* wld_start( void **stack )
/* add an executable page at the top of the address space to defeat
* broken no-exec protections that play with the code selector limit */
- if (is_addr_reserved( (char *)0x80000000 - page_size ))
+ if (find_preload_reserved_area( (char *)0x80000000 - page_size, page_size ) >= 0)
wld_mprotect( (char *)0x80000000 - page_size, page_size, PROT_EXEC | PROT_READ );
/* load the main binary */
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/6
April 30, 2022
[PATCH v3 2/9] loader: Refactor number parsing to own function.
by Jinoh Kang
From: Jinoh Kang <jinoh.kang.kr(a)gmail.com>
Improve readability of WINEPRELOADRESERVE parsing code, and also make
the parser available for other purposes in future patches.
Signed-off-by: Jinoh Kang <jinoh.kang.kr(a)gmail.com>
---
loader/preloader.c | 62 ++++++++++++++++++++++++++++++++++------------
1 file changed, 46 insertions(+), 16 deletions(-)
diff --git a/loader/preloader.c b/loader/preloader.c
index c3dae88ecd0..c964e2aeeb2 100644
--- a/loader/preloader.c
+++ b/loader/preloader.c
@@ -68,6 +68,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
@@ -700,6 +701,42 @@ static size_t wld_strlen( const char *str )
return ptr - str;
}
+/*
+ * parse_ul - parse an unsigned long number with given radix
+ *
+ * Differences from strtoul():
+ * - Does not support radix prefixes ("0x", etc)
+ * - Does not saturate to ULONG_MAX on overflow, wrap around instead
+ * - Indicates overflow via output argument, not errno
+ */
+static inline unsigned long parse_ul( const char *nptr, char **endptr, unsigned int radix, int *overflow )
+{
+ const char *p = nptr;
+ unsigned long value, max_radix_mul;
+ int ovfl = 0;
+
+ value = 0;
+ max_radix_mul = ULONG_MAX / radix;
+ for (;;)
+ {
+ unsigned int digit;
+ if (*p >= '0' && *p <= '9') digit = *p - '0';
+ else if (*p >= 'a' && *p <= 'z') digit = *p - 'a' + 10;
+ else if (*p >= 'A' && *p <= 'Z') digit = *p - 'A' + 10;
+ else break;
+ if (digit >= radix) break;
+ if (value > max_radix_mul) ovfl = 1;
+ value *= radix;
+ if (value > value + digit) ovfl = 1;
+ value += digit;
+ p++;
+ }
+
+ if (endptr) *endptr = (char *)p;
+ if (overflow) *overflow = ovfl;
+ return value;
+}
+
/*
* wld_printf - just the basics
*
@@ -1339,27 +1376,20 @@ found:
*/
static void preload_reserve( const char *str )
{
- const char *p;
+ char *p = (char *)str;
unsigned long result = 0;
void *start = NULL, *end = NULL;
- int i, first = 1;
+ int i;
- for (p = str; *p; p++)
+ result = parse_ul( p, &p, 16, NULL );
+ if (*p == '-')
{
- if (*p >= '0' && *p <= '9') result = result * 16 + *p - '0';
- else if (*p >= 'a' && *p <= 'f') result = result * 16 + *p - 'a' + 10;
- else if (*p >= 'A' && *p <= 'F') result = result * 16 + *p - 'A' + 10;
- else if (*p == '-')
- {
- if (!first) goto error;
- start = (void *)(result & ~page_mask);
- result = 0;
- first = 0;
- }
- else goto error;
+ start = (void *)(result & ~page_mask);
+ result = parse_ul( p + 1, &p, 16, NULL );
+ if (*p) goto error;
+ end = (void *)((result + page_mask) & ~page_mask);
}
- if (!first) end = (void *)((result + page_mask) & ~page_mask);
- else if (result) goto error; /* single value '0' is allowed */
+ else if (*p || result) goto error; /* single value '0' is allowed */
/* sanity checks */
if (end <= start) start = end = NULL;
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/6
April 30, 2022
[PATCH v3 1/9] loader: Refactor argv/envp/auxv management.
by Jinoh Kang
From: Jinoh Kang <jinoh.kang.kr(a)gmail.com>
Collect scattered variables holding stack addresses (e.g. pargc, argv,
envp, auxv) in one place.
This facilitates modifying stack values (e.g. removing argv[0],
switching stacks due to address conflict with reserved regions) without
leaving pointer variables stale.
Signed-off-by: Jinoh Kang <jinoh.kang.kr(a)gmail.com>
---
loader/preloader.c | 146 +++++++++++++++++++++++++++++++++++----------
1 file changed, 115 insertions(+), 31 deletions(-)
diff --git a/loader/preloader.c b/loader/preloader.c
index 585be50624f..c3dae88ecd0 100644
--- a/loader/preloader.c
+++ b/loader/preloader.c
@@ -164,6 +164,25 @@ struct wld_auxv
} a_un;
};
+/* Aggregates information about initial program stack and variables
+ * (e.g. argv and envp) that reside in it.
+ */
+struct stackarg_info
+{
+ void *stack;
+ int argc;
+ char **argv;
+ char **envp;
+ struct wld_auxv *auxv;
+ struct wld_auxv *auxv_end;
+};
+
+/* Currently only contains the main stackarg_info. */
+struct preloader_state
+{
+ struct stackarg_info s;
+};
+
/*
* The __bb_init_func is an empty function only called when file is
* compiled with gcc flags "-fprofile-arcs -ftest-coverage". This
@@ -674,6 +693,13 @@ static inline void *wld_memset( void *dest, int val, size_t len )
return dest;
}
+static size_t wld_strlen( const char *str )
+{
+ const char *ptr = str;
+ while (*ptr) ptr++;
+ return ptr - str;
+}
+
/*
* wld_printf - just the basics
*
@@ -794,6 +820,74 @@ static void dump_auxiliary( struct wld_auxv *av )
}
#endif
+/*
+ * parse_stackargs
+ *
+ * parse out the initial stack for argv, envp, and etc., and store the
+ * information into the given stackarg_info structure.
+ */
+static void parse_stackargs( struct stackarg_info *outinfo, void *stack )
+{
+ int argc;
+ char **argv, **envp, **env_end;
+ struct wld_auxv *auxv, *auxv_end;
+
+ argc = *(int *)stack;
+ argv = (char **)stack + 1;
+ envp = argv + (unsigned int)argc + 1;
+
+ env_end = envp;
+ while (*env_end++)
+ ;
+ auxv = (struct wld_auxv *)env_end;
+
+ auxv_end = auxv;
+ while ((auxv_end++)->a_type != AT_NULL)
+ ;
+
+ outinfo->stack = stack;
+ outinfo->argc = argc;
+ outinfo->argv = argv;
+ outinfo->envp = envp;
+ outinfo->auxv = auxv;
+ outinfo->auxv_end = auxv_end;
+}
+
+/*
+ * stackargs_getenv
+ *
+ * Retrieve the value of an environment variable from stackarg_info.
+ */
+static char *stackargs_getenv( const struct stackarg_info *info, const char *name )
+{
+ size_t namelen = wld_strlen( name );
+ char **envp;
+
+ for (envp = info->envp; *envp; envp++)
+ {
+ if (wld_strncmp( *envp, name, namelen ) == 0 &&
+ (*envp)[namelen] == '=') return *envp + namelen + 1;
+ }
+
+ return NULL;
+}
+
+/*
+ * stackargs_shift_args
+ *
+ * Remove the specific number of arguments from the start of argv.
+ */
+static void stackargs_shift_args( struct stackarg_info *info, int num_args )
+{
+ info->stack = (char **)info->stack + num_args;
+ info->argc -= num_args;
+ info->argv = (char **)info->stack + 1;
+
+ wld_memset( info->stack, 0, sizeof(char *) );
+ /* Don't coalesce zeroing and setting argc -- we *might* support big endian in the future */
+ *(int *)info->stack = info->argc;
+}
+
/*
* set_auxiliary_values
*
@@ -1369,47 +1463,36 @@ static void set_process_name( int argc, char *argv[] )
*/
void* wld_start( void **stack )
{
- long i, *pargc;
- char **argv, **p;
- char *interp, *reserve = NULL;
- struct wld_auxv new_av[8], delete_av[3], *av;
+ long i;
+ char *interp, *reserve;
+ struct wld_auxv new_av[8], delete_av[3];
struct wld_link_map main_binary_map, ld_so_map;
struct wine_preload_info **wine_main_preload_info;
+ struct preloader_state state = { 0 };
- pargc = *stack;
- argv = (char **)pargc + 1;
- if (*pargc < 2) fatal_error( "Usage: %s wine_binary [args]\n", argv[0] );
+ parse_stackargs( &state.s, *stack );
- /* skip over the parameters */
- p = argv + *pargc + 1;
+ if (state.s.argc < 2) fatal_error( "Usage: %s wine_binary [args]\n", state.s.argv[0] );
- /* skip over the environment */
- while (*p)
- {
- static const char res[] = "WINEPRELOADRESERVE=";
- if (!wld_strncmp( *p, res, sizeof(res)-1 )) reserve = *p + sizeof(res) - 1;
- p++;
- }
-
- av = (struct wld_auxv *)(p+1);
- page_size = get_auxiliary( av, AT_PAGESZ, 4096 );
+ page_size = get_auxiliary( state.s.auxv, AT_PAGESZ, 4096 );
page_mask = page_size - 1;
preloader_start = (char *)_start - ((unsigned long)_start & page_mask);
preloader_end = (char *)((unsigned long)(_end + page_mask) & ~page_mask);
#ifdef DUMP_AUX_INFO
- wld_printf( "stack = %p\n", *stack );
- for( i = 0; i < *pargc; i++ ) wld_printf("argv[%lx] = %s\n", i, argv[i]);
- dump_auxiliary( av );
+ wld_printf( "stack = %p\n", state.s.stack );
+ for( i = 0; i < state.s.argc; i++ ) wld_printf("argv[%lx] = %s\n", i, state.s.argv[i]);
+ dump_auxiliary( state.s.auxv );
#endif
/* reserve memory that Wine needs */
+ reserve = stackargs_getenv( &state.s, "WINEPRELOADRESERVE" );
if (reserve) preload_reserve( reserve );
for (i = 0; preload_info[i].size; i++)
{
- if ((char *)av >= (char *)preload_info[i].addr &&
- (char *)pargc <= (char *)preload_info[i].addr + preload_info[i].size)
+ if ((char *)state.s.auxv >= (char *)preload_info[i].addr &&
+ (char *)state.s.stack <= (char *)preload_info[i].addr + preload_info[i].size)
{
remove_preload_range( i );
i--;
@@ -1436,7 +1519,7 @@ void* wld_start( void **stack )
wld_mprotect( (char *)0x80000000 - page_size, page_size, PROT_EXEC | PROT_READ );
/* load the main binary */
- map_so_lib( argv[1], &main_binary_map );
+ map_so_lib( state.s.argv[1], &main_binary_map );
/* load the ELF interpreter */
interp = (char *)main_binary_map.l_addr + main_binary_map.l_interp;
@@ -1453,14 +1536,14 @@ void* wld_start( void **stack )
SET_NEW_AV( 2, AT_PHNUM, main_binary_map.l_phnum );
SET_NEW_AV( 3, AT_PAGESZ, page_size );
SET_NEW_AV( 4, AT_BASE, ld_so_map.l_addr );
- SET_NEW_AV( 5, AT_FLAGS, get_auxiliary( av, AT_FLAGS, 0 ) );
+ SET_NEW_AV( 5, AT_FLAGS, get_auxiliary( state.s.auxv, AT_FLAGS, 0 ) );
SET_NEW_AV( 6, AT_ENTRY, main_binary_map.l_entry );
SET_NEW_AV( 7, AT_NULL, 0 );
#undef SET_NEW_AV
i = 0;
/* delete sysinfo values if addresses conflict */
- if (is_in_preload_range( av, AT_SYSINFO ) || is_in_preload_range( av, AT_SYSINFO_EHDR ))
+ if (is_in_preload_range( state.s.auxv, AT_SYSINFO ) || is_in_preload_range( state.s.auxv, AT_SYSINFO_EHDR ))
{
delete_av[i++].a_type = AT_SYSINFO;
delete_av[i++].a_type = AT_SYSINFO_EHDR;
@@ -1468,11 +1551,12 @@ void* wld_start( void **stack )
delete_av[i].a_type = AT_NULL;
/* get rid of first argument */
- set_process_name( *pargc, argv );
- pargc[1] = pargc[0] - 1;
- *stack = pargc + 1;
+ set_process_name( state.s.argc, state.s.argv );
+ stackargs_shift_args( &state.s, 1 );
- set_auxiliary_values( av, new_av, delete_av, stack );
+ *stack = state.s.stack;
+ set_auxiliary_values( state.s.auxv, new_av, delete_av, stack );
+ /* state is invalid from this point onward */
#ifdef DUMP_AUX_INFO
wld_printf("new stack = %p\n", *stack);
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/6
April 30, 2022
[PATCH v3 0/9] MR6: Avoid performance degradation due to vDSO unmapping (BZ#52313)
by Jinoh Kang (@iamahuman)
Commit f558741fabc116534fa598aa890ffed683a7153b removes vDSO if it
conflicts with reserved ranges:
> Remove the AT_SYSINFO and AT_SYSINFO_EHDR values if the sysinfo page
> is in one of our reserved ranges.
However, missing vDSO leads to performance issues on some syscalls (e.g.
clock_gettime, gettimeofday) and may even lead to crash when run with
some ancient C libraries that does not supply a custom signal restorer.
vDSO pages can clash with reserved ranges especially in a 32-bit address
space with address space layout randomization (ASLR) turned on.
Recent versions of the Linux kernel introduced support for mremap()-ping
vDSO pages, partly in an effort to support checkpoint restore in
userspace (CRIU). Special programs that require specific memory layout
constraints (such as Wine preloader) can take advantage of this support
to modify the address space to meet its requirements.
The following test script has been used to test each changes (use with
`git rebase --exec=...`):
```sh
set -e
make -C ../wine64-build -j5
make -C ../wine32-build -j5
cd ../wine64-build
export WINEPRELOADREMAPSTACK
export WINEPRELOADREMAPVDSO
for WINEPRELOADREMAPSTACK in skip never always force auto on-demand ''
do
for WINEPRELOADREMAPVDSO in skip never always force auto on-demand ''
do
./loader/wine64 wineboot
./loader/wine wineboot
done
done
```
--
v3: loader: Switch stack if the old stack address is in reserved range.
loader: Relocate sigpage on conflict with reserved ranges in ARM.
loader: Relocate vDSO on conflict with reserved ranges.
loader: Fix return type of get_auxiliary().
loader: Don't clobber existing memory mappings when reserving addresses.
loader: Explicitly munmap() the preloader's ELF EHDR.
loader: Generalise is_addr_reserved to find overlapping address ranges.
loader: Refactor number parsing to own function.
loader: Refactor argv/envp/auxv management.
https://gitlab.winehq.org/wine/wine/-/merge_requests/6
April 30, 2022
Re: [PATCH] msadpm: Stop decoding instead of crashing for invalid adpcm
by Eric Pouech
Le 30/04/2022 à 05:28, Moore, Brandon A. a écrit :
Hi Brandon,
two points:
- the lines around your patch don't use tabs, please don't introduce them
- I don't see why you need to test src+1? (il you copied it from the
other routine, it's for stereo handling, while the one you patched is
for mono handling, hence a single adpcm coeff)
A+
April 30, 2022
April 30, 2022
[PATCH] msadpm: Stop decoding instead of crashing for invalid adpcm data.
by Moore, Brandon A.
Apply the same patch from 72528be84fdc for adpcm data sent to mono
destinations in addition to stereo destinations.
Signed-off-by: Brandon Moore <moore.3071(a)osu.edu>
---
dlls/msadp32.acm/msadp32.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/dlls/msadp32.acm/msadp32.c b/dlls/msadp32.acm/msadp32.c
index 2dc11b9239a..a68b13d58f6 100644
--- a/dlls/msadp32.acm/msadp32.c
+++ b/dlls/msadp32.acm/msadp32.c
@@ -319,7 +319,14 @@ static void cvtMMms16K(const ACMDRVSTREAMINSTANCE *adsi,
{
const unsigned char* in_src = src;
- assert(*src <= 6);
+ /* Catch a problem from Lord of the Rings War of the Ring where it
+ * passes invalid data. */
+ if (*src > 6 || *(src + 1) > 6)
+ {
+ *ndst -= nblock * nsamp_blk * adsi->pwfxDst->nBlockAlign;
+ WARN("Invalid ADPCM data, stopping conversion\n");
+ break;
+ }
coeff = MSADPCM_CoeffSet[*src++];
idelta = R16(src); src += 2;
--
2.35.1
April 30, 2022
[PATCH] wined3d: Do not use vkCmdClearColorImage() to clear compressed images.
by Zebediah Figura
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=52922
Signed-off-by: Zebediah Figura <zfigura(a)codeweavers.com>
---
dlls/wined3d/texture.c | 27 +++++++++++++++++++++++----
1 file changed, 23 insertions(+), 4 deletions(-)
diff --git a/dlls/wined3d/texture.c b/dlls/wined3d/texture.c
index 5fd38b49132..49650f2839e 100644
--- a/dlls/wined3d/texture.c
+++ b/dlls/wined3d/texture.c
@@ -5257,9 +5257,10 @@ static void wined3d_texture_vk_download_data(struct wined3d_context *context,
}
}
-static void wined3d_texture_vk_clear(struct wined3d_texture_vk *texture_vk,
+static bool wined3d_texture_vk_clear(struct wined3d_texture_vk *texture_vk,
unsigned int sub_resource_idx, struct wined3d_context *context)
{
+ struct wined3d_texture_sub_resource *sub_resource = &texture_vk->t.sub_resources[sub_resource_idx];
struct wined3d_context_vk *context_vk = wined3d_context_vk(context);
const struct wined3d_format *format = texture_vk->t.resource.format;
const struct wined3d_vk_info *vk_info = context_vk->vk_info;
@@ -5270,12 +5271,24 @@ static void wined3d_texture_vk_clear(struct wined3d_texture_vk *texture_vk,
VkImageAspectFlags aspect_mask;
VkImage vk_image;
+ if (texture_vk->t.resource.format_flags & WINED3DFMT_FLAG_COMPRESSED)
+ {
+ struct wined3d_bo_address addr;
+
+ if (!wined3d_texture_prepare_location(&texture_vk->t, sub_resource_idx, context, WINED3D_LOCATION_SYSMEM))
+ return false;
+ wined3d_texture_get_bo_address(&texture_vk->t, sub_resource_idx, &addr, WINED3D_LOCATION_SYSMEM);
+ memset(addr.addr, 0, sub_resource->size);
+ wined3d_texture_validate_location(&texture_vk->t, sub_resource_idx, WINED3D_LOCATION_SYSMEM);
+ return true;
+ }
+
vk_image = texture_vk->image.vk_image;
if (!(vk_command_buffer = wined3d_context_vk_get_command_buffer(context_vk)))
{
ERR("Failed to get command buffer.\n");
- return;
+ return false;
}
aspect_mask = vk_aspect_mask_from_format(format);
@@ -5305,6 +5318,9 @@ static void wined3d_texture_vk_clear(struct wined3d_texture_vk *texture_vk,
VK_ACCESS_TRANSFER_WRITE_BIT, vk_access_mask_from_bind_flags(texture_vk->t.resource.bind_flags),
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, texture_vk->layout, vk_image, &vk_range);
wined3d_context_vk_reference_texture(context_vk, texture_vk);
+
+ wined3d_texture_validate_location(&texture_vk->t, sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB);
+ return true;
}
static BOOL wined3d_texture_vk_load_texture(struct wined3d_texture_vk *texture_vk,
@@ -5319,8 +5335,11 @@ static BOOL wined3d_texture_vk_load_texture(struct wined3d_texture_vk *texture_v
if (sub_resource->locations & WINED3D_LOCATION_CLEARED)
{
- wined3d_texture_vk_clear(texture_vk, sub_resource_idx, context);
- return TRUE;
+ if (!wined3d_texture_vk_clear(texture_vk, sub_resource_idx, context))
+ return FALSE;
+
+ if (sub_resource->locations & WINED3D_LOCATION_TEXTURE_RGB)
+ return TRUE;
}
if (!(sub_resource->locations & wined3d_texture_sysmem_locations))
--
2.35.1
April 29, 2022
Re: [PATCH v7 3/3] shell32: Partially implement IShellItemImageFactory (icon only, no thumbnail).
by Jinoh Kang
On 4/29/22 23:44, Nikolay Sivov wrote:
> I think this needs a lot of cleanup.
>
> On 4/26/22 21:17, Jinoh Kang wrote:
>>
>>
>> +
>> typedef struct _ShellItem {
>> IShellItem2 IShellItem2_iface;
>> LONG ref;
>> @@ -565,17 +568,205 @@ static ULONG WINAPI ShellItem_IShellItemImageFactory_Release(IShellItemImageFact
>> return IShellItem2_Release(&This->IShellItem2_iface);
>> }
>> +static HRESULT ShellItem_get_icons(ShellItem *This, SIZE size, HICON *big_icon, HICON *small_icon)
>
> You don't really need two icons, for SIIGBF_BIGGERSIZEOK.
SIIGBF_BIGGERSIZEOK does not appear to signify that the size parameter can be entirely disregarded. Windows still takes the size argument as a hint.
Maybe it needs more testing, but the impression I got is that it would choose the image that will experience the *least* distortion should it be eventually resized to the given size. It would then actually perform the resize if SIIGBF_RESIZETOFIT is specified; otherwise, the application is supposed to do the resize as needed.
Although returning an image of arbitrary size isn't *contractually wrong* per se, it may choose a worse candidate for shrinking: for example, icons tend to have the same text size in both versions; thus, resizing a bigger icon would render the text unreadable. Also, it doesn't match Windows behavior anyway.
>> +{
>> + HRESULT hr;
>> + IBindCtx *pbc;
>> + IExtractIconW *ei;
>> + WCHAR icon_file[MAX_PATH];
>> + INT source_index;
>> + UINT gil_in_flags = 0, gil_out_flags;
>> + INT iconsize;
>> +
>> + iconsize = min(size.cx, size.cy);
>> + if (iconsize <= 0 || iconsize > 0x7fff)
>> + iconsize = 0x7fff;
>> +
>> + hr = CreateBindCtx(0, &pbc);
>> + if (FAILED(hr)) goto done;
>> +
>> + hr = IShellItem2_BindToHandler(&This->IShellItem2_iface, pbc, &BHID_SFUIObject,
>> + &IID_IExtractIconW, (void **)&ei);
>> + IBindCtx_Release(pbc);
>> + if (FAILED(hr)) goto done;
>> +
>> + hr = IExtractIconW_GetIconLocation(ei, gil_in_flags, icon_file, MAX_PATH, &source_index, &gil_out_flags);
>> + if (FAILED(hr)) goto free_ei;
>
> You probably can get rid of gil_in_flags and pbc.
For gil_in_flags: ACK. I just wanted to clarify the purpose of the parameter; maybe it wasn't a good idea to unnecessarily make a constant-as-of-now a variable after all.
For pbc: While the IBindCtx is currently ignored, wouldn't it make sense to pass something valid as required by the BindToHandler's interface definition anyway?
>
>> +
>> + if (!(gil_out_flags & GIL_NOTFILENAME))
>> + {
>> + UINT ei_res;
>> +
>> + if (source_index == -1)
>> + source_index = 0; /* special case for some control panel applications */
>> +
>> + FIXME("%s %d\n", debugstr_w(icon_file), source_index);
>> + ei_res = ExtractIconExW(icon_file, source_index, big_icon, small_icon, 1);
>> + if (!ei_res || ei_res == (UINT)-1)
>> + {
>> + WARN("Failed to extract icon.\n");
>> + hr = E_FAIL;
>> + }
>
> Instead of this you can probably do SHGetFileInfo(SHGFI_SYSICONINDEX), and then pick appropriate imagelist.
That would preclude GIL_NOTFILENAME in Wine's current implementation; maybe it doesn't matter that much anyway...
>> + }
>> + else
>> + {
>> + hr = IExtractIconW_Extract(ei, icon_file, source_index, big_icon, small_icon, MAKELONG(iconsize, iconsize));
>> + }
>> +
>> +free_ei:
>> + IExtractIconW_Release(ei);
>> +done:
>> + return hr;
>> +}
>> +
>> +static HICON choose_best_icon(HICON *icons, UINT count, SIZE size_limit, SIZE *out_size)
>> +{
>> + HICON best_icon = NULL;
>> + SIZE best_size = {0, 0};
>> + UINT i;
>> +
>> + for (i = 0; i < count; i++)
>> + {
>> + ICONINFO iinfo;
>> + BITMAP bm;
>> + SIZE size;
>> + BOOL is_color, ret;
>> +
>> + if (!icons[i] || !GetIconInfo(icons[i], &iinfo)) continue;
>> +
>> + is_color = iinfo.hbmColor != NULL;
>> + ret = GetObjectW(is_color ? iinfo.hbmColor : iinfo.hbmMask, sizeof(bm), &bm);
>> + DeleteObject(iinfo.hbmColor);
>> + DeleteObject(iinfo.hbmMask);
>> + if (!ret) continue;
>> +
>> + size.cx = bm.bmWidth;
>> + size.cy = is_color ? abs(bm.bmHeight) : abs(bm.bmHeight) / 2;
>> +
>> + if (!best_icon || (best_size.cx < size.cx && size.cx <= size_limit.cx &&
>> + best_size.cy < size.cy && size.cy <= size_limit.cy))
>> + {
>> + best_icon = icons[i];
>> + best_size = size;
>> + }
>> + }
>> +
>> + *out_size = best_size;
>> + return best_icon;
>> +}
>
> This looks too complicated. System imaglists come in few sizes.
Not all icons belong to the system imagelist, are they?
> Desired size is know on GetImage(), I think it make sense to look for exact match first, and load that.
I don't see how that would simplify the algorithm. An exact match may not always exist, but only subpar candidates.
> For non-file based case Extract() has size argument on its own, should that do something to pick "the best" size?
My intention was not to rely on Extract() to actually do what we want, even as we provide the hint.
>
>> +
>> +static HRESULT ShellItem_get_icon_bitmap(ShellItem *This, IWICImagingFactory *imgfactory,
>> + SIZE size, SIIGBF flags, IWICBitmap **bitmap)
>> +{
>> + HRESULT hr;
>> + HICON icons[2] = { NULL, NULL }, best_icon;
>> + SIZE best_icon_size;
>> + UINT i;
>> +
>> + *bitmap = NULL;
>> +
>> + hr = ShellItem_get_icons(This, size, &icons[0], &icons[1]);
>> + if (FAILED(hr)) return hr;
>> +
>> + best_icon = choose_best_icon(icons, ARRAY_SIZE(icons), size, &best_icon_size);
>> + for (i = 0; i < ARRAY_SIZE(icons); i++)
>> + if (icons[i] && icons[i] != best_icon) DeleteObject(icons[i]);
>> +
>> + if (!best_icon) return E_FAIL;
>> +
>> + hr = IWICImagingFactory_CreateBitmapFromHICON(imgfactory, best_icon, bitmap);
>> + DeleteObject(best_icon);
>> + return hr;
>> +}
>> +
>> +static HRESULT convert_wicbitmapsource_to_gdi(IWICImagingFactory *imgfactory,
>> + IWICBitmapSource *bitmapsource, HBITMAP *gdibitmap)
>> +{
>> + BITMAPINFOHEADER bmi;
>> + HRESULT hr;
>> + UINT width, height;
>> + IWICBitmapSource *newsrc;
>> + HDC dc;
>> + HBITMAP bm;
>> + void *bits;
>> +
>> + *gdibitmap = NULL;
>> +
>> + hr = WICConvertBitmapSource(&GUID_WICPixelFormat32bppBGRA, bitmapsource, &newsrc);
>> + if (FAILED(hr)) goto done;
>> +
>> + hr = IWICBitmapSource_GetSize(newsrc, &width, &height);
>> + if (FAILED(hr)) goto free_newsrc;
>> +
>> + dc = CreateCompatibleDC(NULL);
>> + if (!dc)
>> + {
>> + hr = E_FAIL;
>> + goto free_newsrc;
>> + }
>> +
>> + memset(&bmi, 0, sizeof(bmi));
>> + bmi.biSize = sizeof(bmi);
>> + bmi.biWidth = width;
>> + bmi.biHeight = -height;
>> + bmi.biPlanes = 1;
>> + bmi.biBitCount = 32;
>> + bmi.biCompression = BI_RGB;
>> +
>> + bm = CreateDIBSection(dc, (const BITMAPINFO *)&bmi, DIB_RGB_COLORS, &bits, NULL, 0);
>> + DeleteDC(dc);
> I don't think you need a device context for this.
ACK. TIL that CreateDIBSection(NULL, ..., DIB_RGB_COLORS, ...) is legal.
>
>> + if (!bm)
>> + {
>> + WARN("Cannot create bitmap.\n");
>> + hr = E_FAIL;
>> + goto free_newsrc;
>> + }
>> +
>> + hr = IWICBitmapSource_CopyPixels(newsrc, NULL, width * 4, width * height * 4, bits);
>> + if (FAILED(hr))
>> + {
>> + DeleteObject(bm);
>> + goto free_newsrc;
>> + }
>> +
>> + hr = S_OK;
>> + *gdibitmap = bm;
>> +
>> +free_newsrc:
>> + IWICBitmapSource_Release(newsrc);
>> +done:
>> + return hr;
>> +}
>> +
>> static HRESULT WINAPI ShellItem_IShellItemImageFactory_GetImage(IShellItemImageFactory *iface,
>> SIZE size, SIIGBF flags, HBITMAP *phbm)
>> {
>> ShellItem *This = impl_from_IShellItemImageFactory(iface);
>> + HRESULT hr;
>> + IWICImagingFactory *imgfactory;
>> + IWICBitmap *bitmap = NULL;
>> static int once;
>> if (!once++)
>> - FIXME("%p ({%lu, %lu} %d %p): stub\n", This, size.cx, size.cy, flags, phbm);
>> + FIXME("%p ({%lu, %lu} %d %p): partial stub\n", This, size.cx, size.cy, flags, phbm);
>> *phbm = NULL;
>> - return E_NOTIMPL;
>> +
>> + if (flags != SIIGBF_BIGGERSIZEOK) return E_NOTIMPL;
>> +
>> + hr = WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &imgfactory);
>> + if (SUCCEEDED(hr))
>> + {
>> + hr = ShellItem_get_icon_bitmap(This, imgfactory, size, flags, &bitmap);
>> + if (SUCCEEDED(hr))
>> + {
>> + hr = convert_wicbitmapsource_to_gdi(imgfactory, (IWICBitmapSource *)bitmap, phbm);
>> + IWICBitmap_Release(bitmap);
>> + }
>> + IWICImagingFactory_Release(imgfactory);
>> + }
>> +
>> + return hr;
>> }
>
--
Sincerely,
Jinoh Kang
April 29, 2022
Re: [PATCH v7 3/3] shell32: Partially implement IShellItemImageFactory (icon only, no thumbnail).
by Nikolay Sivov
I think this needs a lot of cleanup.
On 4/26/22 21:17, Jinoh Kang wrote:
>
>
> +
> typedef struct _ShellItem {
> IShellItem2 IShellItem2_iface;
> LONG ref;
> @@ -565,17 +568,205 @@ static ULONG WINAPI ShellItem_IShellItemImageFactory_Release(IShellItemImageFact
> return IShellItem2_Release(&This->IShellItem2_iface);
> }
>
> +static HRESULT ShellItem_get_icons(ShellItem *This, SIZE size, HICON *big_icon, HICON *small_icon)
You don't really need two icons, for SIIGBF_BIGGERSIZEOK.
> +{
> + HRESULT hr;
> + IBindCtx *pbc;
> + IExtractIconW *ei;
> + WCHAR icon_file[MAX_PATH];
> + INT source_index;
> + UINT gil_in_flags = 0, gil_out_flags;
> + INT iconsize;
> +
> + iconsize = min(size.cx, size.cy);
> + if (iconsize <= 0 || iconsize > 0x7fff)
> + iconsize = 0x7fff;
> +
> + hr = CreateBindCtx(0, &pbc);
> + if (FAILED(hr)) goto done;
> +
> + hr = IShellItem2_BindToHandler(&This->IShellItem2_iface, pbc, &BHID_SFUIObject,
> + &IID_IExtractIconW, (void **)&ei);
> + IBindCtx_Release(pbc);
> + if (FAILED(hr)) goto done;
> +
> + hr = IExtractIconW_GetIconLocation(ei, gil_in_flags, icon_file, MAX_PATH, &source_index, &gil_out_flags);
> + if (FAILED(hr)) goto free_ei;
You probably can get rid of gil_in_flags and pbc.
> +
> + if (!(gil_out_flags & GIL_NOTFILENAME))
> + {
> + UINT ei_res;
> +
> + if (source_index == -1)
> + source_index = 0; /* special case for some control panel applications */
> +
> + FIXME("%s %d\n", debugstr_w(icon_file), source_index);
> + ei_res = ExtractIconExW(icon_file, source_index, big_icon, small_icon, 1);
> + if (!ei_res || ei_res == (UINT)-1)
> + {
> + WARN("Failed to extract icon.\n");
> + hr = E_FAIL;
> + }
Instead of this you can probably do SHGetFileInfo(SHGFI_SYSICONINDEX),
and then pick appropriate imagelist.
> + }
> + else
> + {
> + hr = IExtractIconW_Extract(ei, icon_file, source_index, big_icon, small_icon, MAKELONG(iconsize, iconsize));
> + }
> +
> +free_ei:
> + IExtractIconW_Release(ei);
> +done:
> + return hr;
> +}
> +
> +static HICON choose_best_icon(HICON *icons, UINT count, SIZE size_limit, SIZE *out_size)
> +{
> + HICON best_icon = NULL;
> + SIZE best_size = {0, 0};
> + UINT i;
> +
> + for (i = 0; i < count; i++)
> + {
> + ICONINFO iinfo;
> + BITMAP bm;
> + SIZE size;
> + BOOL is_color, ret;
> +
> + if (!icons[i] || !GetIconInfo(icons[i], &iinfo)) continue;
> +
> + is_color = iinfo.hbmColor != NULL;
> + ret = GetObjectW(is_color ? iinfo.hbmColor : iinfo.hbmMask, sizeof(bm), &bm);
> + DeleteObject(iinfo.hbmColor);
> + DeleteObject(iinfo.hbmMask);
> + if (!ret) continue;
> +
> + size.cx = bm.bmWidth;
> + size.cy = is_color ? abs(bm.bmHeight) : abs(bm.bmHeight) / 2;
> +
> + if (!best_icon || (best_size.cx < size.cx && size.cx <= size_limit.cx &&
> + best_size.cy < size.cy && size.cy <= size_limit.cy))
> + {
> + best_icon = icons[i];
> + best_size = size;
> + }
> + }
> +
> + *out_size = best_size;
> + return best_icon;
> +}
This looks too complicated. System imaglists come in few sizes. Desired
size is know on GetImage(), I think it make sense to look for exact
match first, and load that. For non-file based case Extract() has size
argument on its own, should that do something to pick "the best" size?
> +
> +static HRESULT ShellItem_get_icon_bitmap(ShellItem *This, IWICImagingFactory *imgfactory,
> + SIZE size, SIIGBF flags, IWICBitmap **bitmap)
> +{
> + HRESULT hr;
> + HICON icons[2] = { NULL, NULL }, best_icon;
> + SIZE best_icon_size;
> + UINT i;
> +
> + *bitmap = NULL;
> +
> + hr = ShellItem_get_icons(This, size, &icons[0], &icons[1]);
> + if (FAILED(hr)) return hr;
> +
> + best_icon = choose_best_icon(icons, ARRAY_SIZE(icons), size, &best_icon_size);
> + for (i = 0; i < ARRAY_SIZE(icons); i++)
> + if (icons[i] && icons[i] != best_icon) DeleteObject(icons[i]);
> +
> + if (!best_icon) return E_FAIL;
> +
> + hr = IWICImagingFactory_CreateBitmapFromHICON(imgfactory, best_icon, bitmap);
> + DeleteObject(best_icon);
> + return hr;
> +}
> +
> +static HRESULT convert_wicbitmapsource_to_gdi(IWICImagingFactory *imgfactory,
> + IWICBitmapSource *bitmapsource, HBITMAP *gdibitmap)
> +{
> + BITMAPINFOHEADER bmi;
> + HRESULT hr;
> + UINT width, height;
> + IWICBitmapSource *newsrc;
> + HDC dc;
> + HBITMAP bm;
> + void *bits;
> +
> + *gdibitmap = NULL;
> +
> + hr = WICConvertBitmapSource(&GUID_WICPixelFormat32bppBGRA, bitmapsource, &newsrc);
> + if (FAILED(hr)) goto done;
> +
> + hr = IWICBitmapSource_GetSize(newsrc, &width, &height);
> + if (FAILED(hr)) goto free_newsrc;
> +
> + dc = CreateCompatibleDC(NULL);
> + if (!dc)
> + {
> + hr = E_FAIL;
> + goto free_newsrc;
> + }
> +
> + memset(&bmi, 0, sizeof(bmi));
> + bmi.biSize = sizeof(bmi);
> + bmi.biWidth = width;
> + bmi.biHeight = -height;
> + bmi.biPlanes = 1;
> + bmi.biBitCount = 32;
> + bmi.biCompression = BI_RGB;
> +
> + bm = CreateDIBSection(dc, (const BITMAPINFO *)&bmi, DIB_RGB_COLORS, &bits, NULL, 0);
> + DeleteDC(dc);
I don't think you need a device context for this.
> + if (!bm)
> + {
> + WARN("Cannot create bitmap.\n");
> + hr = E_FAIL;
> + goto free_newsrc;
> + }
> +
> + hr = IWICBitmapSource_CopyPixels(newsrc, NULL, width * 4, width * height * 4, bits);
> + if (FAILED(hr))
> + {
> + DeleteObject(bm);
> + goto free_newsrc;
> + }
> +
> + hr = S_OK;
> + *gdibitmap = bm;
> +
> +free_newsrc:
> + IWICBitmapSource_Release(newsrc);
> +done:
> + return hr;
> +}
> +
> static HRESULT WINAPI ShellItem_IShellItemImageFactory_GetImage(IShellItemImageFactory *iface,
> SIZE size, SIIGBF flags, HBITMAP *phbm)
> {
> ShellItem *This = impl_from_IShellItemImageFactory(iface);
> + HRESULT hr;
> + IWICImagingFactory *imgfactory;
> + IWICBitmap *bitmap = NULL;
> static int once;
>
> if (!once++)
> - FIXME("%p ({%lu, %lu} %d %p): stub\n", This, size.cx, size.cy, flags, phbm);
> + FIXME("%p ({%lu, %lu} %d %p): partial stub\n", This, size.cx, size.cy, flags, phbm);
>
> *phbm = NULL;
> - return E_NOTIMPL;
> +
> + if (flags != SIIGBF_BIGGERSIZEOK) return E_NOTIMPL;
> +
> + hr = WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &imgfactory);
> + if (SUCCEEDED(hr))
> + {
> + hr = ShellItem_get_icon_bitmap(This, imgfactory, size, flags, &bitmap);
> + if (SUCCEEDED(hr))
> + {
> + hr = convert_wicbitmapsource_to_gdi(imgfactory, (IWICBitmapSource *)bitmap, phbm);
> + IWICBitmap_Release(bitmap);
> + }
> + IWICImagingFactory_Release(imgfactory);
> + }
> +
> + return hr;
> }
April 29, 2022
Re: [PATCH 6/6] wineoss: Move DRVM_INIT and DRVM_EXIT to the unixlib.
by Andrew Eikum
Signed-off-by: Andrew Eikum <aeikum(a)codeweavers.com>
On Fri, Apr 29, 2022 at 08:29:58AM +0100, Huw Davies wrote:
> Signed-off-by: Huw Davies <huw(a)codeweavers.com>
> ---
> dlls/wineoss.drv/midi.c | 63 --------------------------------------
> dlls/wineoss.drv/oss.c | 1 -
> dlls/wineoss.drv/ossmidi.c | 41 +++++++++++++++++++------
> dlls/wineoss.drv/unixlib.h | 7 -----
> 4 files changed, 31 insertions(+), 81 deletions(-)
>
> diff --git a/dlls/wineoss.drv/midi.c b/dlls/wineoss.drv/midi.c
> index 84a4fac4b74..dda5dabf522 100644
> --- a/dlls/wineoss.drv/midi.c
> +++ b/dlls/wineoss.drv/midi.c
> @@ -34,19 +34,7 @@
> * timers (like select on fd)
> */
>
> -#include "config.h"
> -
> -#include <stdlib.h>
> -#include <string.h>
> #include <stdarg.h>
> -#include <stdio.h>
> -#include <sys/types.h>
> -#include <unistd.h>
> -#include <fcntl.h>
> -#include <errno.h>
> -#include <sys/ioctl.h>
> -#include <poll.h>
> -#include <sys/soundcard.h>
>
> #include "windef.h"
> #include "winbase.h"
> @@ -67,44 +55,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(midi);
> * Low level MIDI implementation *
> *======================================================================*/
>
> -static int MIDI_loadcount;
> -/**************************************************************************
> - * OSS_MidiInit [internal]
> - *
> - * Initializes the MIDI devices information variables
> - */
> -static LRESULT OSS_MidiInit(void)
> -{
> - struct midi_init_params params;
> - UINT err;
> -
> - TRACE("(%i)\n", MIDI_loadcount);
> - if (MIDI_loadcount++)
> - return 1;
> -
> - TRACE("Initializing the MIDI variables.\n");
> -
> - params.err = &err;
> - OSS_CALL(midi_init, ¶ms);
> -
> - return err;
> -}
> -
> -/**************************************************************************
> - * OSS_MidiExit [internal]
> - *
> - * Release the MIDI devices information variables
> - */
> -static LRESULT OSS_MidiExit(void)
> -{
> - TRACE("(%i)\n", MIDI_loadcount);
> -
> - if (--MIDI_loadcount)
> - return 1;
> -
> - return 0;
> -}
> -
> static void notify_client(struct notify_context *notify)
> {
> TRACE("dev_id = %d msg = %d param1 = %04lX param2 = %04lX\n",
> @@ -130,12 +80,6 @@ DWORD WINAPI OSS_midMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
>
> TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
> wDevID, wMsg, dwUser, dwParam1, dwParam2);
> - switch (wMsg) {
> - case DRVM_INIT:
> - return OSS_MidiInit();
> - case DRVM_EXIT:
> - return OSS_MidiExit();
> - }
>
> params.dev_id = wDevID;
> params.msg = wMsg;
> @@ -167,13 +111,6 @@ DWORD WINAPI OSS_modMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
> TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
> wDevID, wMsg, dwUser, dwParam1, dwParam2);
>
> - switch (wMsg) {
> - case DRVM_INIT:
> - return OSS_MidiInit();
> - case DRVM_EXIT:
> - return OSS_MidiExit();
> - }
> -
> params.dev_id = wDevID;
> params.msg = wMsg;
> params.user = dwUser;
> diff --git a/dlls/wineoss.drv/oss.c b/dlls/wineoss.drv/oss.c
> index b0a411ecd9b..a5aea9ee724 100644
> --- a/dlls/wineoss.drv/oss.c
> +++ b/dlls/wineoss.drv/oss.c
> @@ -1405,7 +1405,6 @@ unixlib_entry_t __wine_unix_call_funcs[] =
> set_volumes,
> set_event_handle,
> is_started,
> - midi_init,
> midi_release,
> midi_out_message,
> midi_in_message,
> diff --git a/dlls/wineoss.drv/ossmidi.c b/dlls/wineoss.drv/ossmidi.c
> index 072a9815c35..6677609a5a6 100644
> --- a/dlls/wineoss.drv/ossmidi.c
> +++ b/dlls/wineoss.drv/ossmidi.c
> @@ -83,6 +83,7 @@ static pthread_mutex_t in_buffer_mutex = PTHREAD_MUTEX_INITIALIZER;
> static unsigned int num_dests, num_srcs, num_synths, seq_refs;
> static struct midi_dest dests[MAX_MIDIOUTDRV];
> static struct midi_src srcs[MAX_MIDIINDRV];
> +static int load_count;
>
> static unsigned int num_midi_in_started;
> static int rec_cancel_pipe[2];
> @@ -301,22 +302,23 @@ static int seq_close(int fd)
> return 0;
> }
>
> -NTSTATUS midi_init(void *args)
> +static UINT midi_init(void)
> {
> - struct midi_init_params *params = args;
> int i, status, synth_devs = 255, midi_devs = 255, fd, len;
> struct synth_info sinfo;
> struct midi_info minfo;
> struct midi_dest *dest;
> struct midi_src *src;
>
> + TRACE("(%i)\n", load_count);
> +
> + if (load_count++)
> + return 1;
> +
> /* try to open device */
> fd = seq_open();
> if (fd == -1)
> - {
> - *params->err = -1;
> - return STATUS_SUCCESS;
> - }
> + return -1;
>
> /* find how many Synth devices are there in the system */
> status = ioctl(fd, SNDCTL_SEQ_NRSYNTHS, &synth_devs);
> @@ -324,8 +326,7 @@ NTSTATUS midi_init(void *args)
> {
> ERR("ioctl for nr synth failed.\n");
> seq_close(fd);
> - *params->err = -1;
> - return STATUS_SUCCESS;
> + return -1;
> }
>
> if (synth_devs > MAX_MIDIOUTDRV)
> @@ -506,9 +507,17 @@ wrapup:
> /* close file and exit */
> seq_close(fd);
>
> - *params->err = 0;
> + return 0;
> +}
>
> - return STATUS_SUCCESS;
> +static UINT midi_exit(void)
> +{
> + TRACE("(%i)\n", load_count);
> +
> + if (--load_count)
> + return 1;
> +
> + return 0;
> }
>
> NTSTATUS midi_release(void *args)
> @@ -1634,6 +1643,12 @@ NTSTATUS midi_out_message(void *args)
>
> switch (params->msg)
> {
> + case DRVM_INIT:
> + *params->err = midi_init();
> + break;
> + case DRVM_EXIT:
> + *params->err = midi_exit();
> + break;
> case DRVM_ENABLE:
> case DRVM_DISABLE:
> /* FIXME: Pretend this is supported */
> @@ -1688,6 +1703,12 @@ NTSTATUS midi_in_message(void *args)
>
> switch (params->msg)
> {
> + case DRVM_INIT:
> + *params->err = midi_init();
> + break;
> + case DRVM_EXIT:
> + *params->err = midi_exit();
> + break;
> case DRVM_ENABLE:
> case DRVM_DISABLE:
> /* FIXME: Pretend this is supported */
> diff --git a/dlls/wineoss.drv/unixlib.h b/dlls/wineoss.drv/unixlib.h
> index d3dda7c76f2..6a7dc9288d9 100644
> --- a/dlls/wineoss.drv/unixlib.h
> +++ b/dlls/wineoss.drv/unixlib.h
> @@ -209,11 +209,6 @@ struct is_started_params
> HRESULT result;
> };
>
> -struct midi_init_params
> -{
> - UINT *err;
> -};
> -
> struct notify_context
> {
> BOOL send_notify;
> @@ -280,14 +275,12 @@ enum oss_funcs
> oss_set_volumes,
> oss_set_event_handle,
> oss_is_started,
> - oss_midi_init,
> oss_midi_release,
> oss_midi_out_message,
> oss_midi_in_message,
> oss_midi_notify_wait,
> };
>
> -NTSTATUS midi_init(void *args) DECLSPEC_HIDDEN;
> NTSTATUS midi_release(void *args) DECLSPEC_HIDDEN;
> NTSTATUS midi_out_message(void *args) DECLSPEC_HIDDEN;
> NTSTATUS midi_in_message(void *args) DECLSPEC_HIDDEN;
> --
> 2.25.1
>
>
April 29, 2022
Re: [PATCH 5/6] wineoss: Move MIDM_OPEN and MIDM_CLOSE to the unixlib.
by Andrew Eikum
Signed-off-by: Andrew Eikum <aeikum(a)codeweavers.com>
On Fri, Apr 29, 2022 at 08:29:57AM +0100, Huw Davies wrote:
> Signed-off-by: Huw Davies <huw(a)codeweavers.com>
> ---
> dlls/wineoss.drv/Makefile.in | 2 +-
> dlls/wineoss.drv/midi.c | 243 -----------------------------------
> dlls/wineoss.drv/oss.c | 3 -
> dlls/wineoss.drv/ossmidi.c | 193 +++++++++++++++++++++++++---
> dlls/wineoss.drv/unixlib.h | 35 -----
> 5 files changed, 175 insertions(+), 301 deletions(-)
>
> diff --git a/dlls/wineoss.drv/Makefile.in b/dlls/wineoss.drv/Makefile.in
> index 04b438da71e..13fb18b6004 100644
> --- a/dlls/wineoss.drv/Makefile.in
> +++ b/dlls/wineoss.drv/Makefile.in
> @@ -3,7 +3,7 @@ MODULE = wineoss.drv
> UNIXLIB = wineoss.so
> IMPORTS = uuid ole32 user32 advapi32
> DELAYIMPORTS = winmm
> -EXTRALIBS = $(OSS4_LIBS)
> +EXTRALIBS = $(OSS4_LIBS) $(PTHREAD_LIBS)
> EXTRAINCL = $(OSS4_CFLAGS)
>
> EXTRADLLFLAGS = -mcygwin
> diff --git a/dlls/wineoss.drv/midi.c b/dlls/wineoss.drv/midi.c
> index c83dd55fd6b..84a4fac4b74 100644
> --- a/dlls/wineoss.drv/midi.c
> +++ b/dlls/wineoss.drv/midi.c
> @@ -63,23 +63,10 @@
>
> WINE_DEFAULT_DEBUG_CHANNEL(midi);
>
> -static WINE_MIDIIN *MidiInDev;
> -
> -/* this is the total number of MIDI out devices found */
> -static int MIDM_NumDevs = 0;
> -
> -static int numStartedMidiIn = 0;
> -
> -static int rec_cancel_pipe[2];
> -static HANDLE hThread;
> -
> /*======================================================================*
> * Low level MIDI implementation *
> *======================================================================*/
>
> -static int midiOpenSeq(void);
> -static int midiCloseSeq(int);
> -
> static int MIDI_loadcount;
> /**************************************************************************
> * OSS_MidiInit [internal]
> @@ -100,11 +87,6 @@ static LRESULT OSS_MidiInit(void)
> params.err = &err;
> OSS_CALL(midi_init, ¶ms);
>
> - if (!err)
> - {
> - MidiInDev = params.srcs;
> - MIDM_NumDevs = params.num_srcs;
> - }
> return err;
> }
>
> @@ -120,9 +102,6 @@ static LRESULT OSS_MidiExit(void)
> if (--MIDI_loadcount)
> return 1;
>
> - MidiInDev = NULL;
> - MIDM_NumDevs = 0;
> -
> return 0;
> }
>
> @@ -135,224 +114,6 @@ static void notify_client(struct notify_context *notify)
> notify->instance, notify->param_1, notify->param_2);
> }
>
> -/**************************************************************************
> - * MIDI_NotifyClient [internal]
> - */
> -static void MIDI_NotifyClient(UINT wDevID, WORD wMsg,
> - DWORD_PTR dwParam1, DWORD_PTR dwParam2)
> -{
> - DWORD_PTR dwCallBack;
> - UINT uFlags;
> - HANDLE hDev;
> - DWORD_PTR dwInstance;
> -
> - TRACE("wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",
> - wDevID, wMsg, dwParam1, dwParam2);
> -
> - switch (wMsg) {
> - case MIM_OPEN:
> - case MIM_CLOSE:
> - case MIM_DATA:
> - case MIM_LONGDATA:
> - case MIM_ERROR:
> - case MIM_LONGERROR:
> - case MIM_MOREDATA:
> - if (wDevID > MIDM_NumDevs) return;
> -
> - dwCallBack = MidiInDev[wDevID].midiDesc.dwCallback;
> - uFlags = MidiInDev[wDevID].wFlags;
> - hDev = MidiInDev[wDevID].midiDesc.hMidi;
> - dwInstance = MidiInDev[wDevID].midiDesc.dwInstance;
> - break;
> - default:
> - ERR("Unsupported MSW-MIDI message %u\n", wMsg);
> - return;
> - }
> -
> - DriverCallback(dwCallBack, uFlags, hDev, wMsg, dwInstance, dwParam1, dwParam2);
> -}
> -
> -/**************************************************************************
> - * midiOpenSeq [internal]
> - */
> -static int midiOpenSeq(void)
> -{
> - struct midi_seq_open_params params;
> -
> - params.close = 0;
> - params.fd = -1;
> - OSS_CALL(midi_seq_open, ¶ms);
> -
> - return params.fd;
> -}
> -
> -/**************************************************************************
> - * midiCloseSeq [internal]
> - */
> -static int midiCloseSeq(int fd)
> -{
> - struct midi_seq_open_params params;
> -
> - params.close = 1;
> - params.fd = fd;
> - OSS_CALL(midi_seq_open, ¶ms);
> -
> - return 0;
> -}
> -
> -static void handle_midi_data(unsigned char *buffer, unsigned int len)
> -{
> - struct midi_handle_data_params params;
> -
> - params.buffer = buffer;
> - params.len = len;
> - OSS_CALL(midi_handle_data, ¶ms);
> -}
> -
> -static DWORD WINAPI midRecThread(void *arg)
> -{
> - int fd = (int)(INT_PTR)arg;
> - unsigned char buffer[256];
> - int len;
> - struct pollfd pollfd[2];
> -
> - pollfd[0].fd = rec_cancel_pipe[0];
> - pollfd[0].events = POLLIN;
> - pollfd[1].fd = fd;
> - pollfd[1].events = POLLIN;
> -
> - while (1)
> - {
> - /* Check if an event is present */
> - if (poll(pollfd, ARRAY_SIZE(pollfd), -1) <= 0)
> - continue;
> -
> - if (pollfd[0].revents & POLLIN) /* cancelled */
> - break;
> -
> - len = read(fd, buffer, sizeof(buffer));
> -
> - if (len > 0 && len % 4 == 0)
> - handle_midi_data(buffer, len);
> - }
> - return 0;
> -}
> -
> -/**************************************************************************
> - * midOpen [internal]
> - */
> -static DWORD midOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
> -{
> - int fd;
> -
> - TRACE("(%04X, %p, %08X);\n", wDevID, lpDesc, dwFlags);
> -
> - if (lpDesc == NULL) {
> - WARN("Invalid Parameter !\n");
> - return MMSYSERR_INVALPARAM;
> - }
> -
> - /* FIXME :
> - * how to check that content of lpDesc is correct ?
> - */
> - if (wDevID >= MIDM_NumDevs) {
> - WARN("wDevID too large (%u) !\n", wDevID);
> - return MMSYSERR_BADDEVICEID;
> - }
> - if (MidiInDev[wDevID].state == -1) {
> - WARN("device disabled\n");
> - return MIDIERR_NODEVICE;
> - }
> - if (MidiInDev[wDevID].midiDesc.hMidi != 0) {
> - WARN("device already open !\n");
> - return MMSYSERR_ALLOCATED;
> - }
> - if ((dwFlags & MIDI_IO_STATUS) != 0) {
> - WARN("No support for MIDI_IO_STATUS in dwFlags yet, ignoring it\n");
> - dwFlags &= ~MIDI_IO_STATUS;
> - }
> - if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
> - FIXME("Bad dwFlags\n");
> - return MMSYSERR_INVALFLAG;
> - }
> -
> - fd = midiOpenSeq();
> - if (fd < 0) {
> - return MMSYSERR_ERROR;
> - }
> -
> - if (numStartedMidiIn++ == 0) {
> - pipe(rec_cancel_pipe);
> - hThread = CreateThread(NULL, 0, midRecThread, (void *)(INT_PTR)fd, 0, NULL);
> - if (!hThread) {
> - close(rec_cancel_pipe[0]);
> - close(rec_cancel_pipe[1]);
> - numStartedMidiIn = 0;
> - WARN("Couldn't create thread for midi-in\n");
> - midiCloseSeq(fd);
> - return MMSYSERR_ERROR;
> - }
> - SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);
> - TRACE("Created thread for midi-in\n");
> - }
> -
> - MidiInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
> -
> - MidiInDev[wDevID].lpQueueHdr = NULL;
> - MidiInDev[wDevID].midiDesc = *lpDesc;
> - MidiInDev[wDevID].state = 0;
> - MidiInDev[wDevID].incLen = 0;
> - MidiInDev[wDevID].startTime = 0;
> - MidiInDev[wDevID].fd = fd;
> -
> - MIDI_NotifyClient(wDevID, MIM_OPEN, 0L, 0L);
> - return MMSYSERR_NOERROR;
> -}
> -
> -/**************************************************************************
> - * midClose [internal]
> - */
> -static DWORD midClose(WORD wDevID)
> -{
> - int ret = MMSYSERR_NOERROR;
> -
> - TRACE("(%04X);\n", wDevID);
> -
> - if (wDevID >= MIDM_NumDevs) {
> - WARN("wDevID too big (%u) !\n", wDevID);
> - return MMSYSERR_BADDEVICEID;
> - }
> - if (MidiInDev[wDevID].midiDesc.hMidi == 0) {
> - WARN("device not opened !\n");
> - return MMSYSERR_ERROR;
> - }
> - if (MidiInDev[wDevID].lpQueueHdr != 0) {
> - return MIDIERR_STILLPLAYING;
> - }
> -
> - if (MidiInDev[wDevID].fd == -1) {
> - WARN("ooops !\n");
> - return MMSYSERR_ERROR;
> - }
> - if (--numStartedMidiIn == 0) {
> - TRACE("Stopping thread for midi-in\n");
> - write(rec_cancel_pipe[1], "x", 1);
> - if (WaitForSingleObject(hThread, 5000) != WAIT_OBJECT_0) {
> - WARN("Thread end not signaled, force termination\n");
> - TerminateThread(hThread, 0);
> - }
> - close(rec_cancel_pipe[0]);
> - close(rec_cancel_pipe[1]);
> - TRACE("Stopped thread for midi-in\n");
> - }
> - midiCloseSeq(MidiInDev[wDevID].fd);
> - MidiInDev[wDevID].fd = -1;
> -
> - MIDI_NotifyClient(wDevID, MIM_CLOSE, 0L, 0L);
> - MidiInDev[wDevID].midiDesc.hMidi = 0;
> - return ret;
> -}
> -
> /*======================================================================*
> * MIDI entry points *
> *======================================================================*/
> @@ -374,10 +135,6 @@ DWORD WINAPI OSS_midMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
> return OSS_MidiInit();
> case DRVM_EXIT:
> return OSS_MidiExit();
> - case MIDM_OPEN:
> - return midOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
> - case MIDM_CLOSE:
> - return midClose(wDevID);
> }
>
> params.dev_id = wDevID;
> diff --git a/dlls/wineoss.drv/oss.c b/dlls/wineoss.drv/oss.c
> index c5b422a60c9..b0a411ecd9b 100644
> --- a/dlls/wineoss.drv/oss.c
> +++ b/dlls/wineoss.drv/oss.c
> @@ -1410,7 +1410,4 @@ unixlib_entry_t __wine_unix_call_funcs[] =
> midi_out_message,
> midi_in_message,
> midi_notify_wait,
> -
> - midi_seq_open,
> - midi_handle_data,
> };
> diff --git a/dlls/wineoss.drv/ossmidi.c b/dlls/wineoss.drv/ossmidi.c
> index 9c8ca8a8f39..072a9815c35 100644
> --- a/dlls/wineoss.drv/ossmidi.c
> +++ b/dlls/wineoss.drv/ossmidi.c
> @@ -33,6 +33,7 @@
> #include <stdint.h>
> #include <time.h>
> #include <unistd.h>
> +#include <poll.h>
> #include <errno.h>
> #include <sys/types.h>
> #include <sys/stat.h>
> @@ -45,6 +46,7 @@
> #define WIN32_NO_STATUS
> #include "winternl.h"
> #include "audioclient.h"
> +#include "mmddk.h"
>
> #include "wine/debug.h"
> #include "wine/unixlib.h"
> @@ -62,12 +64,30 @@ struct midi_dest
> int fd;
> };
>
> +struct midi_src
> +{
> + int state; /* -1 disabled, 0 is no recording started, 1 in recording, bit 2 set if in sys exclusive recording */
> + MIDIOPENDESC midiDesc;
> + WORD wFlags;
> + MIDIHDR *lpQueueHdr;
> + unsigned char incoming[3];
> + unsigned char incPrev;
> + char incLen;
> + UINT startTime;
> + MIDIINCAPSW caps;
> + int fd;
> +};
> +
> static pthread_mutex_t in_buffer_mutex = PTHREAD_MUTEX_INITIALIZER;
>
> static unsigned int num_dests, num_srcs, num_synths, seq_refs;
> static struct midi_dest dests[MAX_MIDIOUTDRV];
> static struct midi_src srcs[MAX_MIDIINDRV];
>
> +static unsigned int num_midi_in_started;
> +static int rec_cancel_pipe[2];
> +static pthread_t rec_thread_id;
> +
> static pthread_mutex_t notify_mutex = PTHREAD_MUTEX_INITIALIZER;
> static pthread_cond_t notify_read_cond = PTHREAD_COND_INITIALIZER;
> static pthread_cond_t notify_write_cond = PTHREAD_COND_INITIALIZER;
> @@ -281,18 +301,6 @@ static int seq_close(int fd)
> return 0;
> }
>
> -NTSTATUS midi_seq_open(void *args)
> -{
> - struct midi_seq_open_params *params = args;
> -
> - if (!params->close)
> - params->fd = seq_open();
> - else
> - seq_close(params->fd);
> -
> - return STATUS_SUCCESS;
> -}
> -
> NTSTATUS midi_init(void *args)
> {
> struct midi_init_params *params = args;
> @@ -499,8 +507,6 @@ wrapup:
> seq_close(fd);
>
> *params->err = 0;
> - params->num_srcs = num_srcs;
> - params->srcs = srcs;
>
> return STATUS_SUCCESS;
> }
> @@ -1313,11 +1319,8 @@ static void handle_regular_data(struct midi_src *src, unsigned char value, UINT
> }
> }
>
> -NTSTATUS midi_handle_data(void *args)
> +static void handle_midi_data(unsigned char *buffer, unsigned int len)
> {
> - struct midi_handle_data_params *params = args;
> - unsigned char *buffer = params->buffer;
> - unsigned int len = params->len;
> unsigned int time = get_time_msec(), i;
> struct midi_src *src;
> unsigned char value;
> @@ -1339,7 +1342,153 @@ NTSTATUS midi_handle_data(void *args)
> else
> handle_regular_data(src, value, time - src->startTime);
> }
> - return STATUS_SUCCESS;
> +}
> +
> +static void *rec_thread_proc(void *arg)
> +{
> + int fd = PtrToLong(arg);
> + unsigned char buffer[256];
> + int len;
> + struct pollfd pollfd[2];
> +
> + pollfd[0].fd = rec_cancel_pipe[0];
> + pollfd[0].events = POLLIN;
> + pollfd[1].fd = fd;
> + pollfd[1].events = POLLIN;
> +
> + while (1)
> + {
> + /* Check if an event is present */
> + if (poll(pollfd, ARRAY_SIZE(pollfd), -1) <= 0)
> + continue;
> +
> + if (pollfd[0].revents & POLLIN) /* cancelled */
> + break;
> +
> + len = read(fd, buffer, sizeof(buffer));
> +
> + if (len > 0 && len % 4 == 0)
> + handle_midi_data(buffer, len);
> + }
> + return NULL;
> +}
> +
> +static UINT midi_in_open(WORD dev_id, MIDIOPENDESC *desc, UINT flags, struct notify_context *notify)
> +{
> + struct midi_src *src;
> + int fd;
> +
> + TRACE("(%04X, %p, %08X);\n", dev_id, desc, flags);
> +
> + if (desc == NULL)
> + {
> + WARN("Invalid Parameter !\n");
> + return MMSYSERR_INVALPARAM;
> + }
> +
> + /* FIXME :
> + * how to check that content of lpDesc is correct ?
> + */
> + if (dev_id >= num_srcs)
> + {
> + WARN("wDevID too large (%u) !\n", dev_id);
> + return MMSYSERR_BADDEVICEID;
> + }
> + src = srcs + dev_id;
> + if (src->state == -1)
> + {
> + WARN("device disabled\n");
> + return MIDIERR_NODEVICE;
> + }
> + if (src->midiDesc.hMidi != 0)
> + {
> + WARN("device already open !\n");
> + return MMSYSERR_ALLOCATED;
> + }
> + if ((flags & MIDI_IO_STATUS) != 0)
> + {
> + WARN("No support for MIDI_IO_STATUS in dwFlags yet, ignoring it\n");
> + flags &= ~MIDI_IO_STATUS;
> + }
> + if ((flags & ~CALLBACK_TYPEMASK) != 0)
> + {
> + FIXME("Bad flags\n");
> + return MMSYSERR_INVALFLAG;
> + }
> +
> + fd = seq_open();
> + if (fd < 0)
> + return MMSYSERR_ERROR;
> +
> + if (num_midi_in_started++ == 0)
> + {
> + pipe(rec_cancel_pipe);
> + if (pthread_create(&rec_thread_id, NULL, rec_thread_proc, LongToPtr(fd)))
> + {
> + close(rec_cancel_pipe[0]);
> + close(rec_cancel_pipe[1]);
> + num_midi_in_started = 0;
> + WARN("Couldn't create thread for midi-in\n");
> + seq_close(fd);
> + return MMSYSERR_ERROR;
> + }
> + TRACE("Created thread for midi-in\n");
> + }
> +
> + src->wFlags = HIWORD(flags & CALLBACK_TYPEMASK);
> +
> + src->lpQueueHdr = NULL;
> + src->midiDesc = *desc;
> + src->state = 0;
> + src->incLen = 0;
> + src->startTime = 0;
> + src->fd = fd;
> +
> + set_in_notify(notify, src, dev_id, MIM_OPEN, 0, 0);
> + return MMSYSERR_NOERROR;
> +}
> +
> +static UINT midi_in_close(WORD dev_id, struct notify_context *notify)
> +{
> + struct midi_src *src;
> +
> + TRACE("(%04X);\n", dev_id);
> +
> + if (dev_id >= num_srcs)
> + {
> + WARN("dev_id too big (%u) !\n", dev_id);
> + return MMSYSERR_BADDEVICEID;
> + }
> + src = srcs + dev_id;
> + if (src->midiDesc.hMidi == 0)
> + {
> + WARN("device not opened !\n");
> + return MMSYSERR_ERROR;
> + }
> + if (src->lpQueueHdr != 0)
> + return MIDIERR_STILLPLAYING;
> +
> + if (src->fd == -1)
> + {
> + WARN("ooops !\n");
> + return MMSYSERR_ERROR;
> + }
> + if (--num_midi_in_started == 0)
> + {
> + TRACE("Stopping thread for midi-in\n");
> + write(rec_cancel_pipe[1], "x", 1);
> + pthread_join(rec_thread_id, NULL);
> + close(rec_cancel_pipe[0]);
> + close(rec_cancel_pipe[1]);
> + TRACE("Stopped thread for midi-in\n");
> + }
> + seq_close(src->fd);
> + src->fd = -1;
> +
> + set_in_notify(notify, src, dev_id, MIM_CLOSE, 0, 0);
> + src->midiDesc.hMidi = 0;
> +
> + return MMSYSERR_NOERROR;
> }
>
> static UINT midi_in_add_buffer(WORD dev_id, MIDIHDR *hdr, UINT hdr_size)
> @@ -1544,6 +1693,12 @@ NTSTATUS midi_in_message(void *args)
> /* FIXME: Pretend this is supported */
> *params->err = MMSYSERR_NOERROR;
> break;
> + case MIDM_OPEN:
> + *params->err = midi_in_open(params->dev_id, (MIDIOPENDESC *)params->param_1, params->param_2, params->notify);
> + break;
> + case MIDM_CLOSE:
> + *params->err = midi_in_close(params->dev_id, params->notify);
> + break;
> case MIDM_ADDBUFFER:
> *params->err = midi_in_add_buffer(params->dev_id, (MIDIHDR *)params->param_1, params->param_2);
> break;
> diff --git a/dlls/wineoss.drv/unixlib.h b/dlls/wineoss.drv/unixlib.h
> index 90d0c47421c..d3dda7c76f2 100644
> --- a/dlls/wineoss.drv/unixlib.h
> +++ b/dlls/wineoss.drv/unixlib.h
> @@ -209,27 +209,9 @@ struct is_started_params
> HRESULT result;
> };
>
> -#include <mmddk.h> /* temporary */
> -
> -typedef struct midi_src
> -{
> - int state; /* -1 disabled, 0 is no recording started, 1 in recording, bit 2 set if in sys exclusive recording */
> - MIDIOPENDESC midiDesc;
> - WORD wFlags;
> - MIDIHDR *lpQueueHdr;
> - unsigned char incoming[3];
> - unsigned char incPrev;
> - char incLen;
> - UINT startTime;
> - MIDIINCAPSW caps;
> - int fd;
> -} WINE_MIDIIN;
> -
> struct midi_init_params
> {
> UINT *err;
> - unsigned int num_srcs;
> - struct midi_src *srcs;
> };
>
> struct notify_context
> @@ -273,18 +255,6 @@ struct midi_notify_wait_params
> struct notify_context *notify;
> };
>
> -struct midi_seq_open_params
> -{
> - int close;
> - int fd;
> -};
> -
> -struct midi_handle_data_params
> -{
> - unsigned char *buffer;
> - unsigned int len;
> -};
> -
> enum oss_funcs
> {
> oss_test_connect,
> @@ -315,9 +285,6 @@ enum oss_funcs
> oss_midi_out_message,
> oss_midi_in_message,
> oss_midi_notify_wait,
> -
> - oss_midi_seq_open, /* temporary */
> - oss_midi_handle_data,
> };
>
> NTSTATUS midi_init(void *args) DECLSPEC_HIDDEN;
> @@ -325,8 +292,6 @@ NTSTATUS midi_release(void *args) DECLSPEC_HIDDEN;
> NTSTATUS midi_out_message(void *args) DECLSPEC_HIDDEN;
> NTSTATUS midi_in_message(void *args) DECLSPEC_HIDDEN;
> NTSTATUS midi_notify_wait(void *args) DECLSPEC_HIDDEN;
> -NTSTATUS midi_seq_open(void *args) DECLSPEC_HIDDEN;
> -NTSTATUS midi_handle_data(void *args) DECLSPEC_HIDDEN;
>
> extern unixlib_handle_t oss_handle;
>
> --
> 2.25.1
>
>
April 29, 2022
Re: [PATCH 4/6] wineoss: Use a pipe to signal the end of the record thread.
by Andrew Eikum
Signed-off-by: Andrew Eikum <aeikum(a)codeweavers.com>
On Fri, Apr 29, 2022 at 08:29:56AM +0100, Huw Davies wrote:
> Signed-off-by: Huw Davies <huw(a)codeweavers.com>
> ---
> dlls/wineoss.drv/midi.c | 42 ++++++++++++++++++++---------------------
> 1 file changed, 21 insertions(+), 21 deletions(-)
>
> diff --git a/dlls/wineoss.drv/midi.c b/dlls/wineoss.drv/midi.c
> index 0afd9985c03..c83dd55fd6b 100644
> --- a/dlls/wineoss.drv/midi.c
> +++ b/dlls/wineoss.drv/midi.c
> @@ -70,7 +70,7 @@ static int MIDM_NumDevs = 0;
>
> static int numStartedMidiIn = 0;
>
> -static int end_thread;
> +static int rec_cancel_pipe[2];
> static HANDLE hThread;
>
> /*======================================================================*
> @@ -214,30 +214,26 @@ static DWORD WINAPI midRecThread(void *arg)
> int fd = (int)(INT_PTR)arg;
> unsigned char buffer[256];
> int len;
> - struct pollfd pfd;
> + struct pollfd pollfd[2];
>
> - TRACE("Thread startup\n");
> -
> - pfd.fd = fd;
> - pfd.events = POLLIN;
> -
> - while(!end_thread) {
> - TRACE("Thread loop\n");
> + pollfd[0].fd = rec_cancel_pipe[0];
> + pollfd[0].events = POLLIN;
> + pollfd[1].fd = fd;
> + pollfd[1].events = POLLIN;
>
> + while (1)
> + {
> /* Check if an event is present */
> - if (poll(&pfd, 1, 250) <= 0)
> + if (poll(pollfd, ARRAY_SIZE(pollfd), -1) <= 0)
> continue;
> -
> - len = read(fd, buffer, sizeof(buffer));
> - TRACE("Received %d bytes\n", len);
>
> - if (len < 0) continue;
> - if ((len % 4) != 0) {
> - WARN("Bad length %d, errno %d (%s)\n", len, errno, strerror(errno));
> - continue;
> - }
> + if (pollfd[0].revents & POLLIN) /* cancelled */
> + break;
> +
> + len = read(fd, buffer, sizeof(buffer));
>
> - handle_midi_data(buffer, len);
> + if (len > 0 && len % 4 == 0)
> + handle_midi_data(buffer, len);
> }
> return 0;
> }
> @@ -286,9 +282,11 @@ static DWORD midOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
> }
>
> if (numStartedMidiIn++ == 0) {
> - end_thread = 0;
> + pipe(rec_cancel_pipe);
> hThread = CreateThread(NULL, 0, midRecThread, (void *)(INT_PTR)fd, 0, NULL);
> if (!hThread) {
> + close(rec_cancel_pipe[0]);
> + close(rec_cancel_pipe[1]);
> numStartedMidiIn = 0;
> WARN("Couldn't create thread for midi-in\n");
> midiCloseSeq(fd);
> @@ -338,11 +336,13 @@ static DWORD midClose(WORD wDevID)
> }
> if (--numStartedMidiIn == 0) {
> TRACE("Stopping thread for midi-in\n");
> - end_thread = 1;
> + write(rec_cancel_pipe[1], "x", 1);
> if (WaitForSingleObject(hThread, 5000) != WAIT_OBJECT_0) {
> WARN("Thread end not signaled, force termination\n");
> TerminateThread(hThread, 0);
> }
> + close(rec_cancel_pipe[0]);
> + close(rec_cancel_pipe[1]);
> TRACE("Stopped thread for midi-in\n");
> }
> midiCloseSeq(MidiInDev[wDevID].fd);
> --
> 2.25.1
>
>
April 29, 2022
Re: [PATCH 3/6] wineoss: Introduce a helper to retrieve the time.
by Andrew Eikum
Signed-off-by: Andrew Eikum <aeikum(a)codeweavers.com>
On Fri, Apr 29, 2022 at 08:29:55AM +0100, Huw Davies wrote:
> The motivation is that this will need to be called from a
> non-Win32 thread and so shouldn't use the Win32 API. An
> added benefit is that it will eliminate the 16ms jitter
> associated with GetTickCount().
>
> Signed-off-by: Huw Davies <huw(a)codeweavers.com>
> ---
> dlls/wineoss.drv/ossmidi.c | 20 +++++++++++++++++---
> 1 file changed, 17 insertions(+), 3 deletions(-)
>
> diff --git a/dlls/wineoss.drv/ossmidi.c b/dlls/wineoss.drv/ossmidi.c
> index 1695f1d2f7b..9c8ca8a8f39 100644
> --- a/dlls/wineoss.drv/ossmidi.c
> +++ b/dlls/wineoss.drv/ossmidi.c
> @@ -30,6 +30,8 @@
> #include <stdarg.h>
> #include <string.h>
> #include <stdio.h>
> +#include <stdint.h>
> +#include <time.h>
> #include <unistd.h>
> #include <errno.h>
> #include <sys/types.h>
> @@ -155,6 +157,18 @@ static void in_buffer_unlock(void)
> pthread_mutex_unlock(&in_buffer_mutex);
> }
>
> +static uint64_t get_time_msec(void)
> +{
> + struct timespec now = {0, 0};
> +
> +#ifdef CLOCK_MONOTONIC_RAW
> + if (!clock_gettime(CLOCK_MONOTONIC_RAW, &now))
> + return (uint64_t)now.tv_sec * 1000 + now.tv_nsec / 1000000;
> +#endif
> + clock_gettime(CLOCK_MONOTONIC, &now);
> + return (uint64_t)now.tv_sec * 1000 + now.tv_nsec / 1000000;
> +}
> +
> /*
> * notify buffer: The notification ring buffer is implemented so that
> * there is always at least one unused sentinel before the current
> @@ -1304,7 +1318,7 @@ NTSTATUS midi_handle_data(void *args)
> struct midi_handle_data_params *params = args;
> unsigned char *buffer = params->buffer;
> unsigned int len = params->len;
> - unsigned int time = NtGetTickCount(), i;
> + unsigned int time = get_time_msec(), i;
> struct midi_src *src;
> unsigned char value;
> WORD dev_id;
> @@ -1415,7 +1429,7 @@ static UINT midi_in_start(WORD dev_id)
> if (src->state == -1) return MIDIERR_NODEVICE;
>
> src->state = 1;
> - src->startTime = NtGetTickCount();
> + src->startTime = get_time_msec();
> return MMSYSERR_NOERROR;
> }
>
> @@ -1435,7 +1449,7 @@ static UINT midi_in_stop(WORD dev_id)
>
> static UINT midi_in_reset(WORD dev_id, struct notify_context *notify)
> {
> - UINT cur_time = NtGetTickCount();
> + UINT cur_time = get_time_msec();
> UINT err = MMSYSERR_NOERROR;
> struct midi_src *src;
> MIDIHDR *hdr;
> --
> 2.25.1
>
>
April 29, 2022
Re: [PATCH 2/6] wineoss: Move the midi in data handlers to the unixlib.
by Andrew Eikum
Signed-off-by: Andrew Eikum <aeikum(a)codeweavers.com>
On Fri, Apr 29, 2022 at 08:29:54AM +0100, Huw Davies wrote:
> The syscall itself is temporary.
>
> Signed-off-by: Huw Davies <huw(a)codeweavers.com>
> ---
> dlls/wineoss.drv/midi.c | 129 +------------------------
> dlls/wineoss.drv/oss.c | 2 +-
> dlls/wineoss.drv/ossmidi.c | 189 +++++++++++++++++++++++++++++++++++--
> dlls/wineoss.drv/unixlib.h | 10 +-
> 4 files changed, 196 insertions(+), 134 deletions(-)
>
> diff --git a/dlls/wineoss.drv/midi.c b/dlls/wineoss.drv/midi.c
> index b3f980ab3da..0afd9985c03 100644
> --- a/dlls/wineoss.drv/midi.c
> +++ b/dlls/wineoss.drv/midi.c
> @@ -126,16 +126,6 @@ static LRESULT OSS_MidiExit(void)
> return 0;
> }
>
> -static void in_buffer_lock(void)
> -{
> - OSS_CALL(midi_in_lock, ULongToPtr(1));
> -}
> -
> -static void in_buffer_unlock(void)
> -{
> - OSS_CALL(midi_in_lock, ULongToPtr(0));
> -}
> -
> static void notify_client(struct notify_context *notify)
> {
> TRACE("dev_id = %d msg = %d param1 = %04lX param2 = %04lX\n",
> @@ -210,123 +200,13 @@ static int midiCloseSeq(int fd)
> return 0;
> }
>
> -static void handle_sysex_data(struct midi_src *src, unsigned char value, UINT time)
> -{
> - MIDIHDR *hdr;
> - BOOL done = FALSE;
> -
> - src->state |= 2;
> - src->incLen = 0;
> -
> - in_buffer_lock();
> -
> - hdr = src->lpQueueHdr;
> - if (hdr)
> - {
> - BYTE *data = (BYTE *)hdr->lpData;
> -
> - data[hdr->dwBytesRecorded++] = value;
> - if (hdr->dwBytesRecorded == hdr->dwBufferLength)
> - done = TRUE;
> - }
> -
> - if (value == 0xf7) /* end */
> - {
> - src->state &= ~2;
> - done = TRUE;
> - }
> -
> - if (done && hdr)
> - {
> - src->lpQueueHdr = hdr->lpNext;
> - hdr->dwFlags &= ~MHDR_INQUEUE;
> - hdr->dwFlags |= MHDR_DONE;
> - MIDI_NotifyClient(src - MidiInDev, MIM_LONGDATA, (UINT_PTR)hdr, time);
> - }
> -
> - in_buffer_unlock();
> -}
> -
> -static void handle_regular_data(struct midi_src *src, unsigned char value, UINT time)
> -{
> - UINT to_send = 0;
> -
> -#define IS_CMD(_x) (((_x) & 0x80) == 0x80)
> -#define IS_SYS_CMD(_x) (((_x) & 0xF0) == 0xF0)
> -
> - if (!IS_CMD(value) && src->incLen == 0) /* try to reuse old cmd */
> - {
> - if (IS_CMD(src->incPrev) && !IS_SYS_CMD(src->incPrev))
> - {
> - src->incoming[0] = src->incPrev;
> - src->incLen = 1;
> - }
> - else
> - {
> - /* FIXME: should generate MIM_ERROR notification */
> - return;
> - }
> - }
> - src->incoming[(int)src->incLen++] = value;
> - if (src->incLen == 1 && !IS_SYS_CMD(src->incoming[0]))
> - /* store new cmd, just in case */
> - src->incPrev = src->incoming[0];
> -
> -#undef IS_CMD
> -#undef IS_SYS_CMD
> -
> - switch (src->incoming[0] & 0xF0)
> - {
> - case MIDI_NOTEOFF:
> - case MIDI_NOTEON:
> - case MIDI_KEY_PRESSURE:
> - case MIDI_CTL_CHANGE:
> - case MIDI_PITCH_BEND:
> - if (src->incLen == 3)
> - to_send = (src->incoming[2] << 16) | (src->incoming[1] << 8) |
> - src->incoming[0];
> - break;
> - case MIDI_PGM_CHANGE:
> - case MIDI_CHN_PRESSURE:
> - if (src->incLen == 2)
> - to_send = (src->incoming[1] << 8) | src->incoming[0];
> - break;
> - case MIDI_SYSTEM_PREFIX:
> - if (src->incLen == 1)
> - to_send = src->incoming[0];
> - break;
> - }
> -
> - if (to_send)
> - {
> - src->incLen = 0;
> - MIDI_NotifyClient(src - MidiInDev, MIM_DATA, to_send, time);
> - }
> -}
> -
> static void handle_midi_data(unsigned char *buffer, unsigned int len)
> {
> - unsigned int time = GetTickCount(), i;
> - struct midi_src *src;
> - unsigned char value;
> - WORD dev_id;
> + struct midi_handle_data_params params;
>
> - for (i = 0; i < len; i += (buffer[i] & 0x80) ? 8 : 4)
> - {
> - if (buffer[i] != SEQ_MIDIPUTC) continue;
> -
> - dev_id = buffer[i + 2];
> - value = buffer[i + 1];
> -
> - if (dev_id >= MIDM_NumDevs) continue;
> - src = MidiInDev + dev_id;
> - if (src->state <= 0) continue;
> -
> - if (value == 0xf0 || src->state & 2) /* system exclusive */
> - handle_sysex_data(src, value, time - src->startTime);
> - else
> - handle_regular_data(src, value, time - src->startTime);
> - }
> + params.buffer = buffer;
> + params.len = len;
> + OSS_CALL(midi_handle_data, ¶ms);
> }
>
> static DWORD WINAPI midRecThread(void *arg)
> @@ -565,6 +445,7 @@ static DWORD WINAPI notify_thread(void *p)
> {
> OSS_CALL(midi_notify_wait, ¶ms);
> if (quit) break;
> + if (notify.send_notify) notify_client(¬ify);
> }
> return 0;
> }
> diff --git a/dlls/wineoss.drv/oss.c b/dlls/wineoss.drv/oss.c
> index 8fda9270a4e..c5b422a60c9 100644
> --- a/dlls/wineoss.drv/oss.c
> +++ b/dlls/wineoss.drv/oss.c
> @@ -1412,5 +1412,5 @@ unixlib_entry_t __wine_unix_call_funcs[] =
> midi_notify_wait,
>
> midi_seq_open,
> - midi_in_lock,
> + midi_handle_data,
> };
> diff --git a/dlls/wineoss.drv/ossmidi.c b/dlls/wineoss.drv/ossmidi.c
> index 0790eaaec1a..1695f1d2f7b 100644
> --- a/dlls/wineoss.drv/ossmidi.c
> +++ b/dlls/wineoss.drv/ossmidi.c
> @@ -68,7 +68,11 @@ static struct midi_src srcs[MAX_MIDIINDRV];
>
> static pthread_mutex_t notify_mutex = PTHREAD_MUTEX_INITIALIZER;
> static pthread_cond_t notify_read_cond = PTHREAD_COND_INITIALIZER;
> +static pthread_cond_t notify_write_cond = PTHREAD_COND_INITIALIZER;
> static BOOL notify_quit;
> +#define NOTIFY_BUFFER_SIZE 64 + 1 /* + 1 for the sentinel */
> +static struct notify_context notify_buffer[NOTIFY_BUFFER_SIZE];
> +static struct notify_context *notify_read = notify_buffer, *notify_write = notify_buffer;
>
> typedef struct sVoice
> {
> @@ -151,19 +155,59 @@ static void in_buffer_unlock(void)
> pthread_mutex_unlock(&in_buffer_mutex);
> }
>
> -NTSTATUS midi_in_lock(void *args)
> +/*
> + * notify buffer: The notification ring buffer is implemented so that
> + * there is always at least one unused sentinel before the current
> + * read position in order to allow detection of the full vs empty
> + * state.
> + */
> +static struct notify_context *notify_buffer_next(struct notify_context *notify)
> {
> - if (args) in_buffer_lock();
> - else in_buffer_unlock();
> + if (++notify >= notify_buffer + ARRAY_SIZE(notify_buffer))
> + notify = notify_buffer;
>
> - return STATUS_SUCCESS;
> + return notify;
> +}
> +
> +static BOOL notify_buffer_empty(void)
> +{
> + return notify_read == notify_write;
> +}
> +
> +static BOOL notify_buffer_full(void)
> +{
> + return notify_buffer_next(notify_write) == notify_read;
> +}
> +
> +static BOOL notify_buffer_add(struct notify_context *notify)
> +{
> + if (notify_buffer_full()) return FALSE;
> +
> + *notify_write = *notify;
> + notify_write = notify_buffer_next(notify_write);
> + return TRUE;
> +}
> +
> +static BOOL notify_buffer_remove(struct notify_context *notify)
> +{
> + if (notify_buffer_empty()) return FALSE;
> +
> + *notify = *notify_read;
> + notify_read = notify_buffer_next(notify_read);
> + return TRUE;
> }
>
> static void notify_post(struct notify_context *notify)
> {
> pthread_mutex_lock(¬ify_mutex);
>
> - if (notify) FIXME("Not yet handled\n");
> + if (notify)
> + {
> + while (notify_buffer_full())
> + pthread_cond_wait(¬ify_write_cond, ¬ify_mutex);
> +
> + notify_buffer_add(notify);
> + }
> else notify_quit = TRUE;
> pthread_cond_signal(¬ify_read_cond);
>
> @@ -1157,6 +1201,133 @@ static UINT midi_out_reset(WORD dev_id)
> return MMSYSERR_NOERROR;
> }
>
> +static void handle_sysex_data(struct midi_src *src, unsigned char value, UINT time)
> +{
> + struct notify_context notify;
> + MIDIHDR *hdr;
> + BOOL done = FALSE;
> +
> + src->state |= 2;
> + src->incLen = 0;
> +
> + in_buffer_lock();
> +
> + hdr = src->lpQueueHdr;
> + if (hdr)
> + {
> + BYTE *data = (BYTE *)hdr->lpData;
> +
> + data[hdr->dwBytesRecorded++] = value;
> + if (hdr->dwBytesRecorded == hdr->dwBufferLength)
> + done = TRUE;
> + }
> +
> + if (value == 0xf7) /* end */
> + {
> + src->state &= ~2;
> + done = TRUE;
> + }
> +
> + if (done && hdr)
> + {
> + src->lpQueueHdr = hdr->lpNext;
> + hdr->dwFlags &= ~MHDR_INQUEUE;
> + hdr->dwFlags |= MHDR_DONE;
> + set_in_notify(¬ify, src, src - srcs, MIM_LONGDATA, (UINT_PTR)hdr, time);
> + notify_post(¬ify);
> + }
> +
> + in_buffer_unlock();
> +}
> +
> +static void handle_regular_data(struct midi_src *src, unsigned char value, UINT time)
> +{
> + struct notify_context notify;
> + UINT to_send = 0;
> +
> +#define IS_CMD(_x) (((_x) & 0x80) == 0x80)
> +#define IS_SYS_CMD(_x) (((_x) & 0xF0) == 0xF0)
> +
> + if (!IS_CMD(value) && src->incLen == 0) /* try to reuse old cmd */
> + {
> + if (IS_CMD(src->incPrev) && !IS_SYS_CMD(src->incPrev))
> + {
> + src->incoming[0] = src->incPrev;
> + src->incLen = 1;
> + }
> + else
> + {
> + /* FIXME: should generate MIM_ERROR notification */
> + return;
> + }
> + }
> + src->incoming[(int)src->incLen++] = value;
> + if (src->incLen == 1 && !IS_SYS_CMD(src->incoming[0]))
> + /* store new cmd, just in case */
> + src->incPrev = src->incoming[0];
> +
> +#undef IS_CMD
> +#undef IS_SYS_CMD
> +
> + switch (src->incoming[0] & 0xF0)
> + {
> + case MIDI_NOTEOFF:
> + case MIDI_NOTEON:
> + case MIDI_KEY_PRESSURE:
> + case MIDI_CTL_CHANGE:
> + case MIDI_PITCH_BEND:
> + if (src->incLen == 3)
> + to_send = (src->incoming[2] << 16) | (src->incoming[1] << 8) |
> + src->incoming[0];
> + break;
> + case MIDI_PGM_CHANGE:
> + case MIDI_CHN_PRESSURE:
> + if (src->incLen == 2)
> + to_send = (src->incoming[1] << 8) | src->incoming[0];
> + break;
> + case MIDI_SYSTEM_PREFIX:
> + if (src->incLen == 1)
> + to_send = src->incoming[0];
> + break;
> + }
> +
> + if (to_send)
> + {
> + src->incLen = 0;
> + set_in_notify(¬ify, src, src - srcs, MIM_DATA, to_send, time);
> + notify_post(¬ify);
> + }
> +}
> +
> +NTSTATUS midi_handle_data(void *args)
> +{
> + struct midi_handle_data_params *params = args;
> + unsigned char *buffer = params->buffer;
> + unsigned int len = params->len;
> + unsigned int time = NtGetTickCount(), i;
> + struct midi_src *src;
> + unsigned char value;
> + WORD dev_id;
> +
> + for (i = 0; i < len; i += (buffer[i] & 0x80) ? 8 : 4)
> + {
> + if (buffer[i] != SEQ_MIDIPUTC) continue;
> +
> + dev_id = buffer[i + 2];
> + value = buffer[i + 1];
> +
> + if (dev_id >= num_srcs) continue;
> + src = srcs + dev_id;
> + if (src->state <= 0) continue;
> +
> + if (value == 0xf0 || src->state & 2) /* system exclusive */
> + handle_sysex_data(src, value, time - src->startTime);
> + else
> + handle_regular_data(src, value, time - src->startTime);
> + }
> + return STATUS_SUCCESS;
> +}
> +
> static UINT midi_in_add_buffer(WORD dev_id, MIDIHDR *hdr, UINT hdr_size)
> {
> struct midi_src *src;
> @@ -1397,11 +1568,15 @@ NTSTATUS midi_notify_wait(void *args)
>
> pthread_mutex_lock(¬ify_mutex);
>
> - while (!notify_quit)
> + while (!notify_quit && notify_buffer_empty())
> pthread_cond_wait(¬ify_read_cond, ¬ify_mutex);
>
> *params->quit = notify_quit;
> -
> + if (!notify_quit)
> + {
> + notify_buffer_remove(params->notify);
> + pthread_cond_signal(¬ify_write_cond);
> + }
> pthread_mutex_unlock(¬ify_mutex);
>
> return STATUS_SUCCESS;
> diff --git a/dlls/wineoss.drv/unixlib.h b/dlls/wineoss.drv/unixlib.h
> index ddeba49556c..90d0c47421c 100644
> --- a/dlls/wineoss.drv/unixlib.h
> +++ b/dlls/wineoss.drv/unixlib.h
> @@ -279,6 +279,12 @@ struct midi_seq_open_params
> int fd;
> };
>
> +struct midi_handle_data_params
> +{
> + unsigned char *buffer;
> + unsigned int len;
> +};
> +
> enum oss_funcs
> {
> oss_test_connect,
> @@ -311,7 +317,7 @@ enum oss_funcs
> oss_midi_notify_wait,
>
> oss_midi_seq_open, /* temporary */
> - oss_midi_in_lock,
> + oss_midi_handle_data,
> };
>
> NTSTATUS midi_init(void *args) DECLSPEC_HIDDEN;
> @@ -320,7 +326,7 @@ NTSTATUS midi_out_message(void *args) DECLSPEC_HIDDEN;
> NTSTATUS midi_in_message(void *args) DECLSPEC_HIDDEN;
> NTSTATUS midi_notify_wait(void *args) DECLSPEC_HIDDEN;
> NTSTATUS midi_seq_open(void *args) DECLSPEC_HIDDEN;
> -NTSTATUS midi_in_lock(void *args) DECLSPEC_HIDDEN;
> +NTSTATUS midi_handle_data(void *args) DECLSPEC_HIDDEN;
>
> extern unixlib_handle_t oss_handle;
>
> --
> 2.25.1
>
>
April 29, 2022
Re: [PATCH 1/6] wineoss: Introduce a notification thread.
by Andrew Eikum
Signed-off-by: Andrew Eikum <aeikum(a)codeweavers.com>
On Fri, Apr 29, 2022 at 08:29:53AM +0100, Huw Davies wrote:
> Currently the thread just blocks until told to quit by midi_release.
> Eventually this thread will dispatch the MIM_DATA and MIM_LONGDATA
> notifications.
>
> Signed-off-by: Huw Davies <huw(a)codeweavers.com>
> ---
> dlls/wineoss.drv/midi.c | 21 ++++++++++++++++++++
> dlls/wineoss.drv/oss.c | 2 ++
> dlls/wineoss.drv/ossmidi.c | 39 ++++++++++++++++++++++++++++++++++++++
> dlls/wineoss.drv/unixlib.h | 10 ++++++++++
> 4 files changed, 72 insertions(+)
>
> diff --git a/dlls/wineoss.drv/midi.c b/dlls/wineoss.drv/midi.c
> index e36a737624a..b3f980ab3da 100644
> --- a/dlls/wineoss.drv/midi.c
> +++ b/dlls/wineoss.drv/midi.c
> @@ -552,6 +552,23 @@ DWORD WINAPI OSS_modMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
> return err;
> }
>
> +static DWORD WINAPI notify_thread(void *p)
> +{
> + struct midi_notify_wait_params params;
> + struct notify_context notify;
> + BOOL quit;
> +
> + params.notify = ¬ify;
> + params.quit = &quit;
> +
> + while (1)
> + {
> + OSS_CALL(midi_notify_wait, ¶ms);
> + if (quit) break;
> + }
> + return 0;
> +}
> +
> /**************************************************************************
> * DriverProc (WINEOSS.1)
> */
> @@ -563,7 +580,11 @@ LRESULT CALLBACK OSS_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
>
> switch(wMsg) {
> case DRV_LOAD:
> + CloseHandle(CreateThread(NULL, 0, notify_thread, NULL, 0, NULL));
> + return 1;
> case DRV_FREE:
> + OSS_CALL(midi_release, NULL);
> + return 1;
> case DRV_OPEN:
> case DRV_CLOSE:
> case DRV_ENABLE:
> diff --git a/dlls/wineoss.drv/oss.c b/dlls/wineoss.drv/oss.c
> index a9081f2cac9..8fda9270a4e 100644
> --- a/dlls/wineoss.drv/oss.c
> +++ b/dlls/wineoss.drv/oss.c
> @@ -1406,8 +1406,10 @@ unixlib_entry_t __wine_unix_call_funcs[] =
> set_event_handle,
> is_started,
> midi_init,
> + midi_release,
> midi_out_message,
> midi_in_message,
> + midi_notify_wait,
>
> midi_seq_open,
> midi_in_lock,
> diff --git a/dlls/wineoss.drv/ossmidi.c b/dlls/wineoss.drv/ossmidi.c
> index 86d766eceaf..0790eaaec1a 100644
> --- a/dlls/wineoss.drv/ossmidi.c
> +++ b/dlls/wineoss.drv/ossmidi.c
> @@ -66,6 +66,10 @@ static unsigned int num_dests, num_srcs, num_synths, seq_refs;
> static struct midi_dest dests[MAX_MIDIOUTDRV];
> static struct midi_src srcs[MAX_MIDIINDRV];
>
> +static pthread_mutex_t notify_mutex = PTHREAD_MUTEX_INITIALIZER;
> +static pthread_cond_t notify_read_cond = PTHREAD_COND_INITIALIZER;
> +static BOOL notify_quit;
> +
> typedef struct sVoice
> {
> int note; /* 0 means not used */
> @@ -155,6 +159,17 @@ NTSTATUS midi_in_lock(void *args)
> return STATUS_SUCCESS;
> }
>
> +static void notify_post(struct notify_context *notify)
> +{
> + pthread_mutex_lock(¬ify_mutex);
> +
> + if (notify) FIXME("Not yet handled\n");
> + else notify_quit = TRUE;
> + pthread_cond_signal(¬ify_read_cond);
> +
> + pthread_mutex_unlock(¬ify_mutex);
> +}
> +
> static void set_in_notify(struct notify_context *notify, struct midi_src *src, WORD dev_id, WORD msg,
> UINT_PTR param_1, UINT_PTR param_2)
> {
> @@ -432,6 +447,14 @@ wrapup:
> return STATUS_SUCCESS;
> }
>
> +NTSTATUS midi_release(void *args)
> +{
> + /* stop the notify_wait thread */
> + notify_post(NULL);
> +
> + return STATUS_SUCCESS;
> +}
> +
> /* FIXME: this is a bad idea, it's even not static... */
> SEQ_DEFINEBUF(1024);
>
> @@ -1367,3 +1390,19 @@ NTSTATUS midi_in_message(void *args)
>
> return STATUS_SUCCESS;
> }
> +
> +NTSTATUS midi_notify_wait(void *args)
> +{
> + struct midi_notify_wait_params *params = args;
> +
> + pthread_mutex_lock(¬ify_mutex);
> +
> + while (!notify_quit)
> + pthread_cond_wait(¬ify_read_cond, ¬ify_mutex);
> +
> + *params->quit = notify_quit;
> +
> + pthread_mutex_unlock(¬ify_mutex);
> +
> + return STATUS_SUCCESS;
> +}
> diff --git a/dlls/wineoss.drv/unixlib.h b/dlls/wineoss.drv/unixlib.h
> index 867e1ff656e..ddeba49556c 100644
> --- a/dlls/wineoss.drv/unixlib.h
> +++ b/dlls/wineoss.drv/unixlib.h
> @@ -267,6 +267,12 @@ struct midi_in_message_params
> struct notify_context *notify;
> };
>
> +struct midi_notify_wait_params
> +{
> + BOOL *quit;
> + struct notify_context *notify;
> +};
> +
> struct midi_seq_open_params
> {
> int close;
> @@ -299,16 +305,20 @@ enum oss_funcs
> oss_set_event_handle,
> oss_is_started,
> oss_midi_init,
> + oss_midi_release,
> oss_midi_out_message,
> oss_midi_in_message,
> + oss_midi_notify_wait,
>
> oss_midi_seq_open, /* temporary */
> oss_midi_in_lock,
> };
>
> NTSTATUS midi_init(void *args) DECLSPEC_HIDDEN;
> +NTSTATUS midi_release(void *args) DECLSPEC_HIDDEN;
> NTSTATUS midi_out_message(void *args) DECLSPEC_HIDDEN;
> NTSTATUS midi_in_message(void *args) DECLSPEC_HIDDEN;
> +NTSTATUS midi_notify_wait(void *args) DECLSPEC_HIDDEN;
> NTSTATUS midi_seq_open(void *args) DECLSPEC_HIDDEN;
> NTSTATUS midi_in_lock(void *args) DECLSPEC_HIDDEN;
>
> --
> 2.25.1
>
>
April 29, 2022
[PATCH 2/2] uiautomationcore/tests: Add tests for UiaProviderFromIAccessible.
by Connor McAdams
Signed-off-by: Connor McAdams <cmcadams(a)codeweavers.com>
---
dlls/uiautomationcore/tests/Makefile.in | 2 +-
dlls/uiautomationcore/tests/uiautomation.c | 424 +++++++++++++++++++++
2 files changed, 425 insertions(+), 1 deletion(-)
diff --git a/dlls/uiautomationcore/tests/Makefile.in b/dlls/uiautomationcore/tests/Makefile.in
index fbd53507fbe..53ed6f6e380 100644
--- a/dlls/uiautomationcore/tests/Makefile.in
+++ b/dlls/uiautomationcore/tests/Makefile.in
@@ -1,5 +1,5 @@
TESTDLL = uiautomationcore.dll
-IMPORTS = uiautomationcore user32 ole32 oleaut32
+IMPORTS = uiautomationcore user32 ole32 oleaut32 oleacc
C_SRCS = \
uiautomation.c
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c
index 501875a20e7..246f8aba7c1 100644
--- a/dlls/uiautomationcore/tests/uiautomation.c
+++ b/dlls/uiautomationcore/tests/uiautomation.c
@@ -23,9 +23,322 @@
#include "windows.h"
#include "initguid.h"
#include "uiautomation.h"
+#include "ocidl.h"
#include "wine/test.h"
+static HRESULT (WINAPI *pUiaProviderFromIAccessible)(IAccessible *, long, DWORD, IRawElementProviderSimple **);
+
+#define DEFINE_EXPECT(func) \
+ static BOOL expect_ ## func = FALSE, called_ ## func = FALSE
+
+#define SET_EXPECT(func) \
+ do { called_ ## func = FALSE; expect_ ## func = TRUE; } while(0)
+
+#define CHECK_EXPECT2(func) \
+ do { \
+ ok(expect_ ##func, "unexpected call " #func "\n"); \
+ called_ ## func = TRUE; \
+ }while(0)
+
+#define CHECK_EXPECT(func) \
+ do { \
+ CHECK_EXPECT2(func); \
+ expect_ ## func = FALSE; \
+ }while(0)
+
+#define CHECK_CALLED(func) \
+ do { \
+ ok(called_ ## func, "expected " #func "\n"); \
+ expect_ ## func = called_ ## func = FALSE; \
+ }while(0)
+
+DEFINE_EXPECT(Accessible_accNavigate);
+
+static LONG Accessible_ref = 1;
+static IAccessible Accessible;
+static IOleWindow OleWindow;
+static HWND Accessible_hwnd = NULL;
+static HWND OleWindow_hwnd = NULL;
+
+static BOOL check_variant_i4(VARIANT *v, int val)
+{
+ if (V_VT(v) == VT_I4 && V_I4(v) == val)
+ return TRUE;
+
+ return FALSE;
+}
+
+static HRESULT WINAPI Accessible_QueryInterface(IAccessible *iface, REFIID riid, void **obj)
+{
+ *obj = NULL;
+ if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDispatch) ||
+ IsEqualIID(riid, &IID_IAccessible))
+ *obj = iface;
+ else if (IsEqualIID(riid, &IID_IOleWindow))
+ *obj = &OleWindow;
+ else
+ return E_NOINTERFACE;
+
+ IAccessible_AddRef(iface);
+ return S_OK;
+}
+
+static ULONG WINAPI Accessible_AddRef(IAccessible *iface)
+{
+ return InterlockedIncrement(&Accessible_ref);
+}
+
+static ULONG WINAPI Accessible_Release(IAccessible *iface)
+{
+ return InterlockedDecrement(&Accessible_ref);
+}
+
+static HRESULT WINAPI Accessible_GetTypeInfoCount(IAccessible *iface, UINT *pctinfo)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible_GetTypeInfo(IAccessible *iface, UINT iTInfo,
+ LCID lcid, ITypeInfo **out_tinfo)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible_GetIDsOfNames(IAccessible *iface, REFIID riid,
+ LPOLESTR *rg_names, UINT name_count, LCID lcid, DISPID *rg_disp_id)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible_Invoke(IAccessible *iface, DISPID disp_id_member,
+ REFIID riid, LCID lcid, WORD flags, DISPPARAMS *disp_params,
+ VARIANT *var_result, EXCEPINFO *excep_info, UINT *arg_err)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible_get_accParent(IAccessible *iface, IDispatch **out_parent)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible_get_accChildCount(IAccessible *iface, LONG *out_count)
+{
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible_get_accChild(IAccessible *iface, VARIANT child_id,
+ IDispatch **out_child)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible_get_accName(IAccessible *iface, VARIANT child_id,
+ BSTR *out_name)
+{
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible_get_accValue(IAccessible *iface, VARIANT child_id,
+ BSTR *out_value)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible_get_accDescription(IAccessible *iface, VARIANT child_id,
+ BSTR *out_description)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible_get_accRole(IAccessible *iface, VARIANT child_id,
+ VARIANT *out_role)
+{
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible_get_accState(IAccessible *iface, VARIANT child_id,
+ VARIANT *out_state)
+{
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible_get_accHelp(IAccessible *iface, VARIANT child_id,
+ BSTR *out_help)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible_get_accHelpTopic(IAccessible *iface,
+ BSTR *out_help_file, VARIANT child_id, LONG *out_topic_id)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible_get_accKeyboardShortcut(IAccessible *iface, VARIANT child_id,
+ BSTR *out_kbd_shortcut)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible_get_accFocus(IAccessible *iface, VARIANT *pchild_id)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible_get_accSelection(IAccessible *iface, VARIANT *out_selection)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible_get_accDefaultAction(IAccessible *iface, VARIANT child_id,
+ BSTR *out_default_action)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible_accSelect(IAccessible *iface, LONG select_flags,
+ VARIANT child_id)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible_accLocation(IAccessible *iface, LONG *out_left,
+ LONG *out_top, LONG *out_width, LONG *out_height, VARIANT child_id)
+{
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible_accNavigate(IAccessible *iface, LONG nav_direction,
+ VARIANT child_id_start, VARIANT *out_var)
+{
+ CHECK_EXPECT(Accessible_accNavigate);
+ VariantInit(out_var);
+
+ /*
+ * This is an undocumented way for UI Automation to get an HWND for
+ * IAccessible's contained in a Direct Annotation wrapper object.
+ */
+ if ((nav_direction == 10) && check_variant_i4(&child_id_start, CHILDID_SELF))
+ {
+ V_VT(out_var) = VT_I4;
+ V_I4(out_var) = HandleToUlong(Accessible_hwnd);
+ return S_OK;
+ }
+ return S_FALSE;
+}
+
+static HRESULT WINAPI Accessible_accHitTest(IAccessible *iface, LONG left, LONG top,
+ VARIANT *out_child_id)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible_accDoDefaultAction(IAccessible *iface, VARIANT child_id)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible_put_accName(IAccessible *iface, VARIANT child_id,
+ BSTR name)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI Accessible_put_accValue(IAccessible *iface, VARIANT child_id,
+ BSTR value)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+static IAccessibleVtbl AccessibleVtbl = {
+ Accessible_QueryInterface,
+ Accessible_AddRef,
+ Accessible_Release,
+ Accessible_GetTypeInfoCount,
+ Accessible_GetTypeInfo,
+ Accessible_GetIDsOfNames,
+ Accessible_Invoke,
+ Accessible_get_accParent,
+ Accessible_get_accChildCount,
+ Accessible_get_accChild,
+ Accessible_get_accName,
+ Accessible_get_accValue,
+ Accessible_get_accDescription,
+ Accessible_get_accRole,
+ Accessible_get_accState,
+ Accessible_get_accHelp,
+ Accessible_get_accHelpTopic,
+ Accessible_get_accKeyboardShortcut,
+ Accessible_get_accFocus,
+ Accessible_get_accSelection,
+ Accessible_get_accDefaultAction,
+ Accessible_accSelect,
+ Accessible_accLocation,
+ Accessible_accNavigate,
+ Accessible_accHitTest,
+ Accessible_accDoDefaultAction,
+ Accessible_put_accName,
+ Accessible_put_accValue
+};
+
+static HRESULT WINAPI OleWindow_QueryInterface(IOleWindow *iface, REFIID riid, void **obj)
+{
+ return IAccessible_QueryInterface(&Accessible, riid, obj);
+}
+
+static ULONG WINAPI OleWindow_AddRef(IOleWindow *iface)
+{
+ return IAccessible_AddRef(&Accessible);
+}
+
+static ULONG WINAPI OleWindow_Release(IOleWindow *iface)
+{
+ return IAccessible_Release(&Accessible);
+}
+
+static HRESULT WINAPI OleWindow_GetWindow(IOleWindow *iface, HWND *hwnd)
+{
+ *hwnd = OleWindow_hwnd;
+ return S_OK;
+}
+
+static HRESULT WINAPI OleWindow_ContextSensitiveHelp(IOleWindow *iface, BOOL f_enter_mode)
+{
+ return E_NOTIMPL;
+}
+
+static const IOleWindowVtbl OleWindowVtbl = {
+ OleWindow_QueryInterface,
+ OleWindow_AddRef,
+ OleWindow_Release,
+ OleWindow_GetWindow,
+ OleWindow_ContextSensitiveHelp
+};
+
+static IAccessible Accessible = {&AccessibleVtbl};
+static IOleWindow OleWindow = {&OleWindowVtbl};
+
static LRESULT WINAPI test_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return DefWindowProcA(hwnd, message, wParam, lParam);
@@ -264,8 +577,119 @@ static void test_uia_reserved_value_ifaces(void)
CoUninitialize();
}
+static void test_UiaProviderFromIAccessible(void)
+{
+ IRawElementProviderSimple *elprov;
+ enum ProviderOptions prov_opt;
+ IAccessible *acc;
+ WNDCLASSA cls;
+ HRESULT hr;
+ HWND hwnd;
+ VARIANT v;
+
+
+ cls.style = 0;
+ cls.lpfnWndProc = test_wnd_proc;
+ cls.cbClsExtra = 0;
+ cls.cbWndExtra = 0;
+ cls.hInstance = GetModuleHandleA(NULL);
+ cls.hIcon = 0;
+ cls.hCursor = NULL;
+ cls.hbrBackground = NULL;
+ cls.lpszMenuName = NULL;
+ cls.lpszClassName = "UiaProviderFromIAccessible class";
+
+ RegisterClassA(&cls);
+
+ hwnd = CreateWindowA("UiaProviderFromIAccessible class", "Test window", WS_OVERLAPPEDWINDOW,
+ 0, 0, 100, 100, NULL, NULL, NULL, NULL);
+
+ hr = pUiaProviderFromIAccessible(NULL, CHILDID_SELF, UIA_PFIA_DEFAULT, &elprov);
+ ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
+
+ hr = pUiaProviderFromIAccessible(&Accessible, CHILDID_SELF, UIA_PFIA_DEFAULT, NULL);
+ ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr);
+
+ /*
+ * UiaProviderFromIAccessible will not wrap an MSAA proxy, this is
+ * detected by checking for the 'IIS_IsOleaccProxy' service from the
+ * IServiceProvider interface.
+ */
+ hr = CreateStdAccessibleObject(hwnd, OBJID_CLIENT, &IID_IAccessible, (void**)&acc);
+ ok(hr == S_OK, "got %#lx\n", hr);
+ ok(!!acc, "acc == NULL\n");
+
+ hr = pUiaProviderFromIAccessible(acc, CHILDID_SELF, UIA_PFIA_DEFAULT, &elprov);
+ ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
+ IAccessible_Release(acc);
+
+ /* Don't return an HWND from accNavigate or OleWindow. */
+ SET_EXPECT(Accessible_accNavigate);
+ Accessible_hwnd = NULL;
+ OleWindow_hwnd = NULL;
+ hr = pUiaProviderFromIAccessible(&Accessible, CHILDID_SELF, UIA_PFIA_DEFAULT, &elprov);
+ ok(hr == E_FAIL, "Unexpected hr %#lx.\n", hr);
+ CHECK_CALLED(Accessible_accNavigate);
+
+ /* Return an HWND from accNavigate, not OleWindow. */
+ SET_EXPECT(Accessible_accNavigate);
+ Accessible_hwnd = hwnd;
+ OleWindow_hwnd = NULL;
+ hr = pUiaProviderFromIAccessible(&Accessible, CHILDID_SELF, UIA_PFIA_DEFAULT, &elprov);
+ ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+ CHECK_CALLED(Accessible_accNavigate);
+ ok(Accessible_ref == 2, "Unexpected refcnt %ld\n", Accessible_ref);
+ IRawElementProviderSimple_Release(elprov);
+ ok(Accessible_ref == 1, "Unexpected refcnt %ld\n", Accessible_ref);
+
+ /* Return an HWND from OleWindow, not accNavigate. */
+ Accessible_hwnd = NULL;
+ OleWindow_hwnd = hwnd;
+ hr = pUiaProviderFromIAccessible(&Accessible, CHILDID_SELF, UIA_PFIA_DEFAULT, &elprov);
+ ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+ ok(Accessible_ref == 2, "Unexpected refcnt %ld\n", Accessible_ref);
+
+ hr = IRawElementProviderSimple_get_ProviderOptions(elprov, &prov_opt);
+ ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+ ok((prov_opt == (ProviderOptions_ServerSideProvider | ProviderOptions_UseComThreading)) ||
+ broken(prov_opt == ProviderOptions_ClientSideProvider), /* Windows < 10 1507 */
+ "Unexpected provider options %#x\n", prov_opt);
+
+ hr = IRawElementProviderSimple_GetPropertyValue(elprov, UIA_ProviderDescriptionPropertyId, &v);
+ ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+ ok(V_VT(&v) == VT_BSTR, "V_VT(&v) = %d\n", V_VT(&v));
+ VariantClear(&v);
+
+ IRawElementProviderSimple_Release(elprov);
+ ok(Accessible_ref == 1, "Unexpected refcnt %ld\n", Accessible_ref);
+
+ /* ChildID other than CHILDID_SELF. */
+ hr = pUiaProviderFromIAccessible(&Accessible, 1, UIA_PFIA_DEFAULT, &elprov);
+ ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+ ok(Accessible_ref == 2, "Unexpected refcnt %ld\n", Accessible_ref);
+ IRawElementProviderSimple_Release(elprov);
+ ok(Accessible_ref == 1, "Unexpected refcnt %ld\n", Accessible_ref);
+
+ DestroyWindow(hwnd);
+ UnregisterClassA("pUiaProviderFromIAccessible class", NULL);
+ Accessible_hwnd = NULL;
+ OleWindow_hwnd = NULL;
+}
+
START_TEST(uiautomation)
{
+ HMODULE uia_dll = LoadLibraryA("uiautomationcore.dll");
+
test_UiaHostProviderFromHwnd();
test_uia_reserved_value_ifaces();
+ if (uia_dll)
+ {
+ pUiaProviderFromIAccessible = (void *)GetProcAddress(uia_dll, "UiaProviderFromIAccessible");
+ if (pUiaProviderFromIAccessible)
+ test_UiaProviderFromIAccessible();
+ else
+ win_skip("UiaProviderFromIAccessible not exported by uiautomationcore.dll\n");
+
+ FreeLibrary(uia_dll);
+ }
}
--
2.25.1
April 29, 2022
[PATCH 1/2] uiautomationcore: Implement UiaProviderFromIAccessible.
by Connor McAdams
Signed-off-by: Connor McAdams <cmcadams(a)codeweavers.com>
---
dlls/uiautomationcore/Makefile.in | 3 +-
dlls/uiautomationcore/uia_main.c | 1 +
dlls/uiautomationcore/uia_provider.c | 226 ++++++++++++++++++++
dlls/uiautomationcore/uiautomationcore.spec | 2 +-
include/uiautomationcoreapi.h | 4 +
5 files changed, 234 insertions(+), 2 deletions(-)
create mode 100644 dlls/uiautomationcore/uia_provider.c
diff --git a/dlls/uiautomationcore/Makefile.in b/dlls/uiautomationcore/Makefile.in
index f0973fdec4c..bda3614f051 100644
--- a/dlls/uiautomationcore/Makefile.in
+++ b/dlls/uiautomationcore/Makefile.in
@@ -5,4 +5,5 @@ IMPORTS = uuid ole32 oleaut32 user32
EXTRADLLFLAGS = -Wb,--prefer-native
C_SRCS = \
- uia_main.c
+ uia_main.c \
+ uia_provider.c
diff --git a/dlls/uiautomationcore/uia_main.c b/dlls/uiautomationcore/uia_main.c
index a303e71cf76..9f257684333 100644
--- a/dlls/uiautomationcore/uia_main.c
+++ b/dlls/uiautomationcore/uia_main.c
@@ -20,6 +20,7 @@
#include "initguid.h"
#include "uiautomation.h"
+#include "ocidl.h"
#include "wine/debug.h"
#include "wine/heap.h"
diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c
new file mode 100644
index 00000000000..790593dbcab
--- /dev/null
+++ b/dlls/uiautomationcore/uia_provider.c
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2022 Connor McAdams for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#define COBJMACROS
+
+#include "uiautomation.h"
+#include "ocidl.h"
+
+#include "wine/debug.h"
+#include "wine/heap.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(uiautomation);
+
+static void variant_init_i4(VARIANT *v, int val)
+{
+ V_VT(v) = VT_I4;
+ V_I4(v) = val;
+}
+
+/*
+ * UiaProviderFromIAccessible IRawElementProviderSimple interface.
+ */
+struct msaa_provider {
+ IRawElementProviderSimple IRawElementProviderSimple_iface;
+ LONG refcount;
+
+ IAccessible *acc;
+ VARIANT cid;
+ HWND hwnd;
+};
+
+static inline struct msaa_provider *impl_from_msaa_provider(IRawElementProviderSimple *iface)
+{
+ return CONTAINING_RECORD(iface, struct msaa_provider, IRawElementProviderSimple_iface);
+}
+
+HRESULT WINAPI msaa_provider_QueryInterface(IRawElementProviderSimple *iface, REFIID riid, void **ppv)
+{
+ *ppv = NULL;
+ if (IsEqualIID(riid, &IID_IRawElementProviderSimple) || IsEqualIID(riid, &IID_IUnknown))
+ *ppv = iface;
+ else
+ return E_NOINTERFACE;
+
+ IRawElementProviderSimple_AddRef(iface);
+ return S_OK;
+}
+
+ULONG WINAPI msaa_provider_AddRef(IRawElementProviderSimple *iface)
+{
+ struct msaa_provider *msaa_prov = impl_from_msaa_provider(iface);
+ ULONG refcount = InterlockedIncrement(&msaa_prov->refcount);
+
+ TRACE("%p, refcount %ld\n", iface, refcount);
+
+ return refcount;
+}
+
+ULONG WINAPI msaa_provider_Release(IRawElementProviderSimple *iface)
+{
+ struct msaa_provider *msaa_prov = impl_from_msaa_provider(iface);
+ ULONG refcount = InterlockedDecrement(&msaa_prov->refcount);
+
+ TRACE("%p, refcount %ld\n", iface, refcount);
+
+ if (!refcount)
+ {
+ IAccessible_Release(msaa_prov->acc);
+ heap_free(msaa_prov);
+ }
+
+ return refcount;
+}
+
+HRESULT WINAPI msaa_provider_get_ProviderOptions(IRawElementProviderSimple *iface,
+ enum ProviderOptions *ret_val)
+{
+ TRACE("%p, %p\n", iface, ret_val);
+ *ret_val = ProviderOptions_ServerSideProvider | ProviderOptions_UseComThreading;
+ return S_OK;
+}
+
+HRESULT WINAPI msaa_provider_GetPatternProvider(IRawElementProviderSimple *iface,
+ PATTERNID pattern_id, IUnknown **ret_val)
+{
+ FIXME("%p, %d, %p: stub!\n", iface, pattern_id, ret_val);
+ *ret_val = NULL;
+ return E_NOTIMPL;
+}
+
+HRESULT WINAPI msaa_provider_GetPropertyValue(IRawElementProviderSimple *iface,
+ PROPERTYID prop_id, VARIANT *ret_val)
+{
+ TRACE("%p, %d, %p\n", iface, prop_id, ret_val);
+
+ VariantInit(ret_val);
+ switch (prop_id)
+ {
+ case UIA_ProviderDescriptionPropertyId:
+ V_VT(ret_val) = VT_BSTR;
+ V_BSTR(ret_val) = SysAllocString(L"Wine: MSAA Proxy");
+ break;
+
+ default:
+ FIXME("Unimplemented propertyId %d\n", prop_id);
+ break;
+ }
+
+ return S_OK;
+}
+
+HRESULT WINAPI msaa_provider_get_HostRawElementProvider(IRawElementProviderSimple *iface,
+ IRawElementProviderSimple **ret_val)
+{
+ FIXME("%p, %p: stub!\n", iface, ret_val);
+ *ret_val = NULL;
+ return E_NOTIMPL;
+}
+
+static const IRawElementProviderSimpleVtbl msaa_provider_vtbl = {
+ msaa_provider_QueryInterface,
+ msaa_provider_AddRef,
+ msaa_provider_Release,
+ msaa_provider_get_ProviderOptions,
+ msaa_provider_GetPatternProvider,
+ msaa_provider_GetPropertyValue,
+ msaa_provider_get_HostRawElementProvider,
+};
+
+/***********************************************************************
+ * UiaProviderFromIAccessible (uiautomationcore.@)
+ */
+HRESULT WINAPI UiaProviderFromIAccessible(IAccessible *acc, long child_id, DWORD flags,
+ IRawElementProviderSimple **elprov)
+{
+ struct msaa_provider *msaa_prov;
+ IServiceProvider *serv_prov;
+ HWND hwnd = NULL;
+ IOleWindow *win;
+ HRESULT hr;
+
+ TRACE("(%p, %ld, %#lx, %p)\n", acc, child_id, flags, elprov);
+
+ if (elprov)
+ *elprov = NULL;
+
+ if (!elprov)
+ return E_POINTER;
+ if (!acc)
+ return E_INVALIDARG;
+
+ if (flags != UIA_PFIA_DEFAULT)
+ {
+ FIXME("unsupported flags %#lx\n", flags);
+ return E_NOTIMPL;
+ }
+
+ hr = IAccessible_QueryInterface(acc, &IID_IServiceProvider, (void **)&serv_prov);
+ if (SUCCEEDED(hr))
+ {
+ IUnknown *unk;
+
+ hr = IServiceProvider_QueryService(serv_prov, &IIS_IsOleaccProxy, &IID_IUnknown, (void **)&unk);
+ if (SUCCEEDED(hr))
+ {
+ WARN("Cannot wrap an oleacc proxy IAccessible!\n");
+ IUnknown_Release(unk);
+ IServiceProvider_Release(serv_prov);
+ return E_INVALIDARG;
+ }
+
+ IServiceProvider_Release(serv_prov);
+ }
+
+ hr = IAccessible_QueryInterface(acc, &IID_IOleWindow, (void **)&win);
+ if (SUCCEEDED(hr))
+ {
+ hr = IOleWindow_GetWindow(win, &hwnd);
+ if (FAILED(hr))
+ hwnd = NULL;
+ IOleWindow_Release(win);
+ }
+
+ if (!IsWindow(hwnd))
+ {
+ VARIANT v, cid;
+
+ VariantInit(&v);
+ variant_init_i4(&cid, CHILDID_SELF);
+ hr = IAccessible_accNavigate(acc, 10, cid, &v);
+ if (SUCCEEDED(hr) && V_VT(&v) == VT_I4)
+ hwnd = ULongToHandle(V_I4(&v));
+
+ if (!IsWindow(hwnd))
+ return E_FAIL;
+ }
+
+ msaa_prov = heap_alloc(sizeof(*msaa_prov));
+ if (!msaa_prov)
+ return E_OUTOFMEMORY;
+
+ msaa_prov->IRawElementProviderSimple_iface.lpVtbl = &msaa_provider_vtbl;
+ msaa_prov->refcount = 1;
+ msaa_prov->hwnd = hwnd;
+ variant_init_i4(&msaa_prov->cid, child_id);
+ msaa_prov->acc = acc;
+ IAccessible_AddRef(acc);
+ *elprov = &msaa_prov->IRawElementProviderSimple_iface;
+
+ return S_OK;
+}
diff --git a/dlls/uiautomationcore/uiautomationcore.spec b/dlls/uiautomationcore/uiautomationcore.spec
index 82071bd2317..70d78d52085 100644
--- a/dlls/uiautomationcore/uiautomationcore.spec
+++ b/dlls/uiautomationcore/uiautomationcore.spec
@@ -83,7 +83,7 @@
@ stub UiaNodeRelease
@ stub UiaPatternRelease
#@ stub UiaProviderForNonClient
-#@ stub UiaProviderFromIAccessible
+@ stdcall UiaProviderFromIAccessible(ptr long long ptr)
@ stub UiaRaiseAsyncContentLoadedEvent
@ stdcall UiaRaiseAutomationEvent(ptr long)
@ stdcall UiaRaiseAutomationPropertyChangedEvent(ptr long int128 int128)
diff --git a/include/uiautomationcoreapi.h b/include/uiautomationcoreapi.h
index 563d5c602bd..22b3888dc6e 100644
--- a/include/uiautomationcoreapi.h
+++ b/include/uiautomationcoreapi.h
@@ -34,6 +34,9 @@ extern "C" {
#define UiaAppendRuntimeId 3
#define UiaRootObjectId -25
+#define UIA_PFIA_DEFAULT 0x00
+#define UIA_PFIA_UNWRAP_BRIDGE 0x01
+
DECLARE_HANDLE(HUIANODE);
DECLARE_HANDLE(HUIAPATTERNOBJECT);
DECLARE_HANDLE(HUIATEXTRANGE);
@@ -71,6 +74,7 @@ void WINAPI UiaRegisterProviderCallback(UiaProviderCallback *pCallback);
LRESULT WINAPI UiaReturnRawElementProvider(HWND hwnd, WPARAM wParam, LPARAM lParam, IRawElementProviderSimple *elprov);
BOOL WINAPI UiaTextRangeRelease(HUIATEXTRANGE hobj);
HRESULT WINAPI UiaHostProviderFromHwnd(HWND hwnd, IRawElementProviderSimple **elprov);
+HRESULT WINAPI UiaProviderFromIAccessible(IAccessible *acc, long child_id, DWORD flags, IRawElementProviderSimple **elprov);
#ifdef __cplusplus
}
--
2.25.1
April 29, 2022
Re: [PATCH vkd3d 1/8] vkd3d-shader/hlsl: Detect missing loads on rhs when splitting copies of non-numeric types.
by Henri Verbeet
Signed-off-by: Henri Verbeet <hverbeet(a)codeweavers.com>
April 29, 2022
Re: [PATCH vkd3d 3/8] tests: Test initialization of implicit size arrays.
by Henri Verbeet
Signed-off-by: Henri Verbeet <hverbeet(a)codeweavers.com>
April 29, 2022
Re: [PATCH vkd3d 8/8] vkd3d-shader/hlsl: Handle branches in copy propagation.
by Henri Verbeet
Signed-off-by: Henri Verbeet <hverbeet(a)codeweavers.com>
April 29, 2022
Re: [PATCH vkd3d 7/8] vkd3d-shader/hlsl: Allow storing to matrices.
by Henri Verbeet
Signed-off-by: Henri Verbeet <hverbeet(a)codeweavers.com>
April 29, 2022
Re: [PATCH vkd3d 2/8] tests: Test complex broadcasts.
by Henri Verbeet
Signed-off-by: Henri Verbeet <hverbeet(a)codeweavers.com>
April 29, 2022
[PATCH v3 2/2] programs/sc: Allow using separate arguments for parameter name and value.
by Torge Matthies
In addition to using one argument for both parameter name and value.
This fixes a regression from commit 8b38c91d83844dea882922055ced7cdeb79c1693.
Signed-off-by: Torge Matthies <tmatthies(a)codeweavers.com>
---
programs/sc/sc.c | 101 ++++++++++++++++++++++++++++-------------
programs/sc/tests/sc.c | 95 ++++++++++++++++++++------------------
2 files changed, 120 insertions(+), 76 deletions(-)
diff --git a/programs/sc/sc.c b/programs/sc/sc.c
index 43148ab99d60..bd7e093b61d2 100644
--- a/programs/sc/sc.c
+++ b/programs/sc/sc.c
@@ -27,6 +27,26 @@
WINE_DEFAULT_DEBUG_CHANNEL(sc);
+static BOOL parse_string_param( int argc, const WCHAR *argv[], unsigned int *index,
+ const WCHAR *param_name, size_t name_len, const WCHAR **out )
+{
+ if (!wcsnicmp( argv[*index], param_name, name_len ))
+ {
+ if (argv[*index][name_len])
+ {
+ *out = &argv[*index][name_len];
+ return TRUE;
+ }
+ else if (*index < argc - 1)
+ {
+ *index += 1;
+ *out = argv[*index];
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
struct create_params
{
const WCHAR *displayname;
@@ -56,48 +76,56 @@ static BOOL parse_create_params( int argc, const WCHAR *argv[], struct create_pa
cp->obj = NULL;
cp->password = NULL;
+#define PARSE(x, y) parse_string_param( (argc), (argv), &(i), (x), ARRAY_SIZE(x) - 1, (y) )
for (i = 0; i < argc; i++)
{
- if (!wcsnicmp( argv[i], L"displayname=", 12 )) cp->displayname = argv[i] + 12;
- if (!wcsnicmp( argv[i], L"binpath=", 8 )) cp->binpath = argv[i] + 8;
- if (!wcsnicmp( argv[i], L"group=", 6 )) cp->group = argv[i] + 6;
- if (!wcsnicmp( argv[i], L"depend=", 7 )) cp->depend = argv[i] + 7;
- if (!wcsnicmp( argv[i], L"obj=", 4 )) cp->obj = argv[i] + 4;
- if (!wcsnicmp( argv[i], L"password=", 9 )) cp->password = argv[i] + 9;
-
- if (!wcsnicmp( argv[i], L"tag=", 4 ))
+ const WCHAR *tag, *type, *start, *error;
+
+ if (PARSE( L"displayname=", &cp->displayname )) continue;
+ if (PARSE( L"binpath=", &cp->binpath )) continue;
+ if (PARSE( L"group=", &cp->group )) continue;
+ if (PARSE( L"depend=", &cp->depend )) continue;
+ if (PARSE( L"obj=", &cp->obj )) continue;
+ if (PARSE( L"password=", &cp->password )) continue;
+
+ if (PARSE( L"tag=", &tag ))
{
- if (!wcsicmp( argv[i] + 4, L"yes" ))
+ if (!wcsicmp( tag, L"yes" ))
{
WINE_FIXME("tag argument not supported\n");
cp->tag = TRUE;
}
+ continue;
}
- if (!wcsnicmp( argv[i], L"type=", 5 ))
+ if (PARSE( L"type=", &type ))
{
- if (!wcsicmp( argv[i] + 5, L"own" )) cp->type = SERVICE_WIN32_OWN_PROCESS;
- if (!wcsicmp( argv[i] + 5, L"share" )) cp->type = SERVICE_WIN32_SHARE_PROCESS;
- if (!wcsicmp( argv[i] + 5, L"kernel" )) cp->type = SERVICE_KERNEL_DRIVER;
- if (!wcsicmp( argv[i] + 5, L"filesys" )) cp->type = SERVICE_FILE_SYSTEM_DRIVER;
- if (!wcsicmp( argv[i] + 5, L"rec" )) cp->type = SERVICE_RECOGNIZER_DRIVER;
- if (!wcsicmp( argv[i] + 5, L"interact" )) cp->type |= SERVICE_INTERACTIVE_PROCESS;
+ if (!wcsicmp( type, L"own" )) cp->type = SERVICE_WIN32_OWN_PROCESS;
+ else if (!wcsicmp( type, L"share" )) cp->type = SERVICE_WIN32_SHARE_PROCESS;
+ else if (!wcsicmp( type, L"kernel" )) cp->type = SERVICE_KERNEL_DRIVER;
+ else if (!wcsicmp( type, L"filesys" )) cp->type = SERVICE_FILE_SYSTEM_DRIVER;
+ else if (!wcsicmp( type, L"rec" )) cp->type = SERVICE_RECOGNIZER_DRIVER;
+ else if (!wcsicmp( type, L"interact" )) cp->type |= SERVICE_INTERACTIVE_PROCESS;
+ continue;
}
- if (!wcsnicmp( argv[i], L"start=", 6 ))
+ if (PARSE( L"start=", &start ))
{
- if (!wcsicmp( argv[i] + 6, L"boot" )) cp->start = SERVICE_BOOT_START;
- if (!wcsicmp( argv[i] + 6, L"system" )) cp->start = SERVICE_SYSTEM_START;
- if (!wcsicmp( argv[i] + 6, L"auto" )) cp->start = SERVICE_AUTO_START;
- if (!wcsicmp( argv[i] + 6, L"demand" )) cp->start = SERVICE_DEMAND_START;
- if (!wcsicmp( argv[i] + 6, L"disabled" )) cp->start = SERVICE_DISABLED;
+ if (!wcsicmp( start, L"boot" )) cp->start = SERVICE_BOOT_START;
+ else if (!wcsicmp( start, L"system" )) cp->start = SERVICE_SYSTEM_START;
+ else if (!wcsicmp( start, L"auto" )) cp->start = SERVICE_AUTO_START;
+ else if (!wcsicmp( start, L"demand" )) cp->start = SERVICE_DEMAND_START;
+ else if (!wcsicmp( start, L"disabled" )) cp->start = SERVICE_DISABLED;
+ continue;
}
- if (!wcsnicmp( argv[i], L"error=", 6 ))
+ if (PARSE( L"error=", &error ))
{
- if (!wcsicmp( argv[i] + 6, L"normal" )) cp->error = SERVICE_ERROR_NORMAL;
- if (!wcsicmp( argv[i] + 6, L"severe" )) cp->error = SERVICE_ERROR_SEVERE;
- if (!wcsicmp( argv[i] + 6, L"critical" )) cp->error = SERVICE_ERROR_CRITICAL;
- if (!wcsicmp( argv[i] + 6, L"ignore" )) cp->error = SERVICE_ERROR_IGNORE;
+ if (!wcsicmp( error, L"normal" )) cp->error = SERVICE_ERROR_NORMAL;
+ else if (!wcsicmp( error, L"severe" )) cp->error = SERVICE_ERROR_SEVERE;
+ else if (!wcsicmp( error, L"critical" )) cp->error = SERVICE_ERROR_CRITICAL;
+ else if (!wcsicmp( error, L"ignore" )) cp->error = SERVICE_ERROR_IGNORE;
+ continue;
}
}
+#undef PARSE
if (!cp->binpath) return FALSE;
return TRUE;
}
@@ -156,16 +184,25 @@ static BOOL parse_failure_params( int argc, const WCHAR *argv[], SERVICE_FAILURE
fa->cActions = 0;
fa->lpsaActions = NULL;
+#define PARSE(x, y) parse_string_param( (argc), (argv), &(i), (x), ARRAY_SIZE(x) - 1, (y) )
for (i = 0; i < argc; i++)
{
- if (!wcsnicmp( argv[i], L"reset=", 6 )) fa->dwResetPeriod = wcstol( argv[i] + 6, NULL, 10 );
- if (!wcsnicmp( argv[i], L"reboot=", 7 )) fa->lpRebootMsg = (WCHAR *)argv[i] + 7;
- if (!wcsnicmp( argv[i], L"command=", 8 )) fa->lpCommand = (WCHAR *)argv[i] + 8;
- if (!wcsnicmp( argv[i], L"actions=", 8 ))
+ const WCHAR *reset, *actions;
+
+ if (PARSE( L"reset=", &reset ))
+ {
+ fa->dwResetPeriod = wcstol( reset, NULL, 10 );
+ continue;
+ }
+ if (PARSE( L"reboot=", (const WCHAR **)&fa->lpRebootMsg )) continue;
+ if (PARSE( L"command=", (const WCHAR **)&fa->lpCommand )) continue;
+ if (PARSE( L"actions=", &actions ))
{
- if (!parse_failure_actions( argv[i] + 8, fa )) return FALSE;
+ if (!parse_failure_actions( actions, fa )) return FALSE;
+ continue;
}
}
+#undef PARSE
return TRUE;
}
diff --git a/programs/sc/tests/sc.c b/programs/sc/tests/sc.c
index 66493ed578c7..57046905a4b0 100644
--- a/programs/sc/tests/sc.c
+++ b/programs/sc/tests/sc.c
@@ -168,13 +168,20 @@ static void test_create_service(BOOL elevated)
DWORD expected_start_type;
DWORD expected_service_type;
const char * expected_binary_path;
+ DWORD broken;
} start_types[] = {
- { "boot type= kernel", SERVICE_BOOT_START, SERVICE_KERNEL_DRIVER, TEST_SERVICE_BINARY_START_BOOT },
- { "system type= kernel", SERVICE_SYSTEM_START, SERVICE_KERNEL_DRIVER, TEST_SERVICE_BINARY_START_SYSTEM },
- { "auto", SERVICE_AUTO_START, SERVICE_WIN32_OWN_PROCESS, TEST_SERVICE_BINARY },
- { "demand", SERVICE_DEMAND_START, SERVICE_WIN32_OWN_PROCESS, TEST_SERVICE_BINARY },
- { "disabled", SERVICE_DISABLED, SERVICE_WIN32_OWN_PROCESS, TEST_SERVICE_BINARY },
- { "delayed-auto", SERVICE_DELAYED_AUTO_START, SERVICE_WIN32_OWN_PROCESS, TEST_SERVICE_BINARY }
+ { "boot type= kernel", SERVICE_BOOT_START, SERVICE_KERNEL_DRIVER, TEST_SERVICE_BINARY_START_BOOT,
+ BROKEN_BINPATH | BROKEN_DISPLAY_NAME },
+ { "system type= kernel", SERVICE_SYSTEM_START, SERVICE_KERNEL_DRIVER, TEST_SERVICE_BINARY_START_SYSTEM,
+ BROKEN_BINPATH | BROKEN_DISPLAY_NAME },
+ { "auto", SERVICE_AUTO_START, SERVICE_WIN32_OWN_PROCESS, TEST_SERVICE_BINARY,
+ BROKEN_DISPLAY_NAME },
+ { "demand", SERVICE_DEMAND_START, SERVICE_WIN32_OWN_PROCESS, TEST_SERVICE_BINARY,
+ BROKEN_DISPLAY_NAME },
+ { "disabled", SERVICE_DISABLED, SERVICE_WIN32_OWN_PROCESS, TEST_SERVICE_BINARY,
+ BROKEN_DISPLAY_NAME },
+ { "delayed-auto", SERVICE_DELAYED_AUTO_START, SERVICE_WIN32_OWN_PROCESS, TEST_SERVICE_BINARY,
+ BROKEN_START | BROKEN_DISPLAY_NAME | BROKEN_DELAYED_AUTO_START }
};
static struct {
const char *param;
@@ -217,45 +224,45 @@ static void test_create_service(BOOL elevated)
/* binpath= */
run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= \"" TEST_SERVICE_BINARY "\"", &r);
- todo_wine check_exit_code(SC_EXIT_SUCCESS);
+ check_exit_code(SC_EXIT_SUCCESS);
check_test_service(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, "", NULL,
- BROKEN_CREATE);
+ BROKEN_DISPLAY_NAME);
/* existing service */
run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= \"" TEST_SERVICE_BINARY "\" start= auto", &r);
todo_wine check_exit_code(SC_EXIT_SERVICE_EXISTS);
check_test_service(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, "", NULL,
- BROKEN_CREATE);
- delete_test_service(TRUE, TRUE);
+ BROKEN_DISPLAY_NAME);
+ delete_test_service(TRUE, FALSE);
/* type= */
run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= \"" TEST_SERVICE_BINARY "\" type= invalid", &r);
todo_wine check_exit_code(SC_EXIT_INVALID_COMMAND_LINE);
- delete_test_service(FALSE, FALSE);
+ delete_test_service(FALSE, TRUE);
run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= \"" TEST_SERVICE_BINARY "\" type= own", &r);
- todo_wine check_exit_code(SC_EXIT_SUCCESS);
+ check_exit_code(SC_EXIT_SUCCESS);
check_test_service(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, "", NULL,
- BROKEN_CREATE);
- delete_test_service(TRUE, TRUE);
+ BROKEN_DISPLAY_NAME);
+ delete_test_service(TRUE, FALSE);
run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= \"" TEST_SERVICE_BINARY "\" type= interact", &r);
todo_wine check_exit_code(SC_EXIT_INVALID_PARAMETER);
- delete_test_service(FALSE, FALSE);
+ delete_test_service(FALSE, TRUE);
run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= \"" TEST_SERVICE_BINARY "\" type= interact type= own", &r);
- todo_wine check_exit_code(SC_EXIT_SUCCESS);
+ check_exit_code(SC_EXIT_SUCCESS);
check_test_service(SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS, SERVICE_DEMAND_START,
- SERVICE_ERROR_NORMAL, "", NULL, BROKEN_CREATE);
- delete_test_service(TRUE, TRUE);
+ SERVICE_ERROR_NORMAL, "", NULL, BROKEN_TYPE | BROKEN_DISPLAY_NAME);
+ delete_test_service(TRUE, FALSE);
/* start= */
run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= \"" TEST_SERVICE_BINARY "\" start= invalid", &r);
todo_wine check_exit_code(SC_EXIT_INVALID_COMMAND_LINE);
- delete_test_service(FALSE, FALSE);
+ delete_test_service(FALSE, TRUE);
for (i = 0; i < ARRAY_SIZE(start_types); i++)
{
@@ -264,11 +271,11 @@ static void test_create_service(BOOL elevated)
strcpy(cmdline, "sc create " TEST_SERVICE_NAME " binpath= \"" TEST_SERVICE_BINARY "\" start= ");
strcat(cmdline, start_types[i].param);
run_sc_exe(cmdline, &r);
- todo_wine check_exit_code(SC_EXIT_SUCCESS);
+ check_exit_code(SC_EXIT_SUCCESS);
check_service_definition(TEST_SERVICE_NAME, start_types[i].expected_binary_path,
start_types[i].expected_service_type, start_types[i].expected_start_type,
- SERVICE_ERROR_NORMAL, "", TEST_SERVICE_NAME, BROKEN_CREATE);
- delete_test_service(TRUE, TRUE);
+ SERVICE_ERROR_NORMAL, "", TEST_SERVICE_NAME, start_types[i].broken);
+ delete_test_service(TRUE, FALSE);
}
/* error= */
@@ -280,53 +287,53 @@ static void test_create_service(BOOL elevated)
strcpy(cmdline, "sc create " TEST_SERVICE_NAME " binpath= \"" TEST_SERVICE_BINARY "\" error= ");
strcat(cmdline, error_severities[i].param);
run_sc_exe(cmdline, &r);
- todo_wine check_exit_code(SC_EXIT_SUCCESS);
+ check_exit_code(SC_EXIT_SUCCESS);
check_test_service(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START,
- error_severities[i].expected_error_control, "", NULL, BROKEN_CREATE);
- delete_test_service(TRUE, TRUE);
+ error_severities[i].expected_error_control, "", NULL, BROKEN_DISPLAY_NAME);
+ delete_test_service(TRUE, FALSE);
}
/* tag= */
run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= \"" TEST_SERVICE_BINARY "\" tag= yes", &r);
todo_wine check_exit_code(SC_EXIT_INVALID_PARAMETER);
- delete_test_service(FALSE, FALSE);
+ delete_test_service(FALSE, TRUE);
run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= \"" TEST_SERVICE_BINARY "\" tag= no", &r);
- todo_wine check_exit_code(SC_EXIT_SUCCESS);
+ check_exit_code(SC_EXIT_SUCCESS);
check_test_service(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, "", NULL,
- BROKEN_CREATE);
- delete_test_service(TRUE, TRUE);
+ BROKEN_DISPLAY_NAME);
+ delete_test_service(TRUE, FALSE);
/* depend= */
run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= \"" TEST_SERVICE_BINARY "\" depend= " TEST_SERVICE_NAME, &r);
todo_wine check_exit_code(SC_EXIT_CIRCULAR_DEPENDENCY);
- delete_test_service(FALSE, FALSE);
+ delete_test_service(FALSE, TRUE);
run_sc_exe("sc create " TEST_SERVICE_NAME2 " binpath= \"" TEST_SERVICE_BINARY "\" depend= " TEST_SERVICE_NAME, &r);
- todo_wine check_exit_code(SC_EXIT_SUCCESS);
+ check_exit_code(SC_EXIT_SUCCESS);
check_test_service2(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
- TEST_SERVICE_NAME, NULL, BROKEN_CREATE);
- delete_test_service2(TRUE, TRUE);
+ TEST_SERVICE_NAME, NULL, BROKEN_DEPEND | BROKEN_DISPLAY_NAME);
+ delete_test_service2(TRUE, FALSE);
run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= " TEST_SERVICE_BINARY, &r);
- todo_wine check_exit_code(SC_EXIT_SUCCESS);
+ check_exit_code(SC_EXIT_SUCCESS);
run_sc_exe("sc create " TEST_SERVICE_NAME2 " binpath= \"" TEST_SERVICE_BINARY "\" depend= " TEST_SERVICE_NAME, &r);
- todo_wine check_exit_code(SC_EXIT_SUCCESS);
+ check_exit_code(SC_EXIT_SUCCESS);
check_test_service2(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
- TEST_SERVICE_NAME, NULL, BROKEN_CREATE);
- delete_test_service2(TRUE, TRUE);
- delete_test_service(TRUE, TRUE);
+ TEST_SERVICE_NAME, NULL, BROKEN_DEPEND | BROKEN_DISPLAY_NAME);
+ delete_test_service2(TRUE, FALSE);
+ delete_test_service(TRUE, FALSE);
/* displayname= */
run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= " TEST_SERVICE_BINARY
" displayname= \"Wine Test Service\"", &r);
- todo_wine check_exit_code(SC_EXIT_SUCCESS);
+ check_exit_code(SC_EXIT_SUCCESS);
check_test_service(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, "",
- "Wine Test Service", BROKEN_CREATE);
- delete_test_service(TRUE, TRUE);
+ "Wine Test Service", 0);
+ delete_test_service(TRUE, FALSE);
/* without spaces */
@@ -349,10 +356,10 @@ static void test_create_service(BOOL elevated)
run_sc_exe("SC CREATE " TEST_SERVICE_NAME2 " BINPATH= \"" TEST_SERVICE_BINARY "\" TYPE= OWN START= AUTO"
" ERROR= NORMAL TAG= NO DEPEND= " TEST_SERVICE_NAME " DISPLAYNAME= \"Wine Test Service\"", &r);
- todo_wine check_exit_code(SC_EXIT_SUCCESS);
+ check_exit_code(SC_EXIT_SUCCESS);
check_test_service2(SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
- TEST_SERVICE_NAME, "Wine Test Service", BROKEN_CREATE);
- delete_test_service2(TRUE, TRUE);
+ TEST_SERVICE_NAME, "Wine Test Service", BROKEN_DEPEND);
+ delete_test_service2(TRUE, FALSE);
#undef delete_test_service2
#undef check_test_service2
--
2.36.0
April 29, 2022
[PATCH v3 1/2] programs/sc: Add tests.
by Torge Matthies
Signed-off-by: Torge Matthies <tmatthies(a)codeweavers.com>
---
configure.ac | 1 +
programs/sc/tests/Makefile.in | 5 +
programs/sc/tests/sc.c | 402 ++++++++++++++++++++++++++++++++++
3 files changed, 408 insertions(+)
create mode 100644 programs/sc/tests/Makefile.in
create mode 100644 programs/sc/tests/sc.c
diff --git a/configure.ac b/configure.ac
index 74c80fd7fa8c..98f56fc849fa 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3322,6 +3322,7 @@ WINE_CONFIG_MAKEFILE(programs/rpcss)
WINE_CONFIG_MAKEFILE(programs/rundll.exe16,enable_win16)
WINE_CONFIG_MAKEFILE(programs/rundll32)
WINE_CONFIG_MAKEFILE(programs/sc)
+WINE_CONFIG_MAKEFILE(programs/sc/tests)
WINE_CONFIG_MAKEFILE(programs/schtasks)
WINE_CONFIG_MAKEFILE(programs/schtasks/tests)
WINE_CONFIG_MAKEFILE(programs/sdbinst)
diff --git a/programs/sc/tests/Makefile.in b/programs/sc/tests/Makefile.in
new file mode 100644
index 000000000000..24a875fb9051
--- /dev/null
+++ b/programs/sc/tests/Makefile.in
@@ -0,0 +1,5 @@
+TESTDLL = sc.exe
+IMPORTS = advapi32
+
+C_SRCS = \
+ sc.c
diff --git a/programs/sc/tests/sc.c b/programs/sc/tests/sc.c
new file mode 100644
index 000000000000..66493ed578c7
--- /dev/null
+++ b/programs/sc/tests/sc.c
@@ -0,0 +1,402 @@
+/*
+ * Copyright 2022 Torge Matthies for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <windows.h>
+#include <winsvc.h>
+#include <stdio.h>
+#include "wine/test.h"
+
+#define lok ok_(__FILE__,line)
+
+#define TEST_SERVICE_NAME "wine_test_svc"
+#define TEST_SERVICE_NAME2 "wine_test_svc_2"
+#define TEST_SERVICE_BINARY "c:\\windows\\system32\\cmd.exe"
+#define TEST_SERVICE_BINARY_START_BOOT "\\SystemRoot\\system32\\cmd.exe"
+#define TEST_SERVICE_BINARY_START_SYSTEM "\\??\\" TEST_SERVICE_BINARY
+
+#define SC_EXIT_SUCCESS ERROR_SUCCESS
+#define SC_EXIT_INVALID_PARAMETER ERROR_INVALID_PARAMETER
+#define SC_EXIT_CIRCULAR_DEPENDENCY ERROR_CIRCULAR_DEPENDENCY
+#define SC_EXIT_SERVICE_DOES_NOT_EXIST ERROR_SERVICE_DOES_NOT_EXIST
+#define SC_EXIT_SERVICE_EXISTS ERROR_SERVICE_EXISTS
+#define SC_EXIT_INVALID_COMMAND_LINE ERROR_INVALID_COMMAND_LINE
+
+static HANDLE nul_file;
+static SC_HANDLE scmgr;
+
+/* Copied and modified from the reg.exe tests */
+#define run_sc_exe(c,r) run_sc_exe_(__FILE__,__LINE__,c,r)
+static BOOL run_sc_exe_(const char *file, unsigned line, const char *cmd, DWORD *rc)
+{
+ STARTUPINFOA si = {sizeof(STARTUPINFOA)};
+ PROCESS_INFORMATION pi;
+ BOOL bret;
+ DWORD ret;
+ char cmdline[256];
+
+ si.dwFlags = STARTF_USESTDHANDLES;
+ si.hStdInput = nul_file;
+ si.hStdOutput = nul_file;
+ si.hStdError = nul_file;
+
+ strcpy(cmdline, cmd);
+ if (!CreateProcessA(NULL, cmdline, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
+ return FALSE;
+
+ ret = WaitForSingleObject(pi.hProcess, 10000);
+ if (ret == WAIT_TIMEOUT)
+ TerminateProcess(pi.hProcess, 1);
+
+ bret = GetExitCodeProcess(pi.hProcess, rc);
+ lok(bret, "GetExitCodeProcess failed: %ld\n", GetLastError());
+
+ CloseHandle(pi.hThread);
+ CloseHandle(pi.hProcess);
+ return bret;
+}
+
+#define BROKEN_CREATE 0x000000001UL
+#define BROKEN_BINPATH 0x000000002UL
+#define BROKEN_TYPE 0x000000004UL
+#define BROKEN_START 0x000000008UL
+#define BROKEN_ERROR 0x000000010UL
+#define BROKEN_DEPEND 0x000000020UL
+#define BROKEN_DISPLAY_NAME 0x000000040UL
+#define BROKEN_DELAYED_AUTO_START 0x000000080UL
+#define BROKEN_ALL ~0UL
+
+#define SERVICE_DELAYED_AUTO_START (SERVICE_AUTO_START | 0x80000000)
+
+#define check_service_definition(n,bi,t,s,e,de,di,br) check_service_definition_(__FILE__,__LINE__,n,bi,t,s,e,de,di,br)
+static void check_service_definition_(const char *file, unsigned line, char const *name,
+ const char *binpath, DWORD type, DWORD start, DWORD error,
+ const char *depend, const char *display_name, DWORD broken)
+{
+ SERVICE_DELAYED_AUTO_START_INFO delayed_auto_info = {0};
+ union {
+ char buffer[8192];
+ QUERY_SERVICE_CONFIGA config;
+ } cfg;
+ BOOL delayed_auto;
+ SC_HANDLE svc;
+ DWORD needed;
+ BOOL ret;
+
+ delayed_auto = !!(start & 0x80000000);
+ start &= ~0x80000000;
+
+ if (!scmgr)
+ return;
+
+ svc = OpenServiceA(scmgr, name, GENERIC_READ);
+ todo_wine_if(broken & BROKEN_CREATE)
+ lok(!!svc, "OpenServiceA failed: %ld\n", GetLastError());
+ if (!svc)
+ return;
+
+ ret = QueryServiceConfigA(svc, &cfg.config, sizeof(cfg.buffer), &needed);
+ lok(!!ret, "QueryServiceConfigA failed: %ld\n", GetLastError());
+ if (!ret)
+ goto done;
+
+ ret = QueryServiceConfig2A(svc, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, (LPBYTE)&delayed_auto_info,
+ sizeof(delayed_auto_info), &needed);
+ todo_wine lok(!!ret, "QueryServiceConfig2A(SERVICE_CONFIG_DELAYED_AUTO_START_INFO) failed: %ld\n",
+ GetLastError());
+
+#define check_str(a, b, msg) lok((a) && (b) && (a) != (b) && !strcmp((a), (b)), msg ": %s != %s\n", \
+ debugstr_a((a)), debugstr_a((b)))
+#define check_dw(a, b, msg) lok((a) == (b), msg ": 0x%lx != 0x%lx\n", a, b)
+
+ todo_wine_if(broken & BROKEN_BINPATH)
+ check_str(cfg.config.lpBinaryPathName, binpath, "Wrong binary path");
+ todo_wine_if(broken & BROKEN_TYPE)
+ check_dw(cfg.config.dwServiceType, type, "Wrong service type");
+ todo_wine_if(broken & BROKEN_START)
+ check_dw(cfg.config.dwStartType, start, "Wrong start type");
+ todo_wine_if(broken & BROKEN_ERROR)
+ check_dw(cfg.config.dwErrorControl, error, "Wrong error control");
+ todo_wine_if(broken & BROKEN_DEPEND)
+ check_str(cfg.config.lpDependencies, depend, "Wrong dependencies");
+ todo_wine_if(broken & BROKEN_DISPLAY_NAME)
+ check_str(cfg.config.lpDisplayName, display_name, "Wrong display name");
+ todo_wine_if(broken & BROKEN_DELAYED_AUTO_START)
+ check_dw((DWORD)delayed_auto_info.fDelayedAutostart, (DWORD)delayed_auto, "Wrong delayed autostart value");
+
+#undef check_dw
+#undef check_str
+
+done:
+ CloseServiceHandle(svc);
+}
+
+#define delete_service(n,e,b) delete_service_(__FILE__,__LINE__,n,e,b)
+static void delete_service_(const char *file, unsigned line, const char *name, DWORD expected_status, BOOL broken)
+{
+ char command[256];
+ BOOL bret;
+ DWORD r;
+
+ strcpy(command, "sc delete ");
+ strcat(command, name);
+ bret = run_sc_exe_(file, line, command, &r);
+ lok(bret, "run_sc_exe failed\n");
+ if (expected_status != SC_EXIT_SUCCESS && !strcmp(winetest_platform, "wine"))
+ expected_status = 1;
+ todo_wine_if(broken) lok(r == expected_status, "got exit code %ld, expected %ld\n", r, expected_status);
+}
+
+static void test_create_service(BOOL elevated)
+{
+ static struct {
+ const char *param;
+ DWORD expected_start_type;
+ DWORD expected_service_type;
+ const char * expected_binary_path;
+ } start_types[] = {
+ { "boot type= kernel", SERVICE_BOOT_START, SERVICE_KERNEL_DRIVER, TEST_SERVICE_BINARY_START_BOOT },
+ { "system type= kernel", SERVICE_SYSTEM_START, SERVICE_KERNEL_DRIVER, TEST_SERVICE_BINARY_START_SYSTEM },
+ { "auto", SERVICE_AUTO_START, SERVICE_WIN32_OWN_PROCESS, TEST_SERVICE_BINARY },
+ { "demand", SERVICE_DEMAND_START, SERVICE_WIN32_OWN_PROCESS, TEST_SERVICE_BINARY },
+ { "disabled", SERVICE_DISABLED, SERVICE_WIN32_OWN_PROCESS, TEST_SERVICE_BINARY },
+ { "delayed-auto", SERVICE_DELAYED_AUTO_START, SERVICE_WIN32_OWN_PROCESS, TEST_SERVICE_BINARY }
+ };
+ static struct {
+ const char *param;
+ DWORD expected_error_control;
+ } error_severities[] = {
+ { "normal", SERVICE_ERROR_NORMAL },
+ { "severe", SERVICE_ERROR_SEVERE },
+ { "critical", SERVICE_ERROR_CRITICAL },
+ { "ignore", SERVICE_ERROR_IGNORE }
+ };
+ unsigned int i;
+ DWORD r;
+
+ if (!elevated)
+ {
+ win_skip("\"sc create\" tests need elevated permissions\n");
+ return;
+ }
+
+#define check_exit_code(x) ok(r == (x), "got exit code %ld, expected %d\n", r, (x))
+#define check_test_service(t,s,e,de,di,br) \
+ check_service_definition(TEST_SERVICE_NAME, TEST_SERVICE_BINARY, t, s, e, de, di ? di : TEST_SERVICE_NAME, br)
+#define delete_test_service(x, y) \
+ delete_service(TEST_SERVICE_NAME, (x) ? SC_EXIT_SUCCESS : SC_EXIT_SERVICE_DOES_NOT_EXIST, (y))
+#define check_test_service2(t,s,e,de,di,br) \
+ check_service_definition(TEST_SERVICE_NAME2, TEST_SERVICE_BINARY, t, s, e, de, di ? di : TEST_SERVICE_NAME2, br)
+#define delete_test_service2(x, y) \
+ delete_service(TEST_SERVICE_NAME2, (x) ? SC_EXIT_SUCCESS : SC_EXIT_SERVICE_DOES_NOT_EXIST, (y))
+
+ /* too few parameters */
+
+ run_sc_exe("sc create", &r);
+ todo_wine check_exit_code(SC_EXIT_INVALID_COMMAND_LINE);
+ delete_test_service(FALSE, FALSE);
+
+ run_sc_exe("sc create " TEST_SERVICE_NAME, &r);
+ todo_wine check_exit_code(SC_EXIT_INVALID_COMMAND_LINE);
+ delete_test_service(FALSE, FALSE);
+
+ /* binpath= */
+
+ run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= \"" TEST_SERVICE_BINARY "\"", &r);
+ todo_wine check_exit_code(SC_EXIT_SUCCESS);
+ check_test_service(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, "", NULL,
+ BROKEN_CREATE);
+
+ /* existing service */
+
+ run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= \"" TEST_SERVICE_BINARY "\" start= auto", &r);
+ todo_wine check_exit_code(SC_EXIT_SERVICE_EXISTS);
+ check_test_service(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, "", NULL,
+ BROKEN_CREATE);
+ delete_test_service(TRUE, TRUE);
+
+ /* type= */
+
+ run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= \"" TEST_SERVICE_BINARY "\" type= invalid", &r);
+ todo_wine check_exit_code(SC_EXIT_INVALID_COMMAND_LINE);
+ delete_test_service(FALSE, FALSE);
+
+ run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= \"" TEST_SERVICE_BINARY "\" type= own", &r);
+ todo_wine check_exit_code(SC_EXIT_SUCCESS);
+ check_test_service(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, "", NULL,
+ BROKEN_CREATE);
+ delete_test_service(TRUE, TRUE);
+
+ run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= \"" TEST_SERVICE_BINARY "\" type= interact", &r);
+ todo_wine check_exit_code(SC_EXIT_INVALID_PARAMETER);
+ delete_test_service(FALSE, FALSE);
+
+ run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= \"" TEST_SERVICE_BINARY "\" type= interact type= own", &r);
+ todo_wine check_exit_code(SC_EXIT_SUCCESS);
+ check_test_service(SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS, SERVICE_DEMAND_START,
+ SERVICE_ERROR_NORMAL, "", NULL, BROKEN_CREATE);
+ delete_test_service(TRUE, TRUE);
+
+ /* start= */
+
+ run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= \"" TEST_SERVICE_BINARY "\" start= invalid", &r);
+ todo_wine check_exit_code(SC_EXIT_INVALID_COMMAND_LINE);
+ delete_test_service(FALSE, FALSE);
+
+ for (i = 0; i < ARRAY_SIZE(start_types); i++)
+ {
+ char cmdline[256];
+
+ strcpy(cmdline, "sc create " TEST_SERVICE_NAME " binpath= \"" TEST_SERVICE_BINARY "\" start= ");
+ strcat(cmdline, start_types[i].param);
+ run_sc_exe(cmdline, &r);
+ todo_wine check_exit_code(SC_EXIT_SUCCESS);
+ check_service_definition(TEST_SERVICE_NAME, start_types[i].expected_binary_path,
+ start_types[i].expected_service_type, start_types[i].expected_start_type,
+ SERVICE_ERROR_NORMAL, "", TEST_SERVICE_NAME, BROKEN_CREATE);
+ delete_test_service(TRUE, TRUE);
+ }
+
+ /* error= */
+
+ for (i = 0; i < ARRAY_SIZE(error_severities); i++)
+ {
+ char cmdline[256];
+
+ strcpy(cmdline, "sc create " TEST_SERVICE_NAME " binpath= \"" TEST_SERVICE_BINARY "\" error= ");
+ strcat(cmdline, error_severities[i].param);
+ run_sc_exe(cmdline, &r);
+ todo_wine check_exit_code(SC_EXIT_SUCCESS);
+ check_test_service(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START,
+ error_severities[i].expected_error_control, "", NULL, BROKEN_CREATE);
+ delete_test_service(TRUE, TRUE);
+ }
+
+ /* tag= */
+
+ run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= \"" TEST_SERVICE_BINARY "\" tag= yes", &r);
+ todo_wine check_exit_code(SC_EXIT_INVALID_PARAMETER);
+ delete_test_service(FALSE, FALSE);
+
+ run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= \"" TEST_SERVICE_BINARY "\" tag= no", &r);
+ todo_wine check_exit_code(SC_EXIT_SUCCESS);
+ check_test_service(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, "", NULL,
+ BROKEN_CREATE);
+ delete_test_service(TRUE, TRUE);
+
+ /* depend= */
+
+ run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= \"" TEST_SERVICE_BINARY "\" depend= " TEST_SERVICE_NAME, &r);
+ todo_wine check_exit_code(SC_EXIT_CIRCULAR_DEPENDENCY);
+ delete_test_service(FALSE, FALSE);
+
+ run_sc_exe("sc create " TEST_SERVICE_NAME2 " binpath= \"" TEST_SERVICE_BINARY "\" depend= " TEST_SERVICE_NAME, &r);
+ todo_wine check_exit_code(SC_EXIT_SUCCESS);
+ check_test_service2(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
+ TEST_SERVICE_NAME, NULL, BROKEN_CREATE);
+ delete_test_service2(TRUE, TRUE);
+
+ run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= " TEST_SERVICE_BINARY, &r);
+ todo_wine check_exit_code(SC_EXIT_SUCCESS);
+ run_sc_exe("sc create " TEST_SERVICE_NAME2 " binpath= \"" TEST_SERVICE_BINARY "\" depend= " TEST_SERVICE_NAME, &r);
+ todo_wine check_exit_code(SC_EXIT_SUCCESS);
+ check_test_service2(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
+ TEST_SERVICE_NAME, NULL, BROKEN_CREATE);
+ delete_test_service2(TRUE, TRUE);
+ delete_test_service(TRUE, TRUE);
+
+ /* displayname= */
+
+ run_sc_exe("sc create " TEST_SERVICE_NAME " binpath= " TEST_SERVICE_BINARY
+ " displayname= \"Wine Test Service\"", &r);
+ todo_wine check_exit_code(SC_EXIT_SUCCESS);
+ check_test_service(SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, "",
+ "Wine Test Service", BROKEN_CREATE);
+ delete_test_service(TRUE, TRUE);
+
+ /* without spaces */
+
+ run_sc_exe("sc create " TEST_SERVICE_NAME2 " binpath=\"" TEST_SERVICE_BINARY "\" type=own start=auto"
+ " error=normal tag=no depend=" TEST_SERVICE_NAME " displayname=\"Wine Test Service\"", &r);
+ ok(r == SC_EXIT_SUCCESS || broken(r == SC_EXIT_INVALID_COMMAND_LINE), "got exit code %ld, expected %d\n",
+ r, SC_EXIT_SUCCESS);
+ if (r == SC_EXIT_SUCCESS)
+ {
+ check_test_service2(SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
+ TEST_SERVICE_NAME, "Wine Test Service", BROKEN_DEPEND);
+ delete_test_service2(TRUE, FALSE);
+ }
+ else
+ {
+ delete_test_service2(FALSE, FALSE);
+ }
+
+ /* case-insensitive */
+
+ run_sc_exe("SC CREATE " TEST_SERVICE_NAME2 " BINPATH= \"" TEST_SERVICE_BINARY "\" TYPE= OWN START= AUTO"
+ " ERROR= NORMAL TAG= NO DEPEND= " TEST_SERVICE_NAME " DISPLAYNAME= \"Wine Test Service\"", &r);
+ todo_wine check_exit_code(SC_EXIT_SUCCESS);
+ check_test_service2(SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
+ TEST_SERVICE_NAME, "Wine Test Service", BROKEN_CREATE);
+ delete_test_service2(TRUE, TRUE);
+
+#undef delete_test_service2
+#undef check_test_service2
+#undef delete_test_service
+#undef check_test_service
+#undef check_exit_code
+}
+
+/* taken from winetest, only whitespace changes */
+static int running_elevated(void)
+{
+ HANDLE token;
+ TOKEN_ELEVATION elevation_info;
+ DWORD size;
+
+ /* Get the process token */
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
+ return -1;
+
+ /* Get the elevation info from the token */
+ if (!GetTokenInformation(token, TokenElevation, &elevation_info, sizeof(TOKEN_ELEVATION), &size))
+ {
+ CloseHandle(token);
+ return -1;
+ }
+ CloseHandle(token);
+
+ return elevation_info.TokenIsElevated;
+}
+
+
+START_TEST(sc)
+{
+ SECURITY_ATTRIBUTES secattr = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
+ BOOL elevated = running_elevated();
+
+ nul_file = CreateFileA("NUL", GENERIC_READ | GENERIC_WRITE, 0, &secattr, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+
+ scmgr = OpenSCManagerA(NULL, NULL, GENERIC_READ);
+ ok(!!scmgr, "OpenSCManagerA failed: %ld\n", GetLastError());
+
+ test_create_service(elevated);
+
+ CloseServiceHandle(scmgr);
+ CloseHandle(nul_file);
+}
--
2.36.0
April 29, 2022
[PATCH vkd3d v2 4/4] vkd3d: Map timeline semaphore values to fence virtual values and submit commands in worker threads.
by Conor McCarthy
Correct fence behaviour requires:
Map monotonically increasing timeline values to fence virtual values to
avoid invalid use of Vulkan timeline semaphores. In particular, non-
increasing values and value jumps of >= 4G are required for d3d12.
Create a worker thread for each queue to handle queue commands. This
allows blocking of wait submission until an unblocking signal is
submitted, so out-of-order waits are handled correctly when two d3d12
queues are mapped to the same Vulkan queue.
Threaded queue submission also fixes the old fence implementation so it is
fully functional, though a bit less efficient than timeline semaphores.
Based in part on vkd3d-proton patches by Hans-Kristian Arntzen.
Signed-off-by: Conor McCarthy <cmccarthy(a)codeweavers.com>
---
v2: Always broadcast on the null event condition after signaling.
Threaded command queues on their own seem to bring out the problems with the
existing fence implementation, which results in games crashing. Combining them
with the fence changes avoids that issue.
---
libs/vkd3d/command.c | 895 +++++++++++++++++++++----------------
libs/vkd3d/device.c | 79 ----
libs/vkd3d/vkd3d_private.h | 81 +++-
tests/d3d12.c | 4 +-
4 files changed, 565 insertions(+), 494 deletions(-)
diff --git a/libs/vkd3d/command.c b/libs/vkd3d/command.c
index 55e6be58..4efe391c 100644
--- a/libs/vkd3d/command.c
+++ b/libs/vkd3d/command.c
@@ -23,6 +23,7 @@
static void d3d12_fence_incref(struct d3d12_fence *fence);
static void d3d12_fence_decref(struct d3d12_fence *fence);
static HRESULT d3d12_fence_signal(struct d3d12_fence *fence, uint64_t value, VkFence vk_fence);
+static void d3d12_fence_signal_timeline_semaphore(struct d3d12_fence *fence, uint64_t timeline_value);
HRESULT vkd3d_queue_create(struct d3d12_device *device,
uint32_t family_index, const VkQueueFamilyProperties *properties, struct vkd3d_queue **queue)
@@ -48,9 +49,6 @@ HRESULT vkd3d_queue_create(struct d3d12_device *device,
object->vk_queue_flags = properties->queueFlags;
object->timestamp_bits = properties->timestampValidBits;
- object->wait_completion_semaphore = VK_NULL_HANDLE;
- object->pending_wait_completion_value = 0;
-
object->semaphores = NULL;
object->semaphores_size = 0;
object->semaphore_count = 0;
@@ -66,20 +64,6 @@ HRESULT vkd3d_queue_create(struct d3d12_device *device,
return S_OK;
}
-bool vkd3d_queue_init_timeline_semaphore(struct vkd3d_queue *queue, struct d3d12_device *device)
-{
- VkResult vr;
-
- if (!queue->wait_completion_semaphore
- && (vr = vkd3d_create_timeline_semaphore(device, 0, &queue->wait_completion_semaphore)) < 0)
- {
- WARN("Failed to create timeline semaphore, vr %d.\n", vr);
- return false;
- }
-
- return true;
-}
-
void vkd3d_queue_destroy(struct vkd3d_queue *queue, struct d3d12_device *device)
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
@@ -94,8 +78,6 @@ void vkd3d_queue_destroy(struct vkd3d_queue *queue, struct d3d12_device *device)
vkd3d_free(queue->semaphores);
- VK_CALL(vkDestroySemaphore(device->vk_device, queue->wait_completion_semaphore, NULL));
-
for (i = 0; i < ARRAY_SIZE(queue->old_vk_semaphores); ++i)
{
if (queue->old_vk_semaphores[i])
@@ -265,7 +247,7 @@ static VkResult vkd3d_queue_create_vk_semaphore_locked(struct vkd3d_queue *queue
}
/* Fence worker thread */
-static HRESULT vkd3d_enqueue_gpu_fence(struct vkd3d_fence_worker *worker,
+static bool vkd3d_enqueue_gpu_fence(struct vkd3d_fence_worker *worker,
VkFence vk_fence, struct d3d12_fence *fence, uint64_t value,
struct vkd3d_queue *queue, uint64_t queue_sequence_number)
{
@@ -277,7 +259,7 @@ static HRESULT vkd3d_enqueue_gpu_fence(struct vkd3d_fence_worker *worker,
if ((rc = vkd3d_mutex_lock(&worker->mutex)))
{
ERR("Failed to lock mutex, error %d.\n", rc);
- return hresult_from_errno(rc);
+ return false;
}
if (!vkd3d_array_reserve((void **)&worker->fences, &worker->fences_size,
@@ -285,7 +267,7 @@ static HRESULT vkd3d_enqueue_gpu_fence(struct vkd3d_fence_worker *worker,
{
ERR("Failed to add GPU fence.\n");
vkd3d_mutex_unlock(&worker->mutex);
- return E_OUTOFMEMORY;
+ return false;
}
waiting_fence = &worker->fences[worker->fence_count++];
@@ -299,7 +281,7 @@ static HRESULT vkd3d_enqueue_gpu_fence(struct vkd3d_fence_worker *worker,
vkd3d_cond_signal(&worker->cond);
vkd3d_mutex_unlock(&worker->mutex);
- return S_OK;
+ return true;
}
static void vkd3d_wait_for_gpu_timeline_semaphore(struct vkd3d_fence_worker *worker,
@@ -308,9 +290,7 @@ static void vkd3d_wait_for_gpu_timeline_semaphore(struct vkd3d_fence_worker *wor
const struct d3d12_device *device = worker->device;
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
VkSemaphoreWaitInfoKHR wait_info;
- uint64_t counter_value;
VkResult vr;
- HRESULT hr;
wait_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO_KHR;
wait_info.pNext = NULL;
@@ -328,19 +308,10 @@ static void vkd3d_wait_for_gpu_timeline_semaphore(struct vkd3d_fence_worker *wor
return;
}
- if ((vr = VK_CALL(vkGetSemaphoreCounterValueKHR(device->vk_device, waiting_fence->u.vk_semaphore,
- &counter_value))) < 0)
- {
- ERR("Failed to get Vulkan semaphore value, vr %d.\n", vr);
- }
- else
- {
- TRACE("Signaling fence %p value %#"PRIx64".\n", waiting_fence->fence, waiting_fence->value);
- if (FAILED(hr = d3d12_fence_signal(waiting_fence->fence, counter_value, VK_NULL_HANDLE)))
- ERR("Failed to signal D3D12 fence, hr %#x.\n", hr);
+ TRACE("Signaling fence %p value %#"PRIx64".\n", waiting_fence->fence, waiting_fence->value);
+ d3d12_fence_signal_timeline_semaphore(waiting_fence->fence, waiting_fence->value);
- d3d12_fence_decref(waiting_fence->fence);
- }
+ d3d12_fence_decref(waiting_fence->fence);
}
static void vkd3d_wait_for_gpu_fence(struct vkd3d_fence_worker *worker,
@@ -434,7 +405,7 @@ static HRESULT vkd3d_fence_worker_start(struct vkd3d_fence_worker *worker,
worker->fences = NULL;
worker->fences_size = 0;
- worker->wait_for_gpu_fence = device->use_timeline_semaphores
+ worker->wait_for_gpu_fence = device->vk_info.KHR_timeline_semaphore
? vkd3d_wait_for_gpu_timeline_semaphore : vkd3d_wait_for_gpu_fence;
if ((rc = vkd3d_mutex_init(&worker->mutex)))
@@ -606,17 +577,17 @@ static void d3d12_fence_garbage_collect_vk_semaphores_locked(struct d3d12_fence
current = &fence->semaphores[i];
/* The semaphore doesn't have a pending signal operation if the fence
* was signaled. */
- if ((current->vk_fence || current->is_acquired) && !destroy_all)
+ if ((current->u.binary.vk_fence || current->u.binary.is_acquired) && !destroy_all)
{
++i;
continue;
}
- if (current->vk_fence)
+ if (current->u.binary.vk_fence)
WARN("Destroying potentially pending semaphore.\n");
- assert(!current->is_acquired);
+ assert(!current->u.binary.is_acquired);
- VK_CALL(vkDestroySemaphore(device->vk_device, current->vk_semaphore, NULL));
+ VK_CALL(vkDestroySemaphore(device->vk_device, current->u.binary.vk_semaphore, NULL));
fence->semaphores[i] = fence->semaphores[--fence->semaphore_count];
}
@@ -652,23 +623,16 @@ static void d3d12_fence_destroy_vk_objects(struct d3d12_fence *fence)
vkd3d_mutex_unlock(&fence->mutex);
}
-static struct vkd3d_signaled_semaphore *d3d12_fence_acquire_vk_semaphore(struct d3d12_fence *fence,
+static struct vkd3d_signaled_semaphore *d3d12_fence_acquire_vk_semaphore_locked(struct d3d12_fence *fence,
uint64_t value, uint64_t *completed_value)
{
struct vkd3d_signaled_semaphore *semaphore;
struct vkd3d_signaled_semaphore *current;
uint64_t semaphore_value;
unsigned int i;
- int rc;
TRACE("fence %p, value %#"PRIx64".\n", fence, value);
- if ((rc = vkd3d_mutex_lock(&fence->mutex)))
- {
- ERR("Failed to lock mutex, error %d.\n", rc);
- return VK_NULL_HANDLE;
- }
-
semaphore = NULL;
semaphore_value = ~(uint64_t)0;
@@ -676,7 +640,7 @@ static struct vkd3d_signaled_semaphore *d3d12_fence_acquire_vk_semaphore(struct
{
current = &fence->semaphores[i];
/* Prefer a semaphore with the smallest value. */
- if (!current->is_acquired && current->value >= value && semaphore_value >= current->value)
+ if (!current->u.binary.is_acquired && current->value >= value && semaphore_value >= current->value)
{
semaphore = current;
semaphore_value = current->value;
@@ -686,12 +650,10 @@ static struct vkd3d_signaled_semaphore *d3d12_fence_acquire_vk_semaphore(struct
}
if (semaphore)
- semaphore->is_acquired = true;
+ semaphore->u.binary.is_acquired = true;
*completed_value = fence->value;
- vkd3d_mutex_unlock(&fence->mutex);
-
return semaphore;
}
@@ -705,7 +667,7 @@ static void d3d12_fence_remove_vk_semaphore(struct d3d12_fence *fence, struct vk
return;
}
- assert(semaphore->is_acquired);
+ assert(semaphore->u.binary.is_acquired);
*semaphore = fence->semaphores[--fence->semaphore_count];
@@ -722,32 +684,24 @@ static void d3d12_fence_release_vk_semaphore(struct d3d12_fence *fence, struct v
return;
}
- assert(semaphore->is_acquired);
- semaphore->is_acquired = false;
+ assert(semaphore->u.binary.is_acquired);
+ semaphore->u.binary.is_acquired = false;
vkd3d_mutex_unlock(&fence->mutex);
}
-static HRESULT d3d12_fence_add_vk_semaphore(struct d3d12_fence *fence,
- VkSemaphore vk_semaphore, VkFence vk_fence, uint64_t value)
+static bool d3d12_fence_add_vk_semaphore(struct d3d12_fence *fence, VkSemaphore vk_semaphore,
+ VkFence vk_fence, uint64_t value, const struct vkd3d_queue *signalling_queue)
{
struct vkd3d_signaled_semaphore *semaphore;
- HRESULT hr = S_OK;
int rc;
TRACE("fence %p, value %#"PRIx64".\n", fence, value);
- if (!(semaphore = vkd3d_malloc(sizeof(*semaphore))))
- {
- ERR("Failed to add semaphore.\n");
- return E_OUTOFMEMORY;
- }
-
if ((rc = vkd3d_mutex_lock(&fence->mutex)))
{
ERR("Failed to lock mutex, error %d.\n", rc);
- vkd3d_free(semaphore);
- return E_FAIL;
+ return false;
}
d3d12_fence_garbage_collect_vk_semaphores_locked(fence, false);
@@ -762,16 +716,17 @@ static HRESULT d3d12_fence_add_vk_semaphore(struct d3d12_fence *fence,
semaphore = &fence->semaphores[fence->semaphore_count++];
semaphore->value = value;
- semaphore->vk_semaphore = vk_semaphore;
- semaphore->vk_fence = vk_fence;
- semaphore->is_acquired = false;
+ semaphore->u.binary.vk_semaphore = vk_semaphore;
+ semaphore->u.binary.vk_fence = vk_fence;
+ semaphore->u.binary.is_acquired = false;
+ semaphore->signalling_queue = signalling_queue;
vkd3d_mutex_unlock(&fence->mutex);
- return hr;
+ return true;
}
-static bool d3d12_fence_signal_external_events_locked(struct d3d12_fence *fence)
+static void d3d12_fence_signal_external_events_locked(struct d3d12_fence *fence)
{
struct d3d12_device *device = fence->device;
bool signal_null_event_cond = false;
@@ -803,7 +758,21 @@ static bool d3d12_fence_signal_external_events_locked(struct d3d12_fence *fence)
fence->event_count = j;
- return signal_null_event_cond;
+ if (signal_null_event_cond)
+ vkd3d_cond_broadcast(&fence->null_event_cond);
+}
+
+static void d3d12_fence_update_pending_value_locked(struct d3d12_fence *fence)
+{
+ uint64_t new_max_pending_value = 0;
+ unsigned int i;
+
+ for (i = 0; i < fence->semaphore_count; ++i)
+ new_max_pending_value = max(fence->semaphores[i].value, new_max_pending_value);
+
+ fence->max_pending_value = max(fence->value, new_max_pending_value);
+ /* If we're signalling the fence, wake up any submission threads which can now safely kick work. */
+ vkd3d_cond_broadcast(&fence->cond);
}
static HRESULT d3d12_fence_signal(struct d3d12_fence *fence, uint64_t value, VkFence vk_fence)
@@ -821,8 +790,7 @@ static HRESULT d3d12_fence_signal(struct d3d12_fence *fence, uint64_t value, VkF
fence->value = value;
- if (d3d12_fence_signal_external_events_locked(fence))
- vkd3d_cond_broadcast(&fence->null_event_cond);
+ d3d12_fence_signal_external_events_locked(fence);
if (vk_fence)
{
@@ -831,8 +799,8 @@ static HRESULT d3d12_fence_signal(struct d3d12_fence *fence, uint64_t value, VkF
for (i = 0; i < fence->semaphore_count; ++i)
{
current = &fence->semaphores[i];
- if (current->vk_fence == vk_fence)
- current->vk_fence = VK_NULL_HANDLE;
+ if (current->u.binary.vk_fence == vk_fence)
+ current->u.binary.vk_fence = VK_NULL_HANDLE;
}
for (i = 0; i < ARRAY_SIZE(fence->old_vk_fences); ++i)
@@ -849,11 +817,135 @@ static HRESULT d3d12_fence_signal(struct d3d12_fence *fence, uint64_t value, VkF
VK_CALL(vkDestroyFence(device->vk_device, vk_fence, NULL));
}
+ d3d12_fence_update_pending_value_locked(fence);
+
vkd3d_mutex_unlock(&fence->mutex);
return S_OK;
}
+static void d3d12_fence_block_until_pending_value_reaches_locked(struct d3d12_fence *fence, uint64_t pending_value)
+{
+ while (pending_value > fence->max_pending_value)
+ {
+ TRACE("Blocking wait on fence %p until it reaches 0x%"PRIx64".\n", fence, pending_value);
+ vkd3d_cond_wait(&fence->cond, &fence->mutex);
+ }
+}
+
+static bool d3d12_fence_can_elide_wait_semaphore_locked(const struct d3d12_fence *fence,
+ uint64_t wait_value, const struct vkd3d_queue *waiting_queue)
+{
+ unsigned int i;
+
+ /* Relevant if the semaphore has been signalled already on host.
+ * We should not wait on the timeline semaphore directly, we can simply submit in-place. */
+ if (fence->value >= wait_value)
+ return true;
+
+ /* We can elide a wait if we can use the submission order guarantee.
+ * If there is a pending signal on this queue which will satisfy the wait,
+ * submission barrier will implicitly complete the wait,
+ * and we don't have to eat the overhead of submitting an extra wait on top.
+ * This will essentially always trigger on single-queue.
+ */
+ for (i = 0; i < fence->semaphore_count; ++i)
+ {
+ if (fence->semaphores[i].signalling_queue == waiting_queue && fence->semaphores[i].value >= wait_value)
+ return true;
+ }
+
+ return false;
+}
+
+static uint64_t d3d12_fence_add_pending_signal_locked(struct d3d12_fence *fence, uint64_t virtual_value,
+ const struct vkd3d_queue *signalling_queue)
+{
+ struct vkd3d_signaled_semaphore *semaphore;
+
+ if (!vkd3d_array_reserve((void**)&fence->semaphores, &fence->semaphores_size,
+ fence->semaphore_count + 1, sizeof(*fence->semaphores)))
+ {
+ return 0;
+ }
+
+ semaphore = &fence->semaphores[fence->semaphore_count++];
+ semaphore->value = virtual_value;
+ semaphore->u.timeline_value = ++fence->pending_timeline_value;
+ semaphore->signalling_queue = signalling_queue;
+ return fence->pending_timeline_value;
+}
+
+static uint64_t d3d12_fence_get_timeline_wait_value_locked(struct d3d12_fence *fence, uint64_t virtual_value)
+{
+ uint64_t target_timeline_value = UINT64_MAX;
+ unsigned int i;
+
+ /* This shouldn't happen, we will have elided the wait completely in can_elide_wait_semaphore_locked. */
+ assert(virtual_value > fence->value);
+
+ /* Find the smallest physical value which is at least the virtual value. */
+ for (i = 0; i < fence->semaphore_count; ++i)
+ {
+ if (virtual_value <= fence->semaphores[i].value)
+ target_timeline_value = min(target_timeline_value, fence->semaphores[i].u.timeline_value);
+ }
+
+ if (target_timeline_value == UINT64_MAX)
+ {
+ FIXME("Cannot find a pending timeline semaphore wait value. Emitting a noop wait.\n");
+ return 0;
+ }
+ else
+ {
+ return target_timeline_value;
+ }
+}
+
+static void d3d12_fence_signal_timeline_semaphore(struct d3d12_fence *fence, uint64_t timeline_value)
+{
+ bool did_signal;
+ unsigned int i;
+ int rc;
+
+ if ((rc = vkd3d_mutex_lock(&fence->mutex)))
+ {
+ ERR("Failed to lock mutex, error %d.\n", rc);
+ return;
+ }
+
+ /* With multiple fence workers, it is possible that signal calls are out of
+ * order. The physical value itself is monotonic, but we need to make sure
+ * that all signals happen in correct order if there are fence rewinds.
+ * We don't expect the loop to run more than once, but there might be
+ * extreme edge cases where we signal 2 or more. */
+ while (fence->timeline_value < timeline_value)
+ {
+ ++fence->timeline_value;
+ did_signal = false;
+
+ for (i = 0; i < fence->semaphore_count; ++i)
+ {
+ if (fence->timeline_value == fence->semaphores[i].u.timeline_value)
+ {
+ fence->value = fence->semaphores[i].value;
+ d3d12_fence_signal_external_events_locked(fence);
+ fence->semaphores[i] = fence->semaphores[--fence->semaphore_count];
+ did_signal = true;
+ break;
+ }
+ }
+
+ if (!did_signal)
+ FIXME("Did not signal a virtual value.\n");
+ }
+
+ /* In case we have a rewind signalled from GPU, we need to recompute the max pending timeline value. */
+ d3d12_fence_update_pending_value_locked(fence);
+
+ vkd3d_mutex_unlock(&fence->mutex);
+}
+
static HRESULT STDMETHODCALLTYPE d3d12_fence_QueryInterface(ID3D12Fence *iface,
REFIID riid, void **object)
{
@@ -921,6 +1013,8 @@ static void d3d12_fence_decref(struct d3d12_fence *fence)
vkd3d_free(fence->semaphores);
if ((rc = vkd3d_mutex_destroy(&fence->mutex)))
ERR("Failed to destroy mutex, error %d.\n", rc);
+ if ((rc = vkd3d_cond_destroy(&fence->cond)))
+ ERR("Failed to destroy cond, error %d.\n", rc);
vkd3d_cond_destroy(&fence->null_event_cond);
vkd3d_free(fence);
@@ -1060,100 +1154,8 @@ static HRESULT STDMETHODCALLTYPE d3d12_fence_SetEventOnCompletion(ID3D12Fence *i
return S_OK;
}
-static inline bool d3d12_fence_gpu_wait_is_completed(const struct d3d12_fence *fence, unsigned int i)
-{
- const struct d3d12_device *device = fence->device;
- const struct vkd3d_vk_device_procs *vk_procs;
- uint64_t value;
- VkResult vr;
-
- vk_procs = &device->vk_procs;
-
- if ((vr = VK_CALL(vkGetSemaphoreCounterValueKHR(device->vk_device,
- fence->gpu_waits[i].queue->wait_completion_semaphore, &value))) >= 0)
- {
- return value >= fence->gpu_waits[i].pending_value;
- }
-
- ERR("Failed to get Vulkan semaphore status, vr %d.\n", vr);
- return true;
-}
-
-static inline bool d3d12_fence_has_pending_gpu_ops_locked(struct d3d12_fence *fence)
-{
- const struct d3d12_device *device = fence->device;
- const struct vkd3d_vk_device_procs *vk_procs;
- uint64_t value;
- unsigned int i;
- VkResult vr;
-
- for (i = 0; i < fence->gpu_wait_count; ++i)
- {
- if (d3d12_fence_gpu_wait_is_completed(fence, i) && i < --fence->gpu_wait_count)
- fence->gpu_waits[i] = fence->gpu_waits[fence->gpu_wait_count];
- }
- if (fence->gpu_wait_count)
- return true;
-
- /* Check for pending signals too. */
- if (fence->value >= fence->pending_timeline_value)
- return false;
-
- vk_procs = &device->vk_procs;
-
- /* Check the actual semaphore value in case fence->value update is lagging. */
- if ((vr = VK_CALL(vkGetSemaphoreCounterValueKHR(device->vk_device, fence->timeline_semaphore, &value))) < 0)
- {
- ERR("Failed to get Vulkan semaphore status, vr %d.\n", vr);
- return false;
- }
-
- return value < fence->pending_timeline_value;
-}
-
-/* Replace the VkSemaphore with a new one to allow a lower value to be set. Ideally apps will
- * only use this to reset the fence when no operations are pending on the queue. */
-static HRESULT d3d12_fence_reinit_timeline_semaphore_locked(struct d3d12_fence *fence, uint64_t value)
-{
- const struct d3d12_device *device = fence->device;
- const struct vkd3d_vk_device_procs *vk_procs;
- VkSemaphore timeline_semaphore;
- VkResult vr;
-
- if (d3d12_fence_has_pending_gpu_ops_locked(fence))
- {
- /* This situation is not very likely because it means a fence with pending waits and/or signals was
- * signalled on the CPU to a lower value. For now, emit a fixme so it can be patched if necessary.
- * A patch already exists for this but it's not pretty. */
- FIXME("Unable to re-initialise timeline semaphore to a lower value due to pending GPU ops.\n");
- return E_FAIL;
- }
-
- if ((vr = vkd3d_create_timeline_semaphore(device, value, &timeline_semaphore)) < 0)
- {
- WARN("Failed to create timeline semaphore, vr %d.\n", vr);
- return hresult_from_vk_result(vr);
- }
-
- fence->value = value;
- fence->pending_timeline_value = value;
-
- WARN("Replacing timeline semaphore with a new object.\n");
-
- vk_procs = &device->vk_procs;
-
- VK_CALL(vkDestroySemaphore(device->vk_device, fence->timeline_semaphore, NULL));
- fence->timeline_semaphore = timeline_semaphore;
-
- return S_OK;
-}
-
static HRESULT d3d12_fence_signal_cpu_timeline_semaphore(struct d3d12_fence *fence, uint64_t value)
{
- const struct d3d12_device *device = fence->device;
- VkSemaphoreSignalInfoKHR info;
- HRESULT hr = S_OK;
- VkResult vr;
int rc;
if ((rc = vkd3d_mutex_lock(&fence->mutex)))
@@ -1162,48 +1164,13 @@ static HRESULT d3d12_fence_signal_cpu_timeline_semaphore(struct d3d12_fence *fen
return hresult_from_errno(rc);
}
- /* We must only signal a value which is greater than the current value.
- * That value can be in the range of current known value (fence->value), or as large as pending_timeline_value.
- * Pending timeline value signal might be blocked by another synchronization primitive, and thus statically
- * cannot be that value, so the safest thing to do is to check the current value which is updated by the fence
- * wait thread continuously. This check is technically racy since the value might be immediately out of date,
- * but there is no way to avoid this. */
- if (value > fence->value)
- {
- const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
-
- /* Sanity check against the delta limit. */
- if (value - fence->value > device->vk_info.timeline_semaphore_properties.maxTimelineSemaphoreValueDifference)
- {
- FIXME("Timeline semaphore delta is %"PRIu64", but implementation only supports a delta of %"PRIu64".\n",
- value - fence->value, device->vk_info.timeline_semaphore_properties.maxTimelineSemaphoreValueDifference);
- }
-
- info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SIGNAL_INFO_KHR;
- info.pNext = NULL;
- info.semaphore = fence->timeline_semaphore;
- info.value = value;
- if ((vr = VK_CALL(vkSignalSemaphoreKHR(device->vk_device, &info))) >= 0)
- {
- fence->value = value;
- if (value > fence->pending_timeline_value)
- fence->pending_timeline_value = value;
- }
- else
- {
- ERR("Failed to signal timeline semaphore, vr %d.\n", vr);
- hr = hresult_from_vk_result(vr);
- }
- }
- else if (value < fence->value)
- {
- hr = d3d12_fence_reinit_timeline_semaphore_locked(fence, value);
- }
-
+ fence->value = value;
d3d12_fence_signal_external_events_locked(fence);
+ d3d12_fence_update_pending_value_locked(fence);
vkd3d_mutex_unlock(&fence->mutex);
- return hr;
+
+ return S_OK;
}
static HRESULT STDMETHODCALLTYPE d3d12_fence_Signal(ID3D12Fence *iface, UINT64 value)
@@ -1256,6 +1223,7 @@ static HRESULT d3d12_fence_init(struct d3d12_fence *fence, struct d3d12_device *
fence->refcount = 1;
fence->value = initial_value;
+ fence->max_pending_value = initial_value;
if ((rc = vkd3d_mutex_init(&fence->mutex)))
{
@@ -1263,11 +1231,18 @@ static HRESULT d3d12_fence_init(struct d3d12_fence *fence, struct d3d12_device *
return hresult_from_errno(rc);
}
+ if ((rc = vkd3d_cond_init(&fence->cond)))
+ {
+ ERR("Failed to initialize cond variable, error %d.\n", rc);
+ hr = hresult_from_errno(rc);
+ goto fail_destroy_mutex;
+ }
+
if ((rc = vkd3d_cond_init(&fence->null_event_cond)))
{
ERR("Failed to initialize cond variable, error %d.\n", rc);
- vkd3d_mutex_destroy(&fence->mutex);
- return hresult_from_errno(rc);
+ hr = hresult_from_errno(rc);
+ goto fail_destroy_cond;
}
if (flags)
@@ -1278,14 +1253,15 @@ static HRESULT d3d12_fence_init(struct d3d12_fence *fence, struct d3d12_device *
fence->event_count = 0;
fence->timeline_semaphore = VK_NULL_HANDLE;
- if (device->use_timeline_semaphores && (vr = vkd3d_create_timeline_semaphore(device, initial_value,
+ fence->timeline_value = 0;
+ fence->pending_timeline_value = 0;
+ if (device->vk_info.KHR_timeline_semaphore && (vr = vkd3d_create_timeline_semaphore(device, 0,
&fence->timeline_semaphore)) < 0)
{
WARN("Failed to create timeline semaphore, vr %d.\n", vr);
- return hresult_from_vk_result(vr);
+ hr = hresult_from_vk_result(vr);
+ goto fail_destroy_null_cond;
}
- fence->pending_timeline_value = initial_value;
- fence->gpu_wait_count = 0;
fence->semaphores = NULL;
fence->semaphores_size = 0;
@@ -1295,14 +1271,21 @@ static HRESULT d3d12_fence_init(struct d3d12_fence *fence, struct d3d12_device *
if (FAILED(hr = vkd3d_private_store_init(&fence->private_store)))
{
- vkd3d_mutex_destroy(&fence->mutex);
- vkd3d_cond_destroy(&fence->null_event_cond);
- return hr;
+ goto fail_destroy_null_cond;
}
d3d12_device_add_ref(fence->device = device);
return S_OK;
+
+fail_destroy_null_cond:
+ vkd3d_cond_destroy(&fence->null_event_cond);
+fail_destroy_cond:
+ vkd3d_cond_destroy(&fence->cond);
+fail_destroy_mutex:
+ vkd3d_mutex_destroy(&fence->mutex);
+
+ return hr;
}
HRESULT d3d12_fence_create(struct d3d12_device *device,
@@ -6076,6 +6059,41 @@ HRESULT d3d12_command_list_create(struct d3d12_device *device,
return S_OK;
}
+static HRESULT d3d12_command_queue_add_submission_locked(struct d3d12_command_queue *queue,
+ const struct d3d12_command_queue_submission *sub)
+{
+ if (!vkd3d_array_reserve((void**)&queue->submissions, &queue->submissions_size,
+ queue->submissions_count + 1, sizeof(*queue->submissions)))
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ queue->submissions[queue->submissions_count++] = *sub;
+ vkd3d_cond_signal(&queue->submission_cond);
+ return S_OK;
+}
+
+static HRESULT d3d12_command_queue_add_submission(struct d3d12_command_queue *queue,
+ const struct d3d12_command_queue_submission *sub)
+{
+ HRESULT hr;
+
+ vkd3d_mutex_lock(&queue->submission_mutex);
+ hr = d3d12_command_queue_add_submission_locked(queue, sub);
+ vkd3d_mutex_unlock(&queue->submission_mutex);
+ return hr;
+}
+
+static void d3d12_command_queue_submit_stop(struct d3d12_command_queue *queue)
+{
+ struct d3d12_command_queue_submission sub;
+ HRESULT hr;
+
+ sub.type = VKD3D_SUBMISSION_STOP;
+ if (FAILED(hr = d3d12_command_queue_add_submission(queue, &sub)))
+ ERR("Failed to submit command, hr %#x.\n", hr);
+}
+
/* ID3D12CommandQueue */
static inline struct d3d12_command_queue *impl_from_ID3D12CommandQueue(ID3D12CommandQueue *iface)
{
@@ -6126,7 +6144,12 @@ static ULONG STDMETHODCALLTYPE d3d12_command_queue_Release(ID3D12CommandQueue *i
struct d3d12_device *device = command_queue->device;
vkd3d_fence_worker_stop(&command_queue->fence_worker, device);
+ d3d12_command_queue_submit_stop(command_queue);
+ vkd3d_join_thread(device->vkd3d_instance, &command_queue->submission_thread);
+ vkd3d_mutex_destroy(&command_queue->submission_mutex);
+ vkd3d_cond_destroy(&command_queue->submission_cond);
+ vkd3d_free(command_queue->submissions);
vkd3d_private_store_destroy(&command_queue->private_store);
vkd3d_free(command_queue);
@@ -6231,18 +6254,17 @@ static void STDMETHODCALLTYPE d3d12_command_queue_ExecuteCommandLists(ID3D12Comm
UINT command_list_count, ID3D12CommandList * const *command_lists)
{
struct d3d12_command_queue *command_queue = impl_from_ID3D12CommandQueue(iface);
- const struct vkd3d_vk_device_procs *vk_procs;
+ struct d3d12_command_queue_submission sub;
struct d3d12_command_list *cmd_list;
- struct VkSubmitInfo submit_desc;
VkCommandBuffer *buffers;
- VkQueue vk_queue;
unsigned int i;
- VkResult vr;
+ HRESULT hr;
TRACE("iface %p, command_list_count %u, command_lists %p.\n",
iface, command_list_count, command_lists);
- vk_procs = &command_queue->device->vk_procs;
+ if (!command_list_count)
+ return;
if (!(buffers = vkd3d_calloc(command_list_count, sizeof(*buffers))))
{
@@ -6265,29 +6287,11 @@ static void STDMETHODCALLTYPE d3d12_command_queue_ExecuteCommandLists(ID3D12Comm
buffers[i] = cmd_list->vk_command_buffer;
}
- submit_desc.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
- submit_desc.pNext = NULL;
- submit_desc.waitSemaphoreCount = 0;
- submit_desc.pWaitSemaphores = NULL;
- submit_desc.pWaitDstStageMask = NULL;
- submit_desc.commandBufferCount = command_list_count;
- submit_desc.pCommandBuffers = buffers;
- submit_desc.signalSemaphoreCount = 0;
- submit_desc.pSignalSemaphores = NULL;
-
- if (!(vk_queue = vkd3d_queue_acquire(command_queue->vkd3d_queue)))
- {
- ERR("Failed to acquire queue %p.\n", command_queue->vkd3d_queue);
- vkd3d_free(buffers);
- return;
- }
-
- if ((vr = VK_CALL(vkQueueSubmit(vk_queue, 1, &submit_desc, VK_NULL_HANDLE))) < 0)
- ERR("Failed to submit queue(s), vr %d.\n", vr);
-
- vkd3d_queue_release(command_queue->vkd3d_queue);
-
- vkd3d_free(buffers);
+ sub.type = VKD3D_SUBMISSION_EXECUTE;
+ sub.u.execute.cmd = buffers;
+ sub.u.execute.cmd_count = command_list_count;
+ if (FAILED(hr = d3d12_command_queue_add_submission(command_queue, &sub)))
+ ERR("Failed to submit command, hr %#x.\n", hr);
}
static void STDMETHODCALLTYPE d3d12_command_queue_SetMarker(ID3D12CommandQueue *iface,
@@ -6309,39 +6313,7 @@ static void STDMETHODCALLTYPE d3d12_command_queue_EndEvent(ID3D12CommandQueue *i
FIXME("iface %p stub!\n", iface);
}
-static HRESULT d3d12_fence_update_gpu_signal_timeline_semaphore(struct d3d12_fence *fence, uint64_t value)
-{
- const struct d3d12_device *device = fence->device;
- int rc;
-
- if ((rc = vkd3d_mutex_lock(&fence->mutex)))
- {
- ERR("Failed to lock mutex, error %d.\n", rc);
- return hresult_from_errno(rc);
- }
-
- /* If we're attempting to async signal a fence with a value which is not strictly increasing the payload value,
- * warn about this case. Do not treat this as an error since it works at least with RADV and Nvidia drivers and
- * there's no workaround on the GPU side. */
- if (value <= fence->pending_timeline_value)
- {
- WARN("Fence %p values are not strictly increasing. Pending values: old %"PRIu64", new %"PRIu64".\n",
- fence, fence->pending_timeline_value, value);
- }
- /* Sanity check against the delta limit. Use the current fence value. */
- else if (value - fence->value > device->vk_info.timeline_semaphore_properties.maxTimelineSemaphoreValueDifference)
- {
- FIXME("Timeline semaphore delta is %"PRIu64", but implementation only supports a delta of %"PRIu64".\n",
- value - fence->value, device->vk_info.timeline_semaphore_properties.maxTimelineSemaphoreValueDifference);
- }
- fence->pending_timeline_value = value;
-
- vkd3d_mutex_unlock(&fence->mutex);
-
- return S_OK;
-}
-
-static HRESULT vkd3d_enqueue_timeline_semaphore(struct vkd3d_fence_worker *worker, VkSemaphore vk_semaphore,
+static void vkd3d_enqueue_timeline_semaphore(struct vkd3d_fence_worker *worker, VkSemaphore vk_semaphore,
struct d3d12_fence *fence, uint64_t value, struct vkd3d_queue *queue)
{
struct vkd3d_waiting_fence *waiting_fence;
@@ -6352,7 +6324,7 @@ static HRESULT vkd3d_enqueue_timeline_semaphore(struct vkd3d_fence_worker *worke
if ((rc = vkd3d_mutex_lock(&worker->mutex)))
{
ERR("Failed to lock mutex, error %d.\n", rc);
- return hresult_from_errno(rc);
+ return;
}
if (!vkd3d_array_reserve((void **)&worker->fences, &worker->fences_size,
@@ -6360,7 +6332,7 @@ static HRESULT vkd3d_enqueue_timeline_semaphore(struct vkd3d_fence_worker *worke
{
ERR("Failed to add GPU timeline semaphore.\n");
vkd3d_mutex_unlock(&worker->mutex);
- return E_OUTOFMEMORY;
+ return;
}
waiting_fence = &worker->fences[worker->fence_count++];
@@ -6372,39 +6344,58 @@ static HRESULT vkd3d_enqueue_timeline_semaphore(struct vkd3d_fence_worker *worke
vkd3d_cond_signal(&worker->cond);
vkd3d_mutex_unlock(&worker->mutex);
-
- return S_OK;
}
static HRESULT STDMETHODCALLTYPE d3d12_command_queue_Signal(ID3D12CommandQueue *iface,
ID3D12Fence *fence_iface, UINT64 value)
{
struct d3d12_command_queue *command_queue = impl_from_ID3D12CommandQueue(iface);
+ struct d3d12_fence *fence = unsafe_impl_from_ID3D12Fence(fence_iface);
+ struct d3d12_command_queue_submission sub;
+
+ TRACE("iface %p, fence %p, value %#"PRIx64".\n", iface, fence_iface, value);
+
+ d3d12_fence_incref(fence);
+
+ sub.type = VKD3D_SUBMISSION_SIGNAL;
+ sub.u.signal.fence = fence;
+ sub.u.signal.value = value;
+ return d3d12_command_queue_add_submission(command_queue, &sub);
+}
+
+static void d3d12_command_queue_signal(struct d3d12_command_queue *command_queue,
+ struct d3d12_fence *fence, uint64_t value)
+{
VkTimelineSemaphoreSubmitInfoKHR timeline_submit_info;
const struct vkd3d_vk_device_procs *vk_procs;
VkSemaphore vk_semaphore = VK_NULL_HANDLE;
VkFence vk_fence = VK_NULL_HANDLE;
struct vkd3d_queue *vkd3d_queue;
uint64_t sequence_number = 0;
+ uint64_t timeline_value = 0;
struct d3d12_device *device;
- struct d3d12_fence *fence;
VkSubmitInfo submit_info;
VkQueue vk_queue;
+ int rc = -1;
VkResult vr;
- HRESULT hr;
-
- TRACE("iface %p, fence %p, value %#"PRIx64".\n", iface, fence_iface, value);
device = command_queue->device;
vk_procs = &device->vk_procs;
vkd3d_queue = command_queue->vkd3d_queue;
- fence = unsafe_impl_from_ID3D12Fence(fence_iface);
-
- if (device->use_timeline_semaphores)
+ if (device->vk_info.KHR_timeline_semaphore)
{
- if (FAILED(hr = d3d12_fence_update_gpu_signal_timeline_semaphore(fence, value)))
- return hr;
+ if ((rc = vkd3d_mutex_lock(&fence->mutex)))
+ {
+ ERR("Failed to lock mutex, error %d.\n", rc);
+ return;
+ }
+ if (!(timeline_value = d3d12_fence_add_pending_signal_locked(fence, value, vkd3d_queue)))
+ {
+ ERR("Failed to add pending signal.\n");
+ vkd3d_mutex_unlock(&fence->mutex);
+ return;
+ }
vk_semaphore = fence->timeline_semaphore;
assert(vk_semaphore);
@@ -6414,18 +6405,17 @@ static HRESULT STDMETHODCALLTYPE d3d12_command_queue_Signal(ID3D12CommandQueue *
if ((vr = d3d12_fence_create_vk_fence(fence, &vk_fence)) < 0)
{
WARN("Failed to create Vulkan fence, vr %d.\n", vr);
- goto fail_vkresult;
+ goto fail;
}
}
if (!(vk_queue = vkd3d_queue_acquire(vkd3d_queue)))
{
ERR("Failed to acquire queue %p.\n", vkd3d_queue);
- hr = E_FAIL;
goto fail;
}
- if (!device->use_timeline_semaphores && (vr = vkd3d_queue_create_vk_semaphore_locked(vkd3d_queue,
+ if (!device->vk_info.KHR_timeline_semaphore && (vr = vkd3d_queue_create_vk_semaphore_locked(vkd3d_queue,
device, &vk_semaphore)) < 0)
{
ERR("Failed to create Vulkan semaphore, vr %d.\n", vr);
@@ -6442,11 +6432,11 @@ static HRESULT STDMETHODCALLTYPE d3d12_command_queue_Signal(ID3D12CommandQueue *
submit_info.signalSemaphoreCount = vk_semaphore ? 1 : 0;
submit_info.pSignalSemaphores = &vk_semaphore;
- if (device->use_timeline_semaphores)
+ if (device->vk_info.KHR_timeline_semaphore)
{
timeline_submit_info.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO_KHR;
timeline_submit_info.pNext = NULL;
- timeline_submit_info.pSignalSemaphoreValues = &value;
+ timeline_submit_info.pSignalSemaphoreValues = &timeline_value;
timeline_submit_info.signalSemaphoreValueCount = submit_info.signalSemaphoreCount;
timeline_submit_info.waitSemaphoreValueCount = 0;
timeline_submit_info.pWaitSemaphoreValues = NULL;
@@ -6454,7 +6444,7 @@ static HRESULT STDMETHODCALLTYPE d3d12_command_queue_Signal(ID3D12CommandQueue *
}
vr = VK_CALL(vkQueueSubmit(vk_queue, 1, &submit_info, vk_fence));
- if (!device->use_timeline_semaphores && vr >= 0)
+ if (!device->vk_info.KHR_timeline_semaphore && vr >= 0)
{
sequence_number = ++vkd3d_queue->submitted_sequence_number;
@@ -6465,41 +6455,43 @@ static HRESULT STDMETHODCALLTYPE d3d12_command_queue_Signal(ID3D12CommandQueue *
vkd3d_queue_release(vkd3d_queue);
+ if (!rc)
+ {
+ vkd3d_mutex_unlock(&fence->mutex);
+ rc = -1;
+ }
+
if (vr < 0)
{
WARN("Failed to submit signal operation, vr %d.\n", vr);
- goto fail_vkresult;
+ goto fail;
}
- if (device->use_timeline_semaphores)
+ if (device->vk_info.KHR_timeline_semaphore)
{
return vkd3d_enqueue_timeline_semaphore(&command_queue->fence_worker,
- vk_semaphore, fence, value, vkd3d_queue);
+ vk_semaphore, fence, timeline_value, vkd3d_queue);
}
- if (vk_semaphore && SUCCEEDED(hr = d3d12_fence_add_vk_semaphore(fence, vk_semaphore, vk_fence, value)))
+ if (vk_semaphore && d3d12_fence_add_vk_semaphore(fence, vk_semaphore, vk_fence, value, vkd3d_queue))
vk_semaphore = VK_NULL_HANDLE;
vr = VK_CALL(vkGetFenceStatus(device->vk_device, vk_fence));
if (vr == VK_NOT_READY)
{
- if (SUCCEEDED(hr = vkd3d_enqueue_gpu_fence(&command_queue->fence_worker,
- vk_fence, fence, value, vkd3d_queue, sequence_number)))
- {
+ if (vkd3d_enqueue_gpu_fence(&command_queue->fence_worker, vk_fence, fence, value, vkd3d_queue, sequence_number))
vk_fence = VK_NULL_HANDLE;
- }
}
else if (vr == VK_SUCCESS)
{
TRACE("Already signaled %p, value %#"PRIx64".\n", fence, value);
- hr = d3d12_fence_signal(fence, value, vk_fence);
+ d3d12_fence_signal(fence, value, vk_fence);
vk_fence = VK_NULL_HANDLE;
vkd3d_queue_update_sequence_number(vkd3d_queue, sequence_number, device);
}
else
{
FIXME("Failed to get fence status, vr %d.\n", vr);
- hr = hresult_from_vk_result(vr);
}
if (vk_fence || vk_semaphore)
@@ -6509,18 +6501,17 @@ static HRESULT STDMETHODCALLTYPE d3d12_command_queue_Signal(ID3D12CommandQueue *
goto fail;
}
- return hr;
+ return;
-fail_vkresult:
- hr = hresult_from_vk_result(vr);
fail:
+ if (!rc)
+ vkd3d_mutex_unlock(&fence->mutex);
VK_CALL(vkDestroyFence(device->vk_device, vk_fence, NULL));
- if (!device->use_timeline_semaphores)
+ if (!device->vk_info.KHR_timeline_semaphore)
VK_CALL(vkDestroySemaphore(device->vk_device, vk_semaphore, NULL));
- return hr;
}
-static HRESULT d3d12_command_queue_wait_binary_semaphore(struct d3d12_command_queue *command_queue,
+static void d3d12_command_queue_wait_binary_semaphore_locked(struct d3d12_command_queue *command_queue,
struct d3d12_fence *fence, uint64_t value)
{
static const VkPipelineStageFlagBits wait_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
@@ -6531,23 +6522,24 @@ static HRESULT d3d12_command_queue_wait_binary_semaphore(struct d3d12_command_qu
VkSubmitInfo submit_info;
VkQueue vk_queue;
VkResult vr;
- HRESULT hr;
vk_procs = &command_queue->device->vk_procs;
queue = command_queue->vkd3d_queue;
- semaphore = d3d12_fence_acquire_vk_semaphore(fence, value, &completed_value);
+ semaphore = d3d12_fence_acquire_vk_semaphore_locked(fence, value, &completed_value);
+
+ vkd3d_mutex_unlock(&fence->mutex);
+
if (!semaphore && completed_value >= value)
{
/* We don't get a Vulkan semaphore if the fence was signaled on CPU. */
TRACE("Already signaled %p, value %#"PRIx64".\n", fence, completed_value);
- return S_OK;
+ return;
}
if (!(vk_queue = vkd3d_queue_acquire(queue)))
{
ERR("Failed to acquire queue %p.\n", queue);
- hr = E_FAIL;
goto fail;
}
@@ -6564,13 +6556,13 @@ static HRESULT d3d12_command_queue_wait_binary_semaphore(struct d3d12_command_qu
}
vkd3d_queue_release(queue);
- return S_OK;
+ return;
}
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submit_info.pNext = NULL;
submit_info.waitSemaphoreCount = 1;
- submit_info.pWaitSemaphores = &semaphore->vk_semaphore;
+ submit_info.pWaitSemaphores = &semaphore->u.binary.vk_semaphore;
submit_info.pWaitDstStageMask = &wait_stage_mask;
submit_info.commandBufferCount = 0;
submit_info.pCommandBuffers = NULL;
@@ -6582,13 +6574,12 @@ static HRESULT d3d12_command_queue_wait_binary_semaphore(struct d3d12_command_qu
{
ERR("Failed to allocate memory for semaphore.\n");
vkd3d_queue_release(queue);
- hr = E_OUTOFMEMORY;
goto fail;
}
if ((vr = VK_CALL(vkQueueSubmit(vk_queue, 1, &submit_info, VK_NULL_HANDLE))) >= 0)
{
- queue->semaphores[queue->semaphore_count].vk_semaphore = semaphore->vk_semaphore;
+ queue->semaphores[queue->semaphore_count].vk_semaphore = semaphore->u.binary.vk_semaphore;
queue->semaphores[queue->semaphore_count].sequence_number = queue->submitted_sequence_number + 1;
++queue->semaphore_count;
@@ -6601,60 +6592,17 @@ static HRESULT d3d12_command_queue_wait_binary_semaphore(struct d3d12_command_qu
if (vr < 0)
{
WARN("Failed to submit wait operation, vr %d.\n", vr);
- hr = hresult_from_vk_result(vr);
goto fail;
}
d3d12_fence_remove_vk_semaphore(fence, semaphore);
- return S_OK;
+ return;
fail:
d3d12_fence_release_vk_semaphore(fence, semaphore);
- return hr;
}
-static inline void d3d12_fence_update_gpu_wait(struct d3d12_fence *fence, const struct vkd3d_queue *queue)
-{
- unsigned int i;
- bool found;
- int rc;
-
- if ((rc = vkd3d_mutex_lock(&fence->mutex)))
- {
- ERR("Failed to lock mutex, error %d.\n", rc);
- return;
- }
-
- for (i = 0, found = false; i < fence->gpu_wait_count; ++i)
- {
- if (fence->gpu_waits[i].queue == queue)
- {
- fence->gpu_waits[i].pending_value = queue->pending_wait_completion_value;
- found = true;
- }
- else if (d3d12_fence_gpu_wait_is_completed(fence, i) && i < --fence->gpu_wait_count)
- {
- fence->gpu_waits[i] = fence->gpu_waits[fence->gpu_wait_count];
- }
- }
-
- if (!found)
- {
- if (fence->gpu_wait_count < ARRAY_SIZE(fence->gpu_waits))
- {
- fence->gpu_waits[fence->gpu_wait_count].queue = queue;
- fence->gpu_waits[fence->gpu_wait_count++].pending_value = queue->pending_wait_completion_value;
- }
- else
- {
- FIXME("Unable to track GPU fence wait.\n");
- }
- }
-
- vkd3d_mutex_unlock(&fence->mutex);
-}
-
-static HRESULT d3d12_command_queue_wait_timeline_semaphore(struct d3d12_command_queue *command_queue,
+static void d3d12_command_queue_wait(struct d3d12_command_queue *command_queue,
struct d3d12_fence *fence, uint64_t value)
{
static const VkPipelineStageFlagBits wait_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
@@ -6662,25 +6610,47 @@ static HRESULT d3d12_command_queue_wait_timeline_semaphore(struct d3d12_command_
const struct vkd3d_vk_device_procs *vk_procs;
struct vkd3d_queue *queue;
VkSubmitInfo submit_info;
+ uint64_t wait_count;
VkQueue vk_queue;
VkResult vr;
vk_procs = &command_queue->device->vk_procs;
queue = command_queue->vkd3d_queue;
+ vkd3d_mutex_lock(&fence->mutex);
+
+ /* This is the critical part required to support out-of-order signal.
+ * Normally we would be able to submit waits and signals out of order, but
+ * we don't have virtualized queues in Vulkan, so we need to handle the case
+ * where multiple queues alias over the same physical queue, so effectively,
+ * we need to manage out-of-order submits ourselves. */
+ d3d12_fence_block_until_pending_value_reaches_locked(fence, value);
+
+ /* If a host signal unblocked us, or we know that the fence has reached a specific value, there is no need
+ * to queue up a wait. */
+ if (d3d12_fence_can_elide_wait_semaphore_locked(fence, value, queue))
+ {
+ TRACE("Eliding wait on fence %p, value %#"PRIx64".\n", fence, value);
+ vkd3d_mutex_unlock(&fence->mutex);
+ return;
+ }
+
+ if (!command_queue->device->vk_info.KHR_timeline_semaphore)
+ return d3d12_command_queue_wait_binary_semaphore_locked(command_queue, fence, value);
+
+ wait_count = d3d12_fence_get_timeline_wait_value_locked(fence, value);
+
+ /* We can unlock the fence here. The queue semaphore will not be signalled to signal_value
+ * until we have submitted, so the semaphore cannot be destroyed before the call to vkQueueSubmit. */
+ vkd3d_mutex_unlock(&fence->mutex);
+
assert(fence->timeline_semaphore);
timeline_submit_info.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO_KHR;
timeline_submit_info.pNext = NULL;
+ timeline_submit_info.waitSemaphoreValueCount = 1;
+ timeline_submit_info.pWaitSemaphoreValues = &wait_count;
timeline_submit_info.signalSemaphoreValueCount = 0;
timeline_submit_info.pSignalSemaphoreValues = NULL;
- timeline_submit_info.waitSemaphoreValueCount = 1;
- timeline_submit_info.pWaitSemaphoreValues = &value;
-
- if (!(vk_queue = vkd3d_queue_acquire(queue)))
- {
- ERR("Failed to acquire queue %p.\n", queue);
- return E_FAIL;
- }
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submit_info.pNext = &timeline_submit_info;
@@ -6692,14 +6662,11 @@ static HRESULT d3d12_command_queue_wait_timeline_semaphore(struct d3d12_command_
submit_info.signalSemaphoreCount = 0;
submit_info.pSignalSemaphores = NULL;
- ++queue->pending_wait_completion_value;
-
- submit_info.signalSemaphoreCount = 1;
- submit_info.pSignalSemaphores = &queue->wait_completion_semaphore;
- timeline_submit_info.signalSemaphoreValueCount = 1;
- timeline_submit_info.pSignalSemaphoreValues = &queue->pending_wait_completion_value;
-
- d3d12_fence_update_gpu_wait(fence, queue);
+ if (!(vk_queue = vkd3d_queue_acquire(queue)))
+ {
+ ERR("Failed to acquire queue %p.\n", queue);
+ return;
+ }
vr = VK_CALL(vkQueueSubmit(vk_queue, 1, &submit_info, VK_NULL_HANDLE));
@@ -6708,10 +6675,7 @@ static HRESULT d3d12_command_queue_wait_timeline_semaphore(struct d3d12_command_
if (vr < 0)
{
WARN("Failed to submit wait operation, vr %d.\n", vr);
- return hresult_from_vk_result(vr);
}
-
- return S_OK;
}
static HRESULT STDMETHODCALLTYPE d3d12_command_queue_Wait(ID3D12CommandQueue *iface,
@@ -6719,14 +6683,16 @@ static HRESULT STDMETHODCALLTYPE d3d12_command_queue_Wait(ID3D12CommandQueue *if
{
struct d3d12_command_queue *command_queue = impl_from_ID3D12CommandQueue(iface);
struct d3d12_fence *fence = unsafe_impl_from_ID3D12Fence(fence_iface);
+ struct d3d12_command_queue_submission sub;
TRACE("iface %p, fence %p, value %#"PRIx64".\n", iface, fence_iface, value);
- if (command_queue->device->use_timeline_semaphores)
- return d3d12_command_queue_wait_timeline_semaphore(command_queue, fence, value);
+ d3d12_fence_incref(fence);
- FIXME_ONCE("KHR_timeline_semaphore is not available or incompatible. Some wait commands may be unsupported.\n");
- return d3d12_command_queue_wait_binary_semaphore(command_queue, fence, value);
+ sub.type = VKD3D_SUBMISSION_WAIT;
+ sub.u.wait.fence = fence;
+ sub.u.wait.value = value;
+ return d3d12_command_queue_add_submission(command_queue, &sub);
}
static HRESULT STDMETHODCALLTYPE d3d12_command_queue_GetTimestampFrequency(ID3D12CommandQueue *iface,
@@ -6850,10 +6816,118 @@ static const struct ID3D12CommandQueueVtbl d3d12_command_queue_vtbl =
d3d12_command_queue_GetDesc,
};
+static void d3d12_command_queue_acquire_serialised(struct d3d12_command_queue *queue)
+{
+ /* In order to make sure all pending operations queued so far have been submitted, we build a drain
+ * task which will increment the queue_drain_count once the thread has finished all its work. */
+ struct d3d12_command_queue_submission sub;
+ uint64_t current_drain;
+ HRESULT hr;
+
+ sub.type = VKD3D_SUBMISSION_DRAIN;
+
+ vkd3d_mutex_lock(&queue->submission_mutex);
+
+ current_drain = ++queue->target_drain_count;
+ if (FAILED(hr = d3d12_command_queue_add_submission_locked(queue, &sub)))
+ ERR("Failed to submit command, hr %#x.\n", hr);
+
+ while (current_drain != queue->queue_drain_count)
+ vkd3d_cond_wait(&queue->submission_cond, &queue->submission_mutex);
+}
+
+static void d3d12_command_queue_release_serialised(struct d3d12_command_queue *queue)
+{
+ vkd3d_mutex_unlock(&queue->submission_mutex);
+}
+
+static void d3d12_command_queue_execute(struct d3d12_command_queue *command_queue,
+ VkCommandBuffer *cmd, unsigned int count)
+{
+ const struct vkd3d_vk_device_procs *vk_procs = &command_queue->device->vk_procs;
+ struct vkd3d_queue *vkd3d_queue = command_queue->vkd3d_queue;
+ VkSubmitInfo submit_desc;
+ VkQueue vk_queue;
+ VkResult vr;
+
+ TRACE("queue %p, command_list_count %u, command_lists %p.\n",
+ command_queue, count, cmd);
+
+ memset(&submit_desc, 0, sizeof(submit_desc));
+
+ if (!(vk_queue = vkd3d_queue_acquire(vkd3d_queue)))
+ {
+ ERR("Failed to acquire queue %p.\n", vkd3d_queue);
+ return;
+ }
+
+ submit_desc.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+ submit_desc.commandBufferCount = count;
+ submit_desc.pCommandBuffers = cmd;
+
+ if ((vr = VK_CALL(vkQueueSubmit(vk_queue, 1, &submit_desc, VK_NULL_HANDLE))) < 0)
+ ERR("Failed to submit queue(s), vr %d.\n", vr);
+
+ vkd3d_queue_release(vkd3d_queue);
+}
+
+static void *d3d12_command_queue_submission_worker_main(void *userdata)
+{
+ struct d3d12_command_queue_submission submission;
+ struct d3d12_command_queue *queue = userdata;
+
+ vkd3d_set_thread_name("vkd3d_queue");
+
+ for (;;)
+ {
+ vkd3d_mutex_lock(&queue->submission_mutex);
+ while (!queue->submissions_count)
+ vkd3d_cond_wait(&queue->submission_cond, &queue->submission_mutex);
+
+ submission = queue->submissions[0];
+ memmove(queue->submissions, queue->submissions + 1, --queue->submissions_count * sizeof(submission));
+ vkd3d_mutex_unlock(&queue->submission_mutex);
+
+ switch (submission.type)
+ {
+ case VKD3D_SUBMISSION_WAIT:
+ d3d12_command_queue_wait(queue, submission.u.wait.fence, submission.u.wait.value);
+ d3d12_fence_decref(submission.u.wait.fence);
+ break;
+
+ case VKD3D_SUBMISSION_SIGNAL:
+ d3d12_command_queue_signal(queue, submission.u.signal.fence, submission.u.signal.value);
+ d3d12_fence_decref(submission.u.signal.fence);
+ break;
+
+ case VKD3D_SUBMISSION_EXECUTE:
+ d3d12_command_queue_execute(queue, submission.u.execute.cmd, submission.u.execute.cmd_count);
+ vkd3d_free(submission.u.execute.cmd);
+ break;
+
+ case VKD3D_SUBMISSION_DRAIN:
+ vkd3d_mutex_lock(&queue->submission_mutex);
+ ++queue->queue_drain_count;
+ vkd3d_cond_signal(&queue->submission_cond);
+ vkd3d_mutex_unlock(&queue->submission_mutex);
+ break;
+
+ case VKD3D_SUBMISSION_STOP:
+ TRACE("Stopping command queue %p.\n", queue);
+ return NULL;
+
+ default:
+ FIXME("Unhandled submission type %u.\n", submission.type);
+ break;
+ }
+ }
+}
+
static HRESULT d3d12_command_queue_init(struct d3d12_command_queue *queue,
struct d3d12_device *device, const D3D12_COMMAND_QUEUE_DESC *desc)
{
HRESULT hr;
+ int rc;
queue->ID3D12CommandQueue_iface.lpVtbl = &d3d12_command_queue_vtbl;
queue->refcount = 1;
@@ -6868,6 +6942,12 @@ static HRESULT d3d12_command_queue_init(struct d3d12_command_queue *queue,
queue->last_waited_fence = NULL;
queue->last_waited_fence_value = 0;
+ queue->submissions = NULL;
+ queue->submissions_count = 0;
+ queue->submissions_size = 0;
+ queue->target_drain_count = 0;
+ queue->queue_drain_count = 0;
+
if (desc->Priority == D3D12_COMMAND_QUEUE_PRIORITY_GLOBAL_REALTIME)
{
FIXME("Global realtime priority is not implemented.\n");
@@ -6882,15 +6962,40 @@ static HRESULT d3d12_command_queue_init(struct d3d12_command_queue *queue,
if (FAILED(hr = vkd3d_private_store_init(&queue->private_store)))
return hr;
+ if ((rc = vkd3d_mutex_init(&queue->submission_mutex)) < 0)
+ {
+ hr = hresult_from_errno(rc);
+ goto fail_submission_mutex;
+ }
+
+ if ((rc = vkd3d_cond_init(&queue->submission_cond)) < 0)
+ {
+ hr = hresult_from_errno(rc);
+ goto fail_submission_cond;
+ }
+
if (FAILED(hr = vkd3d_fence_worker_start(&queue->fence_worker, queue->vkd3d_queue, device)))
+ goto fail_fence_worker_start;
+
+ if ((rc = vkd3d_create_thread(device->vkd3d_instance, d3d12_command_queue_submission_worker_main, queue, &queue->submission_thread)) < 0)
{
- vkd3d_private_store_destroy(&queue->private_store);
- return hr;
+ hr = hresult_from_errno(rc);
+ goto fail_pthread_create;
}
d3d12_device_add_ref(queue->device = device);
return S_OK;
+
+fail_pthread_create:
+ vkd3d_fence_worker_stop(&queue->fence_worker, device);
+fail_fence_worker_start:
+ vkd3d_cond_destroy(&queue->submission_cond);
+fail_submission_cond:
+ vkd3d_mutex_destroy(&queue->submission_mutex);
+fail_submission_mutex:
+ vkd3d_private_store_destroy(&queue->private_store);
+ return hr;
}
HRESULT d3d12_command_queue_create(struct d3d12_device *device,
@@ -6926,6 +7031,9 @@ VkQueue vkd3d_acquire_vk_queue(ID3D12CommandQueue *queue)
{
struct d3d12_command_queue *d3d12_queue = impl_from_ID3D12CommandQueue(queue);
+ /* For external users of the Vulkan queue, we must ensure that the queue is drained
+ * so that submissions happen in the desired order. */
+ d3d12_command_queue_acquire_serialised(d3d12_queue);
return vkd3d_queue_acquire(d3d12_queue->vkd3d_queue);
}
@@ -6933,7 +7041,8 @@ void vkd3d_release_vk_queue(ID3D12CommandQueue *queue)
{
struct d3d12_command_queue *d3d12_queue = impl_from_ID3D12CommandQueue(queue);
- return vkd3d_queue_release(d3d12_queue->vkd3d_queue);
+ vkd3d_queue_release(d3d12_queue->vkd3d_queue);
+ d3d12_command_queue_release_serialised(d3d12_queue);
}
/* ID3D12CommandSignature */
diff --git a/libs/vkd3d/device.c b/libs/vkd3d/device.c
index 5f8108ec..49145797 100644
--- a/libs/vkd3d/device.c
+++ b/libs/vkd3d/device.c
@@ -747,7 +747,6 @@ struct vkd3d_physical_device_info
VkPhysicalDeviceTexelBufferAlignmentPropertiesEXT texel_buffer_alignment_properties;
VkPhysicalDeviceTransformFeedbackPropertiesEXT xfb_properties;
VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT vertex_divisor_properties;
- VkPhysicalDeviceTimelineSemaphorePropertiesKHR timeline_semaphore_properties;
VkPhysicalDeviceProperties2KHR properties2;
@@ -772,7 +771,6 @@ static void vkd3d_physical_device_info_init(struct vkd3d_physical_device_info *i
VkPhysicalDeviceDescriptorIndexingPropertiesEXT *descriptor_indexing_properties;
VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT *vertex_divisor_properties;
VkPhysicalDeviceTexelBufferAlignmentPropertiesEXT *buffer_alignment_properties;
- VkPhysicalDeviceTimelineSemaphorePropertiesKHR *timeline_semaphore_properties;
VkPhysicalDeviceDescriptorIndexingFeaturesEXT *descriptor_indexing_features;
VkPhysicalDeviceRobustness2FeaturesEXT *robustness2_features;
VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT *vertex_divisor_features;
@@ -799,7 +797,6 @@ static void vkd3d_physical_device_info_init(struct vkd3d_physical_device_info *i
vertex_divisor_features = &info->vertex_divisor_features;
vertex_divisor_properties = &info->vertex_divisor_properties;
timeline_semaphore_features = &info->timeline_semaphore_features;
- timeline_semaphore_properties = &info->timeline_semaphore_properties;
xfb_features = &info->xfb_features;
xfb_properties = &info->xfb_properties;
@@ -841,8 +838,6 @@ static void vkd3d_physical_device_info_init(struct vkd3d_physical_device_info *i
vk_prepend_struct(&info->properties2, xfb_properties);
vertex_divisor_properties->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_PROPERTIES_EXT;
vk_prepend_struct(&info->properties2, vertex_divisor_properties);
- timeline_semaphore_properties->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_PROPERTIES_KHR;
- vk_prepend_struct(&info->properties2, timeline_semaphore_properties);
if (vulkan_info->KHR_get_physical_device_properties2)
VK_CALL(vkGetPhysicalDeviceProperties2KHR(physical_device, &info->properties2));
@@ -1431,7 +1426,6 @@ static HRESULT vkd3d_init_device_caps(struct d3d12_device *device,
vulkan_info->rasterization_stream = physical_device_info->xfb_properties.transformFeedbackRasterizationStreamSelect;
vulkan_info->transform_feedback_queries = physical_device_info->xfb_properties.transformFeedbackQueries;
vulkan_info->max_vertex_attrib_divisor = max(physical_device_info->vertex_divisor_properties.maxVertexAttribDivisor, 1);
- vulkan_info->timeline_semaphore_properties = physical_device_info->timeline_semaphore_properties;
device->feature_options.DoublePrecisionFloatShaderOps = features->shaderFloat64;
device->feature_options.OutputMergerLogicOp = features->logicOp;
@@ -1908,75 +1902,6 @@ static bool d3d12_is_64k_msaa_supported(struct d3d12_device *device)
&& info.Alignment <= 0x10000;
}
-/* A lower value can be signalled on a D3D12 fence. Vulkan timeline semaphores
- * do not support this, but test if it works anyway. */
-static bool d3d12_is_timeline_semaphore_supported(const struct d3d12_device *device)
-{
- const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
- VkTimelineSemaphoreSubmitInfoKHR timeline_submit_info;
- VkSemaphore timeline_semaphore;
- VkSubmitInfo submit_info;
- bool result = false;
- uint64_t value = 0;
- VkQueue vk_queue;
- VkResult vr;
-
- if (!device->vk_info.KHR_timeline_semaphore)
- return false;
-
- if ((vr = vkd3d_create_timeline_semaphore(device, 1, &timeline_semaphore)) < 0)
- {
- WARN("Failed to create timeline semaphore, vr %d.\n", vr);
- return false;
- }
-
- if (!(vk_queue = vkd3d_queue_acquire(device->direct_queue)))
- {
- ERR("Failed to acquire queue %p.\n", device->direct_queue);
- VK_CALL(vkDestroySemaphore(device->vk_device, timeline_semaphore, NULL));
- return false;
- }
-
- submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
- submit_info.pNext = &timeline_submit_info;
- submit_info.waitSemaphoreCount = 0;
- submit_info.pWaitSemaphores = NULL;
- submit_info.pWaitDstStageMask = NULL;
- submit_info.commandBufferCount = 0;
- submit_info.pCommandBuffers = NULL;
- submit_info.signalSemaphoreCount = 1;
- submit_info.pSignalSemaphores = &timeline_semaphore;
-
- timeline_submit_info.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO_KHR;
- timeline_submit_info.pNext = NULL;
- timeline_submit_info.pSignalSemaphoreValues = &value;
- timeline_submit_info.signalSemaphoreValueCount = 1;
- timeline_submit_info.waitSemaphoreValueCount = 0;
- timeline_submit_info.pWaitSemaphoreValues = NULL;
-
- vr = VK_CALL(vkQueueSubmit(vk_queue, 1, &submit_info, VK_NULL_HANDLE));
-
- if (vr >= 0)
- {
- if ((vr = VK_CALL(vkQueueWaitIdle(vk_queue))) < 0)
- WARN("Failed to wait for queue, vr %d.\n", vr);
-
- if ((vr = VK_CALL(vkGetSemaphoreCounterValueKHR(device->vk_device, timeline_semaphore, &value))) < 0)
- ERR("Failed to get Vulkan semaphore status, vr %d.\n", vr);
- else if (!(result = !value))
- WARN("Disabling timeline semaphore use due to incompatible behaviour.\n");
- }
- else
- {
- WARN("Failed to submit signal operation, vr %d.\n", vr);
- }
-
- vkd3d_queue_release(device->direct_queue);
- VK_CALL(vkDestroySemaphore(device->vk_device, timeline_semaphore, NULL));
-
- return result;
-}
-
static HRESULT vkd3d_create_vk_device(struct d3d12_device *device,
const struct vkd3d_device_create_info *create_info)
{
@@ -2075,10 +2000,6 @@ static HRESULT vkd3d_create_vk_device(struct d3d12_device *device,
}
device->feature_options4.MSAA64KBAlignedTextureSupported = d3d12_is_64k_msaa_supported(device);
- device->use_timeline_semaphores = d3d12_is_timeline_semaphore_supported(device)
- && vkd3d_queue_init_timeline_semaphore(device->direct_queue, device)
- && vkd3d_queue_init_timeline_semaphore(device->compute_queue, device)
- && vkd3d_queue_init_timeline_semaphore(device->copy_queue, device);
TRACE("Created Vulkan device %p.\n", vk_device);
diff --git a/libs/vkd3d/vkd3d_private.h b/libs/vkd3d/vkd3d_private.h
index 4e03145d..9989f20a 100644
--- a/libs/vkd3d/vkd3d_private.h
+++ b/libs/vkd3d/vkd3d_private.h
@@ -59,7 +59,6 @@
#define VKD3D_MAX_SHADER_EXTENSIONS 3u
#define VKD3D_MAX_SHADER_STAGES 5u
#define VKD3D_MAX_VK_SYNC_OBJECTS 4u
-#define VKD3D_MAX_FENCE_WAITING_QUEUES 4u
#define VKD3D_MAX_DESCRIPTOR_SETS 64u
/* D3D12 binding tier 3 has a limit of 2048 samplers. */
#define VKD3D_MAX_DESCRIPTOR_SET_SAMPLERS 2048u
@@ -152,8 +151,6 @@ struct vkd3d_vulkan_info
VkPhysicalDeviceTexelBufferAlignmentPropertiesEXT texel_buffer_alignment_properties;
- VkPhysicalDeviceTimelineSemaphorePropertiesKHR timeline_semaphore_properties;
-
unsigned int shader_extension_count;
enum vkd3d_shader_spirv_extension shader_extensions[VKD3D_MAX_SHADER_EXTENSIONS];
@@ -502,15 +499,17 @@ HRESULT vkd3d_set_private_data_interface(struct vkd3d_private_store *store, cons
struct vkd3d_signaled_semaphore
{
uint64_t value;
- VkSemaphore vk_semaphore;
- VkFence vk_fence;
- bool is_acquired;
-};
-
-struct vkd3d_pending_fence_wait
-{
- const struct vkd3d_queue *queue;
- uint64_t pending_value;
+ union
+ {
+ struct
+ {
+ VkSemaphore vk_semaphore;
+ VkFence vk_fence;
+ bool is_acquired;
+ } binary;
+ uint64_t timeline_value;
+ } u;
+ const struct vkd3d_queue *signalling_queue;
};
/* ID3D12Fence */
@@ -521,7 +520,9 @@ struct d3d12_fence
LONG refcount;
uint64_t value;
+ uint64_t max_pending_value;
struct vkd3d_mutex mutex;
+ struct vkd3d_cond cond;
struct vkd3d_cond null_event_cond;
struct vkd3d_waiting_event
@@ -534,9 +535,8 @@ struct d3d12_fence
size_t event_count;
VkSemaphore timeline_semaphore;
+ uint64_t timeline_value;
uint64_t pending_timeline_value;
- struct vkd3d_pending_fence_wait gpu_waits[VKD3D_MAX_FENCE_WAITING_QUEUES];
- unsigned int gpu_wait_count;
struct vkd3d_signaled_semaphore *semaphores;
size_t semaphores_size;
@@ -1294,9 +1294,6 @@ struct vkd3d_queue
VkQueueFlags vk_queue_flags;
uint32_t timestamp_bits;
- VkSemaphore wait_completion_semaphore;
- uint64_t pending_wait_completion_value;
-
struct
{
VkSemaphore vk_semaphore;
@@ -1311,10 +1308,47 @@ struct vkd3d_queue
VkQueue vkd3d_queue_acquire(struct vkd3d_queue *queue);
HRESULT vkd3d_queue_create(struct d3d12_device *device, uint32_t family_index,
const VkQueueFamilyProperties *properties, struct vkd3d_queue **queue);
-bool vkd3d_queue_init_timeline_semaphore(struct vkd3d_queue *queue, struct d3d12_device *device);
void vkd3d_queue_destroy(struct vkd3d_queue *queue, struct d3d12_device *device);
void vkd3d_queue_release(struct vkd3d_queue *queue);
+enum vkd3d_submission_type
+{
+ VKD3D_SUBMISSION_WAIT,
+ VKD3D_SUBMISSION_SIGNAL,
+ VKD3D_SUBMISSION_EXECUTE,
+ VKD3D_SUBMISSION_DRAIN,
+ VKD3D_SUBMISSION_STOP,
+};
+
+struct d3d12_command_queue_submission_wait
+{
+ struct d3d12_fence *fence;
+ uint64_t value;
+};
+
+struct d3d12_command_queue_submission_signal
+{
+ struct d3d12_fence *fence;
+ uint64_t value;
+};
+
+struct d3d12_command_queue_submission_execute
+{
+ VkCommandBuffer *cmd;
+ unsigned int cmd_count;
+};
+
+struct d3d12_command_queue_submission
+{
+ enum vkd3d_submission_type type;
+ union
+ {
+ struct d3d12_command_queue_submission_wait wait;
+ struct d3d12_command_queue_submission_signal signal;
+ struct d3d12_command_queue_submission_execute execute;
+ } u;
+};
+
/* ID3D12CommandQueue */
struct d3d12_command_queue
{
@@ -1331,6 +1365,16 @@ struct d3d12_command_queue
struct d3d12_device *device;
+ struct vkd3d_mutex submission_mutex;
+ struct vkd3d_cond submission_cond;
+ union vkd3d_thread_handle submission_thread;
+
+ struct d3d12_command_queue_submission *submissions;
+ size_t submissions_count;
+ size_t submissions_size;
+ uint64_t target_drain_count;
+ uint64_t queue_drain_count;
+
struct vkd3d_private_store private_store;
};
@@ -1470,7 +1514,6 @@ struct d3d12_device
VkDescriptorPoolSize vk_pool_sizes[VKD3D_DESCRIPTOR_POOL_COUNT];
struct vkd3d_vk_descriptor_heap_layout vk_descriptor_heap_layouts[VKD3D_SET_INDEX_COUNT];
bool use_vk_heaps;
- bool use_timeline_semaphores;
};
HRESULT d3d12_device_create(struct vkd3d_instance *instance,
diff --git a/tests/d3d12.c b/tests/d3d12.c
index 015c3122..5f83a373 100644
--- a/tests/d3d12.c
+++ b/tests/d3d12.c
@@ -33224,9 +33224,7 @@ static void test_queue_wait(void)
command_list = context.list;
queue = context.queue;
- /* 'queue2' must not map to the same command queue as 'queue', or Wait() before GPU signal will fail.
- * Using a compute queue fixes this on most hardware, but it may still fail on low spec hardware. */
- queue2 = create_command_queue(device, D3D12_COMMAND_LIST_TYPE_COMPUTE, D3D12_COMMAND_QUEUE_PRIORITY_NORMAL);
+ queue2 = create_command_queue(device, D3D12_COMMAND_LIST_TYPE_DIRECT, D3D12_COMMAND_QUEUE_PRIORITY_NORMAL);
event = create_event();
ok(event, "Failed to create event.\n");
--
2.35.1
April 29, 2022
[PATCH vkd3d v2 3/4] vkd3d: Replace the signaled semaphore list with a resizable array.
by Conor McCarthy
Order does not need to be preserved here, and another function will add
to this array when mapped timeline semaphores are implemented.
Signed-off-by: Conor McCarthy <cmccarthy(a)codeweavers.com>
---
v2: Tidy up the semaphore garbage collection loop, and removal of a
semaphore from a fence.
---
libs/vkd3d/command.c | 44 +++++++++++++++++++++++---------------
libs/vkd3d/vkd3d_private.h | 4 ++--
2 files changed, 29 insertions(+), 19 deletions(-)
diff --git a/libs/vkd3d/command.c b/libs/vkd3d/command.c
index 7690760c..55e6be58 100644
--- a/libs/vkd3d/command.c
+++ b/libs/vkd3d/command.c
@@ -590,32 +590,34 @@ static void d3d12_fence_garbage_collect_vk_semaphores_locked(struct d3d12_fence
{
struct d3d12_device *device = fence->device;
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
- struct vkd3d_signaled_semaphore *current, *p;
- unsigned int semaphore_count;
+ struct vkd3d_signaled_semaphore *current;
+ unsigned int i, semaphore_count;
semaphore_count = fence->semaphore_count;
if (!destroy_all && semaphore_count < VKD3D_MAX_VK_SYNC_OBJECTS)
return;
- LIST_FOR_EACH_ENTRY_SAFE(current, p, &fence->semaphores, struct vkd3d_signaled_semaphore, entry)
+ i = 0;
+ while (i < fence->semaphore_count)
{
if (!destroy_all && fence->semaphore_count < VKD3D_MAX_VK_SYNC_OBJECTS)
break;
+ current = &fence->semaphores[i];
/* The semaphore doesn't have a pending signal operation if the fence
* was signaled. */
if ((current->vk_fence || current->is_acquired) && !destroy_all)
+ {
+ ++i;
continue;
+ }
if (current->vk_fence)
WARN("Destroying potentially pending semaphore.\n");
assert(!current->is_acquired);
VK_CALL(vkDestroySemaphore(device->vk_device, current->vk_semaphore, NULL));
- list_remove(¤t->entry);
- vkd3d_free(current);
-
- --fence->semaphore_count;
+ fence->semaphores[i] = fence->semaphores[--fence->semaphore_count];
}
if (semaphore_count != fence->semaphore_count)
@@ -656,6 +658,7 @@ static struct vkd3d_signaled_semaphore *d3d12_fence_acquire_vk_semaphore(struct
struct vkd3d_signaled_semaphore *semaphore;
struct vkd3d_signaled_semaphore *current;
uint64_t semaphore_value;
+ unsigned int i;
int rc;
TRACE("fence %p, value %#"PRIx64".\n", fence, value);
@@ -669,8 +672,9 @@ static struct vkd3d_signaled_semaphore *d3d12_fence_acquire_vk_semaphore(struct
semaphore = NULL;
semaphore_value = ~(uint64_t)0;
- LIST_FOR_EACH_ENTRY(current, &fence->semaphores, struct vkd3d_signaled_semaphore, entry)
+ for (i = 0; i < fence->semaphore_count; ++i)
{
+ current = &fence->semaphores[i];
/* Prefer a semaphore with the smallest value. */
if (!current->is_acquired && current->value >= value && semaphore_value >= current->value)
{
@@ -703,10 +707,7 @@ static void d3d12_fence_remove_vk_semaphore(struct d3d12_fence *fence, struct vk
assert(semaphore->is_acquired);
- list_remove(&semaphore->entry);
- vkd3d_free(semaphore);
-
- --fence->semaphore_count;
+ *semaphore = fence->semaphores[--fence->semaphore_count];
vkd3d_mutex_unlock(&fence->mutex);
}
@@ -751,14 +752,20 @@ static HRESULT d3d12_fence_add_vk_semaphore(struct d3d12_fence *fence,
d3d12_fence_garbage_collect_vk_semaphores_locked(fence, false);
+ if (!vkd3d_array_reserve((void**)&fence->semaphores, &fence->semaphores_size,
+ fence->semaphore_count + 1, sizeof(*fence->semaphores)))
+ {
+ ERR("Failed to add semaphore.\n");
+ vkd3d_mutex_unlock(&fence->mutex);
+ return false;
+ }
+
+ semaphore = &fence->semaphores[fence->semaphore_count++];
semaphore->value = value;
semaphore->vk_semaphore = vk_semaphore;
semaphore->vk_fence = vk_fence;
semaphore->is_acquired = false;
- list_add_tail(&fence->semaphores, &semaphore->entry);
- ++fence->semaphore_count;
-
vkd3d_mutex_unlock(&fence->mutex);
return hr;
@@ -821,8 +828,9 @@ static HRESULT d3d12_fence_signal(struct d3d12_fence *fence, uint64_t value, VkF
{
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
- LIST_FOR_EACH_ENTRY(current, &fence->semaphores, struct vkd3d_signaled_semaphore, entry)
+ for (i = 0; i < fence->semaphore_count; ++i)
{
+ current = &fence->semaphores[i];
if (current->vk_fence == vk_fence)
current->vk_fence = VK_NULL_HANDLE;
}
@@ -910,6 +918,7 @@ static void d3d12_fence_decref(struct d3d12_fence *fence)
d3d12_fence_destroy_vk_objects(fence);
vkd3d_free(fence->events);
+ vkd3d_free(fence->semaphores);
if ((rc = vkd3d_mutex_destroy(&fence->mutex)))
ERR("Failed to destroy mutex, error %d.\n", rc);
vkd3d_cond_destroy(&fence->null_event_cond);
@@ -1278,7 +1287,8 @@ static HRESULT d3d12_fence_init(struct d3d12_fence *fence, struct d3d12_device *
fence->pending_timeline_value = initial_value;
fence->gpu_wait_count = 0;
- list_init(&fence->semaphores);
+ fence->semaphores = NULL;
+ fence->semaphores_size = 0;
fence->semaphore_count = 0;
memset(fence->old_vk_fences, 0, sizeof(fence->old_vk_fences));
diff --git a/libs/vkd3d/vkd3d_private.h b/libs/vkd3d/vkd3d_private.h
index 350382cd..4e03145d 100644
--- a/libs/vkd3d/vkd3d_private.h
+++ b/libs/vkd3d/vkd3d_private.h
@@ -501,7 +501,6 @@ HRESULT vkd3d_set_private_data_interface(struct vkd3d_private_store *store, cons
struct vkd3d_signaled_semaphore
{
- struct list entry;
uint64_t value;
VkSemaphore vk_semaphore;
VkFence vk_fence;
@@ -539,7 +538,8 @@ struct d3d12_fence
struct vkd3d_pending_fence_wait gpu_waits[VKD3D_MAX_FENCE_WAITING_QUEUES];
unsigned int gpu_wait_count;
- struct list semaphores;
+ struct vkd3d_signaled_semaphore *semaphores;
+ size_t semaphores_size;
unsigned int semaphore_count;
VkFence old_vk_fences[VKD3D_MAX_VK_SYNC_OBJECTS];
--
2.35.1
April 29, 2022
[PATCH vkd3d v2 2/4] vkd3d: Create one fence worker thread per command queue.
by Conor McCarthy
Simplifies the handling of GPU waits, and in vkd3d-proton is reported
to increase performance when support for multiple Vulkan queues is
enabled, because it avoids the problem of fences being signaled while
they sit in the pending buffer waiting to be moved to the wait buffer.
Based on a vkd3d-proton patch by Philip Rebohle.
Signed-off-by: Conor McCarthy <cmccarthy(a)codeweavers.com>
---
v2: Do not replace device->use_timeline_semaphores
---
libs/vkd3d/command.c | 274 ++++++++++++-------------------------
libs/vkd3d/device.c | 8 +-
libs/vkd3d/vkd3d_private.h | 29 ++--
3 files changed, 96 insertions(+), 215 deletions(-)
diff --git a/libs/vkd3d/command.c b/libs/vkd3d/command.c
index 7a373b34..7690760c 100644
--- a/libs/vkd3d/command.c
+++ b/libs/vkd3d/command.c
@@ -280,22 +280,19 @@ static HRESULT vkd3d_enqueue_gpu_fence(struct vkd3d_fence_worker *worker,
return hresult_from_errno(rc);
}
- if (!vkd3d_array_reserve((void **)&worker->enqueued_fences, &worker->enqueued_fences_size,
- worker->enqueued_fence_count + 1, sizeof(*worker->enqueued_fences)))
+ if (!vkd3d_array_reserve((void **)&worker->fences, &worker->fences_size,
+ worker->fence_count + 1, sizeof(*worker->fences)))
{
ERR("Failed to add GPU fence.\n");
vkd3d_mutex_unlock(&worker->mutex);
return E_OUTOFMEMORY;
}
- worker->enqueued_fences[worker->enqueued_fence_count].vk_fence = vk_fence;
- worker->enqueued_fences[worker->enqueued_fence_count].vk_semaphore = VK_NULL_HANDLE;
- waiting_fence = &worker->enqueued_fences[worker->enqueued_fence_count].waiting_fence;
+ waiting_fence = &worker->fences[worker->fence_count++];
waiting_fence->fence = fence;
waiting_fence->value = value;
- waiting_fence->queue = queue;
+ waiting_fence->u.vk_fence = vk_fence;
waiting_fence->queue_sequence_number = queue_sequence_number;
- ++worker->enqueued_fence_count;
d3d12_fence_incref(fence);
@@ -305,219 +302,124 @@ static HRESULT vkd3d_enqueue_gpu_fence(struct vkd3d_fence_worker *worker,
return S_OK;
}
-static void vkd3d_fence_worker_move_enqueued_fences_locked(struct vkd3d_fence_worker *worker)
-{
- unsigned int i;
- bool timeline;
- size_t count;
- bool ret;
-
- if (!worker->enqueued_fence_count)
- return;
-
- count = worker->fence_count + worker->enqueued_fence_count;
-
- if ((timeline = worker->device->use_timeline_semaphores))
- {
- ret = vkd3d_array_reserve((void **) &worker->vk_semaphores, &worker->vk_semaphores_size,
- count, sizeof(*worker->vk_semaphores));
- ret &= vkd3d_array_reserve((void **) &worker->semaphore_wait_values, &worker->semaphore_wait_values_size,
- count, sizeof(*worker->semaphore_wait_values));
- }
- else
- {
- ret = vkd3d_array_reserve((void **)&worker->vk_fences, &worker->vk_fences_size,
- count, sizeof(*worker->vk_fences));
- }
- ret &= vkd3d_array_reserve((void **)&worker->fences, &worker->fences_size,
- count, sizeof(*worker->fences));
- if (!ret)
- {
- ERR("Failed to reserve memory.\n");
- return;
- }
-
- for (i = 0; i < worker->enqueued_fence_count; ++i)
- {
- struct vkd3d_enqueued_fence *current = &worker->enqueued_fences[i];
-
- if (timeline)
- {
- worker->vk_semaphores[worker->fence_count] = current->vk_semaphore;
- worker->semaphore_wait_values[worker->fence_count] = current->waiting_fence.value;
- }
- else
- {
- worker->vk_fences[worker->fence_count] = current->vk_fence;
- }
-
- worker->fences[worker->fence_count] = current->waiting_fence;
- ++worker->fence_count;
- }
- assert(worker->fence_count == count);
- worker->enqueued_fence_count = 0;
-}
-
-static void vkd3d_wait_for_gpu_timeline_semaphores(struct vkd3d_fence_worker *worker)
+static void vkd3d_wait_for_gpu_timeline_semaphore(struct vkd3d_fence_worker *worker,
+ const struct vkd3d_waiting_fence *waiting_fence)
{
const struct d3d12_device *device = worker->device;
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
VkSemaphoreWaitInfoKHR wait_info;
- VkSemaphore vk_semaphore;
uint64_t counter_value;
- unsigned int i, j;
+ VkResult vr;
HRESULT hr;
- int vr;
-
- if (!worker->fence_count)
- return;
wait_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO_KHR;
wait_info.pNext = NULL;
- wait_info.flags = VK_SEMAPHORE_WAIT_ANY_BIT_KHR;
- wait_info.pSemaphores = worker->vk_semaphores;
- wait_info.semaphoreCount = worker->fence_count;
- wait_info.pValues = worker->semaphore_wait_values;
+ wait_info.flags = 0;
+ wait_info.semaphoreCount = 1;
+ wait_info.pSemaphores = &waiting_fence->u.vk_semaphore;
+ wait_info.pValues = &waiting_fence->value;
vr = VK_CALL(vkWaitSemaphoresKHR(device->vk_device, &wait_info, ~(uint64_t)0));
if (vr == VK_TIMEOUT)
return;
if (vr != VK_SUCCESS)
{
- ERR("Failed to wait for Vulkan timeline semaphores, vr %d.\n", vr);
+ ERR("Failed to wait for Vulkan timeline semaphore, vr %d.\n", vr);
return;
}
- for (i = 0, j = 0; i < worker->fence_count; ++i)
+ if ((vr = VK_CALL(vkGetSemaphoreCounterValueKHR(device->vk_device, waiting_fence->u.vk_semaphore,
+ &counter_value))) < 0)
{
- struct vkd3d_waiting_fence *current = &worker->fences[i];
-
- vk_semaphore = worker->vk_semaphores[i];
- if ((vr = VK_CALL(vkGetSemaphoreCounterValueKHR(device->vk_device, vk_semaphore, &counter_value))) < 0)
- {
- ERR("Failed to get Vulkan semaphore value, vr %d.\n", vr);
- }
- else if (counter_value >= current->value)
- {
- TRACE("Signaling fence %p value %#"PRIx64".\n", current->fence, current->value);
- if (FAILED(hr = d3d12_fence_signal(current->fence, counter_value, VK_NULL_HANDLE)))
- ERR("Failed to signal D3D12 fence, hr %#x.\n", hr);
-
- d3d12_fence_decref(current->fence);
- continue;
- }
+ ERR("Failed to get Vulkan semaphore value, vr %d.\n", vr);
+ }
+ else
+ {
+ TRACE("Signaling fence %p value %#"PRIx64".\n", waiting_fence->fence, waiting_fence->value);
+ if (FAILED(hr = d3d12_fence_signal(waiting_fence->fence, counter_value, VK_NULL_HANDLE)))
+ ERR("Failed to signal D3D12 fence, hr %#x.\n", hr);
- if (i != j)
- {
- worker->vk_semaphores[j] = worker->vk_semaphores[i];
- worker->semaphore_wait_values[j] = worker->semaphore_wait_values[i];
- worker->fences[j] = worker->fences[i];
- }
- ++j;
+ d3d12_fence_decref(waiting_fence->fence);
}
- worker->fence_count = j;
}
-static void vkd3d_wait_for_gpu_fences(struct vkd3d_fence_worker *worker)
+static void vkd3d_wait_for_gpu_fence(struct vkd3d_fence_worker *worker,
+ const struct vkd3d_waiting_fence *waiting_fence)
{
struct d3d12_device *device = worker->device;
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
- unsigned int i, j;
- VkFence vk_fence;
HRESULT hr;
int vr;
- if (!worker->fence_count)
- return;
-
- vr = VK_CALL(vkWaitForFences(device->vk_device,
- worker->fence_count, worker->vk_fences, VK_FALSE, ~(uint64_t)0));
+ vr = VK_CALL(vkWaitForFences(device->vk_device, 1, &waiting_fence->u.vk_fence, VK_FALSE, ~(uint64_t)0));
if (vr == VK_TIMEOUT)
return;
if (vr != VK_SUCCESS)
{
- ERR("Failed to wait for Vulkan fences, vr %d.\n", vr);
+ ERR("Failed to wait for Vulkan fence, vr %d.\n", vr);
return;
}
- for (i = 0, j = 0; i < worker->fence_count; ++i)
- {
- vk_fence = worker->vk_fences[i];
- if (!(vr = VK_CALL(vkGetFenceStatus(device->vk_device, vk_fence))))
- {
- struct vkd3d_waiting_fence *current = &worker->fences[i];
+ TRACE("Signaling fence %p value %#"PRIx64".\n", waiting_fence->fence, waiting_fence->value);
+ if (FAILED(hr = d3d12_fence_signal(waiting_fence->fence, waiting_fence->value, waiting_fence->u.vk_fence)))
+ ERR("Failed to signal D3D12 fence, hr %#x.\n", hr);
- TRACE("Signaling fence %p value %#"PRIx64".\n", current->fence, current->value);
- if (FAILED(hr = d3d12_fence_signal(current->fence, current->value, vk_fence)))
- ERR("Failed to signal D3D12 fence, hr %#x.\n", hr);
+ d3d12_fence_decref(waiting_fence->fence);
- d3d12_fence_decref(current->fence);
-
- vkd3d_queue_update_sequence_number(current->queue, current->queue_sequence_number, device);
- continue;
- }
-
- if (vr != VK_NOT_READY)
- ERR("Failed to get Vulkan fence status, vr %d.\n", vr);
-
- if (i != j)
- {
- worker->vk_fences[j] = worker->vk_fences[i];
- worker->fences[j] = worker->fences[i];
- }
- ++j;
- }
- worker->fence_count = j;
+ vkd3d_queue_update_sequence_number(worker->queue, waiting_fence->queue_sequence_number, device);
}
static void *vkd3d_fence_worker_main(void *arg)
{
+ size_t old_fences_size, cur_fences_size = 0, cur_fence_count = 0;
+ struct vkd3d_waiting_fence *old_fences, *cur_fences = NULL;
struct vkd3d_fence_worker *worker = arg;
+ unsigned int i;
int rc;
vkd3d_set_thread_name("vkd3d_fence");
for (;;)
{
- worker->wait_for_gpu_fences(worker);
+ if ((rc = vkd3d_mutex_lock(&worker->mutex)))
+ {
+ ERR("Failed to lock mutex, error %d.\n", rc);
+ break;
+ }
- if (!worker->fence_count || InterlockedAdd(&worker->enqueued_fence_count, 0))
+ if (!worker->fence_count && !worker->should_exit && (rc = vkd3d_cond_wait(&worker->cond, &worker->mutex)))
{
- if ((rc = vkd3d_mutex_lock(&worker->mutex)))
- {
- ERR("Failed to lock mutex, error %d.\n", rc);
- break;
- }
+ ERR("Failed to wait on condition variable, error %d.\n", rc);
+ vkd3d_mutex_unlock(&worker->mutex);
+ break;
+ }
- if (worker->enqueued_fence_count)
- {
- vkd3d_fence_worker_move_enqueued_fences_locked(worker);
- }
- else
- {
- if (worker->should_exit)
- {
- vkd3d_mutex_unlock(&worker->mutex);
- break;
- }
+ if (worker->should_exit)
+ break;
- if ((rc = vkd3d_cond_wait(&worker->cond, &worker->mutex)))
- {
- ERR("Failed to wait on condition variable, error %d.\n", rc);
- vkd3d_mutex_unlock(&worker->mutex);
- break;
- }
- }
+ old_fences_size = cur_fences_size;
+ old_fences = cur_fences;
- vkd3d_mutex_unlock(&worker->mutex);
- }
+ cur_fence_count = worker->fence_count;
+ cur_fences_size = worker->fences_size;
+ cur_fences = worker->fences;
+
+ worker->fence_count = 0;
+ worker->fences_size = old_fences_size;
+ worker->fences = old_fences;
+
+ vkd3d_mutex_unlock(&worker->mutex);
+
+ for (i = 0; i < cur_fence_count; ++i)
+ worker->wait_for_gpu_fence(worker, &cur_fences[i]);
}
+ vkd3d_free(cur_fences);
return NULL;
}
-HRESULT vkd3d_fence_worker_start(struct vkd3d_fence_worker *worker,
- struct d3d12_device *device)
+static HRESULT vkd3d_fence_worker_start(struct vkd3d_fence_worker *worker,
+ struct vkd3d_queue *queue, struct d3d12_device *device)
{
HRESULT hr;
int rc;
@@ -525,25 +427,15 @@ HRESULT vkd3d_fence_worker_start(struct vkd3d_fence_worker *worker,
TRACE("worker %p.\n", worker);
worker->should_exit = false;
+ worker->queue = queue;
worker->device = device;
- worker->enqueued_fence_count = 0;
- worker->enqueued_fences = NULL;
- worker->enqueued_fences_size = 0;
-
worker->fence_count = 0;
-
- worker->vk_fences = NULL;
- worker->vk_fences_size = 0;
worker->fences = NULL;
worker->fences_size = 0;
- worker->vk_semaphores = NULL;
- worker->vk_semaphores_size = 0;
- worker->semaphore_wait_values = NULL;
- worker->semaphore_wait_values_size = 0;
- worker->wait_for_gpu_fences = device->use_timeline_semaphores
- ? vkd3d_wait_for_gpu_timeline_semaphores : vkd3d_wait_for_gpu_fences;
+ worker->wait_for_gpu_fence = device->use_timeline_semaphores
+ ? vkd3d_wait_for_gpu_timeline_semaphore : vkd3d_wait_for_gpu_fence;
if ((rc = vkd3d_mutex_init(&worker->mutex)))
{
@@ -577,7 +469,7 @@ HRESULT vkd3d_fence_worker_start(struct vkd3d_fence_worker *worker,
return hr;
}
-HRESULT vkd3d_fence_worker_stop(struct vkd3d_fence_worker *worker,
+static HRESULT vkd3d_fence_worker_stop(struct vkd3d_fence_worker *worker,
struct d3d12_device *device)
{
HRESULT hr;
@@ -603,11 +495,7 @@ HRESULT vkd3d_fence_worker_stop(struct vkd3d_fence_worker *worker,
vkd3d_cond_destroy(&worker->cond);
vkd3d_cond_destroy(&worker->fence_destruction_cond);
- vkd3d_free(worker->enqueued_fences);
- vkd3d_free(worker->vk_fences);
vkd3d_free(worker->fences);
- vkd3d_free(worker->vk_semaphores);
- vkd3d_free(worker->semaphore_wait_values);
return S_OK;
}
@@ -6227,6 +6115,8 @@ static ULONG STDMETHODCALLTYPE d3d12_command_queue_Release(ID3D12CommandQueue *i
{
struct d3d12_device *device = command_queue->device;
+ vkd3d_fence_worker_stop(&command_queue->fence_worker, device);
+
vkd3d_private_store_destroy(&command_queue->private_store);
vkd3d_free(command_queue);
@@ -6455,20 +6345,18 @@ static HRESULT vkd3d_enqueue_timeline_semaphore(struct vkd3d_fence_worker *worke
return hresult_from_errno(rc);
}
- if (!vkd3d_array_reserve((void **)&worker->enqueued_fences, &worker->enqueued_fences_size,
- worker->enqueued_fence_count + 1, sizeof(*worker->enqueued_fences)))
+ if (!vkd3d_array_reserve((void **)&worker->fences, &worker->fences_size,
+ worker->fence_count + 1, sizeof(*worker->fences)))
{
ERR("Failed to add GPU timeline semaphore.\n");
vkd3d_mutex_unlock(&worker->mutex);
return E_OUTOFMEMORY;
}
- worker->enqueued_fences[worker->enqueued_fence_count].vk_semaphore = vk_semaphore;
- waiting_fence = &worker->enqueued_fences[worker->enqueued_fence_count].waiting_fence;
+ waiting_fence = &worker->fences[worker->fence_count++];
waiting_fence->fence = fence;
waiting_fence->value = value;
- waiting_fence->queue = queue;
- ++worker->enqueued_fence_count;
+ waiting_fence->u.vk_semaphore = vk_semaphore;
d3d12_fence_incref(fence);
@@ -6574,7 +6462,10 @@ static HRESULT STDMETHODCALLTYPE d3d12_command_queue_Signal(ID3D12CommandQueue *
}
if (device->use_timeline_semaphores)
- return vkd3d_enqueue_timeline_semaphore(&device->fence_worker, vk_semaphore, fence, value, vkd3d_queue);
+ {
+ return vkd3d_enqueue_timeline_semaphore(&command_queue->fence_worker,
+ vk_semaphore, fence, value, vkd3d_queue);
+ }
if (vk_semaphore && SUCCEEDED(hr = d3d12_fence_add_vk_semaphore(fence, vk_semaphore, vk_fence, value)))
vk_semaphore = VK_NULL_HANDLE;
@@ -6582,8 +6473,11 @@ static HRESULT STDMETHODCALLTYPE d3d12_command_queue_Signal(ID3D12CommandQueue *
vr = VK_CALL(vkGetFenceStatus(device->vk_device, vk_fence));
if (vr == VK_NOT_READY)
{
- if (SUCCEEDED(hr = vkd3d_enqueue_gpu_fence(&device->fence_worker, vk_fence, fence, value, vkd3d_queue, sequence_number)))
+ if (SUCCEEDED(hr = vkd3d_enqueue_gpu_fence(&command_queue->fence_worker,
+ vk_fence, fence, value, vkd3d_queue, sequence_number)))
+ {
vk_fence = VK_NULL_HANDLE;
+ }
}
else if (vr == VK_SUCCESS)
{
@@ -6978,6 +6872,12 @@ static HRESULT d3d12_command_queue_init(struct d3d12_command_queue *queue,
if (FAILED(hr = vkd3d_private_store_init(&queue->private_store)))
return hr;
+ if (FAILED(hr = vkd3d_fence_worker_start(&queue->fence_worker, queue->vkd3d_queue, device)))
+ {
+ vkd3d_private_store_destroy(&queue->private_store);
+ return hr;
+ }
+
d3d12_device_add_ref(queue->device = device);
return S_OK;
diff --git a/libs/vkd3d/device.c b/libs/vkd3d/device.c
index 1522065f..5f8108ec 100644
--- a/libs/vkd3d/device.c
+++ b/libs/vkd3d/device.c
@@ -2711,7 +2711,6 @@ static ULONG STDMETHODCALLTYPE d3d12_device_Release(ID3D12Device *iface)
vkd3d_gpu_va_allocator_cleanup(&device->gpu_va_allocator);
vkd3d_gpu_descriptor_allocator_cleanup(&device->gpu_descriptor_allocator);
vkd3d_render_pass_cache_cleanup(&device->render_pass_cache, device);
- vkd3d_fence_worker_stop(&device->fence_worker, device);
d3d12_device_destroy_pipeline_cache(device);
d3d12_device_destroy_vkd3d_queues(device);
for (i = 0; i < ARRAY_SIZE(device->desc_mutex); ++i)
@@ -4346,11 +4345,8 @@ static HRESULT d3d12_device_init(struct d3d12_device *device,
if (FAILED(hr = vkd3d_private_store_init(&device->private_store)))
goto out_free_pipeline_cache;
- if (FAILED(hr = vkd3d_fence_worker_start(&device->fence_worker, device)))
- goto out_free_private_store;
-
if (FAILED(hr = vkd3d_init_format_info(device)))
- goto out_stop_fence_worker;
+ goto out_free_private_store;
if (FAILED(hr = vkd3d_init_null_resources(&device->null_resources, device)))
goto out_cleanup_format_info;
@@ -4382,8 +4378,6 @@ out_destroy_null_resources:
vkd3d_destroy_null_resources(&device->null_resources, device);
out_cleanup_format_info:
vkd3d_cleanup_format_info(device);
-out_stop_fence_worker:
- vkd3d_fence_worker_stop(&device->fence_worker, device);
out_free_private_store:
vkd3d_private_store_destroy(&device->private_store);
out_free_pipeline_cache:
diff --git a/libs/vkd3d/vkd3d_private.h b/libs/vkd3d/vkd3d_private.h
index a0163c8d..350382cd 100644
--- a/libs/vkd3d/vkd3d_private.h
+++ b/libs/vkd3d/vkd3d_private.h
@@ -335,7 +335,11 @@ struct vkd3d_waiting_fence
{
struct d3d12_fence *fence;
uint64_t value;
- struct vkd3d_queue *queue;
+ union
+ {
+ VkFence vk_fence;
+ VkSemaphore vk_semaphore;
+ } u;
uint64_t queue_sequence_number;
};
@@ -347,33 +351,16 @@ struct vkd3d_fence_worker
struct vkd3d_cond fence_destruction_cond;
bool should_exit;
- LONG enqueued_fence_count;
- struct vkd3d_enqueued_fence
- {
- VkFence vk_fence;
- VkSemaphore vk_semaphore;
- struct vkd3d_waiting_fence waiting_fence;
- } *enqueued_fences;
- size_t enqueued_fences_size;
-
size_t fence_count;
- VkFence *vk_fences;
- size_t vk_fences_size;
struct vkd3d_waiting_fence *fences;
size_t fences_size;
- VkSemaphore *vk_semaphores;
- size_t vk_semaphores_size;
- uint64_t *semaphore_wait_values;
- size_t semaphore_wait_values_size;
- void (*wait_for_gpu_fences)(struct vkd3d_fence_worker *worker);
+ void (*wait_for_gpu_fence)(struct vkd3d_fence_worker *worker, const struct vkd3d_waiting_fence *enqueued_fence);
+ struct vkd3d_queue *queue;
struct d3d12_device *device;
};
-HRESULT vkd3d_fence_worker_start(struct vkd3d_fence_worker *worker, struct d3d12_device *device);
-HRESULT vkd3d_fence_worker_stop(struct vkd3d_fence_worker *worker, struct d3d12_device *device);
-
struct vkd3d_gpu_va_allocation
{
D3D12_GPU_VIRTUAL_ADDRESS base;
@@ -1338,6 +1325,7 @@ struct d3d12_command_queue
struct vkd3d_queue *vkd3d_queue;
+ struct vkd3d_fence_worker fence_worker;
const struct d3d12_fence *last_waited_fence;
uint64_t last_waited_fence_value;
@@ -1440,7 +1428,6 @@ struct d3d12_device
struct vkd3d_gpu_descriptor_allocator gpu_descriptor_allocator;
struct vkd3d_gpu_va_allocator gpu_va_allocator;
- struct vkd3d_fence_worker fence_worker;
struct vkd3d_mutex mutex;
struct vkd3d_mutex desc_mutex[8];
--
2.35.1
April 29, 2022
[PATCH vkd3d 1/4] vkd3d: Introduce an internal refcount to d3d12_fence to replace the thread waiting mechanism.
by Conor McCarthy
Simplifies the preservation of fence objects until worker threads are
done with them, and will be needed when threaded queue submission is
added.
Signed-off-by: Conor McCarthy <cmccarthy(a)codeweavers.com>
---
libs/vkd3d/command.c | 72 ++++++++++++--------------------------
libs/vkd3d/vkd3d_private.h | 4 +--
2 files changed, 24 insertions(+), 52 deletions(-)
diff --git a/libs/vkd3d/command.c b/libs/vkd3d/command.c
index 09171fe4..7a373b34 100644
--- a/libs/vkd3d/command.c
+++ b/libs/vkd3d/command.c
@@ -20,6 +20,8 @@
#include "vkd3d_private.h"
+static void d3d12_fence_incref(struct d3d12_fence *fence);
+static void d3d12_fence_decref(struct d3d12_fence *fence);
static HRESULT d3d12_fence_signal(struct d3d12_fence *fence, uint64_t value, VkFence vk_fence);
HRESULT vkd3d_queue_create(struct d3d12_device *device,
@@ -295,7 +297,7 @@ static HRESULT vkd3d_enqueue_gpu_fence(struct vkd3d_fence_worker *worker,
waiting_fence->queue_sequence_number = queue_sequence_number;
++worker->enqueued_fence_count;
- InterlockedIncrement(&fence->pending_worker_operation_count);
+ d3d12_fence_incref(fence);
vkd3d_cond_signal(&worker->cond);
vkd3d_mutex_unlock(&worker->mutex);
@@ -303,37 +305,6 @@ static HRESULT vkd3d_enqueue_gpu_fence(struct vkd3d_fence_worker *worker,
return S_OK;
}
-static void vkd3d_fence_worker_remove_fence(struct vkd3d_fence_worker *worker, struct d3d12_fence *fence)
-{
- LONG count;
- int rc;
-
- if (!(count = InterlockedAdd(&fence->pending_worker_operation_count, 0)))
- return;
-
- WARN("Waiting for %u pending fence operations (fence %p).\n", count, fence);
-
- if ((rc = vkd3d_mutex_lock(&worker->mutex)))
- {
- ERR("Failed to lock mutex, error %d.\n", rc);
- return;
- }
-
- while ((count = InterlockedAdd(&fence->pending_worker_operation_count, 0)))
- {
- TRACE("Still waiting for %u pending fence operations (fence %p).\n", count, fence);
-
- worker->pending_fence_destruction = true;
- vkd3d_cond_signal(&worker->cond);
-
- vkd3d_cond_wait(&worker->fence_destruction_cond, &worker->mutex);
- }
-
- TRACE("Removed fence %p.\n", fence);
-
- vkd3d_mutex_unlock(&worker->mutex);
-}
-
static void vkd3d_fence_worker_move_enqueued_fences_locked(struct vkd3d_fence_worker *worker)
{
unsigned int i;
@@ -432,7 +403,7 @@ static void vkd3d_wait_for_gpu_timeline_semaphores(struct vkd3d_fence_worker *wo
if (FAILED(hr = d3d12_fence_signal(current->fence, counter_value, VK_NULL_HANDLE)))
ERR("Failed to signal D3D12 fence, hr %#x.\n", hr);
- InterlockedDecrement(¤t->fence->pending_worker_operation_count);
+ d3d12_fence_decref(current->fence);
continue;
}
@@ -480,7 +451,7 @@ static void vkd3d_wait_for_gpu_fences(struct vkd3d_fence_worker *worker)
if (FAILED(hr = d3d12_fence_signal(current->fence, current->value, vk_fence)))
ERR("Failed to signal D3D12 fence, hr %#x.\n", hr);
- InterlockedDecrement(¤t->fence->pending_worker_operation_count);
+ d3d12_fence_decref(current->fence);
vkd3d_queue_update_sequence_number(current->queue, current->queue_sequence_number, device);
continue;
@@ -518,12 +489,6 @@ static void *vkd3d_fence_worker_main(void *arg)
break;
}
- if (worker->pending_fence_destruction)
- {
- vkd3d_cond_broadcast(&worker->fence_destruction_cond);
- worker->pending_fence_destruction = false;
- }
-
if (worker->enqueued_fence_count)
{
vkd3d_fence_worker_move_enqueued_fences_locked(worker);
@@ -560,7 +525,6 @@ HRESULT vkd3d_fence_worker_start(struct vkd3d_fence_worker *worker,
TRACE("worker %p.\n", worker);
worker->should_exit = false;
- worker->pending_fence_destruction = false;
worker->device = device;
worker->enqueued_fence_count = 0;
@@ -1026,22 +990,35 @@ static ULONG STDMETHODCALLTYPE d3d12_fence_AddRef(ID3D12Fence *iface)
return refcount;
}
+static void d3d12_fence_incref(struct d3d12_fence *fence)
+{
+ InterlockedIncrement(&fence->internal_refcount);
+}
+
static ULONG STDMETHODCALLTYPE d3d12_fence_Release(ID3D12Fence *iface)
{
struct d3d12_fence *fence = impl_from_ID3D12Fence(iface);
ULONG refcount = InterlockedDecrement(&fence->refcount);
- int rc;
TRACE("%p decreasing refcount to %u.\n", fence, refcount);
if (!refcount)
+ d3d12_fence_decref(fence);
+
+ return refcount;
+}
+
+static void d3d12_fence_decref(struct d3d12_fence *fence)
+{
+ ULONG internal_refcount = InterlockedDecrement(&fence->internal_refcount);
+ int rc;
+
+ if (!internal_refcount)
{
struct d3d12_device *device = fence->device;
vkd3d_private_store_destroy(&fence->private_store);
- vkd3d_fence_worker_remove_fence(&device->fence_worker, fence);
-
d3d12_fence_destroy_vk_objects(fence);
vkd3d_free(fence->events);
@@ -1052,8 +1029,6 @@ static ULONG STDMETHODCALLTYPE d3d12_fence_Release(ID3D12Fence *iface)
d3d12_device_release(device);
}
-
- return refcount;
}
static HRESULT STDMETHODCALLTYPE d3d12_fence_GetPrivateData(ID3D12Fence *iface,
@@ -1380,6 +1355,7 @@ static HRESULT d3d12_fence_init(struct d3d12_fence *fence, struct d3d12_device *
int rc;
fence->ID3D12Fence_iface.lpVtbl = &d3d12_fence_vtbl;
+ fence->internal_refcount = 1;
fence->refcount = 1;
fence->value = initial_value;
@@ -1419,8 +1395,6 @@ static HRESULT d3d12_fence_init(struct d3d12_fence *fence, struct d3d12_device *
memset(fence->old_vk_fences, 0, sizeof(fence->old_vk_fences));
- fence->pending_worker_operation_count = 0;
-
if (FAILED(hr = vkd3d_private_store_init(&fence->private_store)))
{
vkd3d_mutex_destroy(&fence->mutex);
@@ -6496,7 +6470,7 @@ static HRESULT vkd3d_enqueue_timeline_semaphore(struct vkd3d_fence_worker *worke
waiting_fence->queue = queue;
++worker->enqueued_fence_count;
- InterlockedIncrement(&fence->pending_worker_operation_count);
+ d3d12_fence_incref(fence);
vkd3d_cond_signal(&worker->cond);
vkd3d_mutex_unlock(&worker->mutex);
diff --git a/libs/vkd3d/vkd3d_private.h b/libs/vkd3d/vkd3d_private.h
index 56060b6d..a0163c8d 100644
--- a/libs/vkd3d/vkd3d_private.h
+++ b/libs/vkd3d/vkd3d_private.h
@@ -346,7 +346,6 @@ struct vkd3d_fence_worker
struct vkd3d_cond cond;
struct vkd3d_cond fence_destruction_cond;
bool should_exit;
- bool pending_fence_destruction;
LONG enqueued_fence_count;
struct vkd3d_enqueued_fence
@@ -532,6 +531,7 @@ struct vkd3d_pending_fence_wait
struct d3d12_fence
{
ID3D12Fence ID3D12Fence_iface;
+ LONG internal_refcount;
LONG refcount;
uint64_t value;
@@ -555,8 +555,6 @@ struct d3d12_fence
struct list semaphores;
unsigned int semaphore_count;
- LONG pending_worker_operation_count;
-
VkFence old_vk_fences[VKD3D_MAX_VK_SYNC_OBJECTS];
struct d3d12_device *device;
--
2.35.1
April 29, 2022
[PATCH 2/2] programs/cmd: skip too long paths
by Eric Pouech
From: Eric Pouech <eric.pouech(a)gmail.com>
Signed-off-by: Eric Pouech <eric.pouech(a)gmail.com>
---
programs/cmd/builtins.c | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c
index dd3ebf76d50..5ae5a129d90 100644
--- a/programs/cmd/builtins.c
+++ b/programs/cmd/builtins.c
@@ -1355,6 +1355,11 @@ static BOOL WCMD_delete_one (const WCHAR *thisArg) {
DIRECTORY_STACK *nextDir;
WCHAR subParm[MAX_PATH];
+ if (wcslen(thisDir) + wcslen(fd.cFileName) + 1 + wcslen(fname) + wcslen(ext) >= MAX_PATH)
+ {
+ WINE_TRACE("Skipping path too long %ls%ls\\%ls%ls\n", thisDir, fd.cFileName, fname, ext);
+ continue;
+ }
/* Work out search parameter in sub dir */
lstrcpyW (subParm, thisDir);
lstrcatW (subParm, fd.cFileName);
@@ -1761,7 +1766,13 @@ static void WCMD_add_dirstowalk(DIRECTORY_STACK *dirsToWalk) {
(lstrcmpW(fd.cFileName, L"..") != 0) && (lstrcmpW(fd.cFileName, L".") != 0))
{
/* Allocate memory, add to list */
- DIRECTORY_STACK *toWalk = heap_xalloc(sizeof(DIRECTORY_STACK));
+ DIRECTORY_STACK *toWalk;
+ if (wcslen(dirsToWalk->dirName) + 1 + wcslen(fd.cFileName) >= MAX_PATH)
+ {
+ WINE_TRACE("Skipping too long path %ls\\%ls\n", dirsToWalk->dirName, fd.cFileName);
+ continue;
+ }
+ toWalk = heap_xalloc(sizeof(DIRECTORY_STACK));
WINE_TRACE("(%p->%p)\n", remainingDirs, remainingDirs->next);
toWalk->next = remainingDirs->next;
remainingDirs->next = toWalk;
@@ -2321,6 +2332,11 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) {
WINE_TRACE("Processing FOR filename %s\n", wine_dbgstr_w(fd.cFileName));
if (doRecurse) {
+ if (wcslen(dirsToWalk->dirName) + 1 + wcslen(fd.cFileName) >= MAX_PATH)
+ {
+ WINE_TRACE("Skipping too long path %ls\\%ls\n", dirsToWalk->dirName, fd.cFileName);
+ continue;
+ }
lstrcpyW(fullitem, dirsToWalk->dirName);
lstrcatW(fullitem, L"\\");
lstrcatW(fullitem, fd.cFileName);
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/17
April 29, 2022
[PATCH 1/2] programs/cmd: test that read is successful before using its result
by Eric Pouech
From: Eric Pouech <eric.pouech(a)gmail.com>
There are cases where the read can fail (not attached to a console, input
stream mapped to /dev/null...)
Signed-off-by: Eric Pouech <eric.pouech(a)gmail.com>
---
programs/cmd/builtins.c | 25 +++++++++++++++----------
1 file changed, 15 insertions(+), 10 deletions(-)
diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c
index 963a9eaf361..dd3ebf76d50 100644
--- a/programs/cmd/builtins.c
+++ b/programs/cmd/builtins.c
@@ -193,7 +193,8 @@ static BOOL WCMD_ask_confirm (const WCHAR *message, BOOL showSureText,
if (showSureText)
WCMD_output_asis (confirm);
WCMD_output_asis (options);
- WCMD_ReadFile(GetStdHandle(STD_INPUT_HANDLE), answer, ARRAY_SIZE(answer), &count);
+ if (!WCMD_ReadFile(GetStdHandle(STD_INPUT_HANDLE), answer, ARRAY_SIZE(answer), &count))
+ return FALSE;
answer[0] = towupper(answer[0]);
if (answer[0] == Ybuffer[0])
return TRUE;
@@ -383,7 +384,12 @@ void WCMD_choice (const WCHAR * args) {
/* FIXME: Add support for option /T */
answer[1] = 0; /* terminate single character string */
- WCMD_ReadFile(GetStdHandle(STD_INPUT_HANDLE), answer, 1, &count);
+ if (!WCMD_ReadFile(GetStdHandle(STD_INPUT_HANDLE), answer, 1, &count))
+ {
+ heap_free(my_command);
+ errorlevel = 0;
+ return;
+ }
if (!opt_s)
answer[0] = towupper(answer[0]);
@@ -3506,8 +3512,8 @@ void WCMD_setshow_date (void) {
WCMD_output (WCMD_LoadMessage(WCMD_CURRENTDATE), curdate);
if (wcsstr(quals, L"/T") == NULL) {
WCMD_output (WCMD_LoadMessage(WCMD_NEWDATE));
- WCMD_ReadFile(GetStdHandle(STD_INPUT_HANDLE), buffer, ARRAY_SIZE(buffer), &count);
- if (count > 2) {
+ if (WCMD_ReadFile(GetStdHandle(STD_INPUT_HANDLE), buffer, ARRAY_SIZE(buffer), &count) &&
+ count > 2) {
WCMD_output_stderr (WCMD_LoadMessage(WCMD_NYI));
}
}
@@ -4142,8 +4148,7 @@ void WCMD_setshow_env (WCHAR *s) {
if (*p) WCMD_output_asis(p);
/* Read the reply */
- WCMD_ReadFile(GetStdHandle(STD_INPUT_HANDLE), string, ARRAY_SIZE(string), &count);
- if (count > 1) {
+ if (WCMD_ReadFile(GetStdHandle(STD_INPUT_HANDLE), string, ARRAY_SIZE(string), &count) && count > 1) {
string[count-1] = '\0'; /* ReadFile output is not null-terminated! */
if (string[count-2] == '\r') string[count-2] = '\0'; /* Under Windoze we get CRLF! */
WINE_TRACE("set /p: Setting var '%s' to '%s'\n", wine_dbgstr_w(s),
@@ -4295,8 +4300,8 @@ void WCMD_setshow_time (void) {
WCMD_output (WCMD_LoadMessage(WCMD_CURRENTTIME), curtime);
if (wcsstr(quals, L"/T") == NULL) {
WCMD_output (WCMD_LoadMessage(WCMD_NEWTIME));
- WCMD_ReadFile(GetStdHandle(STD_INPUT_HANDLE), buffer, ARRAY_SIZE(buffer), &count);
- if (count > 2) {
+ if (WCMD_ReadFile(GetStdHandle(STD_INPUT_HANDLE), buffer, ARRAY_SIZE(buffer), &count) &&
+ count > 2) {
WCMD_output_stderr (WCMD_LoadMessage(WCMD_NYI));
}
}
@@ -4717,8 +4722,8 @@ int WCMD_volume(BOOL set_label, const WCHAR *path)
HIWORD(serial), LOWORD(serial));
if (set_label) {
WCMD_output (WCMD_LoadMessage(WCMD_VOLUMEPROMPT));
- WCMD_ReadFile(GetStdHandle(STD_INPUT_HANDLE), string, ARRAY_SIZE(string), &count);
- if (count > 1) {
+ if (WCMD_ReadFile(GetStdHandle(STD_INPUT_HANDLE), string, ARRAY_SIZE(string), &count) &&
+ count > 1) {
string[count-1] = '\0'; /* ReadFile output is not null-terminated! */
if (string[count-2] == '\r') string[count-2] = '\0'; /* Under Windoze we get CRLF! */
}
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/17
April 29, 2022
[PATCH 0/2] MR17: programs/cmd: test that read is successful before using its result
by eric pouech (@epo)
There are cases where the read can fail (not attached to a console, input
stream mapped to /dev/null...)
Signed-off-by: Eric Pouech <eric.pouech(a)gmail.com>
--
https://gitlab.winehq.org/wine/wine/-/merge_requests/17
April 29, 2022
[PATCH 1/1] wined3d: Reduce the size of staging BO's for texture uploads.
by Jan Sikorski
From: Jan Sikorski <jsikorski(a)codeweavers.com>
Signed-off-by: Jan Sikorski <jsikorski(a)codeweavers.com>
---
dlls/wined3d/texture.c | 33 +++++++++++++++++++++------------
1 file changed, 21 insertions(+), 12 deletions(-)
diff --git a/dlls/wined3d/texture.c b/dlls/wined3d/texture.c
index 5fd38b49132..c7086f03a42 100644
--- a/dlls/wined3d/texture.c
+++ b/dlls/wined3d/texture.c
@@ -4876,6 +4876,7 @@ static void wined3d_texture_vk_upload_data(struct wined3d_context *context,
struct wined3d_context_vk *context_vk = wined3d_context_vk(context);
unsigned int dst_level, dst_row_pitch, dst_slice_pitch;
struct wined3d_texture_sub_resource *sub_resource;
+ unsigned int src_width, src_height, src_depth;
struct wined3d_bo_address staging_bo_addr;
VkPipelineStageFlags bo_stage_flags = 0;
const struct wined3d_vk_info *vk_info;
@@ -4933,6 +4934,10 @@ static void wined3d_texture_vk_upload_data(struct wined3d_context *context,
sub_resource = &dst_texture_vk->t.sub_resources[dst_sub_resource_idx];
vk_info = context_vk->vk_info;
+ src_width = src_box->right - src_box->left;
+ src_height = src_box->bottom - src_box->top;
+ src_depth = src_box->back - src_box->front;
+
src_offset = src_box->front * src_slice_pitch
+ (src_box->top / src_format->block_height) * src_row_pitch
+ (src_box->left / src_format->block_width) * src_format->block_byte_count;
@@ -4945,10 +4950,15 @@ static void wined3d_texture_vk_upload_data(struct wined3d_context *context,
/* We need to be outside of a render pass for vkCmdPipelineBarrier() and vkCmdCopyBufferToImage() calls below. */
wined3d_context_vk_end_current_render_pass(context_vk);
-
if (!src_bo_addr->buffer_object)
{
- if (!wined3d_context_vk_create_bo(context_vk, sub_resource->size,
+ unsigned int staging_row_pitch, staging_slice_pitch, staging_size;
+
+ wined3d_format_calculate_pitch(src_format, context->device->surface_alignment, src_width, src_height,
+ &staging_row_pitch, &staging_slice_pitch);
+ staging_size = staging_slice_pitch * src_depth;
+
+ if (!wined3d_context_vk_create_bo(context_vk, staging_size,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &staging_bo))
{
ERR("Failed to create staging bo.\n");
@@ -4958,26 +4968,25 @@ static void wined3d_texture_vk_upload_data(struct wined3d_context *context,
staging_bo_addr.buffer_object = &staging_bo.b;
staging_bo_addr.addr = NULL;
if (!(map_ptr = wined3d_context_map_bo_address(context, &staging_bo_addr,
- sub_resource->size, WINED3D_MAP_DISCARD | WINED3D_MAP_WRITE)))
+ staging_size, WINED3D_MAP_DISCARD | WINED3D_MAP_WRITE)))
{
ERR("Failed to map staging bo.\n");
wined3d_context_vk_destroy_bo(context_vk, &staging_bo);
return;
}
- wined3d_format_copy_data(src_format, src_bo_addr->addr + src_offset, src_row_pitch,
- src_slice_pitch, map_ptr, dst_row_pitch, dst_slice_pitch, src_box->right - src_box->left,
- src_box->bottom - src_box->top, src_box->back - src_box->front);
+ wined3d_format_copy_data(src_format, src_bo_addr->addr, src_row_pitch, src_slice_pitch,
+ map_ptr, staging_row_pitch, staging_slice_pitch, src_width, src_height, src_depth);
range.offset = 0;
- range.size = sub_resource->size;
+ range.size = staging_size;
wined3d_context_unmap_bo_address(context, &staging_bo_addr, 1, &range);
src_bo = &staging_bo;
src_offset = 0;
- src_row_pitch = dst_row_pitch;
- src_slice_pitch = dst_slice_pitch;
+ src_row_pitch = staging_row_pitch;
+ src_slice_pitch = staging_slice_pitch;
}
else
{
@@ -5027,9 +5036,9 @@ static void wined3d_texture_vk_upload_data(struct wined3d_context *context,
region.imageOffset.x = dst_x;
region.imageOffset.y = dst_y;
region.imageOffset.z = dst_z;
- region.imageExtent.width = src_box->right - src_box->left;
- region.imageExtent.height = src_box->bottom - src_box->top;
- region.imageExtent.depth = src_box->back - src_box->front;
+ region.imageExtent.width = src_width;
+ region.imageExtent.height = src_height;
+ region.imageExtent.depth = src_depth;
VK_CALL(vkCmdCopyBufferToImage(vk_command_buffer, src_bo->vk_buffer,
dst_texture_vk->image.vk_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion));
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/16
April 29, 2022
[PATCH 0/1] MR16: wined3d: Reduce the size of staging BO's for texture uploads.
by Jan Sikorski (@jsikorski)
Signed-off-by: Jan Sikorski <jsikorski(a)codeweavers.com>
--
https://gitlab.winehq.org/wine/wine/-/merge_requests/16
April 29, 2022
[PATCH] mshtml: Added IHTMLCSSStyleDeclaration::backgroundSize property implementation.
by Hans Leidekker
Signed-off-by: Hans Leidekker <hans(a)codeweavers.com>
---
dlls/mshtml/htmlstyle.c | 13 +++++++++----
dlls/mshtml/htmlstyle.h | 1 +
dlls/mshtml/tests/style.c | 16 ++++++++++++++++
3 files changed, 26 insertions(+), 4 deletions(-)
diff --git a/dlls/mshtml/htmlstyle.c b/dlls/mshtml/htmlstyle.c
index 11a66369c39..44ac21c5f97 100644
--- a/dlls/mshtml/htmlstyle.c
+++ b/dlls/mshtml/htmlstyle.c
@@ -181,6 +181,11 @@ static const style_tbl_entry_t style_tbl[] = {
DISPID_A_BACKGROUNDREPEAT,
0, background_repeat_values
},
+ {
+ L"background-size",
+ DISPID_IHTMLCSSSTYLEDECLARATION_BACKGROUNDSIZE,
+ DISPID_A_IE9_BACKGROUNDSIZE,
+ },
{
L"border",
DISPID_IHTMLCSSSTYLEDECLARATION_BORDER,
@@ -7628,15 +7633,15 @@ static HRESULT WINAPI HTMLCSSStyleDeclaration_get_backgroundOrigin(IHTMLCSSStyle
static HRESULT WINAPI HTMLCSSStyleDeclaration_put_backgroundSize(IHTMLCSSStyleDeclaration *iface, BSTR v)
{
CSSStyle *This = impl_from_IHTMLCSSStyleDeclaration(iface);
- FIXME("(%p)->(%s)\n", This, debugstr_w(v));
- return E_NOTIMPL;
+ TRACE("(%p)->(%s)\n", This, debugstr_w(v));
+ return set_style_property(This, STYLEID_BACKGROUND_SIZE, v);
}
static HRESULT WINAPI HTMLCSSStyleDeclaration_get_backgroundSize(IHTMLCSSStyleDeclaration *iface, BSTR *p)
{
CSSStyle *This = impl_from_IHTMLCSSStyleDeclaration(iface);
- FIXME("(%p)->(%p)\n", This, p);
- return E_NOTIMPL;
+ TRACE("(%p)->(%p)\n", This, p);
+ return get_style_property(This, STYLEID_BACKGROUND_SIZE, p);
}
static HRESULT WINAPI HTMLCSSStyleDeclaration_put_boxShadow(IHTMLCSSStyleDeclaration *iface, BSTR v)
diff --git a/dlls/mshtml/htmlstyle.h b/dlls/mshtml/htmlstyle.h
index 25f87c802b2..e14c1274360 100644
--- a/dlls/mshtml/htmlstyle.h
+++ b/dlls/mshtml/htmlstyle.h
@@ -55,6 +55,7 @@ typedef enum {
STYLEID_BACKGROUND_POSITION_X,
STYLEID_BACKGROUND_POSITION_Y,
STYLEID_BACKGROUND_REPEAT,
+ STYLEID_BACKGROUND_SIZE,
STYLEID_BORDER,
STYLEID_BORDER_BOTTOM,
STYLEID_BORDER_BOTTOM_COLOR,
diff --git a/dlls/mshtml/tests/style.c b/dlls/mshtml/tests/style.c
index bc9afda7617..4b72d144354 100644
--- a/dlls/mshtml/tests/style.c
+++ b/dlls/mshtml/tests/style.c
@@ -793,6 +793,22 @@ static void test_css_style_declaration(IHTMLCSSStyleDeclaration *css_style)
ok(!lstrcmpW(str, L"border-box"), "backgroundClip = %s\n", wine_dbgstr_w(str));
SysFreeString(str);
+ str = (BSTR)0xdeadbeef;
+ hres = IHTMLCSSStyleDeclaration_get_backgroundSize(css_style, &str);
+ ok(hres == S_OK, "get_backgroundSize failed: %08lx\n", hres);
+ ok(str == NULL, "got %s\n", wine_dbgstr_w(str));
+ SysFreeString(str);
+
+ str = SysAllocString(L"100% 100%");
+ hres = IHTMLCSSStyleDeclaration_put_backgroundSize(css_style, str);
+ ok(hres == S_OK, "put_backgroundSize failed: %08lx\n", hres);
+ SysFreeString(str);
+
+ hres = IHTMLCSSStyleDeclaration_get_backgroundSize(css_style, &str);
+ ok(hres == S_OK, "get_backgroundSize failed: %08lx\n", hres);
+ ok(!lstrcmpW(str, L"100% 100%"), "backgroundSize = %s\n", wine_dbgstr_w(str));
+ SysFreeString(str);
+
hres = IHTMLCSSStyleDeclaration_get_opacity(css_style, &v);
ok(hres == S_OK, "get_opacity failed: %08lx\n", hres);
test_var_bstr(&v, NULL);
--
2.30.2
April 29, 2022
[PATCH 1/1] programs/winedbg: correctly read register values
by Eric Pouech
From: Eric Pouech <eric.pouech(a)gmail.com>
(was incorrectly reading all registers as having the size of debugger's DWORD_PTR)
this is mainly needed:
- for a 64-bit debugger attached to a 32-bit debuggee
- when register is of different size than machine word
(regressions introduced in wine-7.0-rc1)
Fixed by accessing registers through dbg_lvalue structure (which contains
correct size information)
Signed-off-by: Eric Pouech <eric.pouech(a)gmail.com>
---
programs/winedbg/debugger.h | 4 ++--
programs/winedbg/memory.c | 4 ++--
programs/winedbg/stack.c | 11 ++++++-----
programs/winedbg/symbol.c | 10 +++-------
4 files changed, 13 insertions(+), 16 deletions(-)
diff --git a/programs/winedbg/debugger.h b/programs/winedbg/debugger.h
index 1ae2dce4689..293de4c0aa5 100644
--- a/programs/winedbg/debugger.h
+++ b/programs/winedbg/debugger.h
@@ -404,7 +404,7 @@ extern BOOL memory_get_current_pc(ADDRESS64* address);
extern BOOL memory_get_current_stack(ADDRESS64* address);
extern BOOL memory_get_string(struct dbg_process* pcs, void* addr, BOOL in_debuggee, BOOL unicode, char* buffer, int size);
extern BOOL memory_get_string_indirect(struct dbg_process* pcs, void* addr, BOOL unicode, WCHAR* buffer, int size);
-extern BOOL memory_get_register(DWORD regno, DWORD_PTR** value, char* buffer, int len);
+extern BOOL memory_get_register(DWORD regno, struct dbg_lvalue* value, char* buffer, int len);
extern void memory_disassemble(const struct dbg_lvalue*, const struct dbg_lvalue*, int instruction_count);
extern BOOL memory_disasm_one_insn(ADDRESS64* addr);
#define MAX_OFFSET_TO_STR_LEN 19
@@ -425,7 +425,7 @@ extern void source_free_files(struct dbg_process* p);
extern void stack_info(int len);
extern void stack_backtrace(DWORD threadID);
extern BOOL stack_set_frame(int newframe);
-extern BOOL stack_get_register_frame(const struct dbg_internal_var* div, DWORD_PTR** pval);
+extern BOOL stack_get_register_frame(const struct dbg_internal_var* div, struct dbg_lvalue* lvalue);
extern unsigned stack_fetch_frames(const dbg_ctx_t *ctx);
extern BOOL stack_get_current_symbol(SYMBOL_INFO* sym);
static inline struct dbg_frame*
diff --git a/programs/winedbg/memory.c b/programs/winedbg/memory.c
index 8321e8b028d..16d322cd82c 100644
--- a/programs/winedbg/memory.c
+++ b/programs/winedbg/memory.c
@@ -783,7 +783,7 @@ void memory_disassemble(const struct dbg_lvalue* xstart,
memory_disasm_one_insn(&last);
}
-BOOL memory_get_register(DWORD regno, DWORD_PTR** value, char* buffer, int len)
+BOOL memory_get_register(DWORD regno, struct dbg_lvalue* lvalue, char* buffer, int len)
{
const struct dbg_internal_var* div;
@@ -813,7 +813,7 @@ BOOL memory_get_register(DWORD regno, DWORD_PTR** value, char* buffer, int len)
{
if (div->val == regno)
{
- if (!stack_get_register_frame(div, value))
+ if (!stack_get_register_frame(div, lvalue))
{
if (buffer) snprintf(buffer, len, "<register %s not accessible in this frame>", div->name);
return FALSE;
diff --git a/programs/winedbg/stack.c b/programs/winedbg/stack.c
index 294694cf85c..ba246889a55 100644
--- a/programs/winedbg/stack.c
+++ b/programs/winedbg/stack.c
@@ -98,12 +98,13 @@ static BOOL stack_set_frame_internal(int newframe)
return TRUE;
}
-BOOL stack_get_register_frame(const struct dbg_internal_var* div, DWORD_PTR** pval)
+BOOL stack_get_register_frame(const struct dbg_internal_var* div, struct dbg_lvalue* lvalue)
{
struct dbg_frame* currfrm = stack_get_curr_frame();
if (currfrm == NULL) return FALSE;
if (currfrm->is_ctx_valid)
- *pval = (DWORD_PTR*)((char*)&currfrm->context + (DWORD_PTR)div->pval);
+ init_lvalue_in_debugger(lvalue, div->typeid,
+ (char*)&currfrm->context + (DWORD_PTR)div->pval);
else
{
enum be_cpu_addr kind;
@@ -114,13 +115,13 @@ BOOL stack_get_register_frame(const struct dbg_internal_var* div, DWORD_PTR** pv
switch (kind)
{
case be_cpu_addr_pc:
- *pval = &currfrm->linear_pc;
+ init_lvalue_in_debugger(lvalue, dbg_itype_unsigned_long_int, &currfrm->linear_pc);
break;
case be_cpu_addr_stack:
- *pval = &currfrm->linear_stack;
+ init_lvalue_in_debugger(lvalue, dbg_itype_unsigned_long_int, &currfrm->linear_stack);
break;
case be_cpu_addr_frame:
- *pval = &currfrm->linear_frame;
+ init_lvalue_in_debugger(lvalue, dbg_itype_unsigned_long_int, &currfrm->linear_frame);
break;
}
}
diff --git a/programs/winedbg/symbol.c b/programs/winedbg/symbol.c
index 9cbfc8b77de..4c4486ce5d8 100644
--- a/programs/winedbg/symbol.c
+++ b/programs/winedbg/symbol.c
@@ -67,24 +67,20 @@ static BOOL fill_sym_lvalue(const SYMBOL_INFO* sym, ULONG_PTR base,
if (buffer) buffer[0] = '\0';
if (sym->Flags & SYMFLAG_REGISTER)
{
- DWORD_PTR* pval;
-
- if (!memory_get_register(sym->Register, &pval, buffer, sz))
+ if (!memory_get_register(sym->Register, lvalue, buffer, sz))
return FALSE;
- init_lvalue(lvalue, FALSE, pval);
}
else if (sym->Flags & SYMFLAG_REGREL)
{
- DWORD_PTR* pval;
size_t l;
*buffer++ = '['; sz--;
- if (!memory_get_register(sym->Register, &pval, buffer, sz))
+ if (!memory_get_register(sym->Register, lvalue, buffer, sz))
return FALSE;
l = strlen(buffer);
sz -= l;
buffer += l;
- init_lvalue(lvalue, TRUE, (void*)(DWORD_PTR)(*pval + sym->Address));
+ init_lvalue(lvalue, TRUE, (void*)(DWORD_PTR)(types_extract_as_integer(lvalue) + sym->Address));
if ((LONG64)sym->Address >= 0)
snprintf(buffer, sz, "+%I64d]", sym->Address);
else
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/15
April 29, 2022
[PATCH 0/1] MR15: programs/winedbg: correctly read register values
by eric pouech (@epo)
(was incorrectly reading all registers as having the size of debugger's DWORD_PTR)
this is mainly needed:
- for a 64-bit debugger attached to a 32-bit debuggee
- when register is of different size than machine word
(regressions introduced in wine-7.0-rc1)
Fixed by accessing registers through dbg_lvalue structure (which contains
correct size information)
Signed-off-by: Eric Pouech <eric.pouech(a)gmail.com>
--
https://gitlab.winehq.org/wine/wine/-/merge_requests/15
April 29, 2022
Re: [PATCH v2 3/8] d2d1: Implement ID2D1EffectContext_GetDpi().
by Ziqing Hui
On 4/29/22 3:39 PM, Nikolay Sivov wrote:
>
>
> On 4/28/22 13:40, Ziqing Hui wrote:
>> static void STDMETHODCALLTYPE d2d_effect_context_GetDpi(ID2D1EffectContext *iface, float *dpi_x, float *dpi_y)
>> {
>> - FIXME("iface %p, dpi_x %p, dpi_y %p stub!\n", iface, dpi_x, dpi_y);
>> + struct d2d_effect_context *effect_context = impl_from_ID2D1EffectContext(iface);
>> +
>> + TRACE("iface %p, dpi_x %p, dpi_y %p.\n", iface, dpi_x, dpi_y);
>> +
>> + return ID2D1DeviceContext_GetDpi(effect_context->device_context, dpi_x, dpi_y);
>> }
>> static HRESULT STDMETHODCALLTYPE d2d_effect_context_CreateEffect(ID2D1EffectContext *iface,
> Were you able to test this? I realize it requires a minimal custom effect to access context object. It makes sense I guess to forward it like you did, as opposed to returning dpi at the time of CreateEffect() call, but it not obvious just from this patch.
>
> Note that you don't need "return" here.
>
Yeah, it can be tested. I can construct a custom effect and test effect context functions in its implementation.
In fact I have already had some custom effect test code in my local branch.
April 29, 2022
[PATCH v3 3/3] d2d1: Implement LoadVertexShader().
by Ziqing Hui
Signed-off-by: Ziqing Hui <zhui(a)codeweavers.com>
---
v3: * Use IUnknown in d2d_shader.
* Use d2d_array_reserve(count + 1) to avoid count checking.
dlls/d2d1/d2d1_private.h | 10 ++++++++++
dlls/d2d1/effect.c | 38 ++++++++++++++++++++++++++++++++++++--
2 files changed, 46 insertions(+), 2 deletions(-)
diff --git a/dlls/d2d1/d2d1_private.h b/dlls/d2d1/d2d1_private.h
index cb9ee6d5b80..b7e1a12b3b8 100644
--- a/dlls/d2d1/d2d1_private.h
+++ b/dlls/d2d1/d2d1_private.h
@@ -568,12 +568,22 @@ struct d2d_device
void d2d_device_init(struct d2d_device *device, ID2D1Factory1 *factory, IDXGIDevice *dxgi_device) DECLSPEC_HIDDEN;
+struct d2d_shader
+{
+ const GUID *id;
+ IUnknown *shader;
+};
+
struct d2d_effect_context
{
ID2D1EffectContext ID2D1EffectContext_iface;
LONG refcount;
struct d2d_device_context *device_context;
+
+ struct d2d_shader *shaders;
+ size_t shaders_size;
+ size_t shader_count;
};
void d2d_effect_context_init(struct d2d_effect_context *effect_context,
diff --git a/dlls/d2d1/effect.c b/dlls/d2d1/effect.c
index a75e3e525f8..1615739f492 100644
--- a/dlls/d2d1/effect.c
+++ b/dlls/d2d1/effect.c
@@ -37,6 +37,15 @@ static inline struct d2d_effect_context *impl_from_ID2D1EffectContext(ID2D1Effec
static void d2d_effect_context_cleanup(struct d2d_effect_context *effect_context)
{
+ unsigned int i;
+
+ for (i = 0; i < effect_context->shader_count; ++i)
+ {
+ if (effect_context->shaders[i].shader)
+ IUnknown_Release(effect_context->shaders[i].shader);
+ }
+ heap_free(effect_context->shaders);
+
ID2D1DeviceContext_Release(&effect_context->device_context->ID2D1DeviceContext_iface);
}
@@ -178,10 +187,35 @@ static HRESULT STDMETHODCALLTYPE d2d_effect_context_LoadPixelShader(ID2D1EffectC
static HRESULT STDMETHODCALLTYPE d2d_effect_context_LoadVertexShader(ID2D1EffectContext *iface,
REFGUID shader_id, const BYTE *buffer, UINT32 buffer_size)
{
- FIXME("iface %p, shader_id %s, buffer %p, buffer_size %u stub!\n",
+ struct d2d_effect_context *effect_context = impl_from_ID2D1EffectContext(iface);
+ ID3D11VertexShader *vertex_shader;
+ struct d2d_shader *shader;
+ HRESULT hr;
+
+ TRACE("iface %p, shader_id %s, buffer %p, buffer_size %u.\n",
iface, debugstr_guid(shader_id), buffer, buffer_size);
- return E_NOTIMPL;
+ if (FAILED(hr = ID3D11Device1_CreateVertexShader(effect_context->device_context->d3d_device,
+ buffer, buffer_size, NULL, &vertex_shader)))
+ {
+ WARN("Failed to create vertex shader, hr %#lx.\n", hr);
+ return hr;
+ }
+
+ if (!d2d_array_reserve((void **)&effect_context->shaders, &effect_context->shaders_size,
+ effect_context->shader_count + 1, sizeof(*effect_context->shaders)))
+ {
+ ERR("Failed to resize shaders array.\n");
+ ID3D11VertexShader_Release(vertex_shader);
+ return E_OUTOFMEMORY;
+ }
+
+ effect_context->shader_count++;
+ shader = &effect_context->shaders[effect_context->shader_count - 1];
+ shader->id = shader_id;
+ shader->shader = (IUnknown *)vertex_shader;
+
+ return S_OK;
}
static HRESULT STDMETHODCALLTYPE d2d_effect_context_LoadComputeShader(ID2D1EffectContext *iface,
--
2.25.1
April 29, 2022
[PATCH v3 2/3] d2d1: Implement ID2D1EffectContext_GetDpi().
by Ziqing Hui
Signed-off-by: Ziqing Hui <zhui(a)codeweavers.com>
---
dlls/d2d1/effect.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/dlls/d2d1/effect.c b/dlls/d2d1/effect.c
index 65183b4f0f1..a75e3e525f8 100644
--- a/dlls/d2d1/effect.c
+++ b/dlls/d2d1/effect.c
@@ -86,7 +86,11 @@ static ULONG STDMETHODCALLTYPE d2d_effect_context_Release(ID2D1EffectContext *if
static void STDMETHODCALLTYPE d2d_effect_context_GetDpi(ID2D1EffectContext *iface, float *dpi_x, float *dpi_y)
{
- FIXME("iface %p, dpi_x %p, dpi_y %p stub!\n", iface, dpi_x, dpi_y);
+ struct d2d_effect_context *effect_context = impl_from_ID2D1EffectContext(iface);
+
+ TRACE("iface %p, dpi_x %p, dpi_y %p.\n", iface, dpi_x, dpi_y);
+
+ return ID2D1DeviceContext_GetDpi(&effect_context->device_context->ID2D1DeviceContext_iface, dpi_x, dpi_y);
}
static HRESULT STDMETHODCALLTYPE d2d_effect_context_CreateEffect(ID2D1EffectContext *iface,
--
2.25.1
April 29, 2022
[PATCH v3 1/3] d2d1: Add stubs for ID2D1EffectContext.
by Ziqing Hui
Signed-off-by: Ziqing Hui <zhui(a)codeweavers.com>
---
v3: * Move CreateEffect() implementation to effect_context in this patch.
* Add effect_context to d2d_effect in this patch.
* Remove the factory reference in struct d2d_effect. Use effect_context->device_context->factory instead.
* Use d2d_device_context in d2d_effect_context.
dlls/d2d1/d2d1_private.h | 31 ++++-
dlls/d2d1/device.c | 18 +--
dlls/d2d1/effect.c | 290 ++++++++++++++++++++++++++++++++++++++-
3 files changed, 320 insertions(+), 19 deletions(-)
diff --git a/dlls/d2d1/d2d1_private.h b/dlls/d2d1/d2d1_private.h
index aa8e8569455..cb9ee6d5b80 100644
--- a/dlls/d2d1/d2d1_private.h
+++ b/dlls/d2d1/d2d1_private.h
@@ -34,6 +34,7 @@
#include "initguid.h"
#endif
#include "dwrite_2.h"
+#include "d2d1effectauthor.h"
enum d2d_brush_type
{
@@ -567,6 +568,17 @@ struct d2d_device
void d2d_device_init(struct d2d_device *device, ID2D1Factory1 *factory, IDXGIDevice *dxgi_device) DECLSPEC_HIDDEN;
+struct d2d_effect_context
+{
+ ID2D1EffectContext ID2D1EffectContext_iface;
+ LONG refcount;
+
+ struct d2d_device_context *device_context;
+};
+
+void d2d_effect_context_init(struct d2d_effect_context *effect_context,
+ struct d2d_device_context *device_context) DECLSPEC_HIDDEN;
+
struct d2d_effect_info
{
const CLSID *clsid;
@@ -583,13 +595,14 @@ struct d2d_effect
const struct d2d_effect_info *info;
- ID2D1Factory *factory;
+ struct d2d_effect_context *effect_context;
ID2D1Image **inputs;
size_t inputs_size;
size_t input_count;
};
-HRESULT d2d_effect_init(struct d2d_effect *effect, ID2D1Factory *factory, const CLSID *effect_id) DECLSPEC_HIDDEN;
+HRESULT d2d_effect_init(struct d2d_effect *effect,
+ struct d2d_effect_context *effect_context, const CLSID *effect_id) DECLSPEC_HIDDEN;
static inline BOOL d2d_array_reserve(void **elements, size_t *capacity, size_t count, size_t size)
{
@@ -694,6 +707,13 @@ static inline const char *debug_d2d_point_2f(const D2D1_POINT_2F *point)
return wine_dbg_sprintf("{%.8e, %.8e}", point->x, point->y);
}
+static inline const char *debug_d2d_point_2l(const D2D1_POINT_2L *point)
+{
+ if (!point)
+ return "(null)";
+ return wine_dbg_sprintf("{%ld, %ld}", point->x, point->y);
+}
+
static inline const char *debug_d2d_rect_f(const D2D1_RECT_F *rect)
{
if (!rect)
@@ -701,6 +721,13 @@ static inline const char *debug_d2d_rect_f(const D2D1_RECT_F *rect)
return wine_dbg_sprintf("(%.8e, %.8e)-(%.8e, %.8e)", rect->left, rect->top, rect->right, rect->bottom);
}
+static inline const char *debug_d2d_rect_l(const D2D1_RECT_L *rect)
+{
+ if (!rect)
+ return "(null)";
+ return wine_dbg_sprintf("(%ld, %ld)-(%ld, %ld)", rect->left, rect->top, rect->right, rect->bottom);
+}
+
static inline const char *debug_d2d_rounded_rect(const D2D1_ROUNDED_RECT *rounded_rect)
{
if (!rounded_rect)
diff --git a/dlls/d2d1/device.c b/dlls/d2d1/device.c
index 13c99458cfa..80badcddaac 100644
--- a/dlls/d2d1/device.c
+++ b/dlls/d2d1/device.c
@@ -1890,25 +1890,19 @@ static HRESULT STDMETHODCALLTYPE d2d_device_context_CreateEffect(ID2D1DeviceCont
REFCLSID effect_id, ID2D1Effect **effect)
{
struct d2d_device_context *context = impl_from_ID2D1DeviceContext(iface);
- struct d2d_effect *object;
+ struct d2d_effect_context *effect_context;
HRESULT hr;
FIXME("iface %p, effect_id %s, effect %p stub!\n", iface, debugstr_guid(effect_id), effect);
- if (!(object = heap_alloc_zero(sizeof(*object))))
+ if (!(effect_context = heap_alloc_zero(sizeof(*effect_context))))
return E_OUTOFMEMORY;
+ d2d_effect_context_init(effect_context, context);
- if (FAILED(hr = d2d_effect_init(object, context->factory, effect_id)))
- {
- WARN("Failed to initialise effect, hr %#lx.\n", hr);
- heap_free(object);
- return hr;
- }
+ hr = ID2D1EffectContext_CreateEffect(&effect_context->ID2D1EffectContext_iface, effect_id, effect);
- TRACE("Created effect %p.\n", object);
- *effect = &object->ID2D1Effect_iface;
-
- return S_OK;
+ ID2D1EffectContext_Release(&effect_context->ID2D1EffectContext_iface);
+ return hr;
}
static HRESULT STDMETHODCALLTYPE d2d_device_context_ID2D1DeviceContext_CreateGradientStopCollection(
diff --git a/dlls/d2d1/effect.c b/dlls/d2d1/effect.c
index 350ac3c7bbf..65183b4f0f1 100644
--- a/dlls/d2d1/effect.c
+++ b/dlls/d2d1/effect.c
@@ -17,7 +17,6 @@
*/
#include "d2d1_private.h"
-#include "d2d1effectauthor.h"
WINE_DEFAULT_DEBUG_CHANNEL(d2d);
@@ -31,6 +30,286 @@ static const struct d2d_effect_info builtin_effects[] =
{&CLSID_D2D1Grayscale, 1, 1, 1},
};
+static inline struct d2d_effect_context *impl_from_ID2D1EffectContext(ID2D1EffectContext *iface)
+{
+ return CONTAINING_RECORD(iface, struct d2d_effect_context, ID2D1EffectContext_iface);
+}
+
+static void d2d_effect_context_cleanup(struct d2d_effect_context *effect_context)
+{
+ ID2D1DeviceContext_Release(&effect_context->device_context->ID2D1DeviceContext_iface);
+}
+
+static HRESULT STDMETHODCALLTYPE d2d_effect_context_QueryInterface(ID2D1EffectContext *iface, REFIID iid, void **out)
+{
+ TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out);
+
+ if (IsEqualGUID(iid, &IID_ID2D1EffectContext)
+ || IsEqualGUID(iid, &IID_IUnknown))
+ {
+ ID2D1EffectContext_AddRef(iface);
+ *out = iface;
+ return S_OK;
+ }
+
+ WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid));
+
+ *out = NULL;
+ return E_NOINTERFACE;
+}
+
+static ULONG STDMETHODCALLTYPE d2d_effect_context_AddRef(ID2D1EffectContext *iface)
+{
+ struct d2d_effect_context *effect_context = impl_from_ID2D1EffectContext(iface);
+ ULONG refcount = InterlockedIncrement(&effect_context->refcount);
+
+ TRACE("%p increasing refcount to %lu.\n", iface, refcount);
+
+ return refcount;
+}
+
+static ULONG STDMETHODCALLTYPE d2d_effect_context_Release(ID2D1EffectContext *iface)
+{
+ struct d2d_effect_context *effect_context = impl_from_ID2D1EffectContext(iface);
+ ULONG refcount = InterlockedDecrement(&effect_context->refcount);
+
+ TRACE("%p decreasing refcount to %lu.\n", iface, refcount);
+
+ if (!refcount)
+ {
+ d2d_effect_context_cleanup(effect_context);
+ heap_free(effect_context);
+ }
+
+ return refcount;
+}
+
+static void STDMETHODCALLTYPE d2d_effect_context_GetDpi(ID2D1EffectContext *iface, float *dpi_x, float *dpi_y)
+{
+ FIXME("iface %p, dpi_x %p, dpi_y %p stub!\n", iface, dpi_x, dpi_y);
+}
+
+static HRESULT STDMETHODCALLTYPE d2d_effect_context_CreateEffect(ID2D1EffectContext *iface,
+ REFCLSID clsid, ID2D1Effect **effect)
+{
+ struct d2d_effect_context *effect_context = impl_from_ID2D1EffectContext(iface);
+ struct d2d_effect *object;
+ HRESULT hr;
+
+ TRACE("iface %p, clsid %s, effect %p.\n", iface, debugstr_guid(clsid), effect);
+
+ if (!(object = heap_alloc_zero(sizeof(*object))))
+ return E_OUTOFMEMORY;
+
+ if (FAILED(hr = d2d_effect_init(object, effect_context, clsid)))
+ {
+ WARN("Failed to initialise effect, hr %#lx.\n", hr);
+ heap_free(object);
+ return hr;
+ }
+
+ TRACE("Created effect %p.\n", object);
+ *effect = &object->ID2D1Effect_iface;
+
+ return S_OK;
+}
+
+static HRESULT STDMETHODCALLTYPE d2d_effect_context_GetMaximumSupportedFeatureLevel(ID2D1EffectContext *iface,
+ const D3D_FEATURE_LEVEL *levels, UINT32 level_count, D3D_FEATURE_LEVEL *max_level)
+{
+ FIXME("iface %p, levels %p, level_count %u, max_level %p stub!\n", iface, levels, level_count, max_level);
+
+ return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE d2d_effect_context_CreateTransformNodeFromEffect(ID2D1EffectContext *iface,
+ ID2D1Effect *effect, ID2D1TransformNode **node)
+{
+ FIXME("iface %p, effect %p, node %p stub!\n", iface, effect, node);
+
+ return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE d2d_effect_context_CreateBlendTransform(ID2D1EffectContext *iface,
+ UINT32 num_inputs, const D2D1_BLEND_DESCRIPTION *description, ID2D1BlendTransform **transform)
+{
+ FIXME("iface %p, num_inputs %u, description %p, transform %p stub!\n", iface, num_inputs, description, transform);
+
+ return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE d2d_effect_context_CreateBorderTransform(ID2D1EffectContext *iface,
+ D2D1_EXTEND_MODE mode_x, D2D1_EXTEND_MODE mode_y, ID2D1BorderTransform **transform)
+{
+ FIXME("iface %p, mode_x %#x, mode_y %#x, transform %p stub!\n", iface, mode_x, mode_y, transform);
+
+ return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE d2d_effect_context_CreateOffsetTransform(ID2D1EffectContext *iface,
+ D2D1_POINT_2L offset, ID2D1OffsetTransform **transform)
+{
+ FIXME("iface %p, offset %s, transform %p stub!\n", iface, debug_d2d_point_2l(&offset), transform);
+
+ return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE d2d_effect_context_CreateBoundsAdjustmentTransform(ID2D1EffectContext *iface,
+ const D2D1_RECT_L *output_rect, ID2D1BoundsAdjustmentTransform **transform)
+{
+ FIXME("iface %p, output_rect %s, transform %p stub!\n", iface, debug_d2d_rect_l(output_rect), transform);
+
+ return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE d2d_effect_context_LoadPixelShader(ID2D1EffectContext *iface,
+ REFGUID shader_id, const BYTE *buffer, UINT32 buffer_size)
+{
+ FIXME("iface %p, shader_id %s, buffer %p, buffer_size %u stub!\n",
+ iface, debugstr_guid(shader_id), buffer, buffer_size);
+
+ return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE d2d_effect_context_LoadVertexShader(ID2D1EffectContext *iface,
+ REFGUID shader_id, const BYTE *buffer, UINT32 buffer_size)
+{
+ FIXME("iface %p, shader_id %s, buffer %p, buffer_size %u stub!\n",
+ iface, debugstr_guid(shader_id), buffer, buffer_size);
+
+ return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE d2d_effect_context_LoadComputeShader(ID2D1EffectContext *iface,
+ REFGUID shader_id, const BYTE *buffer, UINT32 buffer_size)
+{
+ FIXME("iface %p, shader_id %s, buffer %p, buffer_size %u stub!\n",
+ iface, debugstr_guid(shader_id), buffer, buffer_size);
+
+ return E_NOTIMPL;
+}
+
+static BOOL STDMETHODCALLTYPE d2d_effect_context_IsShaderLoaded(ID2D1EffectContext *iface, REFGUID shader_id)
+{
+ FIXME("iface %p, shader_id %s stub!\n", iface, debugstr_guid(shader_id));
+
+ return FALSE;
+}
+
+static HRESULT STDMETHODCALLTYPE d2d_effect_context_CreateResourceTexture(ID2D1EffectContext *iface,
+ const GUID *id, const D2D1_RESOURCE_TEXTURE_PROPERTIES *texture_properties,
+ const BYTE *data, const UINT32 *strides, UINT32 data_size, ID2D1ResourceTexture **texture)
+{
+ FIXME("iface %p, id %s, texture_properties %p, data %p, strides %s, data_size %u, texture %p stub!\n",
+ iface, debugstr_guid(id), texture_properties, data,
+ strides ? wine_dbg_sprintf("%u", *strides) : "(null)", data_size, texture);
+
+ return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE d2d_effect_context_FindResourceTexture(ID2D1EffectContext *iface,
+ const GUID *id, ID2D1ResourceTexture **texture)
+{
+ FIXME("iface %p, id %s, texture %p stub!\n", iface, debugstr_guid(id), texture);
+
+ return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE d2d_effect_context_CreateVertexBuffer(ID2D1EffectContext *iface,
+ const D2D1_VERTEX_BUFFER_PROPERTIES *buffer_properties, const GUID *id,
+ const D2D1_CUSTOM_VERTEX_BUFFER_PROPERTIES *custom_buffer_properties, ID2D1VertexBuffer **buffer)
+{
+ FIXME("iface %p, buffer_properties %p, id %s, custom_buffer_properties %p, buffer %p stub!\n",
+ iface, buffer_properties, debugstr_guid(id), custom_buffer_properties, buffer);
+
+ return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE d2d_effect_context_FindVertexBuffer(ID2D1EffectContext *iface,
+ const GUID *id, ID2D1VertexBuffer **buffer)
+{
+ FIXME("iface %p, id %s, buffer %p stub!\n", iface, debugstr_guid(id), buffer);
+
+ return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE d2d_effect_context_CreateColorContext(ID2D1EffectContext *iface,
+ D2D1_COLOR_SPACE space, const BYTE *profile, UINT32 profile_size, ID2D1ColorContext **color_context)
+{
+ FIXME("iface %p, space %#x, profile %p, profile_size %u, color_context %p stub!\n",
+ iface, space, profile, profile_size, color_context);
+
+ return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE d2d_effect_context_CreateColorContextFromFilename(ID2D1EffectContext *iface,
+ const WCHAR *filename, ID2D1ColorContext **color_context)
+{
+ FIXME("iface %p, filename %s, color_context %p stub!\n", iface, debugstr_w(filename), color_context);
+
+ return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE d2d_effect_context_CreateColorContextFromWicColorContext(ID2D1EffectContext *iface,
+ IWICColorContext *wic_color_context, ID2D1ColorContext **color_context)
+{
+ FIXME("iface %p, wic_color_context %p, color_context %p stub!\n", iface, wic_color_context, color_context);
+
+ return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE d2d_effect_context_CheckFeatureSupport(ID2D1EffectContext *iface,
+ D2D1_FEATURE feature, void *data, UINT32 data_size)
+{
+ FIXME("iface %p, feature %#x, data %p, data_size %u stub!\n", iface, feature, data, data_size);
+
+ return E_NOTIMPL;
+}
+
+static BOOL STDMETHODCALLTYPE d2d_effect_context_IsBufferPrecisionSupported(ID2D1EffectContext *iface,
+ D2D1_BUFFER_PRECISION precision)
+{
+ FIXME("iface %p, precision %#x stub!\n", iface, precision);
+
+ return FALSE;
+}
+
+static const ID2D1EffectContextVtbl d2d_effect_context_vtbl =
+{
+ d2d_effect_context_QueryInterface,
+ d2d_effect_context_AddRef,
+ d2d_effect_context_Release,
+ d2d_effect_context_GetDpi,
+ d2d_effect_context_CreateEffect,
+ d2d_effect_context_GetMaximumSupportedFeatureLevel,
+ d2d_effect_context_CreateTransformNodeFromEffect,
+ d2d_effect_context_CreateBlendTransform,
+ d2d_effect_context_CreateBorderTransform,
+ d2d_effect_context_CreateOffsetTransform,
+ d2d_effect_context_CreateBoundsAdjustmentTransform,
+ d2d_effect_context_LoadPixelShader,
+ d2d_effect_context_LoadVertexShader,
+ d2d_effect_context_LoadComputeShader,
+ d2d_effect_context_IsShaderLoaded,
+ d2d_effect_context_CreateResourceTexture,
+ d2d_effect_context_FindResourceTexture,
+ d2d_effect_context_CreateVertexBuffer,
+ d2d_effect_context_FindVertexBuffer,
+ d2d_effect_context_CreateColorContext,
+ d2d_effect_context_CreateColorContextFromFilename,
+ d2d_effect_context_CreateColorContextFromWicColorContext,
+ d2d_effect_context_CheckFeatureSupport,
+ d2d_effect_context_IsBufferPrecisionSupported,
+};
+
+void d2d_effect_context_init(struct d2d_effect_context *effect_context, struct d2d_device_context *device_context)
+{
+ effect_context->ID2D1EffectContext_iface.lpVtbl = &d2d_effect_context_vtbl;
+ effect_context->refcount = 1;
+ effect_context->device_context = device_context;
+ ID2D1DeviceContext_AddRef(&device_context->ID2D1DeviceContext_iface);
+}
+
static inline struct d2d_effect *impl_from_ID2D1Effect(ID2D1Effect *iface)
{
return CONTAINING_RECORD(iface, struct d2d_effect, ID2D1Effect_iface);
@@ -46,7 +325,7 @@ static void d2d_effect_cleanup(struct d2d_effect *effect)
ID2D1Image_Release(effect->inputs[i]);
}
heap_free(effect->inputs);
- ID2D1Factory_Release(effect->factory);
+ ID2D1EffectContext_Release(&effect->effect_context->ID2D1EffectContext_iface);
}
static HRESULT STDMETHODCALLTYPE d2d_effect_QueryInterface(ID2D1Effect *iface, REFIID iid, void **out)
@@ -362,7 +641,7 @@ static void STDMETHODCALLTYPE d2d_effect_image_GetFactory(ID2D1Image *iface, ID2
TRACE("iface %p, factory %p.\n", iface, factory);
- ID2D1Factory_AddRef(*factory = effect->factory);
+ ID2D1Factory_AddRef(*factory = effect->effect_context->device_context->factory);
}
static const ID2D1ImageVtbl d2d_effect_image_vtbl =
@@ -373,7 +652,7 @@ static const ID2D1ImageVtbl d2d_effect_image_vtbl =
d2d_effect_image_GetFactory,
};
-HRESULT d2d_effect_init(struct d2d_effect *effect, ID2D1Factory *factory, const CLSID *effect_id)
+HRESULT d2d_effect_init(struct d2d_effect *effect, struct d2d_effect_context *effect_context, const CLSID *effect_id)
{
unsigned int i;
@@ -387,7 +666,8 @@ HRESULT d2d_effect_init(struct d2d_effect *effect, ID2D1Factory *factory, const
{
effect->info = &builtin_effects[i];
d2d_effect_SetInputCount(&effect->ID2D1Effect_iface, effect->info->default_input_count);
- ID2D1Factory_AddRef(effect->factory = factory);
+ effect->effect_context = effect_context;
+ ID2D1EffectContext_AddRef(&effect_context->ID2D1EffectContext_iface);
return S_OK;
}
}
--
2.25.1
April 29, 2022
Re: [PATCH 5/5] mf/tests: Expect identical major types for transform info.
by Nikolay Sivov
Signed-off-by: Nikolay Sivov <nsivov(a)codeweavers.com>
April 29, 2022
Re: [PATCH 4/5] mf/tests: Add tests changing the H264 decoder output video format.
by Nikolay Sivov
Signed-off-by: Nikolay Sivov <nsivov(a)codeweavers.com>
April 29, 2022
Re: [PATCH 3/5] mf/tests: Add some IMFTransform output sample attribute tests.
by Nikolay Sivov
Signed-off-by: Nikolay Sivov <nsivov(a)codeweavers.com>
April 29, 2022
Re: [PATCH 2/5] mf/tests: Use real audio data for WMA encoder / decoder tests.
by Nikolay Sivov
Signed-off-by: Nikolay Sivov <nsivov(a)codeweavers.com>
April 29, 2022
Re: [PATCH 1/5] mf/tests: Skip todo_wine tests with a goto statement.
by Nikolay Sivov
Signed-off-by: Nikolay Sivov <nsivov(a)codeweavers.com>
April 29, 2022
Re: MR14v1 - dlls/dbghelp: introduce symt_find_symbol_at()
by Huw Davies (@huw)
Typo in the commit msg: s/whithin/within/
--
https://gitlab.winehq.org/wine/wine/-/merge_requests/14#note_506
April 29, 2022
[PATCH 1/1] dlls/dbghelp: introduce symt_find_symbol_at()
by Eric Pouech
From: Eric Pouech <eric.pouech(a)gmail.com>
To be used in place of symt_find_nearest().
symt_find_symbol_at() ensures that the address passed is whithin the
boundaries of the returned symbol (while find_nearest() doesn't).
This fixes erroneous backtraces in debugger like:
$ ./wine winedbg notepad
WineDbg starting on pid 0104
RtlDefaultNpAcl () at Z:\home\eric\work\wine\dlls\ntdll\sec.c:1731
0x00000170054805 ntdll+0x54805 [Z:\home\eric\work\wine\dlls\ntdll\sec.c:1731]: ret
1731 }
Wine-dbg>bt
Backtrace:
=>0 0x00000170054805 RtlDefaultNpAcl+0x2d5(pAcl=<internal error>) [Z:\home\eric\work\wine\dlls\ntdll\sec.c:1731] in ntdll (0x000001700701a4)
1 0x0000017002d6c4 __wine_pop_frame(pAcl=<internal error>) [Z:\home\eric\work\wine\include\wine\exception.h:273] in ntdll (0x000001700701a4)
2 0x0000017002d6c4 process_breakpoint+0x84() [Z:\home\eric\work\wine\dlls\ntdll\loader.c:3912] in ntdll (0x000001700701a4)
3 0x000001700354c9 LdrInitializeThunk+0x509(context=<register R13 not accessible in this frame>, unknown2=<internal error>, unknown3=<internal error>, unknown4=<internal error>) [Z:\home\eric\work\wine\dlls\ntdll\loader.c:4200] in ntdll (0x000001700701a4)
where RtlDefaultNpAcl() has nothing to do here (it's the symbol below RIP
and we don't have a symbol with debug information for that address).
Signed-off-by: Eric Pouech <eric.pouech(a)gmail.com>
---
dlls/dbghelp/dbghelp.c | 2 +-
dlls/dbghelp/dbghelp_private.h | 2 ++
dlls/dbghelp/dwarf.c | 2 +-
dlls/dbghelp/msc.c | 8 ++++----
dlls/dbghelp/symbol.c | 24 +++++++++++++++++++-----
5 files changed, 27 insertions(+), 11 deletions(-)
diff --git a/dlls/dbghelp/dbghelp.c b/dlls/dbghelp/dbghelp.c
index 6d775b633f8..c154d8d9713 100644
--- a/dlls/dbghelp/dbghelp.c
+++ b/dlls/dbghelp/dbghelp.c
@@ -672,7 +672,7 @@ BOOL WINAPI SymSetScopeFromAddr(HANDLE hProcess, ULONG64 addr)
if (!module_init_pair(&pair, hProcess, addr)) return FALSE;
pair.pcs->localscope_pc = addr;
- if ((sym = symt_find_nearest(pair.effective, addr)) != NULL && sym->symt.tag == SymTagFunction)
+ if ((sym = symt_find_symbol_at(pair.effective, addr)) != NULL && sym->symt.tag == SymTagFunction)
pair.pcs->localscope_symt = &sym->symt;
else
pair.pcs->localscope_symt = NULL;
diff --git a/dlls/dbghelp/dbghelp_private.h b/dlls/dbghelp/dbghelp_private.h
index c9de237c4b9..507724414a5 100644
--- a/dlls/dbghelp/dbghelp_private.h
+++ b/dlls/dbghelp/dbghelp_private.h
@@ -811,6 +811,8 @@ extern void copy_symbolW(SYMBOL_INFOW* siw, const SYMBOL_INFO* si) DECLS
extern void symbol_setname(SYMBOL_INFO* si, const char* name) DECLSPEC_HIDDEN;
extern struct symt_ht*
symt_find_nearest(struct module* module, DWORD_PTR addr) DECLSPEC_HIDDEN;
+extern struct symt_ht*
+ symt_find_symbol_at(struct module* module, DWORD_PTR addr) DECLSPEC_HIDDEN;
extern struct symt_module*
symt_new_module(struct module* module) DECLSPEC_HIDDEN;
extern struct symt_compiland*
diff --git a/dlls/dbghelp/dwarf.c b/dlls/dbghelp/dwarf.c
index 0076b19e5cb..b81f83ac90b 100644
--- a/dlls/dbghelp/dwarf.c
+++ b/dlls/dbghelp/dwarf.c
@@ -2584,7 +2584,7 @@ static void dwarf2_set_line_number(struct module* module, ULONG_PTR address,
TRACE("%s %Ix %s %u\n",
debugstr_w(module->modulename), address, debugstr_a(source_get(module, *psrc)), line);
- symt = symt_find_nearest(module, address);
+ symt = symt_find_symbol_at(module, address);
if (symt_check_tag(&symt->symt, SymTagFunction))
{
func = (struct symt_function*)symt;
diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c
index 56c0f8b58e8..997661f2912 100644
--- a/dlls/dbghelp/msc.c
+++ b/dlls/dbghelp/msc.c
@@ -1461,7 +1461,7 @@ static void codeview_snarf_linetab(const struct msc_debug_info* msc_dbg, const B
*/
if (!func || addr >= func->address + func->size)
{
- func = (struct symt_function*)symt_find_nearest(msc_dbg->module, addr);
+ func = (struct symt_function*)symt_find_symbol_at(msc_dbg->module, addr);
/* FIXME: at least labels support line numbers */
if (!symt_check_tag(&func->symt, SymTagFunction) && !symt_check_tag(&func->symt, SymTagInlineSite))
{
@@ -1534,7 +1534,7 @@ static void codeview_snarf_linetab2(const struct msc_debug_info* msc_dbg, const
lines = CV_RECORD_AFTER(files_hdr);
for (i = 0; i < files_hdr->nLines; i++)
{
- func = (struct symt_function*)symt_find_nearest(msc_dbg->module, lineblk_base + lines[i].offset);
+ func = (struct symt_function*)symt_find_symbol_at(msc_dbg->module, lineblk_base + lines[i].offset);
/* FIXME: at least labels support line numbers */
if (!symt_check_tag(&func->symt, SymTagFunction) && !symt_check_tag(&func->symt, SymTagInlineSite))
{
@@ -1619,7 +1619,7 @@ static inline void codeview_add_variable(const struct msc_debug_info* msc_dbg,
loc.kind = in_tls ? loc_tlsrel : loc_absolute;
loc.reg = 0;
loc.offset = in_tls ? offset : codeview_get_address(msc_dbg, segment, offset);
- if (force || in_tls || !symt_find_nearest(msc_dbg->module, loc.offset))
+ if (force || in_tls || !symt_find_symbol_at(msc_dbg->module, loc.offset))
{
symt_new_global_variable(msc_dbg->module, compiland,
name, is_local, loc, 0,
@@ -2501,7 +2501,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg,
if (!top_func)
{
ULONG_PTR parent_addr = codeview_get_address(msc_dbg, sym->sepcode_v3.sectParent, sym->sepcode_v3.offParent);
- struct symt_ht* parent = symt_find_nearest(msc_dbg->module, parent_addr);
+ struct symt_ht* parent = symt_find_symbol_at(msc_dbg->module, parent_addr);
if (symt_check_tag(&parent->symt, SymTagFunction))
{
struct symt_function* pfunc = (struct symt_function*)parent;
diff --git a/dlls/dbghelp/symbol.c b/dlls/dbghelp/symbol.c
index 64f376b42f0..a81feb56b8d 100644
--- a/dlls/dbghelp/symbol.c
+++ b/dlls/dbghelp/symbol.c
@@ -1027,7 +1027,7 @@ static void symt_get_length(struct module* module, const struct symt* symt, ULON
if (symt_get_info(module, symt, TI_GET_TYPE, &type_index) &&
symt_get_info(module, symt_index2ptr(module, type_index), TI_GET_LENGTH, size)) return;
- *size = 0x1000; /* arbitrary value */
+ *size = 1; /* no size info */
}
/* needed by symt_find_nearest */
@@ -1104,6 +1104,20 @@ struct symt_ht* symt_find_nearest(struct module* module, DWORD_PTR addr)
return module->addr_sorttab[low];
}
+struct symt_ht* symt_find_symbol_at(struct module* module, DWORD_PTR addr)
+{
+ struct symt_ht* nearest = symt_find_nearest(module, addr);
+ if (nearest)
+ {
+ ULONG64 symaddr, symsize;
+ symt_get_address(&nearest->symt, &symaddr);
+ symt_get_length(module, &nearest->symt, &symsize);
+ if (addr < symaddr || addr >= symaddr + symsize)
+ nearest = NULL;
+ }
+ return nearest;
+}
+
static BOOL symt_enum_locals_helper(struct module_pair* pair,
const WCHAR* match, const struct sym_enum* se,
struct symt_function* func, const struct vector* v)
@@ -1262,7 +1276,7 @@ struct symt* symt_get_upper_inlined(struct symt_inlinesite* inlined)
/* lookup in module for an inline site (from addr and inline_ctx) */
struct symt_inlinesite* symt_find_inlined_site(struct module* module, DWORD64 addr, DWORD inline_ctx)
{
- struct symt_ht* symt = symt_find_nearest(module, addr);
+ struct symt_ht* symt = symt_find_symbol_at(module, addr);
if (symt_check_tag(&symt->symt, SymTagFunction))
{
@@ -1284,7 +1298,7 @@ DWORD symt_get_inlinesite_depth(HANDLE hProcess, DWORD64 addr)
if (module_init_pair(&pair, hProcess, addr))
{
- struct symt_ht* symt = symt_find_nearest(pair.effective, addr);
+ struct symt_ht* symt = symt_find_symbol_at(pair.effective, addr);
if (symt_check_tag(&symt->symt, SymTagFunction))
{
struct symt_inlinesite* inlined = symt_find_lowest_inlined((struct symt_function*)symt, addr);
@@ -1518,7 +1532,7 @@ BOOL WINAPI SymFromAddr(HANDLE hProcess, DWORD64 Address,
struct symt_ht* sym;
if (!module_init_pair(&pair, hProcess, Address)) return FALSE;
- if ((sym = symt_find_nearest(pair.effective, Address)) == NULL) return FALSE;
+ if ((sym = symt_find_symbol_at(pair.effective, Address)) == NULL) return FALSE;
symt_fill_sym_info(&pair, NULL, &sym->symt, Symbol);
if (Displacement)
@@ -1905,7 +1919,7 @@ static BOOL get_line_from_addr(HANDLE hProcess, DWORD64 addr,
struct symt_ht* symt;
if (!module_init_pair(&pair, hProcess, addr)) return FALSE;
- if ((symt = symt_find_nearest(pair.effective, addr)) == NULL) return FALSE;
+ if ((symt = symt_find_symbol_at(pair.effective, addr)) == NULL) return FALSE;
if (symt->symt.tag != SymTagFunction && symt->symt.tag != SymTagInlineSite) return FALSE;
return get_line_from_function(&pair, (struct symt_function*)symt, addr, pdwDisplacement, intl);
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/14
April 29, 2022
[PATCH 0/1] MR14: dlls/dbghelp: introduce symt_find_symbol_at()
by eric pouech (@epo)
To be used in place of symt_find_nearest().
symt_find_symbol_at() ensures that the address passed is whithin the
boundaries of the returned symbol (while find_nearest() doesn't).
This fixes erroneous backtraces in debugger like:
$ ./wine winedbg notepad
WineDbg starting on pid 0104
RtlDefaultNpAcl () at Z:\home\eric\work\wine\dlls\ntdll\sec.c:1731
0x00000170054805 ntdll+0x54805 [Z:\home\eric\work\wine\dlls\ntdll\sec.c:1731]: ret
1731 }
Wine-dbg>bt
Backtrace:
=>0 0x00000170054805 RtlDefaultNpAcl+0x2d5(pAcl=<internal error>) [Z:\home\eric\work\wine\dlls\ntdll\sec.c:1731] in ntdll (0x000001700701a4)
1 0x0000017002d6c4 __wine_pop_frame(pAcl=<internal error>) [Z:\home\eric\work\wine\include\wine\exception.h:273] in ntdll (0x000001700701a4)
2 0x0000017002d6c4 process_breakpoint+0x84() [Z:\home\eric\work\wine\dlls\ntdll\loader.c:3912] in ntdll (0x000001700701a4)
3 0x000001700354c9 LdrInitializeThunk+0x509(context=<register R13 not accessible in this frame>, unknown2=<internal error>, unknown3=<internal error>, unknown4=<internal error>) [Z:\home\eric\work\wine\dlls\ntdll\loader.c:4200] in ntdll (0x000001700701a4)
where RtlDefaultNpAcl() has nothing to do here (it's the symbol below RIP
and we don't have a symbol with debug information for that address).
Signed-off-by: Eric Pouech <eric.pouech(a)gmail.com>
--
https://gitlab.winehq.org/wine/wine/-/merge_requests/14
April 29, 2022
Re: [PATCH v2 8/8] d2d1: Implement LoadVertexShader().
by Nikolay Sivov
On 4/29/22 09:50, Ziqing Hui wrote:
>
> On 4/29/22 2:02 PM, Nikolay Sivov wrote:
>>
>> On 4/28/22 13:40, Ziqing Hui wrote:
>>> +struct d2d_shader
>>> +{
>>> + const GUID *id;
>>> + void *shader;
>>> +};
>> This could at least use IUnknown, you can probably use a union later to avoid casts.
>>
>>> + effect_context->shader_count++;
>>> + if (effect_context->shaders_size < effect_context->shader_count)
>>> + {
>>> + if (!d2d_array_reserve((void **)&effect_context->shaders, &effect_context->shaders_size,
>>> + effect_context->shader_count, sizeof(*effect_context->shaders)))
>>> + {
>>> + ERR("Failed to resize shaders array.\n");
>>> + ID3D11VertexShader_Release(vertex_shader);
>>> + return E_OUTOFMEMORY;
>>> + }
>>> + }
>> You should call this to reserve "effect_context->shader_count + 1", no need to check size < count explicitly.
>>
>> Since this is using GUIDs for keys, I suspect it should check for duplicates? IsShaderLoaded() takes just a GUID, so that implies all shader types are in the same list most likely.
>>
>> By the way, have you figured out how shader objects are used later?
>>
> Shader objects will be used in ID2D1DrawTransform to create custom transforms which have custom shaders.
>
> ID2D1DrawTransfrom use ID2D1DrawInfo that has functions like SetPixelShader() which accept shader GUID as an input argument.
> And that's where the loaded shader objects are used.
>
I see, thanks. Maybe it is a good idea to have dummy custom effect in
our tests to see how these methods work. Specifically calling with same
(or null) GUID, and where you can reuse GUID across shader types (I
suspect you can't).
Later for SetPixelShader() we'll see if it returns meaningful error code
if you set e.g. compute shader to it, or only forwards d3d error. But
anyway, IUnknown* should be good for now, instead of void*.
April 29, 2022
Re: [PATCH v4] wintypes: Implement IApiInformationStatics stubs.
by Rémi Bernon
Signed-off-by: Rémi Bernon <rbernon(a)codeweavers.com>
April 29, 2022
Re: [PATCH v2 3/8] d2d1: Implement ID2D1EffectContext_GetDpi().
by Nikolay Sivov
On 4/28/22 13:40, Ziqing Hui wrote:
> static void STDMETHODCALLTYPE d2d_effect_context_GetDpi(ID2D1EffectContext *iface, float *dpi_x, float *dpi_y)
> {
> - FIXME("iface %p, dpi_x %p, dpi_y %p stub!\n", iface, dpi_x, dpi_y);
> + struct d2d_effect_context *effect_context = impl_from_ID2D1EffectContext(iface);
> +
> + TRACE("iface %p, dpi_x %p, dpi_y %p.\n", iface, dpi_x, dpi_y);
> +
> + return ID2D1DeviceContext_GetDpi(effect_context->device_context, dpi_x, dpi_y);
> }
>
> static HRESULT STDMETHODCALLTYPE d2d_effect_context_CreateEffect(ID2D1EffectContext *iface,
Were you able to test this? I realize it requires a minimal custom
effect to access context object. It makes sense I guess to forward it
like you did, as opposed to returning dpi at the time of CreateEffect()
call, but it not obvious just from this patch.
Note that you don't need "return" here.
April 29, 2022
[PATCH v4] wintypes: Implement IApiInformationStatics stubs.
by Zhiyi Zhang
Required for Iragon: Prologue.
Signed-off-by: Zhiyi Zhang <zzhang(a)codeweavers.com>
---
v3: Supersede 233207. Add tests.
v4: Supersede 233864. Link to combase function directly.
configure.ac | 1 +
dlls/wintypes/main.c | 207 ++++++++++++++++
dlls/wintypes/tests/Makefile.in | 5 +
dlls/wintypes/tests/wintypes.c | 427 ++++++++++++++++++++++++++++++++
4 files changed, 640 insertions(+)
create mode 100644 dlls/wintypes/tests/Makefile.in
create mode 100644 dlls/wintypes/tests/wintypes.c
diff --git a/configure.ac b/configure.ac
index 74c80fd7fa8..f0b39d172ba 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3142,6 +3142,7 @@ WINE_CONFIG_MAKEFILE(dlls/wintab32/tests)
WINE_CONFIG_MAKEFILE(dlls/wintrust)
WINE_CONFIG_MAKEFILE(dlls/wintrust/tests)
WINE_CONFIG_MAKEFILE(dlls/wintypes)
+WINE_CONFIG_MAKEFILE(dlls/wintypes/tests)
WINE_CONFIG_MAKEFILE(dlls/winusb)
WINE_CONFIG_MAKEFILE(dlls/wlanapi)
WINE_CONFIG_MAKEFILE(dlls/wlanapi/tests)
diff --git a/dlls/wintypes/main.c b/dlls/wintypes/main.c
index e5fea2f7d30..1ca8d94cd67 100644
--- a/dlls/wintypes/main.c
+++ b/dlls/wintypes/main.c
@@ -28,6 +28,9 @@
#include "activation.h"
+#define WIDL_using_Windows_Foundation_Metadata
+#include "windows.foundation.metadata.h"
+
WINE_DEFAULT_DEBUG_CHANNEL(wintypes);
static const char *debugstr_hstring(HSTRING hstr)
@@ -43,6 +46,7 @@ static const char *debugstr_hstring(HSTRING hstr)
struct wintypes
{
IActivationFactory IActivationFactory_iface;
+ IApiInformationStatics IApiInformationStatics_iface;
LONG ref;
};
@@ -51,9 +55,16 @@ static inline struct wintypes *impl_from_IActivationFactory(IActivationFactory *
return CONTAINING_RECORD(iface, struct wintypes, IActivationFactory_iface);
}
+static inline struct wintypes *impl_from_IApiInformationStatics(IApiInformationStatics *iface)
+{
+ return CONTAINING_RECORD(iface, struct wintypes, IApiInformationStatics_iface);
+}
+
static HRESULT STDMETHODCALLTYPE wintypes_QueryInterface(IActivationFactory *iface, REFIID iid,
void **out)
{
+ struct wintypes *impl = impl_from_IActivationFactory(iface);
+
TRACE("iface %p, iid %s, out %p stub!\n", iface, debugstr_guid(iid), out);
if (IsEqualGUID(iid, &IID_IUnknown)
@@ -66,6 +77,13 @@ static HRESULT STDMETHODCALLTYPE wintypes_QueryInterface(IActivationFactory *ifa
return S_OK;
}
+ if (IsEqualGUID(iid, &IID_IApiInformationStatics))
+ {
+ IUnknown_AddRef(iface);
+ *out = &impl->IApiInformationStatics_iface;
+ return S_OK;
+ }
+
FIXME("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid));
*out = NULL;
return E_NOINTERFACE;
@@ -128,9 +146,198 @@ static const struct IActivationFactoryVtbl activation_factory_vtbl =
wintypes_ActivateInstance,
};
+static HRESULT STDMETHODCALLTYPE api_information_statics_QueryInterface(
+ IApiInformationStatics *iface, REFIID iid, void **out)
+{
+ struct wintypes *impl = impl_from_IApiInformationStatics(iface);
+ return wintypes_QueryInterface(&impl->IActivationFactory_iface, iid, out);
+}
+
+static ULONG STDMETHODCALLTYPE api_information_statics_AddRef(
+ IApiInformationStatics *iface)
+{
+ struct wintypes *impl = impl_from_IApiInformationStatics(iface);
+ return wintypes_AddRef(&impl->IActivationFactory_iface);
+}
+
+static ULONG STDMETHODCALLTYPE api_information_statics_Release(
+ IApiInformationStatics *iface)
+{
+ struct wintypes *impl = impl_from_IApiInformationStatics(iface);
+ return wintypes_Release(&impl->IActivationFactory_iface);
+}
+
+static HRESULT STDMETHODCALLTYPE api_information_statics_GetIids(
+ IApiInformationStatics *iface, ULONG *iid_count, IID **iids)
+{
+ FIXME("iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids);
+ return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE api_information_statics_GetRuntimeClassName(
+ IApiInformationStatics *iface, HSTRING *class_name)
+{
+ FIXME("iface %p, class_name %p stub!\n", iface, class_name);
+ return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE api_information_statics_GetTrustLevel(
+ IApiInformationStatics *iface, TrustLevel *trust_level)
+{
+ FIXME("iface %p, trust_level %p stub!\n", iface, trust_level);
+ return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE api_information_statics_IsTypePresent(
+ IApiInformationStatics *iface, HSTRING type_name, BOOLEAN *value)
+{
+ FIXME("iface %p, type_name %s, value %p stub!\n", iface, debugstr_hstring(type_name), value);
+
+ if (!type_name)
+ return E_INVALIDARG;
+
+ return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE api_information_statics_IsMethodPresent(
+ IApiInformationStatics *iface, HSTRING type_name, HSTRING method_name, BOOLEAN *value)
+{
+ FIXME("iface %p, type_name %s, method_name %s, value %p stub!\n", iface,
+ debugstr_hstring(type_name), debugstr_hstring(method_name), value);
+
+ if (!type_name)
+ return E_INVALIDARG;
+
+ return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE api_information_statics_IsMethodPresentWithArity(
+ IApiInformationStatics *iface, HSTRING type_name, HSTRING method_name,
+ UINT32 input_parameter_count, BOOLEAN *value)
+{
+ FIXME("iface %p, type_name %s, method_name %s, input_parameter_count %u, value %p stub!\n",
+ iface, debugstr_hstring(type_name), debugstr_hstring(method_name),
+ input_parameter_count, value);
+
+ if (!type_name)
+ return E_INVALIDARG;
+
+ return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE api_information_statics_IsEventPresent(
+ IApiInformationStatics *iface, HSTRING type_name, HSTRING event_name, BOOLEAN *value)
+{
+ FIXME("iface %p, type_name %s, event_name %s, value %p stub!\n", iface,
+ debugstr_hstring(type_name), debugstr_hstring(event_name), value);
+
+ if (!type_name)
+ return E_INVALIDARG;
+
+ return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE api_information_statics_IsPropertyPresent(
+ IApiInformationStatics *iface, HSTRING type_name, HSTRING property_name, BOOLEAN *value)
+{
+ FIXME("iface %p, type_name %s, property_name %s, value %p stub!\n", iface,
+ debugstr_hstring(type_name), debugstr_hstring(property_name), value);
+
+ if (!type_name)
+ return E_INVALIDARG;
+
+ return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE api_information_statics_IsReadOnlyPropertyPresent(
+ IApiInformationStatics *iface, HSTRING type_name, HSTRING property_name,
+ BOOLEAN *value)
+{
+ FIXME("iface %p, type_name %s, property_name %s, value %p stub!\n", iface,
+ debugstr_hstring(type_name), debugstr_hstring(property_name), value);
+
+ if (!type_name)
+ return E_INVALIDARG;
+
+ return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE api_information_statics_IsWriteablePropertyPresent(
+ IApiInformationStatics *iface, HSTRING type_name, HSTRING property_name, BOOLEAN *value)
+{
+ FIXME("iface %p, type_name %s, property_name %s, value %p stub!\n", iface,
+ debugstr_hstring(type_name), debugstr_hstring(property_name), value);
+
+ if (!type_name)
+ return E_INVALIDARG;
+
+ return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE api_information_statics_IsEnumNamedValuePresent(
+ IApiInformationStatics *iface, HSTRING enum_type_name, HSTRING value_name, BOOLEAN *value)
+{
+ FIXME("iface %p, enum_type_name %s, value_name %s, value %p stub!\n", iface,
+ debugstr_hstring(enum_type_name), debugstr_hstring(value_name), value);
+
+ if (!enum_type_name)
+ return E_INVALIDARG;
+
+ return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE api_information_statics_IsApiContractPresentByMajor(
+ IApiInformationStatics *iface, HSTRING contract_name, UINT16 major_version, BOOLEAN *value)
+{
+ FIXME("iface %p, contract_name %s, major_version %u, value %p stub!\n", iface,
+ debugstr_hstring(contract_name), major_version, value);
+
+ if (!contract_name)
+ return E_INVALIDARG;
+
+ *value = FALSE;
+ return S_OK;
+}
+
+static HRESULT STDMETHODCALLTYPE api_information_statics_IsApiContractPresentByMajorAndMinor(
+ IApiInformationStatics *iface, HSTRING contract_name, UINT16 major_version,
+ UINT16 minor_version, BOOLEAN *value)
+{
+ FIXME("iface %p, contract_name %s, major_version %u, minor_version %u, value %p stub!\n", iface,
+ debugstr_hstring(contract_name), major_version, minor_version, value);
+
+ if (!contract_name)
+ return E_INVALIDARG;
+
+ return E_NOTIMPL;
+}
+
+static const struct IApiInformationStaticsVtbl api_information_statics_vtbl =
+{
+ api_information_statics_QueryInterface,
+ api_information_statics_AddRef,
+ api_information_statics_Release,
+ /* IInspectable methods */
+ api_information_statics_GetIids,
+ api_information_statics_GetRuntimeClassName,
+ api_information_statics_GetTrustLevel,
+ /* IApiInformationStatics methods */
+ api_information_statics_IsTypePresent,
+ api_information_statics_IsMethodPresent,
+ api_information_statics_IsMethodPresentWithArity,
+ api_information_statics_IsEventPresent,
+ api_information_statics_IsPropertyPresent,
+ api_information_statics_IsReadOnlyPropertyPresent,
+ api_information_statics_IsWriteablePropertyPresent,
+ api_information_statics_IsEnumNamedValuePresent,
+ api_information_statics_IsApiContractPresentByMajor,
+ api_information_statics_IsApiContractPresentByMajorAndMinor
+};
+
static struct wintypes wintypes =
{
{&activation_factory_vtbl},
+ {&api_information_statics_vtbl},
1
};
diff --git a/dlls/wintypes/tests/Makefile.in b/dlls/wintypes/tests/Makefile.in
new file mode 100644
index 00000000000..457551548f9
--- /dev/null
+++ b/dlls/wintypes/tests/Makefile.in
@@ -0,0 +1,5 @@
+TESTDLL = wintypes.dll
+IMPORTS = combase uuid
+
+C_SRCS = \
+ wintypes.c
diff --git a/dlls/wintypes/tests/wintypes.c b/dlls/wintypes/tests/wintypes.c
new file mode 100644
index 00000000000..fb81cf86e99
--- /dev/null
+++ b/dlls/wintypes/tests/wintypes.c
@@ -0,0 +1,427 @@
+/*
+ * Copyright 2022 Zhiyi Zhang for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+#define COBJMACROS
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "winstring.h"
+
+#include "initguid.h"
+#include "roapi.h"
+
+#define WIDL_using_Windows_Foundation_Metadata
+#include "windows.foundation.metadata.h"
+
+#include "wine/test.h"
+
+static void test_IApiInformationStatics(void)
+{
+ static const WCHAR *class_name = L"Windows.Foundation.Metadata.ApiInformation";
+ IAgileObject *agile_object = NULL, *tmp_agile_object = NULL;
+ IInspectable *inspectable = NULL, *tmp_inspectable = NULL;
+ IApiInformationStatics *statics = NULL;
+ IActivationFactory *factory = NULL;
+ HSTRING str, str2;
+ BOOLEAN ret;
+ HRESULT hr;
+
+ hr = RoInitialize(RO_INIT_MULTITHREADED);
+ ok(hr == S_OK, "RoInitialize failed, hr %#lx.\n", hr);
+
+ hr = WindowsCreateString(class_name, wcslen(class_name), &str);
+ ok(hr == S_OK, "WindowsCreateString failed, hr %#lx.\n", hr);
+
+ hr = RoGetActivationFactory(str, &IID_IActivationFactory, (void **)&factory);
+ ok(hr == S_OK || broken(hr == REGDB_E_CLASSNOTREG), "RoGetActivationFactory failed, hr %#lx.\n", hr);
+ WindowsDeleteString(str);
+ if (hr == REGDB_E_CLASSNOTREG)
+ {
+ win_skip("%s runtimeclass not registered, skipping tests.\n", wine_dbgstr_w(class_name));
+ RoUninitialize();
+ return;
+ }
+
+ hr = IActivationFactory_QueryInterface(factory, &IID_IInspectable, (void **)&inspectable);
+ ok(hr == S_OK, "QueryInterface IID_IInspectable failed, hr %#lx.\n", hr);
+
+ hr = IActivationFactory_QueryInterface(factory, &IID_IAgileObject, (void **)&agile_object);
+ ok(hr == S_OK, "QueryInterface IID_IAgileObject failed, hr %#lx.\n", hr);
+
+ hr = IActivationFactory_QueryInterface(factory, &IID_IApiInformationStatics, (void **)&statics);
+ ok(hr == S_OK, "QueryInterface IID_IApiInformationStatics failed, hr %#lx.\n", hr);
+
+ hr = IApiInformationStatics_QueryInterface(statics, &IID_IInspectable, (void **)&tmp_inspectable);
+ ok(hr == S_OK, "QueryInterface IID_IInspectable failed, hr %#lx.\n", hr);
+ ok(tmp_inspectable == inspectable, "QueryInterface IID_IInspectable returned %p, expected %p.\n",
+ tmp_inspectable, inspectable);
+ IInspectable_Release(tmp_inspectable);
+
+ hr = IApiInformationStatics_QueryInterface(statics, &IID_IAgileObject, (void **)&tmp_agile_object);
+ ok(hr == S_OK, "QueryInterface IID_IAgileObject failed, hr %#lx.\n", hr);
+ ok(tmp_agile_object == agile_object, "QueryInterface IID_IAgileObject returned %p, expected %p.\n",
+ tmp_agile_object, agile_object);
+ IAgileObject_Release(tmp_agile_object);
+
+ /* IsTypePresent() */
+ hr = WindowsCreateString(L"Windows.Foundation.FoundationContract",
+ wcslen(L"Windows.Foundation.FoundationContract"), &str);
+ ok(hr == S_OK, "WindowsCreateString failed, hr %#lx.\n", hr);
+
+ hr = IApiInformationStatics_IsTypePresent(statics, NULL, &ret);
+ ok(hr == E_INVALIDARG, "IsTypePresent failed, hr %#lx.\n", hr);
+
+#if 0 /* Crash on Windows */
+ hr = IApiInformationStatics_IsTypePresent(statics, str, NULL);
+ ok(hr == E_INVALIDARG, "IsTypePresent failed, hr %#lx.\n", hr);
+#endif
+
+ ret = FALSE;
+ hr = IApiInformationStatics_IsTypePresent(statics, str, &ret);
+ todo_wine
+ ok(hr == S_OK, "IsTypePresent failed, hr %#lx.\n", hr);
+ todo_wine
+ ok(ret == TRUE, "IsTypePresent returned FALSE.\n");
+
+ WindowsDeleteString(str);
+
+ /* IsMethodPresent() */
+ hr = WindowsCreateString(L"Windows.Foundation.Metadata.IApiInformationStatics",
+ wcslen(L"Windows.Foundation.Metadata.IApiInformationStatics"), &str);
+ ok(hr == S_OK, "WindowsCreateString failed, hr %#lx.\n", hr);
+ hr = WindowsCreateString(L"IsTypePresent", wcslen(L"IsTypePresent"), &str2);
+ ok(hr == S_OK, "WindowsCreateString failed, hr %#lx.\n", hr);
+
+ hr = IApiInformationStatics_IsMethodPresent(statics, NULL, str2, &ret);
+ ok(hr == E_INVALIDARG, "IsMethodPresent failed, hr %#lx.\n", hr);
+
+ ret = TRUE;
+ hr = IApiInformationStatics_IsMethodPresent(statics, str, NULL, &ret);
+ todo_wine
+ ok(hr == S_OK, "IsMethodPresent failed, hr %#lx.\n", hr);
+ todo_wine
+ ok(ret == FALSE, "IsMethodPresent returned TRUE.\n");
+
+#if 0 /* Crash on Windows */
+ hr = IApiInformationStatics_IsMethodPresent(statics, str, str2, NULL);
+ ok(hr == E_INVALIDARG, "IsMethodPresent failed, hr %#lx.\n", hr);
+#endif
+
+ ret = FALSE;
+ hr = IApiInformationStatics_IsMethodPresent(statics, str, str2, &ret);
+ todo_wine
+ ok(hr == S_OK, "IsMethodPresent failed, hr %#lx.\n", hr);
+ todo_wine
+ ok(ret == TRUE, "IsMethodPresent returned FALSE.\n");
+
+ /* IsMethodPresentWithArity() */
+ hr = IApiInformationStatics_IsMethodPresentWithArity(statics, NULL, str2, 1, &ret);
+ ok(hr == E_INVALIDARG, "IsMethodPresentWithArity failed, hr %#lx.\n", hr);
+
+ ret = TRUE;
+ hr = IApiInformationStatics_IsMethodPresentWithArity(statics, str, NULL, 1, &ret);
+ todo_wine
+ ok(hr == S_OK, "IsMethodPresentWithArity failed, hr %#lx.\n", hr);
+ todo_wine
+ ok(ret == FALSE, "IsMethodPresentWithArity returned FALSE.\n");
+
+ ret = TRUE;
+ hr = IApiInformationStatics_IsMethodPresentWithArity(statics, str, str2, 0, &ret);
+ todo_wine
+ ok(hr == S_OK, "IsMethodPresentWithArity failed, hr %#lx.\n", hr);
+ todo_wine
+ ok(ret == FALSE, "IsMethodPresentWithArity returned FALSE.\n");
+
+ ret = TRUE;
+ hr = IApiInformationStatics_IsMethodPresentWithArity(statics, str, str2, 2, &ret);
+ todo_wine
+ ok(hr == S_OK, "IsMethodPresentWithArity failed, hr %#lx.\n", hr);
+ todo_wine
+ ok(ret == FALSE, "IsMethodPresentWithArity returned FALSE.\n");
+
+#if 0 /* Crash on Windows */
+ hr = IApiInformationStatics_IsMethodPresentWithArity(statics, str, str2, 1, NULL);
+ ok(hr == E_INVALIDARG, "IsMethodPresentWithArity failed, hr %#lx.\n", hr);
+#endif
+
+ ret = FALSE;
+ hr = IApiInformationStatics_IsMethodPresentWithArity(statics, str, str2, 1, &ret);
+ todo_wine
+ ok(hr == S_OK, "IsMethodPresentWithArity failed, hr %#lx.\n", hr);
+ todo_wine
+ ok(ret == TRUE, "IsMethodPresentWithArity returned FALSE.\n");
+
+ WindowsDeleteString(str2);
+ WindowsDeleteString(str);
+
+ /* IsEventPresent() */
+ hr = WindowsCreateString(L"Windows.Devices.Enumeration.IDeviceWatcher",
+ wcslen(L"Windows.Devices.Enumeration.IDeviceWatcher"), &str);
+ ok(hr == S_OK, "WindowsCreateString failed, hr %#lx.\n", hr);
+ hr = WindowsCreateString(L"Added", wcslen(L"Added"), &str2);
+ ok(hr == S_OK, "WindowsCreateString failed, hr %#lx.\n", hr);
+
+ hr = IApiInformationStatics_IsEventPresent(statics, NULL, str2, &ret);
+ ok(hr == E_INVALIDARG, "IsEventPresent failed, hr %#lx.\n", hr);
+
+ ret = TRUE;
+ hr = IApiInformationStatics_IsEventPresent(statics, str, NULL, &ret);
+ todo_wine
+ ok(hr == S_OK, "IsEventPresent failed, hr %#lx.\n", hr);
+ todo_wine
+ ok(ret == FALSE, "IsEventPresent returned FALSE.\n");
+
+#if 0 /* Crash on Windows */
+ hr = IApiInformationStatics_IsEventPresent(statics, str, str2, NULL);
+ ok(hr == E_INVALIDARG, "IsEventPresent failed, hr %#lx.\n", hr);
+#endif
+
+ ret = FALSE;
+ hr = IApiInformationStatics_IsEventPresent(statics, str, str2, &ret);
+ todo_wine
+ ok(hr == S_OK, "IsEventPresent failed, hr %#lx.\n", hr);
+ todo_wine
+ ok(ret == TRUE, "IsEventPresent returned FALSE.\n");
+
+ WindowsDeleteString(str2);
+ WindowsDeleteString(str);
+
+ /* IsPropertyPresent() */
+ hr = WindowsCreateString(L"Windows.Devices.Enumeration.IDeviceWatcher",
+ wcslen(L"Windows.Devices.Enumeration.IDeviceWatcher"), &str);
+ ok(hr == S_OK, "WindowsCreateString failed, hr %#lx.\n", hr);
+ hr = WindowsCreateString(L"Status", wcslen(L"Status"), &str2);
+ ok(hr == S_OK, "WindowsCreateString failed, hr %#lx.\n", hr);
+
+ hr = IApiInformationStatics_IsPropertyPresent(statics, NULL, str2, &ret);
+ ok(hr == E_INVALIDARG, "IsPropertyPresent failed, hr %#lx.\n", hr);
+
+ ret = TRUE;
+ hr = IApiInformationStatics_IsPropertyPresent(statics, str, NULL, &ret);
+ todo_wine
+ ok(hr == S_OK, "IsPropertyPresent failed, hr %#lx.\n", hr);
+ todo_wine
+ ok(ret == FALSE, "IsPropertyPresent returned TRUE.\n");
+
+#if 0 /* Crash on Windows */
+ hr = IApiInformationStatics_IsPropertyPresent(statics, str, str2, NULL);
+ ok(hr == E_INVALIDARG, "IsPropertyPresent failed, hr %#lx.\n", hr);
+#endif
+
+ ret = FALSE;
+ hr = IApiInformationStatics_IsPropertyPresent(statics, str, str2, &ret);
+ todo_wine
+ ok(hr == S_OK, "IsPropertyPresent failed, hr %#lx.\n", hr);
+ todo_wine
+ ok(ret == TRUE, "IsPropertyPresent returned FALSE.\n");
+
+ WindowsDeleteString(str2);
+ WindowsDeleteString(str);
+
+ /* IsReadOnlyPropertyPresent() */
+ hr = WindowsCreateString(L"Windows.Devices.Enumeration.IDeviceWatcher",
+ wcslen(L"Windows.Devices.Enumeration.IDeviceWatcher"), &str);
+ ok(hr == S_OK, "WindowsCreateString failed, hr %#lx.\n", hr);
+ hr = WindowsCreateString(L"Id", wcslen(L"Id"), &str2);
+ ok(hr == S_OK, "WindowsCreateString failed, hr %#lx.\n", hr);
+
+ hr = IApiInformationStatics_IsReadOnlyPropertyPresent(statics, NULL, str2, &ret);
+ ok(hr == E_INVALIDARG, "IsReadOnlyPropertyPresent failed, hr %#lx.\n", hr);
+
+ ret = TRUE;
+ hr = IApiInformationStatics_IsReadOnlyPropertyPresent(statics, str, NULL, &ret);
+ todo_wine
+ ok(hr == S_OK, "IsReadOnlyPropertyPresent failed, hr %#lx.\n", hr);
+ todo_wine
+ ok(ret == FALSE, "IsReadOnlyPropertyPresent returned TRUE.\n");
+
+#if 0 /* Crash on Windows */
+ hr = IApiInformationStatics_IsReadOnlyPropertyPresent(statics, str, str2, NULL);
+ ok(hr == E_INVALIDARG, "IsReadOnlyPropertyPresent failed, hr %#lx.\n", hr);
+#endif
+
+ ret = TRUE;
+ hr = IApiInformationStatics_IsReadOnlyPropertyPresent(statics, str, str2, &ret);
+ todo_wine
+ ok(hr == S_OK, "IsReadOnlyPropertyPresent failed, hr %#lx.\n", hr);
+ todo_wine
+ ok(ret == FALSE, "IsReadOnlyPropertyPresent returned TRUE.\n");
+
+ WindowsDeleteString(str2);
+ WindowsDeleteString(str);
+
+ /* IsWriteablePropertyPresent() */
+ hr = WindowsCreateString(L"Windows.Gaming.Input.ForceFeedback.IForceFeedbackEffect",
+ wcslen(L"Windows.Gaming.Input.ForceFeedback.IForceFeedbackEffect"), &str);
+ ok(hr == S_OK, "WindowsCreateString failed, hr %#lx.\n", hr);
+ hr = WindowsCreateString(L"Gain", wcslen(L"Gain"), &str2);
+ ok(hr == S_OK, "WindowsCreateString failed, hr %#lx.\n", hr);
+
+ hr = IApiInformationStatics_IsWriteablePropertyPresent(statics, NULL, str2, &ret);
+ ok(hr == E_INVALIDARG, "IsWriteablePropertyPresent failed, hr %#lx.\n", hr);
+
+ ret = TRUE;
+ hr = IApiInformationStatics_IsWriteablePropertyPresent(statics, str, NULL, &ret);
+ todo_wine
+ ok(hr == S_OK, "IsWriteablePropertyPresent failed, hr %#lx.\n", hr);
+ todo_wine
+ ok(ret == FALSE, "IsWriteablePropertyPresent returned TRUE.\n");
+
+#if 0 /* Crash on Windows */
+ hr = IApiInformationStatics_IsWriteablePropertyPresent(statics, str, str2, NULL);
+ ok(hr == E_INVALIDARG, "IsWriteablePropertyPresent failed, hr %#lx.\n", hr);
+#endif
+
+ ret = FALSE;
+ hr = IApiInformationStatics_IsWriteablePropertyPresent(statics, str, str2, &ret);
+ todo_wine
+ ok(hr == S_OK, "IsWriteablePropertyPresent failed, hr %#lx.\n", hr);
+ todo_wine
+ ok(ret == TRUE || broken(ret == FALSE) /* Win10 1507 */,
+ "IsWriteablePropertyPresent returned FALSE.\n");
+
+ WindowsDeleteString(str2);
+ WindowsDeleteString(str);
+
+ /* IsEnumNamedValuePresent */
+ hr = WindowsCreateString(L"Windows.Foundation.Metadata.GCPressureAmount",
+ wcslen(L"Windows.Foundation.Metadata.GCPressureAmount"), &str);
+ ok(hr == S_OK, "WindowsCreateString failed, hr %#lx.\n", hr);
+ hr = WindowsCreateString(L"Low", wcslen(L"Low"), &str2);
+ ok(hr == S_OK, "WindowsCreateString failed, hr %#lx.\n", hr);
+
+ hr = IApiInformationStatics_IsEnumNamedValuePresent(statics, NULL, str2, &ret);
+ ok(hr == E_INVALIDARG, "IsEnumNamedValuePresent failed, hr %#lx.\n", hr);
+
+ ret = TRUE;
+ hr = IApiInformationStatics_IsEnumNamedValuePresent(statics, str, NULL, &ret);
+ todo_wine
+ ok(hr == S_OK, "IsEnumNamedValuePresent failed, hr %#lx.\n", hr);
+ todo_wine
+ ok(ret == FALSE, "IsEnumNamedValuePresent returned TRUE.\n");
+
+#if 0 /* Crash on Windows */
+ hr = IApiInformationStatics_IsEnumNamedValuePresent(statics, str, str2, NULL);
+ ok(hr == E_INVALIDARG, "IsEnumNamedValuePresent failed, hr %#lx.\n", hr);
+#endif
+
+ ret = FALSE;
+ hr = IApiInformationStatics_IsEnumNamedValuePresent(statics, str, str2, &ret);
+ todo_wine
+ ok(hr == S_OK, "IsEnumNamedValuePresent failed, hr %#lx.\n", hr);
+ todo_wine
+ ok(ret == TRUE, "IsEnumNamedValuePresent returned FALSE.\n");
+
+ ret = TRUE;
+ hr = IApiInformationStatics_IsEnumNamedValuePresent(statics, str, str, &ret);
+ todo_wine
+ ok(hr == S_OK, "IsEnumNamedValuePresent failed, hr %#lx.\n", hr);
+ todo_wine
+ ok(ret == FALSE, "IsEnumNamedValuePresent returned TRUE.\n");
+
+ WindowsDeleteString(str2);
+ WindowsDeleteString(str);
+
+ /* IsApiContractPresentByMajor */
+ hr = WindowsCreateString(L"Windows.Foundation.FoundationContract",
+ wcslen(L"Windows.Foundation.FoundationContract"), &str);
+ ok(hr == S_OK, "WindowsCreateString failed, hr %#lx.\n", hr);
+
+ hr = IApiInformationStatics_IsApiContractPresentByMajor(statics, NULL, 1, &ret);
+ ok(hr == E_INVALIDARG, "IsApiContractPresentByMajor failed, hr %#lx.\n", hr);
+
+#if 0 /* Crash on Windows */
+ hr = IApiInformationStatics_IsApiContractPresentByMajor(statics, str, 1, NULL);
+ ok(hr == E_INVALIDARG, "IsApiContractPresentByMajor failed, hr %#lx.\n", hr);
+#endif
+
+ ret = FALSE;
+ hr = IApiInformationStatics_IsApiContractPresentByMajor(statics, str, 1, &ret);
+ ok(hr == S_OK, "IsApiContractPresentByMajor failed, hr %#lx.\n", hr);
+ todo_wine
+ ok(ret == TRUE, "IsApiContractPresentByMajor returned FALSE.\n");
+
+ ret = FALSE;
+ hr = IApiInformationStatics_IsApiContractPresentByMajor(statics, str, 0, &ret);
+ ok(hr == S_OK, "IsApiContractPresentByMajor failed, hr %#lx.\n", hr);
+ todo_wine
+ ok(ret == TRUE, "IsApiContractPresentByMajor returned FALSE.\n");
+
+ ret = TRUE;
+ hr = IApiInformationStatics_IsApiContractPresentByMajor(statics, str, 999, &ret);
+ ok(hr == S_OK, "IsApiContractPresentByMajor failed, hr %#lx.\n", hr);
+ ok(ret == FALSE, "IsApiContractPresentByMajor returned TRUE.\n");
+
+ WindowsDeleteString(str);
+
+ /* IsApiContractPresentByMajorAndMinor */
+ hr = WindowsCreateString(L"Windows.Foundation.FoundationContract",
+ wcslen(L"Windows.Foundation.FoundationContract"), &str);
+ ok(hr == S_OK, "WindowsCreateString failed, hr %#lx.\n", hr);
+
+ hr = IApiInformationStatics_IsApiContractPresentByMajorAndMinor(statics, NULL, 1, 0, &ret);
+ ok(hr == E_INVALIDARG, "IsApiContractPresentByMajorAndMinor failed, hr %#lx.\n", hr);
+
+#if 0 /* Crash on Windows */
+ hr = IApiInformationStatics_IsApiContractPresentByMajorAndMinor(statics, str, 1, 0, NULL);
+ ok(hr == E_INVALIDARG, "IsApiContractPresentByMajorAndMinor failed, hr %#lx.\n", hr);
+#endif
+
+ ret = FALSE;
+ hr = IApiInformationStatics_IsApiContractPresentByMajorAndMinor(statics, str, 1, 0, &ret);
+ todo_wine
+ ok(hr == S_OK, "IsApiContractPresentByMajorAndMinor failed, hr %#lx.\n", hr);
+ todo_wine
+ ok(ret == TRUE, "IsApiContractPresentByMajorAndMinor returned FALSE.\n");
+
+ ret = FALSE;
+ hr = IApiInformationStatics_IsApiContractPresentByMajorAndMinor(statics, str, 0, 999, &ret);
+ todo_wine
+ ok(hr == S_OK, "IsApiContractPresentByMajorAndMinor failed, hr %#lx.\n", hr);
+ todo_wine
+ ok(ret == TRUE, "IsApiContractPresentByMajorAndMinor returned FALSE.\n");
+
+ ret = FALSE;
+ hr = IApiInformationStatics_IsApiContractPresentByMajorAndMinor(statics, str, 1, 999, &ret);
+ todo_wine
+ ok(hr == S_OK, "IsApiContractPresentByMajorAndMinor failed, hr %#lx.\n", hr);
+ todo_wine
+ ok(ret == TRUE || broken(ret == FALSE) /* Win10 1507 */,
+ "IsApiContractPresentByMajorAndMinor returned FALSE.\n");
+
+ ret = TRUE;
+ hr = IApiInformationStatics_IsApiContractPresentByMajorAndMinor(statics, str, 999, 999, &ret);
+ todo_wine
+ ok(hr == S_OK, "IsApiContractPresentByMajorAndMinor failed, hr %#lx.\n", hr);
+ todo_wine
+ ok(ret == FALSE, "IsApiContractPresentByMajorAndMinor returned TRUE.\n");
+
+ WindowsDeleteString(str);
+
+ IApiInformationStatics_Release(statics);
+ IAgileObject_Release(agile_object);
+ IInspectable_Release(inspectable);
+ IActivationFactory_Release(factory);
+ RoUninitialize();
+}
+
+START_TEST(wintypes)
+{
+ test_IApiInformationStatics();
+}
--
2.32.0
April 29, 2022
[PATCH 6/6] wineoss: Move DRVM_INIT and DRVM_EXIT to the unixlib.
by Huw Davies
Signed-off-by: Huw Davies <huw(a)codeweavers.com>
---
dlls/wineoss.drv/midi.c | 63 --------------------------------------
dlls/wineoss.drv/oss.c | 1 -
dlls/wineoss.drv/ossmidi.c | 41 +++++++++++++++++++------
dlls/wineoss.drv/unixlib.h | 7 -----
4 files changed, 31 insertions(+), 81 deletions(-)
diff --git a/dlls/wineoss.drv/midi.c b/dlls/wineoss.drv/midi.c
index 84a4fac4b74..dda5dabf522 100644
--- a/dlls/wineoss.drv/midi.c
+++ b/dlls/wineoss.drv/midi.c
@@ -34,19 +34,7 @@
* timers (like select on fd)
*/
-#include "config.h"
-
-#include <stdlib.h>
-#include <string.h>
#include <stdarg.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <sys/ioctl.h>
-#include <poll.h>
-#include <sys/soundcard.h>
#include "windef.h"
#include "winbase.h"
@@ -67,44 +55,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(midi);
* Low level MIDI implementation *
*======================================================================*/
-static int MIDI_loadcount;
-/**************************************************************************
- * OSS_MidiInit [internal]
- *
- * Initializes the MIDI devices information variables
- */
-static LRESULT OSS_MidiInit(void)
-{
- struct midi_init_params params;
- UINT err;
-
- TRACE("(%i)\n", MIDI_loadcount);
- if (MIDI_loadcount++)
- return 1;
-
- TRACE("Initializing the MIDI variables.\n");
-
- params.err = &err;
- OSS_CALL(midi_init, ¶ms);
-
- return err;
-}
-
-/**************************************************************************
- * OSS_MidiExit [internal]
- *
- * Release the MIDI devices information variables
- */
-static LRESULT OSS_MidiExit(void)
-{
- TRACE("(%i)\n", MIDI_loadcount);
-
- if (--MIDI_loadcount)
- return 1;
-
- return 0;
-}
-
static void notify_client(struct notify_context *notify)
{
TRACE("dev_id = %d msg = %d param1 = %04lX param2 = %04lX\n",
@@ -130,12 +80,6 @@ DWORD WINAPI OSS_midMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
wDevID, wMsg, dwUser, dwParam1, dwParam2);
- switch (wMsg) {
- case DRVM_INIT:
- return OSS_MidiInit();
- case DRVM_EXIT:
- return OSS_MidiExit();
- }
params.dev_id = wDevID;
params.msg = wMsg;
@@ -167,13 +111,6 @@ DWORD WINAPI OSS_modMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
wDevID, wMsg, dwUser, dwParam1, dwParam2);
- switch (wMsg) {
- case DRVM_INIT:
- return OSS_MidiInit();
- case DRVM_EXIT:
- return OSS_MidiExit();
- }
-
params.dev_id = wDevID;
params.msg = wMsg;
params.user = dwUser;
diff --git a/dlls/wineoss.drv/oss.c b/dlls/wineoss.drv/oss.c
index b0a411ecd9b..a5aea9ee724 100644
--- a/dlls/wineoss.drv/oss.c
+++ b/dlls/wineoss.drv/oss.c
@@ -1405,7 +1405,6 @@ unixlib_entry_t __wine_unix_call_funcs[] =
set_volumes,
set_event_handle,
is_started,
- midi_init,
midi_release,
midi_out_message,
midi_in_message,
diff --git a/dlls/wineoss.drv/ossmidi.c b/dlls/wineoss.drv/ossmidi.c
index 072a9815c35..6677609a5a6 100644
--- a/dlls/wineoss.drv/ossmidi.c
+++ b/dlls/wineoss.drv/ossmidi.c
@@ -83,6 +83,7 @@ static pthread_mutex_t in_buffer_mutex = PTHREAD_MUTEX_INITIALIZER;
static unsigned int num_dests, num_srcs, num_synths, seq_refs;
static struct midi_dest dests[MAX_MIDIOUTDRV];
static struct midi_src srcs[MAX_MIDIINDRV];
+static int load_count;
static unsigned int num_midi_in_started;
static int rec_cancel_pipe[2];
@@ -301,22 +302,23 @@ static int seq_close(int fd)
return 0;
}
-NTSTATUS midi_init(void *args)
+static UINT midi_init(void)
{
- struct midi_init_params *params = args;
int i, status, synth_devs = 255, midi_devs = 255, fd, len;
struct synth_info sinfo;
struct midi_info minfo;
struct midi_dest *dest;
struct midi_src *src;
+ TRACE("(%i)\n", load_count);
+
+ if (load_count++)
+ return 1;
+
/* try to open device */
fd = seq_open();
if (fd == -1)
- {
- *params->err = -1;
- return STATUS_SUCCESS;
- }
+ return -1;
/* find how many Synth devices are there in the system */
status = ioctl(fd, SNDCTL_SEQ_NRSYNTHS, &synth_devs);
@@ -324,8 +326,7 @@ NTSTATUS midi_init(void *args)
{
ERR("ioctl for nr synth failed.\n");
seq_close(fd);
- *params->err = -1;
- return STATUS_SUCCESS;
+ return -1;
}
if (synth_devs > MAX_MIDIOUTDRV)
@@ -506,9 +507,17 @@ wrapup:
/* close file and exit */
seq_close(fd);
- *params->err = 0;
+ return 0;
+}
- return STATUS_SUCCESS;
+static UINT midi_exit(void)
+{
+ TRACE("(%i)\n", load_count);
+
+ if (--load_count)
+ return 1;
+
+ return 0;
}
NTSTATUS midi_release(void *args)
@@ -1634,6 +1643,12 @@ NTSTATUS midi_out_message(void *args)
switch (params->msg)
{
+ case DRVM_INIT:
+ *params->err = midi_init();
+ break;
+ case DRVM_EXIT:
+ *params->err = midi_exit();
+ break;
case DRVM_ENABLE:
case DRVM_DISABLE:
/* FIXME: Pretend this is supported */
@@ -1688,6 +1703,12 @@ NTSTATUS midi_in_message(void *args)
switch (params->msg)
{
+ case DRVM_INIT:
+ *params->err = midi_init();
+ break;
+ case DRVM_EXIT:
+ *params->err = midi_exit();
+ break;
case DRVM_ENABLE:
case DRVM_DISABLE:
/* FIXME: Pretend this is supported */
diff --git a/dlls/wineoss.drv/unixlib.h b/dlls/wineoss.drv/unixlib.h
index d3dda7c76f2..6a7dc9288d9 100644
--- a/dlls/wineoss.drv/unixlib.h
+++ b/dlls/wineoss.drv/unixlib.h
@@ -209,11 +209,6 @@ struct is_started_params
HRESULT result;
};
-struct midi_init_params
-{
- UINT *err;
-};
-
struct notify_context
{
BOOL send_notify;
@@ -280,14 +275,12 @@ enum oss_funcs
oss_set_volumes,
oss_set_event_handle,
oss_is_started,
- oss_midi_init,
oss_midi_release,
oss_midi_out_message,
oss_midi_in_message,
oss_midi_notify_wait,
};
-NTSTATUS midi_init(void *args) DECLSPEC_HIDDEN;
NTSTATUS midi_release(void *args) DECLSPEC_HIDDEN;
NTSTATUS midi_out_message(void *args) DECLSPEC_HIDDEN;
NTSTATUS midi_in_message(void *args) DECLSPEC_HIDDEN;
--
2.25.1
April 29, 2022
[PATCH 5/6] wineoss: Move MIDM_OPEN and MIDM_CLOSE to the unixlib.
by Huw Davies
Signed-off-by: Huw Davies <huw(a)codeweavers.com>
---
dlls/wineoss.drv/Makefile.in | 2 +-
dlls/wineoss.drv/midi.c | 243 -----------------------------------
dlls/wineoss.drv/oss.c | 3 -
dlls/wineoss.drv/ossmidi.c | 193 +++++++++++++++++++++++++---
dlls/wineoss.drv/unixlib.h | 35 -----
5 files changed, 175 insertions(+), 301 deletions(-)
diff --git a/dlls/wineoss.drv/Makefile.in b/dlls/wineoss.drv/Makefile.in
index 04b438da71e..13fb18b6004 100644
--- a/dlls/wineoss.drv/Makefile.in
+++ b/dlls/wineoss.drv/Makefile.in
@@ -3,7 +3,7 @@ MODULE = wineoss.drv
UNIXLIB = wineoss.so
IMPORTS = uuid ole32 user32 advapi32
DELAYIMPORTS = winmm
-EXTRALIBS = $(OSS4_LIBS)
+EXTRALIBS = $(OSS4_LIBS) $(PTHREAD_LIBS)
EXTRAINCL = $(OSS4_CFLAGS)
EXTRADLLFLAGS = -mcygwin
diff --git a/dlls/wineoss.drv/midi.c b/dlls/wineoss.drv/midi.c
index c83dd55fd6b..84a4fac4b74 100644
--- a/dlls/wineoss.drv/midi.c
+++ b/dlls/wineoss.drv/midi.c
@@ -63,23 +63,10 @@
WINE_DEFAULT_DEBUG_CHANNEL(midi);
-static WINE_MIDIIN *MidiInDev;
-
-/* this is the total number of MIDI out devices found */
-static int MIDM_NumDevs = 0;
-
-static int numStartedMidiIn = 0;
-
-static int rec_cancel_pipe[2];
-static HANDLE hThread;
-
/*======================================================================*
* Low level MIDI implementation *
*======================================================================*/
-static int midiOpenSeq(void);
-static int midiCloseSeq(int);
-
static int MIDI_loadcount;
/**************************************************************************
* OSS_MidiInit [internal]
@@ -100,11 +87,6 @@ static LRESULT OSS_MidiInit(void)
params.err = &err;
OSS_CALL(midi_init, ¶ms);
- if (!err)
- {
- MidiInDev = params.srcs;
- MIDM_NumDevs = params.num_srcs;
- }
return err;
}
@@ -120,9 +102,6 @@ static LRESULT OSS_MidiExit(void)
if (--MIDI_loadcount)
return 1;
- MidiInDev = NULL;
- MIDM_NumDevs = 0;
-
return 0;
}
@@ -135,224 +114,6 @@ static void notify_client(struct notify_context *notify)
notify->instance, notify->param_1, notify->param_2);
}
-/**************************************************************************
- * MIDI_NotifyClient [internal]
- */
-static void MIDI_NotifyClient(UINT wDevID, WORD wMsg,
- DWORD_PTR dwParam1, DWORD_PTR dwParam2)
-{
- DWORD_PTR dwCallBack;
- UINT uFlags;
- HANDLE hDev;
- DWORD_PTR dwInstance;
-
- TRACE("wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",
- wDevID, wMsg, dwParam1, dwParam2);
-
- switch (wMsg) {
- case MIM_OPEN:
- case MIM_CLOSE:
- case MIM_DATA:
- case MIM_LONGDATA:
- case MIM_ERROR:
- case MIM_LONGERROR:
- case MIM_MOREDATA:
- if (wDevID > MIDM_NumDevs) return;
-
- dwCallBack = MidiInDev[wDevID].midiDesc.dwCallback;
- uFlags = MidiInDev[wDevID].wFlags;
- hDev = MidiInDev[wDevID].midiDesc.hMidi;
- dwInstance = MidiInDev[wDevID].midiDesc.dwInstance;
- break;
- default:
- ERR("Unsupported MSW-MIDI message %u\n", wMsg);
- return;
- }
-
- DriverCallback(dwCallBack, uFlags, hDev, wMsg, dwInstance, dwParam1, dwParam2);
-}
-
-/**************************************************************************
- * midiOpenSeq [internal]
- */
-static int midiOpenSeq(void)
-{
- struct midi_seq_open_params params;
-
- params.close = 0;
- params.fd = -1;
- OSS_CALL(midi_seq_open, ¶ms);
-
- return params.fd;
-}
-
-/**************************************************************************
- * midiCloseSeq [internal]
- */
-static int midiCloseSeq(int fd)
-{
- struct midi_seq_open_params params;
-
- params.close = 1;
- params.fd = fd;
- OSS_CALL(midi_seq_open, ¶ms);
-
- return 0;
-}
-
-static void handle_midi_data(unsigned char *buffer, unsigned int len)
-{
- struct midi_handle_data_params params;
-
- params.buffer = buffer;
- params.len = len;
- OSS_CALL(midi_handle_data, ¶ms);
-}
-
-static DWORD WINAPI midRecThread(void *arg)
-{
- int fd = (int)(INT_PTR)arg;
- unsigned char buffer[256];
- int len;
- struct pollfd pollfd[2];
-
- pollfd[0].fd = rec_cancel_pipe[0];
- pollfd[0].events = POLLIN;
- pollfd[1].fd = fd;
- pollfd[1].events = POLLIN;
-
- while (1)
- {
- /* Check if an event is present */
- if (poll(pollfd, ARRAY_SIZE(pollfd), -1) <= 0)
- continue;
-
- if (pollfd[0].revents & POLLIN) /* cancelled */
- break;
-
- len = read(fd, buffer, sizeof(buffer));
-
- if (len > 0 && len % 4 == 0)
- handle_midi_data(buffer, len);
- }
- return 0;
-}
-
-/**************************************************************************
- * midOpen [internal]
- */
-static DWORD midOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
-{
- int fd;
-
- TRACE("(%04X, %p, %08X);\n", wDevID, lpDesc, dwFlags);
-
- if (lpDesc == NULL) {
- WARN("Invalid Parameter !\n");
- return MMSYSERR_INVALPARAM;
- }
-
- /* FIXME :
- * how to check that content of lpDesc is correct ?
- */
- if (wDevID >= MIDM_NumDevs) {
- WARN("wDevID too large (%u) !\n", wDevID);
- return MMSYSERR_BADDEVICEID;
- }
- if (MidiInDev[wDevID].state == -1) {
- WARN("device disabled\n");
- return MIDIERR_NODEVICE;
- }
- if (MidiInDev[wDevID].midiDesc.hMidi != 0) {
- WARN("device already open !\n");
- return MMSYSERR_ALLOCATED;
- }
- if ((dwFlags & MIDI_IO_STATUS) != 0) {
- WARN("No support for MIDI_IO_STATUS in dwFlags yet, ignoring it\n");
- dwFlags &= ~MIDI_IO_STATUS;
- }
- if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
- FIXME("Bad dwFlags\n");
- return MMSYSERR_INVALFLAG;
- }
-
- fd = midiOpenSeq();
- if (fd < 0) {
- return MMSYSERR_ERROR;
- }
-
- if (numStartedMidiIn++ == 0) {
- pipe(rec_cancel_pipe);
- hThread = CreateThread(NULL, 0, midRecThread, (void *)(INT_PTR)fd, 0, NULL);
- if (!hThread) {
- close(rec_cancel_pipe[0]);
- close(rec_cancel_pipe[1]);
- numStartedMidiIn = 0;
- WARN("Couldn't create thread for midi-in\n");
- midiCloseSeq(fd);
- return MMSYSERR_ERROR;
- }
- SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);
- TRACE("Created thread for midi-in\n");
- }
-
- MidiInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
-
- MidiInDev[wDevID].lpQueueHdr = NULL;
- MidiInDev[wDevID].midiDesc = *lpDesc;
- MidiInDev[wDevID].state = 0;
- MidiInDev[wDevID].incLen = 0;
- MidiInDev[wDevID].startTime = 0;
- MidiInDev[wDevID].fd = fd;
-
- MIDI_NotifyClient(wDevID, MIM_OPEN, 0L, 0L);
- return MMSYSERR_NOERROR;
-}
-
-/**************************************************************************
- * midClose [internal]
- */
-static DWORD midClose(WORD wDevID)
-{
- int ret = MMSYSERR_NOERROR;
-
- TRACE("(%04X);\n", wDevID);
-
- if (wDevID >= MIDM_NumDevs) {
- WARN("wDevID too big (%u) !\n", wDevID);
- return MMSYSERR_BADDEVICEID;
- }
- if (MidiInDev[wDevID].midiDesc.hMidi == 0) {
- WARN("device not opened !\n");
- return MMSYSERR_ERROR;
- }
- if (MidiInDev[wDevID].lpQueueHdr != 0) {
- return MIDIERR_STILLPLAYING;
- }
-
- if (MidiInDev[wDevID].fd == -1) {
- WARN("ooops !\n");
- return MMSYSERR_ERROR;
- }
- if (--numStartedMidiIn == 0) {
- TRACE("Stopping thread for midi-in\n");
- write(rec_cancel_pipe[1], "x", 1);
- if (WaitForSingleObject(hThread, 5000) != WAIT_OBJECT_0) {
- WARN("Thread end not signaled, force termination\n");
- TerminateThread(hThread, 0);
- }
- close(rec_cancel_pipe[0]);
- close(rec_cancel_pipe[1]);
- TRACE("Stopped thread for midi-in\n");
- }
- midiCloseSeq(MidiInDev[wDevID].fd);
- MidiInDev[wDevID].fd = -1;
-
- MIDI_NotifyClient(wDevID, MIM_CLOSE, 0L, 0L);
- MidiInDev[wDevID].midiDesc.hMidi = 0;
- return ret;
-}
-
/*======================================================================*
* MIDI entry points *
*======================================================================*/
@@ -374,10 +135,6 @@ DWORD WINAPI OSS_midMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
return OSS_MidiInit();
case DRVM_EXIT:
return OSS_MidiExit();
- case MIDM_OPEN:
- return midOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
- case MIDM_CLOSE:
- return midClose(wDevID);
}
params.dev_id = wDevID;
diff --git a/dlls/wineoss.drv/oss.c b/dlls/wineoss.drv/oss.c
index c5b422a60c9..b0a411ecd9b 100644
--- a/dlls/wineoss.drv/oss.c
+++ b/dlls/wineoss.drv/oss.c
@@ -1410,7 +1410,4 @@ unixlib_entry_t __wine_unix_call_funcs[] =
midi_out_message,
midi_in_message,
midi_notify_wait,
-
- midi_seq_open,
- midi_handle_data,
};
diff --git a/dlls/wineoss.drv/ossmidi.c b/dlls/wineoss.drv/ossmidi.c
index 9c8ca8a8f39..072a9815c35 100644
--- a/dlls/wineoss.drv/ossmidi.c
+++ b/dlls/wineoss.drv/ossmidi.c
@@ -33,6 +33,7 @@
#include <stdint.h>
#include <time.h>
#include <unistd.h>
+#include <poll.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
@@ -45,6 +46,7 @@
#define WIN32_NO_STATUS
#include "winternl.h"
#include "audioclient.h"
+#include "mmddk.h"
#include "wine/debug.h"
#include "wine/unixlib.h"
@@ -62,12 +64,30 @@ struct midi_dest
int fd;
};
+struct midi_src
+{
+ int state; /* -1 disabled, 0 is no recording started, 1 in recording, bit 2 set if in sys exclusive recording */
+ MIDIOPENDESC midiDesc;
+ WORD wFlags;
+ MIDIHDR *lpQueueHdr;
+ unsigned char incoming[3];
+ unsigned char incPrev;
+ char incLen;
+ UINT startTime;
+ MIDIINCAPSW caps;
+ int fd;
+};
+
static pthread_mutex_t in_buffer_mutex = PTHREAD_MUTEX_INITIALIZER;
static unsigned int num_dests, num_srcs, num_synths, seq_refs;
static struct midi_dest dests[MAX_MIDIOUTDRV];
static struct midi_src srcs[MAX_MIDIINDRV];
+static unsigned int num_midi_in_started;
+static int rec_cancel_pipe[2];
+static pthread_t rec_thread_id;
+
static pthread_mutex_t notify_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t notify_read_cond = PTHREAD_COND_INITIALIZER;
static pthread_cond_t notify_write_cond = PTHREAD_COND_INITIALIZER;
@@ -281,18 +301,6 @@ static int seq_close(int fd)
return 0;
}
-NTSTATUS midi_seq_open(void *args)
-{
- struct midi_seq_open_params *params = args;
-
- if (!params->close)
- params->fd = seq_open();
- else
- seq_close(params->fd);
-
- return STATUS_SUCCESS;
-}
-
NTSTATUS midi_init(void *args)
{
struct midi_init_params *params = args;
@@ -499,8 +507,6 @@ wrapup:
seq_close(fd);
*params->err = 0;
- params->num_srcs = num_srcs;
- params->srcs = srcs;
return STATUS_SUCCESS;
}
@@ -1313,11 +1319,8 @@ static void handle_regular_data(struct midi_src *src, unsigned char value, UINT
}
}
-NTSTATUS midi_handle_data(void *args)
+static void handle_midi_data(unsigned char *buffer, unsigned int len)
{
- struct midi_handle_data_params *params = args;
- unsigned char *buffer = params->buffer;
- unsigned int len = params->len;
unsigned int time = get_time_msec(), i;
struct midi_src *src;
unsigned char value;
@@ -1339,7 +1342,153 @@ NTSTATUS midi_handle_data(void *args)
else
handle_regular_data(src, value, time - src->startTime);
}
- return STATUS_SUCCESS;
+}
+
+static void *rec_thread_proc(void *arg)
+{
+ int fd = PtrToLong(arg);
+ unsigned char buffer[256];
+ int len;
+ struct pollfd pollfd[2];
+
+ pollfd[0].fd = rec_cancel_pipe[0];
+ pollfd[0].events = POLLIN;
+ pollfd[1].fd = fd;
+ pollfd[1].events = POLLIN;
+
+ while (1)
+ {
+ /* Check if an event is present */
+ if (poll(pollfd, ARRAY_SIZE(pollfd), -1) <= 0)
+ continue;
+
+ if (pollfd[0].revents & POLLIN) /* cancelled */
+ break;
+
+ len = read(fd, buffer, sizeof(buffer));
+
+ if (len > 0 && len % 4 == 0)
+ handle_midi_data(buffer, len);
+ }
+ return NULL;
+}
+
+static UINT midi_in_open(WORD dev_id, MIDIOPENDESC *desc, UINT flags, struct notify_context *notify)
+{
+ struct midi_src *src;
+ int fd;
+
+ TRACE("(%04X, %p, %08X);\n", dev_id, desc, flags);
+
+ if (desc == NULL)
+ {
+ WARN("Invalid Parameter !\n");
+ return MMSYSERR_INVALPARAM;
+ }
+
+ /* FIXME :
+ * how to check that content of lpDesc is correct ?
+ */
+ if (dev_id >= num_srcs)
+ {
+ WARN("wDevID too large (%u) !\n", dev_id);
+ return MMSYSERR_BADDEVICEID;
+ }
+ src = srcs + dev_id;
+ if (src->state == -1)
+ {
+ WARN("device disabled\n");
+ return MIDIERR_NODEVICE;
+ }
+ if (src->midiDesc.hMidi != 0)
+ {
+ WARN("device already open !\n");
+ return MMSYSERR_ALLOCATED;
+ }
+ if ((flags & MIDI_IO_STATUS) != 0)
+ {
+ WARN("No support for MIDI_IO_STATUS in dwFlags yet, ignoring it\n");
+ flags &= ~MIDI_IO_STATUS;
+ }
+ if ((flags & ~CALLBACK_TYPEMASK) != 0)
+ {
+ FIXME("Bad flags\n");
+ return MMSYSERR_INVALFLAG;
+ }
+
+ fd = seq_open();
+ if (fd < 0)
+ return MMSYSERR_ERROR;
+
+ if (num_midi_in_started++ == 0)
+ {
+ pipe(rec_cancel_pipe);
+ if (pthread_create(&rec_thread_id, NULL, rec_thread_proc, LongToPtr(fd)))
+ {
+ close(rec_cancel_pipe[0]);
+ close(rec_cancel_pipe[1]);
+ num_midi_in_started = 0;
+ WARN("Couldn't create thread for midi-in\n");
+ seq_close(fd);
+ return MMSYSERR_ERROR;
+ }
+ TRACE("Created thread for midi-in\n");
+ }
+
+ src->wFlags = HIWORD(flags & CALLBACK_TYPEMASK);
+
+ src->lpQueueHdr = NULL;
+ src->midiDesc = *desc;
+ src->state = 0;
+ src->incLen = 0;
+ src->startTime = 0;
+ src->fd = fd;
+
+ set_in_notify(notify, src, dev_id, MIM_OPEN, 0, 0);
+ return MMSYSERR_NOERROR;
+}
+
+static UINT midi_in_close(WORD dev_id, struct notify_context *notify)
+{
+ struct midi_src *src;
+
+ TRACE("(%04X);\n", dev_id);
+
+ if (dev_id >= num_srcs)
+ {
+ WARN("dev_id too big (%u) !\n", dev_id);
+ return MMSYSERR_BADDEVICEID;
+ }
+ src = srcs + dev_id;
+ if (src->midiDesc.hMidi == 0)
+ {
+ WARN("device not opened !\n");
+ return MMSYSERR_ERROR;
+ }
+ if (src->lpQueueHdr != 0)
+ return MIDIERR_STILLPLAYING;
+
+ if (src->fd == -1)
+ {
+ WARN("ooops !\n");
+ return MMSYSERR_ERROR;
+ }
+ if (--num_midi_in_started == 0)
+ {
+ TRACE("Stopping thread for midi-in\n");
+ write(rec_cancel_pipe[1], "x", 1);
+ pthread_join(rec_thread_id, NULL);
+ close(rec_cancel_pipe[0]);
+ close(rec_cancel_pipe[1]);
+ TRACE("Stopped thread for midi-in\n");
+ }
+ seq_close(src->fd);
+ src->fd = -1;
+
+ set_in_notify(notify, src, dev_id, MIM_CLOSE, 0, 0);
+ src->midiDesc.hMidi = 0;
+
+ return MMSYSERR_NOERROR;
}
static UINT midi_in_add_buffer(WORD dev_id, MIDIHDR *hdr, UINT hdr_size)
@@ -1544,6 +1693,12 @@ NTSTATUS midi_in_message(void *args)
/* FIXME: Pretend this is supported */
*params->err = MMSYSERR_NOERROR;
break;
+ case MIDM_OPEN:
+ *params->err = midi_in_open(params->dev_id, (MIDIOPENDESC *)params->param_1, params->param_2, params->notify);
+ break;
+ case MIDM_CLOSE:
+ *params->err = midi_in_close(params->dev_id, params->notify);
+ break;
case MIDM_ADDBUFFER:
*params->err = midi_in_add_buffer(params->dev_id, (MIDIHDR *)params->param_1, params->param_2);
break;
diff --git a/dlls/wineoss.drv/unixlib.h b/dlls/wineoss.drv/unixlib.h
index 90d0c47421c..d3dda7c76f2 100644
--- a/dlls/wineoss.drv/unixlib.h
+++ b/dlls/wineoss.drv/unixlib.h
@@ -209,27 +209,9 @@ struct is_started_params
HRESULT result;
};
-#include <mmddk.h> /* temporary */
-
-typedef struct midi_src
-{
- int state; /* -1 disabled, 0 is no recording started, 1 in recording, bit 2 set if in sys exclusive recording */
- MIDIOPENDESC midiDesc;
- WORD wFlags;
- MIDIHDR *lpQueueHdr;
- unsigned char incoming[3];
- unsigned char incPrev;
- char incLen;
- UINT startTime;
- MIDIINCAPSW caps;
- int fd;
-} WINE_MIDIIN;
-
struct midi_init_params
{
UINT *err;
- unsigned int num_srcs;
- struct midi_src *srcs;
};
struct notify_context
@@ -273,18 +255,6 @@ struct midi_notify_wait_params
struct notify_context *notify;
};
-struct midi_seq_open_params
-{
- int close;
- int fd;
-};
-
-struct midi_handle_data_params
-{
- unsigned char *buffer;
- unsigned int len;
-};
-
enum oss_funcs
{
oss_test_connect,
@@ -315,9 +285,6 @@ enum oss_funcs
oss_midi_out_message,
oss_midi_in_message,
oss_midi_notify_wait,
-
- oss_midi_seq_open, /* temporary */
- oss_midi_handle_data,
};
NTSTATUS midi_init(void *args) DECLSPEC_HIDDEN;
@@ -325,8 +292,6 @@ NTSTATUS midi_release(void *args) DECLSPEC_HIDDEN;
NTSTATUS midi_out_message(void *args) DECLSPEC_HIDDEN;
NTSTATUS midi_in_message(void *args) DECLSPEC_HIDDEN;
NTSTATUS midi_notify_wait(void *args) DECLSPEC_HIDDEN;
-NTSTATUS midi_seq_open(void *args) DECLSPEC_HIDDEN;
-NTSTATUS midi_handle_data(void *args) DECLSPEC_HIDDEN;
extern unixlib_handle_t oss_handle;
--
2.25.1
April 29, 2022
[PATCH 4/6] wineoss: Use a pipe to signal the end of the record thread.
by Huw Davies
Signed-off-by: Huw Davies <huw(a)codeweavers.com>
---
dlls/wineoss.drv/midi.c | 42 ++++++++++++++++++++---------------------
1 file changed, 21 insertions(+), 21 deletions(-)
diff --git a/dlls/wineoss.drv/midi.c b/dlls/wineoss.drv/midi.c
index 0afd9985c03..c83dd55fd6b 100644
--- a/dlls/wineoss.drv/midi.c
+++ b/dlls/wineoss.drv/midi.c
@@ -70,7 +70,7 @@ static int MIDM_NumDevs = 0;
static int numStartedMidiIn = 0;
-static int end_thread;
+static int rec_cancel_pipe[2];
static HANDLE hThread;
/*======================================================================*
@@ -214,30 +214,26 @@ static DWORD WINAPI midRecThread(void *arg)
int fd = (int)(INT_PTR)arg;
unsigned char buffer[256];
int len;
- struct pollfd pfd;
+ struct pollfd pollfd[2];
- TRACE("Thread startup\n");
-
- pfd.fd = fd;
- pfd.events = POLLIN;
-
- while(!end_thread) {
- TRACE("Thread loop\n");
+ pollfd[0].fd = rec_cancel_pipe[0];
+ pollfd[0].events = POLLIN;
+ pollfd[1].fd = fd;
+ pollfd[1].events = POLLIN;
+ while (1)
+ {
/* Check if an event is present */
- if (poll(&pfd, 1, 250) <= 0)
+ if (poll(pollfd, ARRAY_SIZE(pollfd), -1) <= 0)
continue;
-
- len = read(fd, buffer, sizeof(buffer));
- TRACE("Received %d bytes\n", len);
- if (len < 0) continue;
- if ((len % 4) != 0) {
- WARN("Bad length %d, errno %d (%s)\n", len, errno, strerror(errno));
- continue;
- }
+ if (pollfd[0].revents & POLLIN) /* cancelled */
+ break;
+
+ len = read(fd, buffer, sizeof(buffer));
- handle_midi_data(buffer, len);
+ if (len > 0 && len % 4 == 0)
+ handle_midi_data(buffer, len);
}
return 0;
}
@@ -286,9 +282,11 @@ static DWORD midOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
}
if (numStartedMidiIn++ == 0) {
- end_thread = 0;
+ pipe(rec_cancel_pipe);
hThread = CreateThread(NULL, 0, midRecThread, (void *)(INT_PTR)fd, 0, NULL);
if (!hThread) {
+ close(rec_cancel_pipe[0]);
+ close(rec_cancel_pipe[1]);
numStartedMidiIn = 0;
WARN("Couldn't create thread for midi-in\n");
midiCloseSeq(fd);
@@ -338,11 +336,13 @@ static DWORD midClose(WORD wDevID)
}
if (--numStartedMidiIn == 0) {
TRACE("Stopping thread for midi-in\n");
- end_thread = 1;
+ write(rec_cancel_pipe[1], "x", 1);
if (WaitForSingleObject(hThread, 5000) != WAIT_OBJECT_0) {
WARN("Thread end not signaled, force termination\n");
TerminateThread(hThread, 0);
}
+ close(rec_cancel_pipe[0]);
+ close(rec_cancel_pipe[1]);
TRACE("Stopped thread for midi-in\n");
}
midiCloseSeq(MidiInDev[wDevID].fd);
--
2.25.1
April 29, 2022
[PATCH 3/6] wineoss: Introduce a helper to retrieve the time.
by Huw Davies
The motivation is that this will need to be called from a
non-Win32 thread and so shouldn't use the Win32 API. An
added benefit is that it will eliminate the 16ms jitter
associated with GetTickCount().
Signed-off-by: Huw Davies <huw(a)codeweavers.com>
---
dlls/wineoss.drv/ossmidi.c | 20 +++++++++++++++++---
1 file changed, 17 insertions(+), 3 deletions(-)
diff --git a/dlls/wineoss.drv/ossmidi.c b/dlls/wineoss.drv/ossmidi.c
index 1695f1d2f7b..9c8ca8a8f39 100644
--- a/dlls/wineoss.drv/ossmidi.c
+++ b/dlls/wineoss.drv/ossmidi.c
@@ -30,6 +30,8 @@
#include <stdarg.h>
#include <string.h>
#include <stdio.h>
+#include <stdint.h>
+#include <time.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
@@ -155,6 +157,18 @@ static void in_buffer_unlock(void)
pthread_mutex_unlock(&in_buffer_mutex);
}
+static uint64_t get_time_msec(void)
+{
+ struct timespec now = {0, 0};
+
+#ifdef CLOCK_MONOTONIC_RAW
+ if (!clock_gettime(CLOCK_MONOTONIC_RAW, &now))
+ return (uint64_t)now.tv_sec * 1000 + now.tv_nsec / 1000000;
+#endif
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ return (uint64_t)now.tv_sec * 1000 + now.tv_nsec / 1000000;
+}
+
/*
* notify buffer: The notification ring buffer is implemented so that
* there is always at least one unused sentinel before the current
@@ -1304,7 +1318,7 @@ NTSTATUS midi_handle_data(void *args)
struct midi_handle_data_params *params = args;
unsigned char *buffer = params->buffer;
unsigned int len = params->len;
- unsigned int time = NtGetTickCount(), i;
+ unsigned int time = get_time_msec(), i;
struct midi_src *src;
unsigned char value;
WORD dev_id;
@@ -1415,7 +1429,7 @@ static UINT midi_in_start(WORD dev_id)
if (src->state == -1) return MIDIERR_NODEVICE;
src->state = 1;
- src->startTime = NtGetTickCount();
+ src->startTime = get_time_msec();
return MMSYSERR_NOERROR;
}
@@ -1435,7 +1449,7 @@ static UINT midi_in_stop(WORD dev_id)
static UINT midi_in_reset(WORD dev_id, struct notify_context *notify)
{
- UINT cur_time = NtGetTickCount();
+ UINT cur_time = get_time_msec();
UINT err = MMSYSERR_NOERROR;
struct midi_src *src;
MIDIHDR *hdr;
--
2.25.1
April 29, 2022
[PATCH 2/6] wineoss: Move the midi in data handlers to the unixlib.
by Huw Davies
The syscall itself is temporary.
Signed-off-by: Huw Davies <huw(a)codeweavers.com>
---
dlls/wineoss.drv/midi.c | 129 +------------------------
dlls/wineoss.drv/oss.c | 2 +-
dlls/wineoss.drv/ossmidi.c | 189 +++++++++++++++++++++++++++++++++++--
dlls/wineoss.drv/unixlib.h | 10 +-
4 files changed, 196 insertions(+), 134 deletions(-)
diff --git a/dlls/wineoss.drv/midi.c b/dlls/wineoss.drv/midi.c
index b3f980ab3da..0afd9985c03 100644
--- a/dlls/wineoss.drv/midi.c
+++ b/dlls/wineoss.drv/midi.c
@@ -126,16 +126,6 @@ static LRESULT OSS_MidiExit(void)
return 0;
}
-static void in_buffer_lock(void)
-{
- OSS_CALL(midi_in_lock, ULongToPtr(1));
-}
-
-static void in_buffer_unlock(void)
-{
- OSS_CALL(midi_in_lock, ULongToPtr(0));
-}
-
static void notify_client(struct notify_context *notify)
{
TRACE("dev_id = %d msg = %d param1 = %04lX param2 = %04lX\n",
@@ -210,123 +200,13 @@ static int midiCloseSeq(int fd)
return 0;
}
-static void handle_sysex_data(struct midi_src *src, unsigned char value, UINT time)
-{
- MIDIHDR *hdr;
- BOOL done = FALSE;
-
- src->state |= 2;
- src->incLen = 0;
-
- in_buffer_lock();
-
- hdr = src->lpQueueHdr;
- if (hdr)
- {
- BYTE *data = (BYTE *)hdr->lpData;
-
- data[hdr->dwBytesRecorded++] = value;
- if (hdr->dwBytesRecorded == hdr->dwBufferLength)
- done = TRUE;
- }
-
- if (value == 0xf7) /* end */
- {
- src->state &= ~2;
- done = TRUE;
- }
-
- if (done && hdr)
- {
- src->lpQueueHdr = hdr->lpNext;
- hdr->dwFlags &= ~MHDR_INQUEUE;
- hdr->dwFlags |= MHDR_DONE;
- MIDI_NotifyClient(src - MidiInDev, MIM_LONGDATA, (UINT_PTR)hdr, time);
- }
-
- in_buffer_unlock();
-}
-
-static void handle_regular_data(struct midi_src *src, unsigned char value, UINT time)
-{
- UINT to_send = 0;
-
-#define IS_CMD(_x) (((_x) & 0x80) == 0x80)
-#define IS_SYS_CMD(_x) (((_x) & 0xF0) == 0xF0)
-
- if (!IS_CMD(value) && src->incLen == 0) /* try to reuse old cmd */
- {
- if (IS_CMD(src->incPrev) && !IS_SYS_CMD(src->incPrev))
- {
- src->incoming[0] = src->incPrev;
- src->incLen = 1;
- }
- else
- {
- /* FIXME: should generate MIM_ERROR notification */
- return;
- }
- }
- src->incoming[(int)src->incLen++] = value;
- if (src->incLen == 1 && !IS_SYS_CMD(src->incoming[0]))
- /* store new cmd, just in case */
- src->incPrev = src->incoming[0];
-
-#undef IS_CMD
-#undef IS_SYS_CMD
-
- switch (src->incoming[0] & 0xF0)
- {
- case MIDI_NOTEOFF:
- case MIDI_NOTEON:
- case MIDI_KEY_PRESSURE:
- case MIDI_CTL_CHANGE:
- case MIDI_PITCH_BEND:
- if (src->incLen == 3)
- to_send = (src->incoming[2] << 16) | (src->incoming[1] << 8) |
- src->incoming[0];
- break;
- case MIDI_PGM_CHANGE:
- case MIDI_CHN_PRESSURE:
- if (src->incLen == 2)
- to_send = (src->incoming[1] << 8) | src->incoming[0];
- break;
- case MIDI_SYSTEM_PREFIX:
- if (src->incLen == 1)
- to_send = src->incoming[0];
- break;
- }
-
- if (to_send)
- {
- src->incLen = 0;
- MIDI_NotifyClient(src - MidiInDev, MIM_DATA, to_send, time);
- }
-}
-
static void handle_midi_data(unsigned char *buffer, unsigned int len)
{
- unsigned int time = GetTickCount(), i;
- struct midi_src *src;
- unsigned char value;
- WORD dev_id;
+ struct midi_handle_data_params params;
- for (i = 0; i < len; i += (buffer[i] & 0x80) ? 8 : 4)
- {
- if (buffer[i] != SEQ_MIDIPUTC) continue;
-
- dev_id = buffer[i + 2];
- value = buffer[i + 1];
-
- if (dev_id >= MIDM_NumDevs) continue;
- src = MidiInDev + dev_id;
- if (src->state <= 0) continue;
-
- if (value == 0xf0 || src->state & 2) /* system exclusive */
- handle_sysex_data(src, value, time - src->startTime);
- else
- handle_regular_data(src, value, time - src->startTime);
- }
+ params.buffer = buffer;
+ params.len = len;
+ OSS_CALL(midi_handle_data, ¶ms);
}
static DWORD WINAPI midRecThread(void *arg)
@@ -565,6 +445,7 @@ static DWORD WINAPI notify_thread(void *p)
{
OSS_CALL(midi_notify_wait, ¶ms);
if (quit) break;
+ if (notify.send_notify) notify_client(¬ify);
}
return 0;
}
diff --git a/dlls/wineoss.drv/oss.c b/dlls/wineoss.drv/oss.c
index 8fda9270a4e..c5b422a60c9 100644
--- a/dlls/wineoss.drv/oss.c
+++ b/dlls/wineoss.drv/oss.c
@@ -1412,5 +1412,5 @@ unixlib_entry_t __wine_unix_call_funcs[] =
midi_notify_wait,
midi_seq_open,
- midi_in_lock,
+ midi_handle_data,
};
diff --git a/dlls/wineoss.drv/ossmidi.c b/dlls/wineoss.drv/ossmidi.c
index 0790eaaec1a..1695f1d2f7b 100644
--- a/dlls/wineoss.drv/ossmidi.c
+++ b/dlls/wineoss.drv/ossmidi.c
@@ -68,7 +68,11 @@ static struct midi_src srcs[MAX_MIDIINDRV];
static pthread_mutex_t notify_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t notify_read_cond = PTHREAD_COND_INITIALIZER;
+static pthread_cond_t notify_write_cond = PTHREAD_COND_INITIALIZER;
static BOOL notify_quit;
+#define NOTIFY_BUFFER_SIZE 64 + 1 /* + 1 for the sentinel */
+static struct notify_context notify_buffer[NOTIFY_BUFFER_SIZE];
+static struct notify_context *notify_read = notify_buffer, *notify_write = notify_buffer;
typedef struct sVoice
{
@@ -151,19 +155,59 @@ static void in_buffer_unlock(void)
pthread_mutex_unlock(&in_buffer_mutex);
}
-NTSTATUS midi_in_lock(void *args)
+/*
+ * notify buffer: The notification ring buffer is implemented so that
+ * there is always at least one unused sentinel before the current
+ * read position in order to allow detection of the full vs empty
+ * state.
+ */
+static struct notify_context *notify_buffer_next(struct notify_context *notify)
{
- if (args) in_buffer_lock();
- else in_buffer_unlock();
+ if (++notify >= notify_buffer + ARRAY_SIZE(notify_buffer))
+ notify = notify_buffer;
- return STATUS_SUCCESS;
+ return notify;
+}
+
+static BOOL notify_buffer_empty(void)
+{
+ return notify_read == notify_write;
+}
+
+static BOOL notify_buffer_full(void)
+{
+ return notify_buffer_next(notify_write) == notify_read;
+}
+
+static BOOL notify_buffer_add(struct notify_context *notify)
+{
+ if (notify_buffer_full()) return FALSE;
+
+ *notify_write = *notify;
+ notify_write = notify_buffer_next(notify_write);
+ return TRUE;
+}
+
+static BOOL notify_buffer_remove(struct notify_context *notify)
+{
+ if (notify_buffer_empty()) return FALSE;
+
+ *notify = *notify_read;
+ notify_read = notify_buffer_next(notify_read);
+ return TRUE;
}
static void notify_post(struct notify_context *notify)
{
pthread_mutex_lock(¬ify_mutex);
- if (notify) FIXME("Not yet handled\n");
+ if (notify)
+ {
+ while (notify_buffer_full())
+ pthread_cond_wait(¬ify_write_cond, ¬ify_mutex);
+
+ notify_buffer_add(notify);
+ }
else notify_quit = TRUE;
pthread_cond_signal(¬ify_read_cond);
@@ -1157,6 +1201,133 @@ static UINT midi_out_reset(WORD dev_id)
return MMSYSERR_NOERROR;
}
+static void handle_sysex_data(struct midi_src *src, unsigned char value, UINT time)
+{
+ struct notify_context notify;
+ MIDIHDR *hdr;
+ BOOL done = FALSE;
+
+ src->state |= 2;
+ src->incLen = 0;
+
+ in_buffer_lock();
+
+ hdr = src->lpQueueHdr;
+ if (hdr)
+ {
+ BYTE *data = (BYTE *)hdr->lpData;
+
+ data[hdr->dwBytesRecorded++] = value;
+ if (hdr->dwBytesRecorded == hdr->dwBufferLength)
+ done = TRUE;
+ }
+
+ if (value == 0xf7) /* end */
+ {
+ src->state &= ~2;
+ done = TRUE;
+ }
+
+ if (done && hdr)
+ {
+ src->lpQueueHdr = hdr->lpNext;
+ hdr->dwFlags &= ~MHDR_INQUEUE;
+ hdr->dwFlags |= MHDR_DONE;
+ set_in_notify(¬ify, src, src - srcs, MIM_LONGDATA, (UINT_PTR)hdr, time);
+ notify_post(¬ify);
+ }
+
+ in_buffer_unlock();
+}
+
+static void handle_regular_data(struct midi_src *src, unsigned char value, UINT time)
+{
+ struct notify_context notify;
+ UINT to_send = 0;
+
+#define IS_CMD(_x) (((_x) & 0x80) == 0x80)
+#define IS_SYS_CMD(_x) (((_x) & 0xF0) == 0xF0)
+
+ if (!IS_CMD(value) && src->incLen == 0) /* try to reuse old cmd */
+ {
+ if (IS_CMD(src->incPrev) && !IS_SYS_CMD(src->incPrev))
+ {
+ src->incoming[0] = src->incPrev;
+ src->incLen = 1;
+ }
+ else
+ {
+ /* FIXME: should generate MIM_ERROR notification */
+ return;
+ }
+ }
+ src->incoming[(int)src->incLen++] = value;
+ if (src->incLen == 1 && !IS_SYS_CMD(src->incoming[0]))
+ /* store new cmd, just in case */
+ src->incPrev = src->incoming[0];
+
+#undef IS_CMD
+#undef IS_SYS_CMD
+
+ switch (src->incoming[0] & 0xF0)
+ {
+ case MIDI_NOTEOFF:
+ case MIDI_NOTEON:
+ case MIDI_KEY_PRESSURE:
+ case MIDI_CTL_CHANGE:
+ case MIDI_PITCH_BEND:
+ if (src->incLen == 3)
+ to_send = (src->incoming[2] << 16) | (src->incoming[1] << 8) |
+ src->incoming[0];
+ break;
+ case MIDI_PGM_CHANGE:
+ case MIDI_CHN_PRESSURE:
+ if (src->incLen == 2)
+ to_send = (src->incoming[1] << 8) | src->incoming[0];
+ break;
+ case MIDI_SYSTEM_PREFIX:
+ if (src->incLen == 1)
+ to_send = src->incoming[0];
+ break;
+ }
+
+ if (to_send)
+ {
+ src->incLen = 0;
+ set_in_notify(¬ify, src, src - srcs, MIM_DATA, to_send, time);
+ notify_post(¬ify);
+ }
+}
+
+NTSTATUS midi_handle_data(void *args)
+{
+ struct midi_handle_data_params *params = args;
+ unsigned char *buffer = params->buffer;
+ unsigned int len = params->len;
+ unsigned int time = NtGetTickCount(), i;
+ struct midi_src *src;
+ unsigned char value;
+ WORD dev_id;
+
+ for (i = 0; i < len; i += (buffer[i] & 0x80) ? 8 : 4)
+ {
+ if (buffer[i] != SEQ_MIDIPUTC) continue;
+
+ dev_id = buffer[i + 2];
+ value = buffer[i + 1];
+
+ if (dev_id >= num_srcs) continue;
+ src = srcs + dev_id;
+ if (src->state <= 0) continue;
+
+ if (value == 0xf0 || src->state & 2) /* system exclusive */
+ handle_sysex_data(src, value, time - src->startTime);
+ else
+ handle_regular_data(src, value, time - src->startTime);
+ }
+ return STATUS_SUCCESS;
+}
+
static UINT midi_in_add_buffer(WORD dev_id, MIDIHDR *hdr, UINT hdr_size)
{
struct midi_src *src;
@@ -1397,11 +1568,15 @@ NTSTATUS midi_notify_wait(void *args)
pthread_mutex_lock(¬ify_mutex);
- while (!notify_quit)
+ while (!notify_quit && notify_buffer_empty())
pthread_cond_wait(¬ify_read_cond, ¬ify_mutex);
*params->quit = notify_quit;
-
+ if (!notify_quit)
+ {
+ notify_buffer_remove(params->notify);
+ pthread_cond_signal(¬ify_write_cond);
+ }
pthread_mutex_unlock(¬ify_mutex);
return STATUS_SUCCESS;
diff --git a/dlls/wineoss.drv/unixlib.h b/dlls/wineoss.drv/unixlib.h
index ddeba49556c..90d0c47421c 100644
--- a/dlls/wineoss.drv/unixlib.h
+++ b/dlls/wineoss.drv/unixlib.h
@@ -279,6 +279,12 @@ struct midi_seq_open_params
int fd;
};
+struct midi_handle_data_params
+{
+ unsigned char *buffer;
+ unsigned int len;
+};
+
enum oss_funcs
{
oss_test_connect,
@@ -311,7 +317,7 @@ enum oss_funcs
oss_midi_notify_wait,
oss_midi_seq_open, /* temporary */
- oss_midi_in_lock,
+ oss_midi_handle_data,
};
NTSTATUS midi_init(void *args) DECLSPEC_HIDDEN;
@@ -320,7 +326,7 @@ NTSTATUS midi_out_message(void *args) DECLSPEC_HIDDEN;
NTSTATUS midi_in_message(void *args) DECLSPEC_HIDDEN;
NTSTATUS midi_notify_wait(void *args) DECLSPEC_HIDDEN;
NTSTATUS midi_seq_open(void *args) DECLSPEC_HIDDEN;
-NTSTATUS midi_in_lock(void *args) DECLSPEC_HIDDEN;
+NTSTATUS midi_handle_data(void *args) DECLSPEC_HIDDEN;
extern unixlib_handle_t oss_handle;
--
2.25.1
April 29, 2022
[PATCH 1/6] wineoss: Introduce a notification thread.
by Huw Davies
Currently the thread just blocks until told to quit by midi_release.
Eventually this thread will dispatch the MIM_DATA and MIM_LONGDATA
notifications.
Signed-off-by: Huw Davies <huw(a)codeweavers.com>
---
dlls/wineoss.drv/midi.c | 21 ++++++++++++++++++++
dlls/wineoss.drv/oss.c | 2 ++
dlls/wineoss.drv/ossmidi.c | 39 ++++++++++++++++++++++++++++++++++++++
dlls/wineoss.drv/unixlib.h | 10 ++++++++++
4 files changed, 72 insertions(+)
diff --git a/dlls/wineoss.drv/midi.c b/dlls/wineoss.drv/midi.c
index e36a737624a..b3f980ab3da 100644
--- a/dlls/wineoss.drv/midi.c
+++ b/dlls/wineoss.drv/midi.c
@@ -552,6 +552,23 @@ DWORD WINAPI OSS_modMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
return err;
}
+static DWORD WINAPI notify_thread(void *p)
+{
+ struct midi_notify_wait_params params;
+ struct notify_context notify;
+ BOOL quit;
+
+ params.notify = ¬ify;
+ params.quit = &quit;
+
+ while (1)
+ {
+ OSS_CALL(midi_notify_wait, ¶ms);
+ if (quit) break;
+ }
+ return 0;
+}
+
/**************************************************************************
* DriverProc (WINEOSS.1)
*/
@@ -563,7 +580,11 @@ LRESULT CALLBACK OSS_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
switch(wMsg) {
case DRV_LOAD:
+ CloseHandle(CreateThread(NULL, 0, notify_thread, NULL, 0, NULL));
+ return 1;
case DRV_FREE:
+ OSS_CALL(midi_release, NULL);
+ return 1;
case DRV_OPEN:
case DRV_CLOSE:
case DRV_ENABLE:
diff --git a/dlls/wineoss.drv/oss.c b/dlls/wineoss.drv/oss.c
index a9081f2cac9..8fda9270a4e 100644
--- a/dlls/wineoss.drv/oss.c
+++ b/dlls/wineoss.drv/oss.c
@@ -1406,8 +1406,10 @@ unixlib_entry_t __wine_unix_call_funcs[] =
set_event_handle,
is_started,
midi_init,
+ midi_release,
midi_out_message,
midi_in_message,
+ midi_notify_wait,
midi_seq_open,
midi_in_lock,
diff --git a/dlls/wineoss.drv/ossmidi.c b/dlls/wineoss.drv/ossmidi.c
index 86d766eceaf..0790eaaec1a 100644
--- a/dlls/wineoss.drv/ossmidi.c
+++ b/dlls/wineoss.drv/ossmidi.c
@@ -66,6 +66,10 @@ static unsigned int num_dests, num_srcs, num_synths, seq_refs;
static struct midi_dest dests[MAX_MIDIOUTDRV];
static struct midi_src srcs[MAX_MIDIINDRV];
+static pthread_mutex_t notify_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t notify_read_cond = PTHREAD_COND_INITIALIZER;
+static BOOL notify_quit;
+
typedef struct sVoice
{
int note; /* 0 means not used */
@@ -155,6 +159,17 @@ NTSTATUS midi_in_lock(void *args)
return STATUS_SUCCESS;
}
+static void notify_post(struct notify_context *notify)
+{
+ pthread_mutex_lock(¬ify_mutex);
+
+ if (notify) FIXME("Not yet handled\n");
+ else notify_quit = TRUE;
+ pthread_cond_signal(¬ify_read_cond);
+
+ pthread_mutex_unlock(¬ify_mutex);
+}
+
static void set_in_notify(struct notify_context *notify, struct midi_src *src, WORD dev_id, WORD msg,
UINT_PTR param_1, UINT_PTR param_2)
{
@@ -432,6 +447,14 @@ wrapup:
return STATUS_SUCCESS;
}
+NTSTATUS midi_release(void *args)
+{
+ /* stop the notify_wait thread */
+ notify_post(NULL);
+
+ return STATUS_SUCCESS;
+}
+
/* FIXME: this is a bad idea, it's even not static... */
SEQ_DEFINEBUF(1024);
@@ -1367,3 +1390,19 @@ NTSTATUS midi_in_message(void *args)
return STATUS_SUCCESS;
}
+
+NTSTATUS midi_notify_wait(void *args)
+{
+ struct midi_notify_wait_params *params = args;
+
+ pthread_mutex_lock(¬ify_mutex);
+
+ while (!notify_quit)
+ pthread_cond_wait(¬ify_read_cond, ¬ify_mutex);
+
+ *params->quit = notify_quit;
+
+ pthread_mutex_unlock(¬ify_mutex);
+
+ return STATUS_SUCCESS;
+}
diff --git a/dlls/wineoss.drv/unixlib.h b/dlls/wineoss.drv/unixlib.h
index 867e1ff656e..ddeba49556c 100644
--- a/dlls/wineoss.drv/unixlib.h
+++ b/dlls/wineoss.drv/unixlib.h
@@ -267,6 +267,12 @@ struct midi_in_message_params
struct notify_context *notify;
};
+struct midi_notify_wait_params
+{
+ BOOL *quit;
+ struct notify_context *notify;
+};
+
struct midi_seq_open_params
{
int close;
@@ -299,16 +305,20 @@ enum oss_funcs
oss_set_event_handle,
oss_is_started,
oss_midi_init,
+ oss_midi_release,
oss_midi_out_message,
oss_midi_in_message,
+ oss_midi_notify_wait,
oss_midi_seq_open, /* temporary */
oss_midi_in_lock,
};
NTSTATUS midi_init(void *args) DECLSPEC_HIDDEN;
+NTSTATUS midi_release(void *args) DECLSPEC_HIDDEN;
NTSTATUS midi_out_message(void *args) DECLSPEC_HIDDEN;
NTSTATUS midi_in_message(void *args) DECLSPEC_HIDDEN;
+NTSTATUS midi_notify_wait(void *args) DECLSPEC_HIDDEN;
NTSTATUS midi_seq_open(void *args) DECLSPEC_HIDDEN;
NTSTATUS midi_in_lock(void *args) DECLSPEC_HIDDEN;
--
2.25.1
April 29, 2022
Re: [PATCH v2 8/8] d2d1: Implement LoadVertexShader().
by Ziqing Hui
On 4/29/22 2:02 PM, Nikolay Sivov wrote:
>
>
> On 4/28/22 13:40, Ziqing Hui wrote:
>> +struct d2d_shader
>> +{
>> + const GUID *id;
>> + void *shader;
>> +};
> This could at least use IUnknown, you can probably use a union later to avoid casts.
>
>> + effect_context->shader_count++;
>> + if (effect_context->shaders_size < effect_context->shader_count)
>> + {
>> + if (!d2d_array_reserve((void **)&effect_context->shaders, &effect_context->shaders_size,
>> + effect_context->shader_count, sizeof(*effect_context->shaders)))
>> + {
>> + ERR("Failed to resize shaders array.\n");
>> + ID3D11VertexShader_Release(vertex_shader);
>> + return E_OUTOFMEMORY;
>> + }
>> + }
> You should call this to reserve "effect_context->shader_count + 1", no need to check size < count explicitly.
>
> Since this is using GUIDs for keys, I suspect it should check for duplicates? IsShaderLoaded() takes just a GUID, so that implies all shader types are in the same list most likely.
>
> By the way, have you figured out how shader objects are used later?
>
Shader objects will be used in ID2D1DrawTransform to create custom transforms which have custom shaders.
ID2D1DrawTransfrom use ID2D1DrawInfo that has functions like SetPixelShader() which accept shader GUID as an input argument.
And that's where the loaded shader objects are used.
April 29, 2022
Re: [PATCH v2 1/8] d2d1: Add stubs for ID2D1EffectContext.
by Ziqing Hui
On 4/29/22 1:54 PM, Nikolay Sivov wrote:
>
>
> On 4/28/22 13:40, Ziqing Hui wrote:
>> +void d2d_effect_context_init(struct d2d_effect_context *effect_context)
>> +{
>> + effect_context->ID2D1EffectContext_iface.lpVtbl = &d2d_effect_context_vtbl;
>> + effect_context->refcount = 1;
>> +}
> This patch introduces unused code. To avoid that I would move existing device_context->CreateEffect() to effect_context->CreateEffect() right away in the first patch.
>
> Regarding init helper and patches 2, 4, 7, it seems easier to keep "struct d2d_device_context *" in effect context instead. It provides access to the factory and d3d device.
>
Thanks for reply.
OK, I'll send a v3 version later following your advice.
April 29, 2022
[PATCH 1/1] kernelbase: Add support for progress callback in CopyFileEx.
by Alistair Leslie-Hughes
From: Alistair Leslie-Hughes <leslie_alistair(a)hotmail.com>
Based on patch by Michael Müller.
Signed-off-by: Alistair Leslie-Hughes <leslie_alistair(a)hotmail.com>
---
dlls/kernel32/tests/file.c | 6 ---
dlls/kernelbase/file.c | 76 ++++++++++++++++++++++++++++++++------
2 files changed, 65 insertions(+), 17 deletions(-)
diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c
index 378078c20d5..2dec65e3a0e 100644
--- a/dlls/kernel32/tests/file.c
+++ b/dlls/kernel32/tests/file.c
@@ -1178,23 +1178,17 @@ static void test_CopyFileEx(void)
ok(hfile != INVALID_HANDLE_VALUE, "failed to open destination file, error %ld\n", GetLastError());
SetLastError(0xdeadbeef);
retok = CopyFileExA(source, dest, copy_progress_cb, hfile, NULL, 0);
- todo_wine
ok(!retok, "CopyFileExA unexpectedly succeeded\n");
- todo_wine
ok(GetLastError() == ERROR_REQUEST_ABORTED, "expected ERROR_REQUEST_ABORTED, got %ld\n", GetLastError());
ok(GetFileAttributesA(dest) != INVALID_FILE_ATTRIBUTES, "file was deleted\n");
hfile = CreateFileA(dest, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL, OPEN_EXISTING, 0, 0);
- todo_wine
ok(hfile != INVALID_HANDLE_VALUE, "failed to open destination file, error %ld\n", GetLastError());
SetLastError(0xdeadbeef);
retok = CopyFileExA(source, dest, copy_progress_cb, hfile, NULL, 0);
- todo_wine
ok(!retok, "CopyFileExA unexpectedly succeeded\n");
- todo_wine
ok(GetLastError() == ERROR_REQUEST_ABORTED, "expected ERROR_REQUEST_ABORTED, got %ld\n", GetLastError());
- todo_wine
ok(GetFileAttributesA(dest) == INVALID_FILE_ATTRIBUTES, "file was not deleted\n");
retok = CopyFileExA(source, NULL, copy_progress_cb, hfile, NULL, 0);
diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c
index 8ae982294f6..f314382e4fa 100644
--- a/dlls/kernelbase/file.c
+++ b/dlls/kernelbase/file.c
@@ -490,16 +490,43 @@ BOOL WINAPI DECLSPEC_HOTPATCH AreFileApisANSI(void)
return !oem_file_apis;
}
+static BOOL call_progress_callback(LPPROGRESS_ROUTINE *callback, LARGE_INTEGER size, LARGE_INTEGER transferred,
+ DWORD cbtype, HANDLE src, HANDLE dest, void *param)
+{
+ DWORD cbret;
+
+ if (!*callback)
+ return TRUE;
+
+ cbret = (*callback)( size, transferred, size, transferred, 1, cbtype, src, dest, param );
+ if (cbret == PROGRESS_QUIET)
+ {
+ *callback = NULL;
+ return TRUE;
+ }
+ else if (cbret == PROGRESS_CANCEL)
+ {
+ FILE_DISPOSITION_INFORMATION fdi;
+ IO_STATUS_BLOCK io;
+
+ fdi.DoDeleteFile = TRUE;
+ NtSetInformationFile(dest, &io, &fdi, sizeof(fdi), FileDispositionInformation);
+ }
+
+ return cbret == PROGRESS_CONTINUE;
+}
/***********************************************************************
* CopyFileExW (kernelbase.@)
*/
-BOOL WINAPI CopyFileExW( const WCHAR *source, const WCHAR *dest, LPPROGRESS_ROUTINE progress,
+BOOL WINAPI CopyFileExW( const WCHAR *source, const WCHAR *dest, LPPROGRESS_ROUTINE callback,
void *param, BOOL *cancel_ptr, DWORD flags )
{
static const int buffer_size = 65536;
HANDLE h1, h2;
- FILE_BASIC_INFORMATION info;
+ FILE_NETWORK_OPEN_INFORMATION info;
+ FILE_BASIC_INFORMATION basic_info;
+ LARGE_INTEGER transferred;
IO_STATUS_BLOCK io;
DWORD count;
BOOL ret = FALSE;
@@ -533,9 +560,9 @@ BOOL WINAPI CopyFileExW( const WCHAR *source, const WCHAR *dest, LPPROGRESS_ROUT
return FALSE;
}
- if (!set_ntstatus( NtQueryInformationFile( h1, &io, &info, sizeof(info), FileBasicInformation )))
+ if (!set_ntstatus( NtQueryInformationFile( h1, &io, &info, sizeof(info), FileNetworkOpenInformation )))
{
- WARN("GetFileInformationByHandle returned error for %s\n", debugstr_w(source));
+ WARN("NtQueryInformationFile returned error for %s\n", debugstr_w(source));
HeapFree( GetProcessHeap(), 0, buffer );
CloseHandle( h1 );
return FALSE;
@@ -559,14 +586,30 @@ BOOL WINAPI CopyFileExW( const WCHAR *source, const WCHAR *dest, LPPROGRESS_ROUT
}
}
- if ((h2 = CreateFileW( dest, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+ if ((h2 = CreateFileW( dest, GENERIC_WRITE | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
(flags & COPY_FILE_FAIL_IF_EXISTS) ? CREATE_NEW : CREATE_ALWAYS,
info.FileAttributes, h1 )) == INVALID_HANDLE_VALUE)
{
- WARN("Unable to open dest %s\n", debugstr_w(dest));
- HeapFree( GetProcessHeap(), 0, buffer );
- CloseHandle( h1 );
- return FALSE;
+ /* User has the file opened without FILE_SHARE_DELETE */
+ if (GetLastError() == ERROR_SHARING_VIOLATION)
+ h2 = CreateFileW( dest, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+ (flags & COPY_FILE_FAIL_IF_EXISTS) ? CREATE_NEW : CREATE_ALWAYS,
+ info.FileAttributes, h1 );
+ if (h2 == INVALID_HANDLE_VALUE)
+ {
+ WARN("Unable to open dest %s\n", debugstr_w(dest));
+ HeapFree( GetProcessHeap(), 0, buffer );
+ CloseHandle( h1 );
+ return FALSE;
+ }
+ }
+
+ transferred.QuadPart = 0;
+
+ if (!(call_progress_callback(&callback, info.EndOfFile, transferred, CALLBACK_STREAM_SWITCH, h1, h2, param)))
+ {
+ SetLastError( ERROR_REQUEST_ABORTED );
+ goto done;
}
while (ReadFile( h1, buffer, buffer_size, &count, NULL ) && count)
@@ -578,13 +621,24 @@ BOOL WINAPI CopyFileExW( const WCHAR *source, const WCHAR *dest, LPPROGRESS_ROUT
if (!WriteFile( h2, p, count, &res, NULL ) || !res) goto done;
p += res;
count -= res;
+
+ transferred.QuadPart += res;
+ if (!(call_progress_callback(&callback, info.EndOfFile, transferred, CALLBACK_CHUNK_FINISHED, h1, h2, param)))
+ {
+ SetLastError( ERROR_REQUEST_ABORTED );
+ goto done;
+ }
}
}
ret = TRUE;
done:
/* Maintain the timestamp of source file to destination file */
- info.FileAttributes = 0;
- NtSetInformationFile( h2, &io, &info, sizeof(info), FileBasicInformation );
+ basic_info.CreationTime = info.CreationTime;
+ basic_info.LastAccessTime = info.LastAccessTime;
+ basic_info.LastWriteTime = info.LastWriteTime;
+ basic_info.ChangeTime = info.ChangeTime;
+ basic_info.FileAttributes = 0;
+ NtSetInformationFile( h2, &io, &basic_info, sizeof(basic_info), FileBasicInformation );
HeapFree( GetProcessHeap(), 0, buffer );
CloseHandle( h1 );
CloseHandle( h2 );
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/13
April 29, 2022
[PATCH 0/1] MR13: kernelbase: Add support for progress callback in CopyFileEx.
by Alistair Leslie-Hughes (@alesliehughes)
Based on patch by Michael Müller.
Signed-off-by: Alistair Leslie-Hughes <leslie_alistair(a)hotmail.com>
--
https://gitlab.winehq.org/wine/wine/-/merge_requests/13
April 29, 2022
Re: [PATCH v2 3/5] shell32: Refactor to keep style consistent
by Nikolay Sivov
Signed-off-by: Nikolay Sivov <nsivov(a)codeweavers.com>
April 29, 2022
Re: [PATCH v2 2/5] shell32: Prevent user after free in error case (Coverity)
by Nikolay Sivov
Signed-off-by: Nikolay Sivov <nsivov(a)codeweavers.com>
April 29, 2022
Re: [PATCH v2 8/8] d2d1: Implement LoadVertexShader().
by Nikolay Sivov
On 4/28/22 13:40, Ziqing Hui wrote:
> +struct d2d_shader
> +{
> + const GUID *id;
> + void *shader;
> +};
This could at least use IUnknown, you can probably use a union later to
avoid casts.
> + effect_context->shader_count++;
> + if (effect_context->shaders_size < effect_context->shader_count)
> + {
> + if (!d2d_array_reserve((void **)&effect_context->shaders, &effect_context->shaders_size,
> + effect_context->shader_count, sizeof(*effect_context->shaders)))
> + {
> + ERR("Failed to resize shaders array.\n");
> + ID3D11VertexShader_Release(vertex_shader);
> + return E_OUTOFMEMORY;
> + }
> + }
You should call this to reserve "effect_context->shader_count + 1", no
need to check size < count explicitly.
Since this is using GUIDs for keys, I suspect it should check for
duplicates? IsShaderLoaded() takes just a GUID, so that implies all
shader types are in the same list most likely.
By the way, have you figured out how shader objects are used later?
April 29, 2022
Re: [PATCH v2 1/8] d2d1: Add stubs for ID2D1EffectContext.
by Nikolay Sivov
On 4/28/22 13:40, Ziqing Hui wrote:
> +void d2d_effect_context_init(struct d2d_effect_context *effect_context)
> +{
> + effect_context->ID2D1EffectContext_iface.lpVtbl = &d2d_effect_context_vtbl;
> + effect_context->refcount = 1;
> +}
This patch introduces unused code. To avoid that I would move existing
device_context->CreateEffect() to effect_context->CreateEffect() right
away in the first patch.
Regarding init helper and patches 2, 4, 7, it seems easier to keep
"struct d2d_device_context *" in effect context instead. It provides
access to the factory and d3d device.
April 29, 2022
Re: [PATCH v2 2/2] iphlpapi: Add GetPerTcpConnectionEStats stub.
by Austin English
On Tue, Apr 26, 2022, 11:11 Mohamad Al-Jaf <mohamadaljaf(a)gmail.com> wrote:
> On Tue, Apr 26, 2022 at 2:38 AM Huw Davies <huw(a)codeweavers.com> wrote:
> > I don't think it's unreasonable to wait for Austin to send in his own
> > patches (especially as you're asking the reviewer to wait until Austin
> > sends his sign-off). I appreciate that you've made some changes, but
> > this would be better done in response to a patch that's actually on
> > the mailing list. That way the reviewer can easily follow the
> > progress, unlike the way this was done, which just left me confused
> > about where v1 was.
>
> You're right, it's not unreasonable to wait for him to send it on his
> own, but I just appreciated his input in the mshtmlmedia thread and
> wanted to show my gratitude by confirming the wine-bug, and confirming
> his patch, hence the sign-off from me. I made a Twitch account just to
> test his patch.
>
> I normally wouldn't do something like this, people can
> submit their patch on their own and they should, it's their work after
> all. And to be honest I'd much rather stick to my own work. I just
> wanted to save him the trouble of doing it himself. I take
> no credit for this patch.
>
> If he wants to resubmit under his name it's completely fine by me and
> I'm more than happy to sign-off on the patch, well, that is if my
> sign-off has any bearing.
>
> I apologize in advance if he didn't want this.
>
> --
> Kind regards,
> Mohamad
>
I didn't originally add my sign of because the patch wasn't tested.
I have no problem with you taking over the patch, thanks for checking.
I'm traveling for the next couple weeks, anyway, so won't have a chance to
do much with it for a while.
>
April 29, 2022
[PATCH v2 2/2] include: Add WINE_ALLOC_SIZE attribute to heap_calloc().
by Brendan Shanks
Signed-off-by: Brendan Shanks <bshanks(a)codeweavers.com>
---
include/wine/heap.h | 2 +-
include/winnt.h | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/include/wine/heap.h b/include/wine/heap.h
index 97d3a5662be..fb687c92393 100644
--- a/include/wine/heap.h
+++ b/include/wine/heap.h
@@ -46,7 +46,7 @@ static inline void heap_free(void *mem)
HeapFree(GetProcessHeap(), 0, mem);
}
-static inline void *heap_calloc(SIZE_T count, SIZE_T size)
+static inline void * __WINE_ALLOC_SIZE(1,2) heap_calloc(SIZE_T count, SIZE_T size)
{
SIZE_T len = count * size;
diff --git a/include/winnt.h b/include/winnt.h
index e853ddbc7ae..79df4259f59 100644
--- a/include/winnt.h
+++ b/include/winnt.h
@@ -199,9 +199,9 @@ extern "C" {
#endif
#if defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 3)))
-#define __WINE_ALLOC_SIZE(x) __attribute__((__alloc_size__(x)))
+#define __WINE_ALLOC_SIZE(...) __attribute__((__alloc_size__(__VA_ARGS__)))
#else
-#define __WINE_ALLOC_SIZE(x)
+#define __WINE_ALLOC_SIZE(...)
#endif
/* Anonymous union/struct handling */
--
2.35.1
April 29, 2022
[PATCH v2 1/2] wrc: Support function macros where varargs are the only argument.
by Brendan Shanks
Signed-off-by: Brendan Shanks <bshanks(a)codeweavers.com>
---
v2: wrc needed support added for parsing function macros where '...' is
the only argument.
Negative 'args' was being used to represent a macro using varargs, but
this obviously fails when there are no named arguments.
Like with other variable argument macros, expansion still doesn't work.
tools/wrc/ppl.l | 4 ++--
tools/wrc/ppy.y | 11 ++++++-----
tools/wrc/wpp.c | 5 +++--
tools/wrc/wpp_private.h | 3 ++-
4 files changed, 13 insertions(+), 10 deletions(-)
diff --git a/tools/wrc/ppl.l b/tools/wrc/ppl.l
index 35c3fd6fb44..4748d735fa2 100644
--- a/tools/wrc/ppl.l
+++ b/tools/wrc/ppl.l
@@ -1071,9 +1071,9 @@ static void expand_macro(macexpstackentry_t *mep)
assert(ppp->type == def_macro);
assert(ppp->expanding == 0);
- if((ppp->nargs >= 0 && nargs != ppp->nargs) || (ppp->nargs < 0 && nargs < -ppp->nargs))
+ if((!ppp->variadic && nargs != ppp->nargs) || (ppp->variadic && nargs < ppp->nargs))
{
- ppy_error("Too %s macro arguments (%d)", nargs < abs(ppp->nargs) ? "few" : "many", nargs);
+ ppy_error("Too %s macro arguments (%d)", nargs < ppp->nargs ? "few" : "many", nargs);
return;
}
diff --git a/tools/wrc/ppy.y b/tools/wrc/ppy.y
index ac4423d724f..ce3c409e735 100644
--- a/tools/wrc/ppy.y
+++ b/tools/wrc/ppy.y
@@ -112,6 +112,7 @@ static char *merge_text(char *s1, char *s2);
*/
static char **macro_args; /* Macro parameters array while parsing */
static int nmacro_args;
+static int macro_variadic; /* Macro arguments end with (or consist entirely of) '...' */
%}
@@ -267,7 +268,7 @@ preprocessor
| tUNDEF tIDENT tNL { pp_del_define($2); free($2); }
| tDEFINE opt_text tNL { pp_add_define($1, $2); free($1); free($2); }
| tMACRO res_arg allmargs tMACROEND opt_mtexts tNL {
- pp_add_macro($1, macro_args, nmacro_args, $5);
+ pp_add_macro($1, macro_args, nmacro_args, macro_variadic, $5);
}
| tLINE tSINT tDQSTRING tNL { if($3) fprintf(ppy_out, "# %d %s\n", $2 , $3); free($3); }
| tGCCLINE tSINT tDQSTRING tNL { if($3) fprintf(ppy_out, "# %d %s\n", $2 , $3); free($3); }
@@ -305,16 +306,16 @@ text : tLITERAL { $$ = $1; }
| text tSQSTRING { $$ = merge_text($1, $2); }
;
-res_arg : /* Empty */ { macro_args = NULL; nmacro_args = 0; }
+res_arg : /* Empty */ { macro_args = NULL; nmacro_args = 0; macro_variadic = 0; }
;
-allmargs: /* Empty */ { $$ = 0; macro_args = NULL; nmacro_args = 0; }
+allmargs: /* Empty */ { $$ = 0; macro_args = NULL; nmacro_args = 0; macro_variadic = 0; }
| emargs { $$ = nmacro_args; }
;
emargs : margs { $$ = $1; }
- | margs ',' tELLIPSIS { nmacro_args *= -1; }
- | tELLIPSIS { macro_args = NULL; nmacro_args = 0; }
+ | margs ',' tELLIPSIS { macro_variadic = 1; }
+ | tELLIPSIS { macro_args = NULL; nmacro_args = 0; macro_variadic = 1; }
;
margs : margs ',' tIDENT { $$ = add_new_marg($3); }
diff --git a/tools/wrc/wpp.c b/tools/wrc/wpp.c
index d8d5052870a..57baf44862b 100644
--- a/tools/wrc/wpp.c
+++ b/tools/wrc/wpp.c
@@ -239,7 +239,7 @@ pp_entry_t *pp_add_define(const char *def, const char *text)
return ppp;
}
-pp_entry_t *pp_add_macro(char *id, char *args[], int nargs, mtext_t *exp)
+pp_entry_t *pp_add_macro(char *id, char *args[], int nargs, int variadic, mtext_t *exp)
{
int idx;
pp_entry_t *ppp;
@@ -258,13 +258,14 @@ pp_entry_t *pp_add_macro(char *id, char *args[], int nargs, mtext_t *exp)
ppp->type = def_macro;
ppp->margs = args;
ppp->nargs = nargs;
+ ppp->variadic = variadic;
ppp->subst.mtext= exp;
ppp->filename = xstrdup(pp_status.input ? pp_status.input : "<internal or cmdline>");
ppp->linenumber = pp_status.input ? pp_status.line_number : 0;
list_add_head( &pp_defines[idx], &ppp->entry );
if(pp_status.debug)
{
- fprintf(stderr, "Added macro (%s, %d) <%s(%d)> to <", pp_status.input, pp_status.line_number, ppp->ident, nargs);
+ fprintf(stderr, "Added macro (%s, %d) <%s(%d%s)> to <", pp_status.input, pp_status.line_number, ppp->ident, nargs, variadic ? ",va" : "");
for(; exp; exp = exp->next)
{
switch(exp->type)
diff --git a/tools/wrc/wpp_private.h b/tools/wrc/wpp_private.h
index 435dbcc005a..9d92fa02fbb 100644
--- a/tools/wrc/wpp_private.h
+++ b/tools/wrc/wpp_private.h
@@ -80,6 +80,7 @@ typedef struct pp_entry {
char *ident; /* The key */
char **margs; /* Macro arguments array or NULL if none */
int nargs;
+ int variadic;
union {
mtext_t *mtext; /* The substitution sequence or NULL if none */
char *text;
@@ -156,7 +157,7 @@ typedef struct cval {
pp_entry_t *pplookup(const char *ident);
pp_entry_t *pp_add_define(const char *def, const char *text);
-pp_entry_t *pp_add_macro(char *ident, char *args[], int nargs, mtext_t *exp);
+pp_entry_t *pp_add_macro(char *ident, char *args[], int nargs, int variadic, mtext_t *exp);
void pp_del_define(const char *name);
void *pp_open_include(const char *name, int type, const char *parent_name, char **newpath);
void pp_push_if(pp_if_state_t s);
--
2.35.1
April 29, 2022
Re: [PATCH v3 4/5] ws2_32/tests: Test selecting for FD_ACCEPT while there is a pending AcceptEx() call.
by Marvin
Hi,
While running your changed tests, I think I found new failures.
Being a bot and all I'm not very good at pattern recognition, so I might be
wrong, but could you please double-check?
Full results can be found at:
https://testbot.winehq.org/JobDetails.pl?Key=113806
Your paranoid android.
=== debian11 (32 bit Hebrew:Israel report) ===
ws2_32:
sock.c:5592: Test failed: expected timeout
April 29, 2022
Re: [PATCH 3/4] ntdll: Partially implement NtCancelSynchronousIoFile.
by Daniel Lehman
> If io is specified, it should never cancel any I/O operations other than the one specified by the parameter.
>
> Better keep returning STATUS_NOT_IMPLEMENTED in this case?
ah, i see. i think i know how it's used. will send a new version
thanks
daniel
April 29, 2022
[PATCH 5/5] ws2_32/tests: Verify that AFD_POLL_CONNECT and AFD_POLL_WRITE are signaled simultaneously.
by Zebediah Figura
Signed-off-by: Zebediah Figura <zfigura(a)codeweavers.com>
---
dlls/ws2_32/tests/afd.c | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/dlls/ws2_32/tests/afd.c b/dlls/ws2_32/tests/afd.c
index 5152469f02f..43c401c940a 100644
--- a/dlls/ws2_32/tests/afd.c
+++ b/dlls/ws2_32/tests/afd.c
@@ -724,7 +724,42 @@ static void test_poll(void)
ok(out_params->sockets[0].flags == AFD_POLL_ACCEPT, "got flags %#x\n", out_params->sockets[0].flags);
ok(!out_params->sockets[0].status, "got status %#x\n", out_params->sockets[0].status);
+ server = accept(listener, NULL, NULL);
+ ok(server != -1, "got error %u\n", WSAGetLastError());
+ closesocket(server);
closesocket(client);
+
+ /* Verify that CONNECT and WRITE are signaled simultaneously. */
+
+ client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+ in_params->timeout = -1000 * 10000;
+ in_params->count = 1;
+ in_params->sockets[0].socket = client;
+ in_params->sockets[0].flags = ~0;
+ params_size = offsetof(struct afd_poll_params, sockets[1]);
+
+ ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io,
+ IOCTL_AFD_POLL, in_params, params_size, out_params, params_size);
+ ok(ret == STATUS_PENDING, "got %#x\n", ret);
+
+ ret = connect(client, (struct sockaddr *)&addr, sizeof(addr));
+ ok(!ret, "got error %u\n", WSAGetLastError());
+
+ ret = WaitForSingleObject(event, 200);
+ ok(!ret, "got %#x\n", ret);
+ ok(!io.Status, "got %#lx\n", io.Status);
+ ok(io.Information == offsetof(struct afd_poll_params, sockets[1]), "got %#Ix\n", io.Information);
+ ok(out_params->count == 1, "got count %u\n", out_params->count);
+ ok(out_params->sockets[0].flags == (AFD_POLL_CONNECT | AFD_POLL_WRITE),
+ "got flags %#x\n", out_params->sockets[0].flags);
+ ok(!out_params->sockets[0].status, "got status %#x\n", out_params->sockets[0].status);
+
+ server = accept(listener, NULL, NULL);
+ ok(server != -1, "got error %u\n", WSAGetLastError());
+ closesocket(server);
+ closesocket(client);
+
closesocket(listener);
/* Test UDP sockets. */
--
2.34.1
April 29, 2022
[PATCH v3 4/5] ws2_32/tests: Test selecting for FD_ACCEPT while there is a pending AcceptEx() call.
by Zebediah Figura
Signed-off-by: Zebediah Figura <zfigura(a)codeweavers.com>
---
dlls/ws2_32/tests/sock.c | 48 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 48 insertions(+)
diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c
index 2ad649816eb..78c778586a8 100644
--- a/dlls/ws2_32/tests/sock.c
+++ b/dlls/ws2_32/tests/sock.c
@@ -5308,12 +5308,23 @@ static void test_accept_events(struct event_test_ctx *ctx)
{
const struct sockaddr_in addr = {.sin_family = AF_INET, .sin_addr.s_addr = htonl(INADDR_LOOPBACK)};
SOCKET listener, server, client, client2;
+ GUID acceptex_guid = WSAID_ACCEPTEX;
struct sockaddr_in destaddr;
+ OVERLAPPED overlapped = {0};
+ LPFN_ACCEPTEX pAcceptEx;
+ char buffer[32];
int len, ret;
+ DWORD size;
+
+ overlapped.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
ok(listener != -1, "failed to create socket, error %u\n", WSAGetLastError());
+ ret = WSAIoctl(listener, SIO_GET_EXTENSION_FUNCTION_POINTER, &acceptex_guid, sizeof(acceptex_guid),
+ &pAcceptEx, sizeof(pAcceptEx), &size, NULL, NULL);
+ ok(!ret, "failed to get AcceptEx, error %u\n", WSAGetLastError());
+
select_events(ctx, listener, FD_CONNECT | FD_READ | FD_OOB | FD_ACCEPT);
ret = bind(listener, (const struct sockaddr *)&addr, sizeof(addr));
@@ -5511,7 +5522,44 @@ static void test_accept_events(struct event_test_ctx *ctx)
closesocket(server);
closesocket(client);
+ /* Connect while there is a pending AcceptEx(). */
+
+ select_events(ctx, listener, FD_CONNECT | FD_READ | FD_OOB | FD_ACCEPT);
+
+ server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ ret = pAcceptEx(listener, server, buffer, 0, 0, sizeof(buffer), NULL, &overlapped);
+ ok(!ret, "got %d\n", ret);
+ ok(WSAGetLastError() == ERROR_IO_PENDING, "got error %u\n", WSAGetLastError());
+
+ client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ ret = connect(client, (struct sockaddr *)&destaddr, sizeof(destaddr));
+ ok(!ret, "got error %u\n", WSAGetLastError());
+
+ ret = WaitForSingleObject(overlapped.hEvent, 200);
+ ok(!ret, "got %d\n", ret);
+ ret = GetOverlappedResult((HANDLE)listener, &overlapped, &size, FALSE);
+ ok(ret, "got error %lu\n", GetLastError());
+ ok(!size, "got size %lu\n", size);
+
+ check_events_todo(ctx, 0, 0, 0);
+
+ closesocket(server);
+ closesocket(client);
+
+ client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ ret = connect(client, (struct sockaddr *)&destaddr, sizeof(destaddr));
+ ok(!ret, "got error %u\n", WSAGetLastError());
+
+ check_events_todo(ctx, FD_ACCEPT, 0, 200);
+ check_events(ctx, 0, 0, 0);
+
+ server = accept(listener, NULL, NULL);
+ ok(server != -1, "failed to accept, error %u\n", WSAGetLastError());
+ closesocket(server);
+ closesocket(client);
+
closesocket(listener);
+ CloseHandle(overlapped.hEvent);
}
static void test_connect_events(struct event_test_ctx *ctx)
--
2.34.1
April 29, 2022
[PATCH v3 3/5] ws2_32/tests: Test selecting for FD_READ while there is a pending WSARecv() call.
by Zebediah Figura
Signed-off-by: Zebediah Figura <zfigura(a)codeweavers.com>
---
dlls/ws2_32/tests/sock.c | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c
index 5efff0420eb..2ad649816eb 100644
--- a/dlls/ws2_32/tests/sock.c
+++ b/dlls/ws2_32/tests/sock.c
@@ -5709,11 +5709,16 @@ static void test_write_events(struct event_test_ctx *ctx)
static void test_read_events(struct event_test_ctx *ctx)
{
+ OVERLAPPED overlapped = {0};
SOCKET server, client;
+ DWORD size, flags = 0;
unsigned int i;
char buffer[8];
+ WSABUF wsabuf;
int ret;
+ overlapped.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
+
tcp_socketpair(&client, &server);
set_blocking(client, FALSE);
@@ -5788,8 +5793,36 @@ static void test_read_events(struct event_test_ctx *ctx)
check_events(ctx, 0, 0, 200);
+ /* Send data while there is a pending WSARecv(). */
+
+ select_events(ctx, server, FD_ACCEPT | FD_CLOSE | FD_CONNECT | FD_OOB | FD_READ);
+
+ wsabuf.buf = buffer;
+ wsabuf.len = 1;
+ ret = WSARecv(server, &wsabuf, 1, NULL, &flags, &overlapped, NULL);
+ ok(ret == -1, "got %d\n", ret);
+ ok(WSAGetLastError() == ERROR_IO_PENDING, "got error %u\n", WSAGetLastError());
+
+ ret = send(client, "a", 1, 0);
+ ok(ret == 1, "got %d\n", ret);
+
+ ret = WaitForSingleObject(overlapped.hEvent, 200);
+ ok(!ret, "got %d\n", ret);
+ ret = GetOverlappedResult((HANDLE)server, &overlapped, &size, FALSE);
+ ok(ret, "got error %lu\n", GetLastError());
+ ok(size == 1, "got size %lu\n", size);
+
+ check_events(ctx, 0, 0, 0);
+
+ ret = send(client, "a", 1, 0);
+ ok(ret == 1, "got %d\n", ret);
+
+ check_events(ctx, FD_READ, 0, 200);
+ check_events(ctx, 0, 0, 0);
+
closesocket(server);
closesocket(client);
+ CloseHandle(overlapped.hEvent);
}
static void test_oob_events(struct event_test_ctx *ctx)
--
2.34.1
April 29, 2022
[PATCH v3 2/5] ws2_32/tests: Test polling for AFD_POLL_ACCEPT while there is a pending AcceptEx() call.
by Zebediah Figura
Signed-off-by: Zebediah Figura <zfigura(a)codeweavers.com>
---
dlls/ws2_32/tests/afd.c | 52 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 52 insertions(+)
diff --git a/dlls/ws2_32/tests/afd.c b/dlls/ws2_32/tests/afd.c
index 9d1600a7f6a..5152469f02f 100644
--- a/dlls/ws2_32/tests/afd.c
+++ b/dlls/ws2_32/tests/afd.c
@@ -153,8 +153,10 @@ static void test_poll(void)
struct afd_poll_params *in_params = (struct afd_poll_params *)in_buffer;
struct afd_poll_params *out_params = (struct afd_poll_params *)out_buffer;
int large_buffer_size = 1024 * 1024;
+ GUID acceptex_guid = WSAID_ACCEPTEX;
SOCKET client, server, listener;
OVERLAPPED overlapped = {0};
+ LPFN_ACCEPTEX pAcceptEx;
struct sockaddr_in addr;
DWORD size, flags = 0;
char *large_buffer;
@@ -180,6 +182,10 @@ static void test_poll(void)
ret = getsockname(listener, (struct sockaddr *)&addr, &len);
ok(!ret, "got error %u\n", WSAGetLastError());
+ ret = WSAIoctl(listener, SIO_GET_EXTENSION_FUNCTION_POINTER, &acceptex_guid, sizeof(acceptex_guid),
+ &pAcceptEx, sizeof(pAcceptEx), &size, NULL, NULL);
+ ok(!ret, "failed to get AcceptEx, error %u\n", WSAGetLastError());
+
params_size = offsetof(struct afd_poll_params, sockets[1]);
in_params->count = 1;
@@ -673,6 +679,52 @@ static void test_poll(void)
closesocket(client);
+ /* Test connecting while there is a pending AcceptEx(). */
+
+ in_params->timeout = -1000 * 10000;
+ in_params->count = 1;
+ in_params->sockets[0].socket = listener;
+ in_params->sockets[0].flags = AFD_POLL_ACCEPT;
+
+ ret = NtDeviceIoControlFile((HANDLE)listener, event, NULL, NULL, &io,
+ IOCTL_AFD_POLL, in_params, params_size, out_params, params_size);
+ ok(ret == STATUS_PENDING, "got %#x\n", ret);
+
+ server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ ret = pAcceptEx(listener, server, large_buffer, 0, 0, sizeof(struct sockaddr_in) + 16, NULL, &overlapped);
+ ok(!ret, "got %d\n", ret);
+ ok(WSAGetLastError() == ERROR_IO_PENDING, "got error %u\n", WSAGetLastError());
+
+ client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ ret = connect(client, (struct sockaddr *)&addr, sizeof(addr));
+ ok(!ret, "got error %u\n", WSAGetLastError());
+
+ ret = WaitForSingleObject(overlapped.hEvent, 200);
+ ok(!ret, "got %d\n", ret);
+ ret = GetOverlappedResult((HANDLE)listener, &overlapped, &size, FALSE);
+ ok(ret, "got error %lu\n", GetLastError());
+ ok(!size, "got size %lu\n", size);
+
+ ret = WaitForSingleObject(event, 0);
+ todo_wine ok(ret == WAIT_TIMEOUT, "got %#x\n", ret);
+
+ closesocket(server);
+ closesocket(client);
+
+ client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ ret = connect(client, (struct sockaddr *)&addr, sizeof(addr));
+ ok(!ret, "got error %u\n", WSAGetLastError());
+
+ ret = WaitForSingleObject(event, 200);
+ ok(!ret, "got %#x\n", ret);
+ ok(!io.Status, "got %#lx\n", io.Status);
+ ok(io.Information == offsetof(struct afd_poll_params, sockets[1]), "got %#Ix\n", io.Information);
+ ok(out_params->count == 1, "got count %u\n", out_params->count);
+ ok(out_params->sockets[0].socket == listener, "got socket %#Ix\n", out_params->sockets[0].socket);
+ ok(out_params->sockets[0].flags == AFD_POLL_ACCEPT, "got flags %#x\n", out_params->sockets[0].flags);
+ ok(!out_params->sockets[0].status, "got status %#x\n", out_params->sockets[0].status);
+
+ closesocket(client);
closesocket(listener);
/* Test UDP sockets. */
--
2.34.1
April 29, 2022
[PATCH v3 1/5] ws2_32/tests: Test polling for AFD_POLL_READ while there is a pending WSARecv() call.
by Zebediah Figura
Signed-off-by: Zebediah Figura <zfigura(a)codeweavers.com>
---
dlls/ws2_32/tests/afd.c | 49 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 49 insertions(+)
diff --git a/dlls/ws2_32/tests/afd.c b/dlls/ws2_32/tests/afd.c
index e385715f102..9d1600a7f6a 100644
--- a/dlls/ws2_32/tests/afd.c
+++ b/dlls/ws2_32/tests/afd.c
@@ -154,11 +154,14 @@ static void test_poll(void)
struct afd_poll_params *out_params = (struct afd_poll_params *)out_buffer;
int large_buffer_size = 1024 * 1024;
SOCKET client, server, listener;
+ OVERLAPPED overlapped = {0};
struct sockaddr_in addr;
+ DWORD size, flags = 0;
char *large_buffer;
IO_STATUS_BLOCK io;
LARGE_INTEGER now;
ULONG params_size;
+ WSABUF wsabuf;
HANDLE event;
int ret, len;
@@ -166,6 +169,7 @@ static void test_poll(void)
memset(in_buffer, 0, sizeof(in_buffer));
memset(out_buffer, 0, sizeof(out_buffer));
event = CreateEventW(NULL, TRUE, FALSE, NULL);
+ overlapped.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
ret = bind(listener, (const struct sockaddr *)&bind_addr, sizeof(bind_addr));
@@ -329,6 +333,50 @@ static void test_poll(void)
check_poll(client, event, AFD_POLL_WRITE | AFD_POLL_CONNECT | AFD_POLL_READ);
check_poll(server, event, AFD_POLL_CONNECT);
+ /* Test sending data while there is a pending WSARecv(). */
+
+ in_params->timeout = -1000 * 10000;
+ in_params->count = 1;
+ in_params->sockets[0].socket = server;
+ in_params->sockets[0].flags = AFD_POLL_READ;
+
+ ret = NtDeviceIoControlFile((HANDLE)server, event, NULL, NULL, &io,
+ IOCTL_AFD_POLL, in_params, params_size, out_params, params_size);
+ ok(ret == STATUS_PENDING, "got %#x\n", ret);
+
+ wsabuf.buf = large_buffer;
+ wsabuf.len = 1;
+ ret = WSARecv(server, &wsabuf, 1, NULL, &flags, &overlapped, NULL);
+ ok(ret == -1, "got %d\n", ret);
+ ok(WSAGetLastError() == ERROR_IO_PENDING, "got error %u\n", WSAGetLastError());
+
+ ret = send(client, "a", 1, 0);
+ ok(ret == 1, "got %d\n", ret);
+
+ ret = WaitForSingleObject(overlapped.hEvent, 200);
+ ok(!ret, "got %d\n", ret);
+ ret = GetOverlappedResult((HANDLE)server, &overlapped, &size, FALSE);
+ ok(ret, "got error %lu\n", GetLastError());
+ ok(size == 1, "got size %lu\n", size);
+
+ ret = WaitForSingleObject(event, 0);
+ todo_wine ok(ret == WAIT_TIMEOUT, "got %#x\n", ret);
+
+ ret = send(client, "a", 1, 0);
+ ok(ret == 1, "got %d\n", ret);
+
+ ret = WaitForSingleObject(event, 200);
+ ok(!ret, "got %#x\n", ret);
+ ok(!io.Status, "got %#lx\n", io.Status);
+ ok(io.Information == offsetof(struct afd_poll_params, sockets[1]), "got %#Ix\n", io.Information);
+ ok(out_params->count == 1, "got count %u\n", out_params->count);
+ ok(out_params->sockets[0].socket == server, "got socket %#Ix\n", out_params->sockets[0].socket);
+ ok(out_params->sockets[0].flags == AFD_POLL_READ, "got flags %#x\n", out_params->sockets[0].flags);
+ ok(!out_params->sockets[0].status, "got status %#x\n", out_params->sockets[0].status);
+
+ ret = recv(server, large_buffer, 1, 0);
+ ok(ret == 1, "got %d\n", ret);
+
/* Test sending out-of-band data. */
ret = send(client, "a", 1, MSG_OOB);
@@ -745,6 +793,7 @@ static void test_poll(void)
closesocket(client);
closesocket(server);
+ CloseHandle(overlapped.hEvent);
CloseHandle(event);
free(large_buffer);
}
--
2.34.1
April 29, 2022
[PATCH 7/7] crypt32: Reimplement CertNameToStrA() on top of CertNameToStrW().
by Paul Gofman
From: Paul Gofman <pgofman(a)codeweavers.com>
---
dlls/crypt32/str.c | 250 ++++-----------------------------------
dlls/crypt32/tests/str.c | 107 ++++++++---------
2 files changed, 72 insertions(+), 285 deletions(-)
diff --git a/dlls/crypt32/str.c b/dlls/crypt32/str.c
index 29882ab771e..2c667542dc8 100644
--- a/dlls/crypt32/str.c
+++ b/dlls/crypt32/str.c
@@ -141,115 +141,6 @@ static inline BOOL is_quotable_char(WCHAR c)
}
}
-static DWORD quote_rdn_value_to_str_a(DWORD dwValueType,
- PCERT_RDN_VALUE_BLOB pValue, LPSTR psz, DWORD csz)
-{
- DWORD ret = 0, len, i;
- BOOL needsQuotes = FALSE;
-
- TRACE("(%ld, %p, %p, %ld)\n", dwValueType, pValue, psz, csz);
-
- switch (dwValueType)
- {
- case CERT_RDN_ANY_TYPE:
- break;
- case CERT_RDN_NUMERIC_STRING:
- case CERT_RDN_PRINTABLE_STRING:
- case CERT_RDN_TELETEX_STRING:
- case CERT_RDN_VIDEOTEX_STRING:
- case CERT_RDN_IA5_STRING:
- case CERT_RDN_GRAPHIC_STRING:
- case CERT_RDN_VISIBLE_STRING:
- case CERT_RDN_GENERAL_STRING:
- len = pValue->cbData;
- if (pValue->cbData && isspace(pValue->pbData[0]))
- needsQuotes = TRUE;
- if (pValue->cbData && isspace(pValue->pbData[pValue->cbData - 1]))
- needsQuotes = TRUE;
- for (i = 0; i < pValue->cbData; i++)
- {
- if (is_quotable_char(pValue->pbData[i]))
- needsQuotes = TRUE;
- if (pValue->pbData[i] == '"')
- len += 1;
- }
- if (needsQuotes)
- len += 2;
- if (!psz || !csz)
- ret = len;
- else
- {
- char *ptr = psz;
-
- if (needsQuotes)
- *ptr++ = '"';
- for (i = 0; i < pValue->cbData && ptr - psz < csz; ptr++, i++)
- {
- *ptr = pValue->pbData[i];
- if (pValue->pbData[i] == '"' && ptr - psz < csz - 1)
- *(++ptr) = '"';
- }
- if (needsQuotes && ptr - psz < csz)
- *ptr++ = '"';
- ret = ptr - psz;
- }
- break;
- case CERT_RDN_BMP_STRING:
- case CERT_RDN_UTF8_STRING:
- len = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)pValue->pbData,
- pValue->cbData / sizeof(WCHAR), NULL, 0, NULL, NULL);
- if (pValue->cbData && iswspace(((LPCWSTR)pValue->pbData)[0]))
- needsQuotes = TRUE;
- if (pValue->cbData &&
- iswspace(((LPCWSTR)pValue->pbData)[pValue->cbData / sizeof(WCHAR)-1]))
- needsQuotes = TRUE;
- for (i = 0; i < pValue->cbData / sizeof(WCHAR); i++)
- {
- if (is_quotable_char(((LPCWSTR)pValue->pbData)[i]))
- needsQuotes = TRUE;
- if (((LPCWSTR)pValue->pbData)[i] == '"')
- len += 1;
- }
- if (needsQuotes)
- len += 2;
- if (!psz || !csz)
- ret = len;
- else
- {
- char *dst = psz;
-
- if (needsQuotes)
- *dst++ = '"';
- for (i = 0; i < pValue->cbData / sizeof(WCHAR) &&
- dst - psz < csz; dst++, i++)
- {
- LPCWSTR src = (LPCWSTR)pValue->pbData + i;
-
- WideCharToMultiByte(CP_ACP, 0, src, 1, dst,
- csz - (dst - psz) - 1, NULL, NULL);
- if (*src == '"' && dst - psz < csz - 1)
- *(++dst) = '"';
- }
- if (needsQuotes && dst - psz < csz)
- *dst++ = '"';
- ret = dst - psz;
- }
- break;
- default:
- FIXME("string type %ld unimplemented\n", dwValueType);
- }
- if (psz && csz)
- {
- *(psz + ret) = '\0';
- csz--;
- ret++;
- }
- else
- ret++;
- TRACE("returning %ld (%s)\n", ret, debugstr_a(psz));
- return ret;
-}
-
static DWORD quote_rdn_value_to_str_w(DWORD dwValueType,
PCERT_RDN_VALUE_BLOB pValue, LPWSTR psz, DWORD csz)
{
@@ -345,136 +236,37 @@ static DWORD quote_rdn_value_to_str_w(DWORD dwValueType,
return ret;
}
-/* Adds the prefix prefix to the string pointed to by psz, followed by the
- * character '='. Copies no more than csz characters. Returns the number of
- * characters copied. If psz is NULL, returns the number of characters that
- * would be copied.
- */
-static DWORD CRYPT_AddPrefixA(LPCSTR prefix, LPSTR psz, DWORD csz)
+DWORD WINAPI CertNameToStrA(DWORD encoding_type, PCERT_NAME_BLOB name_blob, DWORD str_type, LPSTR str, DWORD str_len)
{
- DWORD chars;
+ DWORD len, len_mb, ret;
+ LPWSTR strW;
- TRACE("(%s, %p, %ld)\n", debugstr_a(prefix), psz, csz);
+ TRACE("(%ld, %p, %08lx, %p, %ld)\n", encoding_type, name_blob, str_type, str, str_len);
- if (psz)
+ len = CertNameToStrW(encoding_type, name_blob, str_type, NULL, 0);
+
+ if (!(strW = CryptMemAlloc(len * sizeof(*strW))))
{
- chars = min(strlen(prefix), csz);
- memcpy(psz, prefix, chars);
- *(psz + chars) = '=';
- chars++;
+ ERR("No memory.\n");
+ if (str && str_len) *str = 0;
+ return 1;
}
- else
- chars = lstrlenA(prefix) + 1;
- return chars;
-}
-DWORD WINAPI CertNameToStrA(DWORD dwCertEncodingType, PCERT_NAME_BLOB pName,
- DWORD dwStrType, LPSTR psz, DWORD csz)
-{
- static const DWORD unsupportedFlags = CERT_NAME_STR_NO_QUOTING_FLAG |
- CERT_NAME_STR_ENABLE_T61_UNICODE_FLAG;
- static const char commaSep[] = ", ";
- static const char semiSep[] = "; ";
- static const char crlfSep[] = "\r\n";
- static const char plusSep[] = " + ";
- static const char spaceSep[] = " ";
- DWORD ret = 0, bytes = 0;
- BOOL bRet;
- CERT_NAME_INFO *info;
-
- TRACE("(%ld, %p, %08lx, %p, %ld)\n", dwCertEncodingType, pName, dwStrType,
- psz, csz);
- if (dwStrType & unsupportedFlags)
- FIXME("unsupported flags: %08lx\n", dwStrType & unsupportedFlags);
-
- bRet = CryptDecodeObjectEx(dwCertEncodingType, X509_NAME, pName->pbData,
- pName->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &bytes);
- if (bRet)
+ len = CertNameToStrW(encoding_type, name_blob, str_type, strW, len);
+ len_mb = WideCharToMultiByte(CP_ACP, 0, strW, len, NULL, 0, NULL, NULL);
+ if (!str || !str_len)
{
- DWORD i, j, sepLen, rdnSepLen;
- LPCSTR sep, rdnSep;
- BOOL reverse = dwStrType & CERT_NAME_STR_REVERSE_FLAG;
- const CERT_RDN *rdn = info->rgRDN;
-
- if(reverse && info->cRDN > 1) rdn += (info->cRDN - 1);
-
- if (dwStrType & CERT_NAME_STR_SEMICOLON_FLAG)
- sep = semiSep;
- else if (dwStrType & CERT_NAME_STR_CRLF_FLAG)
- sep = crlfSep;
- else
- sep = commaSep;
- sepLen = strlen(sep);
- if (dwStrType & CERT_NAME_STR_NO_PLUS_FLAG)
- rdnSep = spaceSep;
- else
- rdnSep = plusSep;
- rdnSepLen = strlen(rdnSep);
- for (i = 0; (!psz || ret < csz) && i < info->cRDN; i++)
- {
- for (j = 0; (!psz || ret < csz) && j < rdn->cRDNAttr; j++)
- {
- DWORD chars;
- char prefixBuf[13]; /* big enough for SERIALNUMBER */
- LPCSTR prefix = NULL;
-
- if ((dwStrType & 0x000000ff) == CERT_OID_NAME_STR)
- prefix = rdn->rgRDNAttr[j].pszObjId;
- else if ((dwStrType & 0x000000ff) == CERT_X500_NAME_STR)
- {
- PCCRYPT_OID_INFO oidInfo = CryptFindOIDInfo(
- CRYPT_OID_INFO_OID_KEY,
- rdn->rgRDNAttr[j].pszObjId,
- CRYPT_RDN_ATTR_OID_GROUP_ID);
-
- if (oidInfo)
- {
- WideCharToMultiByte(CP_ACP, 0, oidInfo->pwszName, -1,
- prefixBuf, sizeof(prefixBuf), NULL, NULL);
- prefix = prefixBuf;
- }
- else
- prefix = rdn->rgRDNAttr[j].pszObjId;
- }
- if (prefix)
- {
- /* - 1 is needed to account for the NULL terminator. */
- chars = CRYPT_AddPrefixA(prefix,
- psz ? psz + ret : NULL, psz ? csz - ret - 1 : 0);
- ret += chars;
- }
- chars = quote_rdn_value_to_str_a(
- rdn->rgRDNAttr[j].dwValueType,
- &rdn->rgRDNAttr[j].Value, psz ? psz + ret : NULL,
- psz ? csz - ret : 0);
- if (chars)
- ret += chars - 1;
- if (j < rdn->cRDNAttr - 1)
- {
- if (psz && ret < csz - rdnSepLen - 1)
- memcpy(psz + ret, rdnSep, rdnSepLen);
- ret += rdnSepLen;
- }
- }
- if (i < info->cRDN - 1)
- {
- if (psz && ret < csz - sepLen - 1)
- memcpy(psz + ret, sep, sepLen);
- ret += sepLen;
- }
- if(reverse) rdn--;
- else rdn++;
- }
- LocalFree(info);
+ CryptMemFree(strW);
+ return len_mb;
}
- if (psz && csz)
+
+ ret = WideCharToMultiByte(CP_ACP, 0, strW, len, str, str_len, NULL, NULL);
+ if (ret < len_mb)
{
- *(psz + ret) = '\0';
- ret++;
+ str[0] = 0;
+ ret = 1;
}
- else
- ret++;
- TRACE("Returning %s\n", debugstr_a(psz));
+ CryptMemFree(strW);
return ret;
}
diff --git a/dlls/crypt32/tests/str.c b/dlls/crypt32/tests/str.c
index be95a796846..d2106b728a9 100644
--- a/dlls/crypt32/tests/str.c
+++ b/dlls/crypt32/tests/str.c
@@ -276,24 +276,27 @@ static void test_CertRDNValueToStrW(void)
wine_dbgstr_w(ePKIW), wine_dbgstr_w(buffer));
}
-static void test_NameToStrConversionA(PCERT_NAME_BLOB pName, DWORD dwStrType,
- LPCSTR expected, BOOL todo)
+#define test_NameToStrConversionA(a, b, c) test_NameToStrConversionA_(__LINE__, a, b, c)
+static void test_NameToStrConversionA_(unsigned int line, PCERT_NAME_BLOB pName, DWORD dwStrType, LPCSTR expected)
{
- char buffer[2000] = { 0 };
- DWORD i;
-
- i = CertNameToStrA(X509_ASN_ENCODING, pName, dwStrType, NULL, 0);
- todo_wine_if (todo)
- ok(i == strlen(expected) + 1, "Expected %d chars, got %ld\n",
- lstrlenA(expected) + 1, i);
- i = CertNameToStrA(X509_ASN_ENCODING,pName, dwStrType, buffer,
- sizeof(buffer));
- todo_wine_if (todo)
- ok(i == strlen(expected) + 1, "Expected %d chars, got %ld\n",
- lstrlenA(expected) + 1, i);
- todo_wine_if (todo)
- ok(!strcmp(buffer, expected), "Expected %s, got %s\n", expected,
- buffer);
+ char buffer[2000];
+ DWORD len, retlen;
+
+ len = CertNameToStrA(X509_ASN_ENCODING, pName, dwStrType, NULL, 0);
+ ok(len == strlen(expected) + 1, "line %u: Expected %d chars, got %ld.\n", line, lstrlenA(expected) + 1, len);
+ len = CertNameToStrA(X509_ASN_ENCODING,pName, dwStrType, buffer, sizeof(buffer));
+ ok(len == strlen(expected) + 1, "line %u: Expected %d chars, got %ld.\n", line, lstrlenA(expected) + 1, len);
+ ok(!strcmp(buffer, expected), "line %u: Expected %s, got %s.\n", line, expected, buffer);
+
+ memset(buffer, 0xcc, sizeof(buffer));
+ retlen = CertNameToStrA(X509_ASN_ENCODING, pName, dwStrType, buffer, len - 1);
+ ok(retlen == 1, "line %u: expected 1, got %lu\n", line, retlen);
+ ok(!buffer[0], "line %u: string is not zero terminated.\n", line);
+
+ memset(buffer, 0xcc, sizeof(buffer));
+ retlen = CertNameToStrA(X509_ASN_ENCODING, pName, dwStrType, buffer, 0);
+ ok(retlen == len, "line %u: expected %lu chars, got %lu\n", line, len - 1, retlen);
+ ok((unsigned char)buffer[0] == 0xcc, "line %u: got %s\n", line, wine_dbgstr_a(buffer));
}
static BYTE encodedSimpleCN[] = {
@@ -366,79 +369,71 @@ static void test_CertNameToStrA(void)
"Expected positive return and ERROR_SUCCESS, got %ld - %08lx\n",
ret, GetLastError());
+ test_NameToStrConversionA(&context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, issuerStr);
test_NameToStrConversionA(&context->pCertInfo->Issuer,
- CERT_SIMPLE_NAME_STR, issuerStr, FALSE);
- test_NameToStrConversionA(&context->pCertInfo->Issuer,
- CERT_SIMPLE_NAME_STR | CERT_NAME_STR_SEMICOLON_FLAG,
- issuerStrSemicolon, FALSE);
+ CERT_SIMPLE_NAME_STR | CERT_NAME_STR_SEMICOLON_FLAG, issuerStrSemicolon);
test_NameToStrConversionA(&context->pCertInfo->Issuer,
- CERT_SIMPLE_NAME_STR | CERT_NAME_STR_CRLF_FLAG,
- issuerStrCRLF, FALSE);
+ CERT_SIMPLE_NAME_STR | CERT_NAME_STR_CRLF_FLAG, issuerStrCRLF);
+ test_NameToStrConversionA(&context->pCertInfo->Subject, CERT_OID_NAME_STR, subjectStr);
test_NameToStrConversionA(&context->pCertInfo->Subject,
- CERT_OID_NAME_STR, subjectStr, FALSE);
+ CERT_OID_NAME_STR | CERT_NAME_STR_SEMICOLON_FLAG, subjectStrSemicolon);
test_NameToStrConversionA(&context->pCertInfo->Subject,
- CERT_OID_NAME_STR | CERT_NAME_STR_SEMICOLON_FLAG,
- subjectStrSemicolon, FALSE);
- test_NameToStrConversionA(&context->pCertInfo->Subject,
- CERT_OID_NAME_STR | CERT_NAME_STR_CRLF_FLAG,
- subjectStrCRLF, FALSE);
+ CERT_OID_NAME_STR | CERT_NAME_STR_CRLF_FLAG, subjectStrCRLF);
test_NameToStrConversionA(&context->pCertInfo->Subject,
- CERT_X500_NAME_STR, x500SubjectStr, FALSE);
+ CERT_X500_NAME_STR, x500SubjectStr);
test_NameToStrConversionA(&context->pCertInfo->Subject,
CERT_X500_NAME_STR | CERT_NAME_STR_SEMICOLON_FLAG | CERT_NAME_STR_REVERSE_FLAG,
- x500SubjectStrSemicolonReverse, FALSE);
+ x500SubjectStrSemicolonReverse);
CertFreeCertificateContext(context);
}
blob.pbData = encodedSimpleCN;
blob.cbData = sizeof(encodedSimpleCN);
- test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=1", FALSE);
+ test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=1");
blob.pbData = encodedSingleQuotedCN;
blob.cbData = sizeof(encodedSingleQuotedCN);
- test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN='1'", FALSE);
- test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "'1'", FALSE);
+ test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN='1'");
+ test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "'1'");
blob.pbData = encodedSpacedCN;
blob.cbData = sizeof(encodedSpacedCN);
- test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\" 1 \"", FALSE);
- test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\" 1 \"", FALSE);
+ test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\" 1 \"");
+ test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\" 1 \"");
blob.pbData = encodedQuotedCN;
blob.cbData = sizeof(encodedQuotedCN);
- test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\"\"\"1\"\"\"",
- FALSE);
- test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\"\"\"1\"\"\"",
- FALSE);
+ test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\"\"\"1\"\"\"");
+ test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\"\"\"1\"\"\"");
blob.pbData = encodedMultipleAttrCN;
blob.cbData = sizeof(encodedMultipleAttrCN);
- test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\"1+2\"", FALSE);
- test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\"1+2\"", FALSE);
+ test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\"1+2\"");
+ test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\"1+2\"");
blob.pbData = encodedCommaCN;
blob.cbData = sizeof(encodedCommaCN);
- test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\"a,b\"", FALSE);
- test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\"a,b\"", FALSE);
+ test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\"a,b\"");
+ test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\"a,b\"");
blob.pbData = encodedEqualCN;
blob.cbData = sizeof(encodedEqualCN);
- test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\"a=b\"", FALSE);
- test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\"a=b\"", FALSE);
+ test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\"a=b\"");
+ test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\"a=b\"");
blob.pbData = encodedLessThanCN;
blob.cbData = sizeof(encodedLessThanCN);
- test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\"<\"", FALSE);
- test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\"<\"", FALSE);
+ test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\"<\"");
+ test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\"<\"");
blob.pbData = encodedGreaterThanCN;
blob.cbData = sizeof(encodedGreaterThanCN);
- test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\">\"", FALSE);
- test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\">\"", FALSE);
+ test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\">\"");
+ test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\">\"");
blob.pbData = encodedHashCN;
blob.cbData = sizeof(encodedHashCN);
- test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\"#\"", FALSE);
- test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\"#\"", FALSE);
+ test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\"#\"");
+ test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\"#\"");
blob.pbData = encodedSemiCN;
blob.cbData = sizeof(encodedSemiCN);
- test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\";\"", FALSE);
- test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\";\"", FALSE);
+ test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\";\"");
+ test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\";\"");
blob.pbData = encodedNewlineCN;
blob.cbData = sizeof(encodedNewlineCN);
- test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\"a\nb\"", FALSE);
- test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\"a\nb\"", FALSE);
+ test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\"a\nb\"");
+ test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\"a\nb\"");
}
#define test_NameToStrConversionW(a, b, c) test_NameToStrConversionW_(__LINE__, a, b, c)
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/12
April 28, 2022
[PATCH 6/7] crypt32: Reimplement CertRDNValueToStrA() on top of CertRDNValueToStrW().
by Paul Gofman
From: Paul Gofman <pgofman(a)codeweavers.com>
---
dlls/crypt32/str.c | 80 ++++++++++++----------------------------
dlls/crypt32/tests/str.c | 52 ++++++++++++++------------
2 files changed, 53 insertions(+), 79 deletions(-)
diff --git a/dlls/crypt32/str.c b/dlls/crypt32/str.c
index 5ddad1ff4e6..29882ab771e 100644
--- a/dlls/crypt32/str.c
+++ b/dlls/crypt32/str.c
@@ -29,70 +29,38 @@
WINE_DEFAULT_DEBUG_CHANNEL(crypt);
-DWORD WINAPI CertRDNValueToStrA(DWORD dwValueType, PCERT_RDN_VALUE_BLOB pValue,
- LPSTR psz, DWORD csz)
+DWORD WINAPI CertRDNValueToStrA(DWORD type, PCERT_RDN_VALUE_BLOB value_blob,
+ LPSTR value, DWORD value_len)
{
- DWORD ret = 0, len;
+ DWORD len, len_mb, ret;
+ LPWSTR valueW;
- TRACE("(%ld, %p, %p, %ld)\n", dwValueType, pValue, psz, csz);
+ TRACE("(%ld, %p, %p, %ld)\n", type, value_blob, value, value_len);
- switch (dwValueType)
- {
- case CERT_RDN_ANY_TYPE:
- break;
- case CERT_RDN_NUMERIC_STRING:
- case CERT_RDN_PRINTABLE_STRING:
- case CERT_RDN_TELETEX_STRING:
- case CERT_RDN_VIDEOTEX_STRING:
- case CERT_RDN_IA5_STRING:
- case CERT_RDN_GRAPHIC_STRING:
- case CERT_RDN_VISIBLE_STRING:
- case CERT_RDN_GENERAL_STRING:
- len = pValue->cbData;
- if (!psz || !csz)
- ret = len;
- else
- {
- DWORD chars = min(len, csz - 1);
+ len = CertRDNValueToStrW(type, value_blob, NULL, 0);
- if (chars)
- {
- memcpy(psz, pValue->pbData, chars);
- ret += chars;
- csz -= chars;
- }
- }
- break;
- case CERT_RDN_BMP_STRING:
- case CERT_RDN_UTF8_STRING:
- len = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)pValue->pbData,
- pValue->cbData / sizeof(WCHAR), NULL, 0, NULL, NULL);
- if (!psz || !csz)
- ret = len;
- else
- {
- DWORD chars = min(pValue->cbData / sizeof(WCHAR), csz - 1);
+ if (!(valueW = CryptMemAlloc(len * sizeof(*valueW))))
+ {
+ ERR("No memory.\n");
+ if (value && value_len) *value = 0;
+ return 1;
+ }
- if (chars)
- {
- ret = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)pValue->pbData,
- chars, psz, csz - 1, NULL, NULL);
- csz -= ret;
- }
- }
- break;
- default:
- FIXME("string type %ld unimplemented\n", dwValueType);
+ len = CertRDNValueToStrW(type, value_blob, valueW, len);
+ len_mb = WideCharToMultiByte(CP_ACP, 0, valueW, len, NULL, 0, NULL, NULL);
+ if (!value || !value_len)
+ {
+ CryptMemFree(valueW);
+ return len_mb;
}
- if (psz && csz)
+
+ ret = WideCharToMultiByte(CP_ACP, 0, valueW, len, value, value_len, NULL, NULL);
+ if (ret < len_mb)
{
- *(psz + ret) = '\0';
- csz--;
- ret++;
+ value[0] = 0;
+ ret = 1;
}
- else
- ret++;
- TRACE("returning %ld (%s)\n", ret, debugstr_a(psz));
+ CryptMemFree(valueW);
return ret;
}
diff --git a/dlls/crypt32/tests/str.c b/dlls/crypt32/tests/str.c
index 1cfdf8ee7b7..be95a796846 100644
--- a/dlls/crypt32/tests/str.c
+++ b/dlls/crypt32/tests/str.c
@@ -31,7 +31,6 @@ typedef struct _CertRDNAttrEncoding {
DWORD dwValueType;
CERT_RDN_VALUE_BLOB Value;
LPCSTR str;
- BOOL todo;
} CertRDNAttrEncoding, *PCertRDNAttrEncoding;
typedef struct _CertRDNAttrEncodingW {
@@ -133,33 +132,34 @@ static void test_CertRDNValueToStrA(void)
{
CertRDNAttrEncoding attrs[] = {
{ "2.5.4.6", CERT_RDN_PRINTABLE_STRING,
- { sizeof(bin1), bin1 }, "US", FALSE },
+ { sizeof(bin1), bin1 }, "US" },
{ "2.5.4.8", CERT_RDN_PRINTABLE_STRING,
- { sizeof(bin2), bin2 }, "Minnesota", FALSE },
+ { sizeof(bin2), bin2 }, "Minnesota" },
{ "2.5.4.7", CERT_RDN_PRINTABLE_STRING,
- { sizeof(bin3), bin3 }, "Minneapolis", FALSE },
+ { sizeof(bin3), bin3 }, "Minneapolis" },
{ "2.5.4.10", CERT_RDN_PRINTABLE_STRING,
- { sizeof(bin4), bin4 }, "CodeWeavers", FALSE },
+ { sizeof(bin4), bin4 }, "CodeWeavers" },
{ "2.5.4.11", CERT_RDN_PRINTABLE_STRING,
- { sizeof(bin5), bin5 }, "Wine Development", FALSE },
+ { sizeof(bin5), bin5 }, "Wine Development" },
{ "2.5.4.3", CERT_RDN_PRINTABLE_STRING,
- { sizeof(bin6), bin6 }, "localhost", FALSE },
+ { sizeof(bin6), bin6 }, "localhost" },
{ "1.2.840.113549.1.9.1", CERT_RDN_IA5_STRING,
- { sizeof(bin7), bin7 }, "aric(a)codeweavers.com", FALSE },
+ { sizeof(bin7), bin7 }, "aric(a)codeweavers.com" },
{ "0", CERT_RDN_PRINTABLE_STRING,
- { sizeof(bin9), bin9 }, "abc\"def", FALSE },
+ { sizeof(bin9), bin9 }, "abc\"def" },
{ "0", CERT_RDN_PRINTABLE_STRING,
- { sizeof(bin10), bin10 }, "abc'def", FALSE },
+ { sizeof(bin10), bin10 }, "abc'def" },
{ "0", CERT_RDN_PRINTABLE_STRING,
- { sizeof(bin11), bin11 }, "abc, def", FALSE },
+ { sizeof(bin11), bin11 }, "abc, def" },
{ "0", CERT_RDN_PRINTABLE_STRING,
- { sizeof(bin12), bin12 }, " abc ", FALSE },
+ { sizeof(bin12), bin12 }, " abc " },
{ "0", CERT_RDN_PRINTABLE_STRING,
- { sizeof(bin13), bin13 }, "\"def\"", FALSE },
+ { sizeof(bin13), bin13 }, "\"def\"" },
{ "0", CERT_RDN_PRINTABLE_STRING,
- { sizeof(bin14), bin14 }, "1;3", FALSE },
+ { sizeof(bin14), bin14 }, "1;3" },
};
- DWORD i, ret;
+ unsigned int i;
+ DWORD ret, len;
char buffer[2000];
CERT_RDN_VALUE_BLOB blob = { 0, NULL };
static const char ePKI[] = "ePKI Root Certification Authority";
@@ -177,15 +177,21 @@ static void test_CertRDNValueToStrA(void)
for (i = 0; i < ARRAY_SIZE(attrs); i++)
{
- ret = CertRDNValueToStrA(attrs[i].dwValueType, &attrs[i].Value,
+ len = CertRDNValueToStrA(attrs[i].dwValueType, &attrs[i].Value,
buffer, sizeof(buffer));
- todo_wine_if (attrs[i].todo)
- {
- ok(ret == strlen(attrs[i].str) + 1, "Expected length %d, got %ld\n",
- lstrlenA(attrs[i].str) + 1, ret);
- ok(!strcmp(buffer, attrs[i].str), "Expected %s, got %s\n",
- attrs[i].str, buffer);
- }
+ ok(len == strlen(attrs[i].str) + 1, "Expected length %d, got %ld\n",
+ lstrlenA(attrs[i].str) + 1, ret);
+ ok(!strcmp(buffer, attrs[i].str), "Expected %s, got %s\n",
+ attrs[i].str, buffer);
+ memset(buffer, 0xcc, sizeof(buffer));
+ ret = CertRDNValueToStrA(attrs[i].dwValueType, &attrs[i].Value, buffer, len - 1);
+ ok(ret == 1, "Unexpected ret %lu, expected 1, test %u.\n", ret, i);
+ ok(!buffer[0], "Unexpected value %#x, test %u.\n", buffer[0], i);
+ ok(!strncmp(buffer + 1, attrs[i].str + 1, len - 2), "Strings do not match, test %u.\n", i);
+ memset(buffer, 0xcc, sizeof(buffer));
+ ret = CertRDNValueToStrA(attrs[i].dwValueType, &attrs[i].Value, buffer, 0);
+ ok(ret == len, "Unexpected ret %lu, expected %lu, test %u.\n", ret, len, i);
+ ok((unsigned char)buffer[0] == 0xcc, "Unexpected value %#x, test %u.\n", buffer[0], i);
}
blob.pbData = bin8;
blob.cbData = sizeof(bin8);
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/12
April 28, 2022
[PATCH 5/7] crypt32: Fix filling short output in CertGetNameStringA().
by Paul Gofman
From: Paul Gofman <pgofman(a)codeweavers.com>
---
dlls/crypt32/str.c | 60 +++++++++++++++++-----------------------
dlls/crypt32/tests/str.c | 8 ++++--
2 files changed, 32 insertions(+), 36 deletions(-)
diff --git a/dlls/crypt32/str.c b/dlls/crypt32/str.c
index ad0c71d697f..5ddad1ff4e6 100644
--- a/dlls/crypt32/str.c
+++ b/dlls/crypt32/str.c
@@ -1110,46 +1110,38 @@ BOOL WINAPI CertStrToNameW(DWORD dwCertEncodingType, LPCWSTR pszX500,
return ret;
}
-DWORD WINAPI CertGetNameStringA(PCCERT_CONTEXT pCertContext, DWORD dwType,
- DWORD dwFlags, void *pvTypePara, LPSTR pszNameString, DWORD cchNameString)
+DWORD WINAPI CertGetNameStringA(PCCERT_CONTEXT cert, DWORD type,
+ DWORD flags, void *type_para, LPSTR name, DWORD name_len)
{
- DWORD ret;
+ DWORD len, len_mb, ret;
+ LPWSTR nameW;
- TRACE("(%p, %ld, %08lx, %p, %p, %ld)\n", pCertContext, dwType, dwFlags,
- pvTypePara, pszNameString, cchNameString);
+ TRACE("(%p, %ld, %08lx, %p, %p, %ld)\n", cert, type, flags, type_para, name, name_len);
- if (pszNameString)
+ len = CertGetNameStringW(cert, type, flags, type_para, NULL, 0);
+
+ if (!(nameW = CryptMemAlloc(len * sizeof(*nameW))))
{
- LPWSTR wideName;
- DWORD nameLen;
+ ERR("No memory.\n");
+ if (name && name_len) *name = 0;
+ return 1;
+ }
- nameLen = CertGetNameStringW(pCertContext, dwType, dwFlags, pvTypePara,
- NULL, 0);
- wideName = CryptMemAlloc(nameLen * sizeof(WCHAR));
- if (wideName)
- {
- CertGetNameStringW(pCertContext, dwType, dwFlags, pvTypePara,
- wideName, nameLen);
- nameLen = WideCharToMultiByte(CP_ACP, 0, wideName, nameLen,
- pszNameString, cchNameString, NULL, NULL);
- if (nameLen <= cchNameString)
- ret = nameLen;
- else
- {
- pszNameString[cchNameString - 1] = '\0';
- ret = cchNameString;
- }
- CryptMemFree(wideName);
- }
- else
- {
- *pszNameString = '\0';
- ret = 1;
- }
+ len = CertGetNameStringW(cert, type, flags, type_para, nameW, len);
+ len_mb = WideCharToMultiByte(CP_ACP, 0, nameW, len, NULL, 0, NULL, NULL);
+ if (!name || !name_len)
+ {
+ CryptMemFree(nameW);
+ return len_mb;
}
- else
- ret = CertGetNameStringW(pCertContext, dwType, dwFlags, pvTypePara,
- NULL, 0);
+
+ ret = WideCharToMultiByte(CP_ACP, 0, nameW, len, name, name_len, NULL, NULL);
+ if (ret < len_mb)
+ {
+ name[0] = 0;
+ ret = 1;
+ }
+ CryptMemFree(nameW);
return ret;
}
diff --git a/dlls/crypt32/tests/str.c b/dlls/crypt32/tests/str.c
index 3009fb72c03..1cfdf8ee7b7 100644
--- a/dlls/crypt32/tests/str.c
+++ b/dlls/crypt32/tests/str.c
@@ -766,9 +766,13 @@ static void test_CertGetNameString_value_(unsigned int line, PCCERT_CONTEXT cont
ok(!strcmp(str, expected), "line %u: unexpected value %s.\n", line, str);
str[0] = str[1] = 0xcc;
retlen = CertGetNameStringA(context, type, 0, type_para, str, len - 1);
- todo_wine ok(retlen == 1, "line %u: Unexpected len %lu, expected 1.\n", line, retlen);
- todo_wine ok(!str[0], "line %u: unexpected str[0] %#x.\n", line, str[0]);
+ ok(retlen == 1, "line %u: Unexpected len %lu, expected 1.\n", line, retlen);
+ ok(!str[0], "line %u: unexpected str[0] %#x.\n", line, str[0]);
ok(str[1] == expected[1], "line %u: unexpected str[1] %#x.\n", line, str[1]);
+
+ retlen = CertGetNameStringA(context, type, 0, type_para, str, 0);
+ ok(retlen == len, "line %u: Unexpected len %lu, expected 1.\n", line, retlen);
+
retlen = CertGetNameStringW(context, type, 0, type_para, strW, len);
ok(retlen == len, "line %u: unexpected len %lu, expected 1.\n", line, retlen);
ok(!wcscmp(strW, expectedW), "line %u: unexpected value %s.\n", line, debugstr_w(strW));
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/12
April 28, 2022
[PATCH 4/7] crypt32: Fix filling short output in CertGetNameStringW().
by Paul Gofman
From: Paul Gofman <pgofman(a)codeweavers.com>
---
dlls/crypt32/str.c | 100 +++++++++++++++------------------------
dlls/crypt32/tests/str.c | 3 +-
2 files changed, 39 insertions(+), 64 deletions(-)
diff --git a/dlls/crypt32/str.c b/dlls/crypt32/str.c
index 732dcb5ae33..ad0c71d697f 100644
--- a/dlls/crypt32/str.c
+++ b/dlls/crypt32/str.c
@@ -96,8 +96,8 @@ DWORD WINAPI CertRDNValueToStrA(DWORD dwValueType, PCERT_RDN_VALUE_BLOB pValue,
return ret;
}
-DWORD WINAPI CertRDNValueToStrW(DWORD dwValueType, PCERT_RDN_VALUE_BLOB pValue,
- LPWSTR psz, DWORD csz)
+static DWORD rdn_value_to_strW(DWORD dwValueType, PCERT_RDN_VALUE_BLOB pValue,
+ LPWSTR psz, DWORD csz, BOOL partial_copy)
{
DWORD ret = 0, len, i;
@@ -117,8 +117,9 @@ DWORD WINAPI CertRDNValueToStrW(DWORD dwValueType, PCERT_RDN_VALUE_BLOB pValue,
case CERT_RDN_GENERAL_STRING:
len = pValue->cbData;
if (!psz || !csz) ret = len;
- else if (len < csz)
+ else if (len < csz || partial_copy)
{
+ len = min(len, csz - 1);
for (i = 0; i < len; ++i)
psz[i] = pValue->pbData[i];
ret = len;
@@ -129,10 +130,11 @@ DWORD WINAPI CertRDNValueToStrW(DWORD dwValueType, PCERT_RDN_VALUE_BLOB pValue,
len = pValue->cbData / sizeof(WCHAR);
if (!psz || !csz)
ret = len;
- else if (len < csz)
+ else if (len < csz || partial_copy)
{
WCHAR *ptr = psz;
+ len = min(len, csz - 1);
for (i = 0; i < len; ++i)
ptr[i] = ((LPCWSTR)pValue->pbData)[i];
ret = len;
@@ -146,6 +148,12 @@ DWORD WINAPI CertRDNValueToStrW(DWORD dwValueType, PCERT_RDN_VALUE_BLOB pValue,
return ret + 1;
}
+DWORD WINAPI CertRDNValueToStrW(DWORD dwValueType, PCERT_RDN_VALUE_BLOB pValue,
+ LPWSTR psz, DWORD csz)
+{
+ return rdn_value_to_strW(dwValueType, pValue, psz, csz, FALSE);
+}
+
static inline BOOL is_quotable_char(WCHAR c)
{
switch(c)
@@ -1196,13 +1204,24 @@ static DWORD cert_get_name_from_rdn_attr(DWORD encodingType,
oid = szOID_RSA_emailAddr;
nameAttr = CertFindRDNAttr(oid, nameInfo);
if (nameAttr)
- ret = CertRDNValueToStrW(nameAttr->dwValueType, &nameAttr->Value,
- pszNameString, cchNameString);
+ ret = rdn_value_to_strW(nameAttr->dwValueType, &nameAttr->Value,
+ pszNameString, cchNameString, TRUE);
LocalFree(nameInfo);
}
return ret;
}
+static DWORD copy_output_str(WCHAR *dst, const WCHAR *src, DWORD dst_size)
+{
+ DWORD len = wcslen(src);
+
+ if (!dst || !dst_size) return len + 1;
+ len = min(len, dst_size - 1);
+ memcpy(dst, src, len * sizeof(*dst));
+ dst[len] = 0;
+ return len + 1;
+}
+
DWORD WINAPI CertGetNameStringW(PCCERT_CONTEXT pCertContext, DWORD dwType,
DWORD dwFlags, void *pvTypePara, LPWSTR pszNameString, DWORD cchNameString)
{
@@ -1235,23 +1254,14 @@ DWORD WINAPI CertGetNameStringW(PCCERT_CONTEXT pCertContext, DWORD dwType,
PCERT_ALT_NAME_ENTRY entry = cert_find_alt_name_entry(pCertContext,
altNameOID, CERT_ALT_NAME_RFC822_NAME, &info);
- if (entry)
- {
- if (!pszNameString)
- ret = lstrlenW(entry->u.pwszRfc822Name) + 1;
- else if (cchNameString)
- {
- ret = min(lstrlenW(entry->u.pwszRfc822Name), cchNameString - 1);
- memcpy(pszNameString, entry->u.pwszRfc822Name,
- ret * sizeof(WCHAR));
- pszNameString[ret++] = 0;
- }
- }
+ if (entry) ret = copy_output_str(pszNameString, entry->u.pwszRfc822Name, cchNameString);
if (info)
LocalFree(info);
if (!ret)
+ {
ret = cert_get_name_from_rdn_attr(pCertContext->dwCertEncodingType,
name, szOID_RSA_emailAddr, pszNameString, cchNameString);
+ }
break;
}
case CERT_NAME_RDN_TYPE:
@@ -1308,8 +1318,8 @@ DWORD WINAPI CertGetNameStringW(PCCERT_CONTEXT pCertContext, DWORD dwType,
for (i = 0; !nameAttr && i < ARRAY_SIZE(simpleAttributeOIDs); i++)
nameAttr = CertFindRDNAttr(simpleAttributeOIDs[i], nameInfo);
if (nameAttr)
- ret = CertRDNValueToStrW(nameAttr->dwValueType,
- &nameAttr->Value, pszNameString, cchNameString);
+ ret = rdn_value_to_strW(nameAttr->dwValueType,
+ &nameAttr->Value, pszNameString, cchNameString, TRUE);
LocalFree(nameInfo);
}
if (!ret)
@@ -1322,19 +1332,7 @@ DWORD WINAPI CertGetNameStringW(PCCERT_CONTEXT pCertContext, DWORD dwType,
{
if (!entry && altInfo->cAltEntry)
entry = &altInfo->rgAltEntry[0];
- if (entry)
- {
- if (!pszNameString)
- ret = lstrlenW(entry->u.pwszRfc822Name) + 1;
- else if (cchNameString)
- {
- ret = min(lstrlenW(entry->u.pwszRfc822Name),
- cchNameString - 1);
- memcpy(pszNameString, entry->u.pwszRfc822Name,
- ret * sizeof(WCHAR));
- pszNameString[ret++] = 0;
- }
- }
+ if (entry) ret = copy_output_str(pszNameString, entry->u.pwszRfc822Name, cchNameString);
LocalFree(altInfo);
}
}
@@ -1359,17 +1357,8 @@ DWORD WINAPI CertGetNameStringW(PCCERT_CONTEXT pCertContext, DWORD dwType,
PCERT_ALT_NAME_ENTRY entry = cert_find_alt_name_entry(pCertContext,
altNameOID, CERT_ALT_NAME_DNS_NAME, &info);
- if (entry)
- {
- if (!pszNameString)
- ret = lstrlenW(entry->u.pwszDNSName) + 1;
- else if (cchNameString)
- {
- ret = min(lstrlenW(entry->u.pwszDNSName), cchNameString - 1);
- memcpy(pszNameString, entry->u.pwszDNSName, ret * sizeof(WCHAR));
- pszNameString[ret++] = 0;
- }
- }
+ if (entry) ret = copy_output_str(pszNameString, entry->u.pwszDNSName, cchNameString);
+
if (info)
LocalFree(info);
if (!ret)
@@ -1383,17 +1372,8 @@ DWORD WINAPI CertGetNameStringW(PCCERT_CONTEXT pCertContext, DWORD dwType,
PCERT_ALT_NAME_ENTRY entry = cert_find_alt_name_entry(pCertContext,
altNameOID, CERT_ALT_NAME_URL, &info);
- if (entry)
- {
- if (!pszNameString)
- ret = lstrlenW(entry->u.pwszURL) + 1;
- else if (cchNameString)
- {
- ret = min(lstrlenW(entry->u.pwszURL), cchNameString - 1);
- memcpy(pszNameString, entry->u.pwszURL, ret * sizeof(WCHAR));
- pszNameString[ret++] = 0;
- }
- }
+ if (entry) ret = copy_output_str(pszNameString, entry->u.pwszURL, cchNameString);
+
if (info)
LocalFree(info);
break;
@@ -1401,17 +1381,13 @@ DWORD WINAPI CertGetNameStringW(PCCERT_CONTEXT pCertContext, DWORD dwType,
default:
FIXME("unimplemented for type %ld\n", dwType);
ret = 0;
+ break;
}
done:
if (!ret)
{
- if (!pszNameString)
- ret = 1;
- else if (cchNameString)
- {
- pszNameString[0] = 0;
- ret = 1;
- }
+ ret = 1;
+ if (pszNameString && cchNameString) pszNameString[0] = 0;
}
return ret;
}
diff --git a/dlls/crypt32/tests/str.c b/dlls/crypt32/tests/str.c
index 62889242e94..3009fb72c03 100644
--- a/dlls/crypt32/tests/str.c
+++ b/dlls/crypt32/tests/str.c
@@ -774,8 +774,7 @@ static void test_CertGetNameString_value_(unsigned int line, PCCERT_CONTEXT cont
ok(!wcscmp(strW, expectedW), "line %u: unexpected value %s.\n", line, debugstr_w(strW));
strW[0] = strW[1] = 0xcccc;
retlen = CertGetNameStringW(context, type, 0, type_para, strW, len - 1);
- todo_wine_if(type != CERT_NAME_RDN_TYPE)
- ok(retlen == len - 1, "line %u: unexpected len %lu, expected %lu.\n", line, retlen, len - 1);
+ ok(retlen == len - 1, "line %u: unexpected len %lu, expected %lu.\n", line, retlen, len - 1);
ok(!wcsncmp(strW, expectedW, retlen - 1), "line %u: string data mismatch.\n", line);
ok(!strW[retlen - 1], "line %u: string is not zero terminated.\n", line);
retlen = CertGetNameStringA(context, type, 0, type_para, NULL, len - 1);
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/12
April 28, 2022
[PATCH 3/7] crypt32: Fix filling short output in CertRDNValueToStrW().
by Paul Gofman
From: Paul Gofman <pgofman(a)codeweavers.com>
---
dlls/crypt32/str.c | 38 +++++++++++------------------
dlls/crypt32/tests/str.c | 52 ++++++++++++++++++++++------------------
2 files changed, 43 insertions(+), 47 deletions(-)
diff --git a/dlls/crypt32/str.c b/dlls/crypt32/str.c
index 8a1684f07ad..732dcb5ae33 100644
--- a/dlls/crypt32/str.c
+++ b/dlls/crypt32/str.c
@@ -99,7 +99,7 @@ DWORD WINAPI CertRDNValueToStrA(DWORD dwValueType, PCERT_RDN_VALUE_BLOB pValue,
DWORD WINAPI CertRDNValueToStrW(DWORD dwValueType, PCERT_RDN_VALUE_BLOB pValue,
LPWSTR psz, DWORD csz)
{
- DWORD ret = 0, len, i, strLen;
+ DWORD ret = 0, len, i;
TRACE("(%ld, %p, %p, %ld)\n", dwValueType, pValue, psz, csz);
@@ -116,44 +116,34 @@ DWORD WINAPI CertRDNValueToStrW(DWORD dwValueType, PCERT_RDN_VALUE_BLOB pValue,
case CERT_RDN_VISIBLE_STRING:
case CERT_RDN_GENERAL_STRING:
len = pValue->cbData;
- if (!psz || !csz)
- ret = len;
- else
+ if (!psz || !csz) ret = len;
+ else if (len < csz)
{
- WCHAR *ptr = psz;
-
- for (i = 0; i < pValue->cbData && ptr - psz < csz; ptr++, i++)
- *ptr = pValue->pbData[i];
- ret = ptr - psz;
+ for (i = 0; i < len; ++i)
+ psz[i] = pValue->pbData[i];
+ ret = len;
}
break;
case CERT_RDN_BMP_STRING:
case CERT_RDN_UTF8_STRING:
- strLen = len = pValue->cbData / sizeof(WCHAR);
+ len = pValue->cbData / sizeof(WCHAR);
if (!psz || !csz)
ret = len;
- else
+ else if (len < csz)
{
WCHAR *ptr = psz;
- for (i = 0; i < strLen && ptr - psz < csz; ptr++, i++)
- *ptr = ((LPCWSTR)pValue->pbData)[i];
- ret = ptr - psz;
+ for (i = 0; i < len; ++i)
+ ptr[i] = ((LPCWSTR)pValue->pbData)[i];
+ ret = len;
}
break;
default:
FIXME("string type %ld unimplemented\n", dwValueType);
}
- if (psz && csz)
- {
- *(psz + ret) = '\0';
- csz--;
- ret++;
- }
- else
- ret++;
- TRACE("returning %ld (%s)\n", ret, debugstr_w(psz));
- return ret;
+ if (psz && csz) psz[ret] = 0;
+ TRACE("returning %ld (%s)\n", ret + 1, debugstr_w(psz));
+ return ret + 1;
}
static inline BOOL is_quotable_char(WCHAR c)
diff --git a/dlls/crypt32/tests/str.c b/dlls/crypt32/tests/str.c
index f2ee5b96853..62889242e94 100644
--- a/dlls/crypt32/tests/str.c
+++ b/dlls/crypt32/tests/str.c
@@ -39,7 +39,6 @@ typedef struct _CertRDNAttrEncodingW {
DWORD dwValueType;
CERT_RDN_VALUE_BLOB Value;
LPCWSTR str;
- BOOL todo;
} CertRDNAttrEncodingW, *PCertRDNAttrEncodingW;
static BYTE bin1[] = { 0x55, 0x53 };
@@ -202,33 +201,34 @@ static void test_CertRDNValueToStrW(void)
static const WCHAR ePKIW[] = L"ePKI Root Certification Authority";
CertRDNAttrEncodingW attrs[] = {
{ "2.5.4.6", CERT_RDN_PRINTABLE_STRING,
- { sizeof(bin1), bin1 }, L"US", FALSE },
+ { sizeof(bin1), bin1 }, L"US" },
{ "2.5.4.8", CERT_RDN_PRINTABLE_STRING,
- { sizeof(bin2), bin2 }, L"Minnesota", FALSE },
+ { sizeof(bin2), bin2 }, L"Minnesota" },
{ "2.5.4.7", CERT_RDN_PRINTABLE_STRING,
- { sizeof(bin3), bin3 }, L"Minneapolis", FALSE },
+ { sizeof(bin3), bin3 }, L"Minneapolis" },
{ "2.5.4.10", CERT_RDN_PRINTABLE_STRING,
- { sizeof(bin4), bin4 }, L"CodeWeavers", FALSE },
+ { sizeof(bin4), bin4 }, L"CodeWeavers" },
{ "2.5.4.11", CERT_RDN_PRINTABLE_STRING,
- { sizeof(bin5), bin5 }, L"Wine Development", FALSE },
+ { sizeof(bin5), bin5 }, L"Wine Development" },
{ "2.5.4.3", CERT_RDN_PRINTABLE_STRING,
- { sizeof(bin6), bin6 }, L"localhost", FALSE },
+ { sizeof(bin6), bin6 }, L"localhost" },
{ "1.2.840.113549.1.9.1", CERT_RDN_IA5_STRING,
- { sizeof(bin7), bin7 }, L"aric(a)codeweavers.com", FALSE },
+ { sizeof(bin7), bin7 }, L"aric(a)codeweavers.com" },
{ "0", CERT_RDN_PRINTABLE_STRING,
- { sizeof(bin9), bin9 }, L"abc\"def", FALSE },
+ { sizeof(bin9), bin9 }, L"abc\"def" },
{ "0", CERT_RDN_PRINTABLE_STRING,
- { sizeof(bin10), bin10 }, L"abc'def", FALSE },
+ { sizeof(bin10), bin10 }, L"abc'def" },
{ "0", CERT_RDN_PRINTABLE_STRING,
- { sizeof(bin11), bin11 }, L"abc, def", FALSE },
+ { sizeof(bin11), bin11 }, L"abc, def" },
{ "0", CERT_RDN_PRINTABLE_STRING,
- { sizeof(bin12), bin12 }, L" abc ", FALSE },
+ { sizeof(bin12), bin12 }, L" abc " },
{ "0", CERT_RDN_PRINTABLE_STRING,
- { sizeof(bin13), bin13 }, L"\"def\"", FALSE },
+ { sizeof(bin13), bin13 }, L"\"def\"" },
{ "0", CERT_RDN_PRINTABLE_STRING,
- { sizeof(bin14), bin14 }, L"1;3", FALSE },
+ { sizeof(bin14), bin14 }, L"1;3" },
};
- DWORD i, ret;
+ unsigned int i;
+ DWORD ret, len;
WCHAR buffer[2000];
CERT_RDN_VALUE_BLOB blob = { 0, NULL };
@@ -245,14 +245,20 @@ static void test_CertRDNValueToStrW(void)
for (i = 0; i < ARRAY_SIZE(attrs); i++)
{
- ret = CertRDNValueToStrW(attrs[i].dwValueType, &attrs[i].Value, buffer, ARRAY_SIZE(buffer));
- todo_wine_if (attrs[i].todo)
- {
- ok(ret == lstrlenW(attrs[i].str) + 1,
- "Expected length %d, got %ld\n", lstrlenW(attrs[i].str) + 1, ret);
- ok(!lstrcmpW(buffer, attrs[i].str), "Expected %s, got %s\n",
- wine_dbgstr_w(attrs[i].str), wine_dbgstr_w(buffer));
- }
+ len = CertRDNValueToStrW(attrs[i].dwValueType, &attrs[i].Value, buffer, ARRAY_SIZE(buffer));
+ ok(len == lstrlenW(attrs[i].str) + 1,
+ "Expected length %d, got %ld\n", lstrlenW(attrs[i].str) + 1, ret);
+ ok(!lstrcmpW(buffer, attrs[i].str), "Expected %s, got %s\n",
+ wine_dbgstr_w(attrs[i].str), wine_dbgstr_w(buffer));
+ memset(buffer, 0xcc, sizeof(buffer));
+ ret = CertRDNValueToStrW(attrs[i].dwValueType, &attrs[i].Value, buffer, len - 1);
+ ok(ret == 1, "Unexpected ret %lu, expected 1, test %u.\n", ret, i);
+ ok(!buffer[0], "Unexpected value %#x, test %u.\n", buffer[0], i);
+ ok(buffer[1] == 0xcccc, "Unexpected value %#x, test %u.\n", buffer[1], i);
+ memset(buffer, 0xcc, sizeof(buffer));
+ ret = CertRDNValueToStrW(attrs[i].dwValueType, &attrs[i].Value, buffer, 0);
+ ok(ret == len, "Unexpected ret %lu, expected %lu, test %u.\n", ret, len, i);
+ ok(buffer[0] == 0xcccc, "Unexpected value %#x, test %u.\n", buffer[0], i);
}
blob.pbData = bin8;
blob.cbData = sizeof(bin8);
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/12
April 28, 2022
[PATCH 2/7] crypt32: Fix filling short output in cert_name_to_str_with_indent().
by Paul Gofman
From: Paul Gofman <pgofman(a)codeweavers.com>
---
dlls/crypt32/str.c | 61 ++++++++++++++++++++--------------------
dlls/crypt32/tests/str.c | 7 +++--
2 files changed, 34 insertions(+), 34 deletions(-)
diff --git a/dlls/crypt32/str.c b/dlls/crypt32/str.c
index 277aeb70d4a..8a1684f07ad 100644
--- a/dlls/crypt32/str.c
+++ b/dlls/crypt32/str.c
@@ -375,14 +375,6 @@ static DWORD quote_rdn_value_to_str_w(DWORD dwValueType,
default:
FIXME("string type %ld unimplemented\n", dwValueType);
}
- if (psz && csz)
- {
- *(psz + ret) = '\0';
- csz--;
- ret++;
- }
- else
- ret++;
TRACE("returning %ld (%s)\n", ret, debugstr_w(psz));
return ret;
}
@@ -580,6 +572,7 @@ DWORD cert_name_to_str_with_indent(DWORD dwCertEncodingType, DWORD indentLevel,
DWORD ret = 0, bytes = 0;
BOOL bRet;
CERT_NAME_INFO *info;
+ DWORD chars;
if (dwStrType & unsupportedFlags)
FIXME("unsupported flags: %08lx\n", dwStrType & unsupportedFlags);
@@ -607,14 +600,17 @@ DWORD cert_name_to_str_with_indent(DWORD dwCertEncodingType, DWORD indentLevel,
else
rdnSep = L" + ";
rdnSepLen = lstrlenW(rdnSep);
- for (i = 0; (!psz || ret < csz) && i < info->cRDN; i++)
+ if (!csz) psz = NULL;
+ for (i = 0; i < info->cRDN; i++)
{
- for (j = 0; (!psz || ret < csz) && j < rdn->cRDNAttr; j++)
+ if (psz && ret + 1 == csz) break;
+ for (j = 0; j < rdn->cRDNAttr; j++)
{
- DWORD chars;
LPCSTR prefixA = NULL;
LPCWSTR prefixW = NULL;
+ if (psz && ret + 1 == csz) break;
+
if ((dwStrType & 0x000000ff) == CERT_OID_NAME_STR)
prefixA = rdn->rgRDNAttr[j].pszObjId;
else if ((dwStrType & 0x000000ff) == CERT_X500_NAME_STR)
@@ -644,6 +640,7 @@ DWORD cert_name_to_str_with_indent(DWORD dwCertEncodingType, DWORD indentLevel,
chars = lstrlenW(indent);
ret += chars;
}
+ if (psz && ret + 1 == csz) break;
}
if (prefixW)
{
@@ -659,38 +656,40 @@ DWORD cert_name_to_str_with_indent(DWORD dwCertEncodingType, DWORD indentLevel,
psz ? psz + ret : NULL, psz ? csz - ret - 1 : 0);
ret += chars;
}
- chars = quote_rdn_value_to_str_w(
- rdn->rgRDNAttr[j].dwValueType,
- &rdn->rgRDNAttr[j].Value, psz ? psz + ret : NULL,
- psz ? csz - ret : 0);
- if (chars)
- ret += chars - 1;
+ if (psz && ret + 1 == csz) break;
+
+ chars = quote_rdn_value_to_str_w(rdn->rgRDNAttr[j].dwValueType, &rdn->rgRDNAttr[j].Value,
+ psz ? psz + ret : NULL, psz ? csz - ret - 1 : 0);
+ ret += chars;
if (j < rdn->cRDNAttr - 1)
{
- if (psz && ret < csz - rdnSepLen - 1)
- memcpy(psz + ret, rdnSep, rdnSepLen * sizeof(WCHAR));
- ret += rdnSepLen;
+ if (psz)
+ {
+ chars = min(rdnSepLen, csz - ret - 1);
+ memcpy(psz + ret, rdnSep, chars * sizeof(WCHAR));
+ ret += chars;
+ }
+ else ret += rdnSepLen;
}
}
+ if (psz && ret + 1 == csz) break;
if (i < info->cRDN - 1)
{
- if (psz && ret < csz - sepLen - 1)
- memcpy(psz + ret, sep, sepLen * sizeof(WCHAR));
- ret += sepLen;
+ if (psz)
+ {
+ chars = min(sepLen, csz - ret - 1);
+ memcpy(psz + ret, sep, chars * sizeof(WCHAR));
+ ret += chars;
+ }
+ else ret += sepLen;
}
if(reverse) rdn--;
else rdn++;
}
LocalFree(info);
}
- if (psz && csz)
- {
- *(psz + ret) = '\0';
- ret++;
- }
- else
- ret++;
- return ret;
+ if (psz && csz) psz[ret] = 0;
+ return ret + 1;
}
DWORD WINAPI CertNameToStrW(DWORD dwCertEncodingType, PCERT_NAME_BLOB pName,
diff --git a/dlls/crypt32/tests/str.c b/dlls/crypt32/tests/str.c
index e10d5a4fb29..f2ee5b96853 100644
--- a/dlls/crypt32/tests/str.c
+++ b/dlls/crypt32/tests/str.c
@@ -445,14 +445,14 @@ static void test_NameToStrConversionW_(unsigned int line, PCERT_NAME_BLOB pName,
memset(buffer, 0xcc, sizeof(buffer));
retlen = CertNameToStrW(X509_ASN_ENCODING, pName, dwStrType, buffer, len - 1);
- todo_wine ok(retlen == len - 1, "line %u: expected %lu chars, got %lu\n", line, len - 1, retlen);
+ ok(retlen == len - 1, "line %u: expected %lu chars, got %lu\n", line, len - 1, retlen);
ok(!wcsncmp(buffer, expected, retlen - 1), "line %u: expected %s, got %s\n",
line, wine_dbgstr_w(expected), wine_dbgstr_w(buffer));
ok(!buffer[retlen - 1], "line %u: string is not zero terminated.\n", line);
memset(buffer, 0xcc, sizeof(buffer));
retlen = CertNameToStrW(X509_ASN_ENCODING, pName, dwStrType, buffer, 0);
- todo_wine ok(retlen == len, "line %u: expected %lu chars, got %lu\n", line, len - 1, retlen);
+ ok(retlen == len, "line %u: expected %lu chars, got %lu\n", line, len - 1, retlen);
ok(buffer[0] == 0xcccc, "line %u: got %s\n", line, wine_dbgstr_w(buffer));
}
@@ -768,7 +768,8 @@ static void test_CertGetNameString_value_(unsigned int line, PCCERT_CONTEXT cont
ok(!wcscmp(strW, expectedW), "line %u: unexpected value %s.\n", line, debugstr_w(strW));
strW[0] = strW[1] = 0xcccc;
retlen = CertGetNameStringW(context, type, 0, type_para, strW, len - 1);
- todo_wine ok(retlen == len - 1, "line %u: unexpected len %lu, expected %lu.\n", line, retlen, len - 1);
+ todo_wine_if(type != CERT_NAME_RDN_TYPE)
+ ok(retlen == len - 1, "line %u: unexpected len %lu, expected %lu.\n", line, retlen, len - 1);
ok(!wcsncmp(strW, expectedW, retlen - 1), "line %u: string data mismatch.\n", line);
ok(!strW[retlen - 1], "line %u: string is not zero terminated.\n", line);
retlen = CertGetNameStringA(context, type, 0, type_para, NULL, len - 1);
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/12
April 28, 2022
[PATCH 1/7] crypt32/tests: Add tests for short return string buffer.
by Paul Gofman
From: Paul Gofman <pgofman(a)codeweavers.com>
---
dlls/crypt32/tests/str.c | 331 +++++++++++++++------------------------
1 file changed, 127 insertions(+), 204 deletions(-)
diff --git a/dlls/crypt32/tests/str.c b/dlls/crypt32/tests/str.c
index a94381591c0..e10d5a4fb29 100644
--- a/dlls/crypt32/tests/str.c
+++ b/dlls/crypt32/tests/str.c
@@ -429,23 +429,31 @@ static void test_CertNameToStrA(void)
test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\"a\nb\"", FALSE);
}
-static void test_NameToStrConversionW(PCERT_NAME_BLOB pName, DWORD dwStrType,
- LPCWSTR expected, BOOL todo)
+#define test_NameToStrConversionW(a, b, c) test_NameToStrConversionW_(__LINE__, a, b, c)
+static void test_NameToStrConversionW_(unsigned int line, PCERT_NAME_BLOB pName, DWORD dwStrType, LPCWSTR expected)
{
- WCHAR buffer[2000] = { 0 };
- DWORD i;
+ DWORD len, retlen, expected_len;
+ WCHAR buffer[2000];
- i = CertNameToStrW(X509_ASN_ENCODING,pName, dwStrType, NULL, 0);
- todo_wine_if (todo)
- ok(i == lstrlenW(expected) + 1, "Expected %d chars, got %ld\n",
- lstrlenW(expected) + 1, i);
- i = CertNameToStrW(X509_ASN_ENCODING,pName, dwStrType, buffer, ARRAY_SIZE(buffer));
- todo_wine_if (todo)
- ok(i == lstrlenW(expected) + 1, "Expected %d chars, got %ld\n",
- lstrlenW(expected) + 1, i);
- todo_wine_if (todo)
- ok(!lstrcmpW(buffer, expected), "Expected %s, got %s\n",
- wine_dbgstr_w(expected), wine_dbgstr_w(buffer));
+ expected_len = wcslen(expected) + 1;
+ memset(buffer, 0xcc, sizeof(buffer));
+ len = CertNameToStrW(X509_ASN_ENCODING, pName, dwStrType, NULL, 0);
+ ok(len == expected_len, "line %u: expected %lu chars, got %lu\n", line, expected_len, len);
+ retlen = CertNameToStrW(X509_ASN_ENCODING, pName, dwStrType, buffer, ARRAY_SIZE(buffer));
+ ok(retlen == len, "line %u: expected %lu chars, got %lu.\n", line, len, retlen);
+ ok(!wcscmp(buffer, expected), "Expected %s, got %s\n", wine_dbgstr_w(expected), wine_dbgstr_w(buffer));
+
+ memset(buffer, 0xcc, sizeof(buffer));
+ retlen = CertNameToStrW(X509_ASN_ENCODING, pName, dwStrType, buffer, len - 1);
+ todo_wine ok(retlen == len - 1, "line %u: expected %lu chars, got %lu\n", line, len - 1, retlen);
+ ok(!wcsncmp(buffer, expected, retlen - 1), "line %u: expected %s, got %s\n",
+ line, wine_dbgstr_w(expected), wine_dbgstr_w(buffer));
+ ok(!buffer[retlen - 1], "line %u: string is not zero terminated.\n", line);
+
+ memset(buffer, 0xcc, sizeof(buffer));
+ retlen = CertNameToStrW(X509_ASN_ENCODING, pName, dwStrType, buffer, 0);
+ todo_wine ok(retlen == len, "line %u: expected %lu chars, got %lu\n", line, len - 1, retlen);
+ ok(buffer[0] == 0xcccc, "line %u: got %s\n", line, wine_dbgstr_w(buffer));
}
static void test_CertNameToStrW(void)
@@ -479,95 +487,79 @@ static void test_CertNameToStrW(void)
test_NameToStrConversionW(&context->pCertInfo->Issuer,
CERT_SIMPLE_NAME_STR,
- L"US, Minnesota, Minneapolis, CodeWeavers, Wine Development, localhost, aric(a)codeweavers.com", FALSE);
+ L"US, Minnesota, Minneapolis, CodeWeavers, Wine Development, localhost, aric(a)codeweavers.com");
test_NameToStrConversionW(&context->pCertInfo->Issuer,
CERT_SIMPLE_NAME_STR | CERT_NAME_STR_SEMICOLON_FLAG,
- L"US; Minnesota; Minneapolis; CodeWeavers; Wine Development; localhost; aric(a)codeweavers.com", FALSE);
+ L"US; Minnesota; Minneapolis; CodeWeavers; Wine Development; localhost; aric(a)codeweavers.com");
test_NameToStrConversionW(&context->pCertInfo->Issuer,
CERT_SIMPLE_NAME_STR | CERT_NAME_STR_CRLF_FLAG,
- L"US\r\nMinnesota\r\nMinneapolis\r\nCodeWeavers\r\nWine Development\r\nlocalhost\r\naric(a)codeweavers.com",
- FALSE);
+ L"US\r\nMinnesota\r\nMinneapolis\r\nCodeWeavers\r\nWine Development\r\nlocalhost\r\naric(a)codeweavers.com");
test_NameToStrConversionW(&context->pCertInfo->Subject,
CERT_OID_NAME_STR,
L"2.5.4.6=US, 2.5.4.8=Minnesota, 2.5.4.7=Minneapolis, 2.5.4.10=CodeWeavers, 2.5.4.11=Wine Development,"
- " 2.5.4.3=localhost, 1.2.840.113549.1.9.1=aric(a)codeweavers.com", FALSE);
+ " 2.5.4.3=localhost, 1.2.840.113549.1.9.1=aric(a)codeweavers.com");
test_NameToStrConversionW(&context->pCertInfo->Subject,
CERT_OID_NAME_STR | CERT_NAME_STR_SEMICOLON_FLAG,
L"2.5.4.6=US; 2.5.4.8=Minnesota; 2.5.4.7=Minneapolis; 2.5.4.10=CodeWeavers; 2.5.4.11=Wine Development;"
- " 2.5.4.3=localhost; 1.2.840.113549.1.9.1=aric(a)codeweavers.com", FALSE);
+ " 2.5.4.3=localhost; 1.2.840.113549.1.9.1=aric(a)codeweavers.com");
test_NameToStrConversionW(&context->pCertInfo->Subject,
CERT_OID_NAME_STR | CERT_NAME_STR_CRLF_FLAG,
L"2.5.4.6=US\r\n2.5.4.8=Minnesota\r\n2.5.4.7=Minneapolis\r\n2.5.4.10=CodeWeavers\r\n2.5.4.11=Wine "
- "Development\r\n2.5.4.3=localhost\r\n1.2.840.113549.1.9.1=aric(a)codeweavers…", FALSE);
+ "Development\r\n2.5.4.3=localhost\r\n1.2.840.113549.1.9.1=aric(a)codeweavers…");
test_NameToStrConversionW(&context->pCertInfo->Subject,
CERT_X500_NAME_STR | CERT_NAME_STR_SEMICOLON_FLAG | CERT_NAME_STR_REVERSE_FLAG,
L"E=aric(a)codeweavers.com; CN=localhost; OU=Wine Development; O=CodeWeavers; L=Minneapolis; S=Minnesota; "
- "C=US", FALSE);
+ "C=US");
CertFreeCertificateContext(context);
}
blob.pbData = encodedSimpleCN;
blob.cbData = sizeof(encodedSimpleCN);
- test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=1", FALSE);
+ test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=1");
blob.pbData = encodedSingleQuotedCN;
blob.cbData = sizeof(encodedSingleQuotedCN);
- test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN='1'",
- FALSE);
- test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR,
- L"'1'", FALSE);
+ test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN='1'");
+ test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"'1'");
blob.pbData = encodedSpacedCN;
blob.cbData = sizeof(encodedSpacedCN);
- test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\" 1 \"", FALSE);
- test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"\" 1 \"",
- FALSE);
+ test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\" 1 \"");
+ test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"\" 1 \"");
blob.pbData = encodedQuotedCN;
blob.cbData = sizeof(encodedQuotedCN);
- test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\"\"\"1\"\"\"",
- FALSE);
- test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"\"\"\"1\"\"\"",
- FALSE);
+ test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\"\"\"1\"\"\"");
+ test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"\"\"\"1\"\"\"");
blob.pbData = encodedMultipleAttrCN;
blob.cbData = sizeof(encodedMultipleAttrCN);
- test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\"1+2\"",
- FALSE);
- test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR,
- L"\"1+2\"", FALSE);
+ test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\"1+2\"");
+ test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"\"1+2\"");
blob.pbData = encodedCommaCN;
blob.cbData = sizeof(encodedCommaCN);
- test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\"a,b\"", FALSE);
- test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"\"a,b\"",
- FALSE);
+ test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\"a,b\"");
+ test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"\"a,b\"");
blob.pbData = encodedEqualCN;
blob.cbData = sizeof(encodedEqualCN);
- test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\"a=b\"", FALSE);
- test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"\"a=b\"",
- FALSE);
+ test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\"a=b\"");
+ test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"\"a=b\"");
blob.pbData = encodedLessThanCN;
blob.cbData = sizeof(encodedLessThanCN);
- test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\"<\"", FALSE);
- test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"\"<\"",
- FALSE);
+ test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\"<\"");
+ test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"\"<\"");
blob.pbData = encodedGreaterThanCN;
blob.cbData = sizeof(encodedGreaterThanCN);
- test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\">\"",
- FALSE);
- test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR,
- L"\">\"", FALSE);
+ test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\">\"");
+ test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"\">\"");
blob.pbData = encodedHashCN;
blob.cbData = sizeof(encodedHashCN);
- test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\"#\"", FALSE);
- test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"\"#\"",
- FALSE);
+ test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\"#\"");
+ test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"\"#\"");
blob.pbData = encodedSemiCN;
blob.cbData = sizeof(encodedSemiCN);
- test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\";\"", FALSE);
- test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"\";\"",
- FALSE);
+ test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\";\"");
+ test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"\";\"");
blob.pbData = encodedNewlineCN;
blob.cbData = sizeof(encodedNewlineCN);
- test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\"a\nb\"", FALSE);
- test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"\"a\nb\"",
- FALSE);
+ test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\"a\nb\"");
+ test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"\"a\nb\"");
}
struct StrToNameA
@@ -747,153 +739,84 @@ static void test_CertStrToNameW(void)
}
}
-static void test_CertGetNameStringA(void)
+#define test_CertGetNameString_value(a, b, c, d) test_CertGetNameString_value_(__LINE__, a, b, c, d)
+static void test_CertGetNameString_value_(unsigned int line, PCCERT_CONTEXT context, DWORD type, void *type_para,
+ const char *expected)
+{
+ WCHAR expectedW[512];
+ DWORD len, retlen;
+ WCHAR strW[512];
+ unsigned int i;
+ char str[512];
+
+ for (i = 0; expected[i]; ++i)
+ expectedW[i] = expected[i];
+ expectedW[i] = 0;
+
+ len = CertGetNameStringA(context, type, 0, type_para, NULL, 0);
+ ok(len == strlen(expected) + 1, "line %u: unexpected length %ld.\n", line, len);
+ retlen = CertGetNameStringA(context, type, 0, type_para, str, len);
+ ok(retlen == len, "line %u: unexpected len %lu, expected %lu.\n", line, retlen, len);
+ ok(!strcmp(str, expected), "line %u: unexpected value %s.\n", line, str);
+ str[0] = str[1] = 0xcc;
+ retlen = CertGetNameStringA(context, type, 0, type_para, str, len - 1);
+ todo_wine ok(retlen == 1, "line %u: Unexpected len %lu, expected 1.\n", line, retlen);
+ todo_wine ok(!str[0], "line %u: unexpected str[0] %#x.\n", line, str[0]);
+ ok(str[1] == expected[1], "line %u: unexpected str[1] %#x.\n", line, str[1]);
+ retlen = CertGetNameStringW(context, type, 0, type_para, strW, len);
+ ok(retlen == len, "line %u: unexpected len %lu, expected 1.\n", line, retlen);
+ ok(!wcscmp(strW, expectedW), "line %u: unexpected value %s.\n", line, debugstr_w(strW));
+ strW[0] = strW[1] = 0xcccc;
+ retlen = CertGetNameStringW(context, type, 0, type_para, strW, len - 1);
+ todo_wine ok(retlen == len - 1, "line %u: unexpected len %lu, expected %lu.\n", line, retlen, len - 1);
+ ok(!wcsncmp(strW, expectedW, retlen - 1), "line %u: string data mismatch.\n", line);
+ ok(!strW[retlen - 1], "line %u: string is not zero terminated.\n", line);
+ retlen = CertGetNameStringA(context, type, 0, type_para, NULL, len - 1);
+ ok(retlen == len, "line %u: unexpected len %lu, expected %lu\n", line, retlen, len);
+ retlen = CertGetNameStringW(context, type, 0, type_para, NULL, len - 1);
+ ok(retlen == len, "line %u: unexpected len %lu, expected %lu\n", line, retlen, len);
+}
+
+static void test_CertGetNameString(void)
{
+ static const char aric[] = "aric(a)codeweavers.com";
+ static const char localhost[] = "localhost";
PCCERT_CONTEXT context;
+ DWORD len, type;
context = CertCreateCertificateContext(X509_ASN_ENCODING, cert,
sizeof(cert));
- ok(context != NULL, "CertCreateCertificateContext failed: %08lx\n",
- GetLastError());
- if (context)
- {
- static const char aric[] = "aric(a)codeweavers.com";
- static const char localhost[] = "localhost";
- DWORD len, type;
- LPSTR str;
-
- /* Bad string types/types missing from the cert */
- len = CertGetNameStringA(NULL, 0, 0, NULL, NULL, 0);
- ok(len == 1, "expected 1, got %ld\n", len);
- len = CertGetNameStringA(context, 0, 0, NULL, NULL, 0);
- ok(len == 1, "expected 1, got %ld\n", len);
- len = CertGetNameStringA(context, CERT_NAME_URL_TYPE, 0, NULL, NULL,
- 0);
- ok(len == 1, "expected 1, got %ld\n", len);
-
- len = CertGetNameStringA(context, CERT_NAME_EMAIL_TYPE, 0, NULL, NULL,
- 0);
- ok(len == strlen(aric) + 1, "unexpected length %ld\n", len);
- str = HeapAlloc(GetProcessHeap(), 0, len);
- if (str)
- {
- len = CertGetNameStringA(context, CERT_NAME_EMAIL_TYPE, 0, NULL,
- str, len);
- ok(!strcmp(str, aric), "unexpected value %s\n", str);
- HeapFree(GetProcessHeap(), 0, str);
- }
-
- len = CertGetNameStringA(context, CERT_NAME_RDN_TYPE, 0, NULL, NULL,
- 0);
- ok(len == strlen(issuerStr) + 1, "unexpected length %ld\n", len);
- str = HeapAlloc(GetProcessHeap(), 0, len);
- if (str)
- {
- len = CertGetNameStringA(context, CERT_NAME_RDN_TYPE, 0, NULL,
- str, len);
- ok(!strcmp(str, issuerStr), "unexpected value %s\n", str);
- HeapFree(GetProcessHeap(), 0, str);
- }
- type = 0;
- len = CertGetNameStringA(context, CERT_NAME_RDN_TYPE, 0, &type, NULL,
- 0);
- ok(len == strlen(issuerStr) + 1, "unexpected length %ld\n", len);
- str = HeapAlloc(GetProcessHeap(), 0, len);
- if (str)
- {
- len = CertGetNameStringA(context, CERT_NAME_RDN_TYPE, 0, &type,
- str, len);
- ok(!strcmp(str, issuerStr), "unexpected value %s\n", str);
- HeapFree(GetProcessHeap(), 0, str);
- }
- type = CERT_OID_NAME_STR;
- len = CertGetNameStringA(context, CERT_NAME_RDN_TYPE, 0, &type, NULL,
- 0);
- ok(len == strlen(subjectStr) + 1, "unexpected length %ld\n", len);
- str = HeapAlloc(GetProcessHeap(), 0, len);
- if (str)
- {
- len = CertGetNameStringA(context, CERT_NAME_RDN_TYPE, 0, &type,
- str, len);
- ok(!strcmp(str, subjectStr), "unexpected value %s\n", str);
- HeapFree(GetProcessHeap(), 0, str);
- }
-
- len = CertGetNameStringA(context, CERT_NAME_ATTR_TYPE, 0, NULL, NULL,
- 0);
- ok(len == strlen(aric) + 1, "unexpected length %ld\n", len);
- str = HeapAlloc(GetProcessHeap(), 0, len);
- if (str)
- {
- len = CertGetNameStringA(context, CERT_NAME_ATTR_TYPE, 0, NULL,
- str, len);
- ok(!strcmp(str, aric), "unexpected value %s\n", str);
- HeapFree(GetProcessHeap(), 0, str);
- }
- len = CertGetNameStringA(context, CERT_NAME_ATTR_TYPE, 0,
- (void *)szOID_RSA_emailAddr, NULL, 0);
- ok(len == strlen(aric) + 1, "unexpected length %ld\n", len);
- str = HeapAlloc(GetProcessHeap(), 0, len);
- if (str)
- {
- len = CertGetNameStringA(context, CERT_NAME_ATTR_TYPE, 0,
- (void *)szOID_RSA_emailAddr, str, len);
- ok(!strcmp(str, aric), "unexpected value %s\n", str);
- HeapFree(GetProcessHeap(), 0, str);
- }
- len = CertGetNameStringA(context, CERT_NAME_ATTR_TYPE, 0,
- (void *)szOID_COMMON_NAME, NULL, 0);
- ok(len == strlen(localhost) + 1, "unexpected length %ld\n", len);
- str = HeapAlloc(GetProcessHeap(), 0, len);
- if (str)
- {
- len = CertGetNameStringA(context, CERT_NAME_ATTR_TYPE, 0,
- (void *)szOID_COMMON_NAME, str, len);
- ok(!strcmp(str, localhost), "unexpected value %s\n", str);
- HeapFree(GetProcessHeap(), 0, str);
- }
-
- len = CertGetNameStringA(context, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0,
- NULL, NULL, 0);
- ok(len == strlen(localhost) + 1, "unexpected length %ld\n", len);
- str = HeapAlloc(GetProcessHeap(), 0, len);
- if (str)
- {
- len = CertGetNameStringA(context, CERT_NAME_SIMPLE_DISPLAY_TYPE,
- 0, NULL, str, len);
- ok(!strcmp(str, localhost), "unexpected value %s\n", str);
- HeapFree(GetProcessHeap(), 0, str);
- }
-
- len = CertGetNameStringA(context, CERT_NAME_FRIENDLY_DISPLAY_TYPE, 0,
- NULL, NULL, 0);
- ok(len == strlen(localhost) + 1, "unexpected length %ld\n", len);
- str = HeapAlloc(GetProcessHeap(), 0, len);
- if (str)
- {
- len = CertGetNameStringA(context, CERT_NAME_FRIENDLY_DISPLAY_TYPE,
- 0, NULL, str, len);
- ok(!strcmp(str, localhost), "unexpected value %s\n", str);
- HeapFree(GetProcessHeap(), 0, str);
- }
-
- len = CertGetNameStringA(context, CERT_NAME_DNS_TYPE, 0, NULL, NULL,
- 0);
- ok(len == strlen(localhost) + 1, "unexpected length %ld\n", len);
- if (len > 1)
- {
- str = HeapAlloc(GetProcessHeap(), 0, len);
- if (str)
- {
- len = CertGetNameStringA(context, CERT_NAME_DNS_TYPE, 0, NULL,
- str, len);
- ok(!strcmp(str, localhost), "unexpected value %s\n", str);
- HeapFree(GetProcessHeap(), 0, str);
- }
- }
-
- CertFreeCertificateContext(context);
- }
+ ok(!!context, "CertCreateCertificateContext failed, err %lu\n", GetLastError());
+
+ /* Bad string types/types missing from the cert */
+ len = CertGetNameStringA(NULL, 0, 0, NULL, NULL, 0);
+ ok(len == 1, "expected 1, got %lu\n", len);
+ len = CertGetNameStringA(context, 0, 0, NULL, NULL, 0);
+ ok(len == 1, "expected 1, got %lu\n", len);
+ len = CertGetNameStringA(context, CERT_NAME_URL_TYPE, 0, NULL, NULL, 0);
+ ok(len == 1, "expected 1, got %lu\n", len);
+
+ len = CertGetNameStringW(NULL, 0, 0, NULL, NULL, 0);
+ ok(len == 1, "expected 1, got %lu\n", len);
+ len = CertGetNameStringW(context, 0, 0, NULL, NULL, 0);
+ ok(len == 1, "expected 1, got %lu\n", len);
+ len = CertGetNameStringW(context, CERT_NAME_URL_TYPE, 0, NULL, NULL, 0);
+ ok(len == 1, "expected 1, got %lu\n", len);
+
+ test_CertGetNameString_value(context, CERT_NAME_EMAIL_TYPE, NULL, aric);
+ test_CertGetNameString_value(context, CERT_NAME_RDN_TYPE, NULL, issuerStr);
+ type = 0;
+ test_CertGetNameString_value(context, CERT_NAME_RDN_TYPE, &type, issuerStr);
+ type = CERT_OID_NAME_STR;
+ test_CertGetNameString_value(context, CERT_NAME_RDN_TYPE, &type, subjectStr);
+ test_CertGetNameString_value(context, CERT_NAME_ATTR_TYPE, NULL, aric);
+ test_CertGetNameString_value(context, CERT_NAME_ATTR_TYPE, (void *)szOID_RSA_emailAddr, aric);
+ test_CertGetNameString_value(context, CERT_NAME_ATTR_TYPE, (void *)szOID_COMMON_NAME, localhost);
+ test_CertGetNameString_value(context, CERT_NAME_SIMPLE_DISPLAY_TYPE, NULL, localhost);
+ test_CertGetNameString_value(context, CERT_NAME_FRIENDLY_DISPLAY_TYPE, NULL, localhost);
+ test_CertGetNameString_value(context, CERT_NAME_DNS_TYPE, NULL, localhost);
+
+ CertFreeCertificateContext(context);
}
START_TEST(str)
@@ -904,5 +827,5 @@ START_TEST(str)
test_CertNameToStrW();
test_CertStrToNameA();
test_CertStrToNameW();
- test_CertGetNameStringA();
+ test_CertGetNameString();
}
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/12
April 28, 2022
[PATCH 0/7] MR12: crypt32: Fix short output string length handling in str.c plus a bit of refactoring
by Paul Gofman (@gofman)
This is a resend of 233262-233269. Hans reviewed the previous version (https://www.winehq.org/pipermail/wine-devel/2022-April/214386.html) and I sent this one as the follow up with the only change fixing the compiler warning as test.
--
https://gitlab.winehq.org/wine/wine/-/merge_requests/12
April 28, 2022
Re: [PATCH vkd3d 4/8] vkd3d-shader/hlsl: Support initialization of implicit size arrays.
by Zebediah Figura
On 4/28/22 16:30, Francisco Casas wrote:
> April 28, 2022 4:22 PM, "Zebediah Figura" <zfigura(a)codeweavers.com> wrote:
>
>> On 4/28/22 14:45, Francisco Casas wrote:
>>
>>> diff --git a/libs/vkd3d-shader/hlsl.y b/libs/vkd3d-shader/hlsl.y
>>> index 905dbfc5..e7fe74d8 100644
>>> --- a/libs/vkd3d-shader/hlsl.y
>>> +++ b/libs/vkd3d-shader/hlsl.y
>>> @@ -1606,7 +1606,27 @@ static struct list *declare_vars(struct hlsl_ctx *ctx, struct hlsl_type
>>> *basic_t
>>> type = basic_type;
>>> for (i = 0; i < v->arrays.count; ++i)
>>> + {
>>> + if (v->arrays.sizes[i] == HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT)
>>> + {
>>> + unsigned int size = initializer_size(&v->initializer);
>>> + unsigned int elem_components = hlsl_type_component_count(type);
>>> +
>>> + assert(v->initializer.args_count);
>>> +
>>> + v->arrays.sizes[i] = (size + elem_components - 1)/elem_components;
>>> +
>>> + if (size % elem_components != 0)
>>> + {
>>> + hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_WRONG_PARAMETER_COUNT,
>>> + "Cannot initialize implicit array with %u components, expected a multiple of %u.",
>>> + size, elem_components);
>>> + free_parse_initializer(&v->initializer);
>>> + v->initializer.args_count = 0;
>>> + }
>>>> This doesn't seem like it'll do the right thing for implicit sizes on an inner array, especially
>>>> considering that the right thing is probably to fail compilation.
>>>>
>>>> It also won't do the right thing for initializers without braces (viz. also fail compilation).
>>>
>>> These cases are checked in the parse rules introduced in this patch.
>>
>> Oh, I see, I wasn't actually correctly reading the parser rule. That makes sense.
>>
>> On the other hand, handling IMPLICIT inside of the loop, without any checks, was one of the things
>> that made me think "we're not handling inner arrays correctly", so arguably something deserves
>> changing here :-)
>>
>
> Well, the structure of the parsing rule ensures that only the most external array could have
> IMPLICIT side. This is related to the
> assert(i == v->arrays.count - 1);
> suggested by Giovanni.
>
>> I don't see any handling for braceless initializers in this patch, though; am I missing something?
>>
>
> The error is added to the "variable_def:" rule.
> But then again, maybe it is not so clear that the parse rule ensures that only the most external array
> can have IMPLICIT size. I could add the same assertion here too.
Right, the assert works to clarify.
I'm not sure whether "float a[2][]" should be a syntax error, though. As
elsewhere, it'd be more helpful to explain why it's wrong, and probably
also to avoid failing compilation immediately.
> By the way, I am not sure if you changed your stance on checking the implicit size arrays at the parse
> level. I would prefer to keep it there... if I find a way of also handling these unbounded resource
> arrays nicely.
Well, it at least can't be done until we have type information, which
means it'd need to be deferred until declare_vars and
gen_struct_fields(). That sounds like conceptually the right place for
it, to me.
>
>>> I think we need tests for all of these corner cases, and also a test for missing initializers.
>>>>
>>>
>>> Okay, adding them.
>>> + }
>>> type = hlsl_new_array_type(ctx, type, v->arrays.sizes[i]);
>>> + }
>>> vkd3d_free(v->arrays.sizes);
>>> if (type->type != HLSL_CLASS_MATRIX)
>>> @@ -2464,6 +2484,7 @@ static bool add_method_call(struct hlsl_ctx *ctx, struct list *instrs, struct
>>> hl
>>> %token <name> TYPE_IDENTIFIER
>>> %type <arrays> arrays
>>> +%type <arrays> implicit_arrays
>>> %type <assign_op> assign_op
>>> @@ -3108,7 +3129,7 @@ variables_def:
>>> }
>>> variable_decl:
>>> - any_identifier arrays colon_attribute
>>> + any_identifier implicit_arrays colon_attribute
>>> {
>>> $$ = hlsl_alloc(ctx, sizeof(*$$));
>>> $$->loc = @1;
>>> @@ -3137,6 +3158,15 @@ state_block:
>>> variable_def:
>>> variable_decl
>>> + {
>>> + if ($$->arrays.sizes && $$->arrays.sizes[$$->arrays.count - 1] ==
>>> HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT)
>>> + {
>>> + hlsl_error(ctx, &@1, VKD3D_SHADER_ERROR_HLSL_MISSING_INITIALIZER,
>>> + "Implicit array requires initializer.");
>
> Here ^
>
>>> + free_parse_variable_def($$);
>>> + YYABORT;
>>> + }
>>> + }
>>>> This won't work for unbounded resource arrays, which can be declared with an empty pair of brackets
>>>> (and no initializer).
>>>
>>> I see, I didn't knew about those.
>>> It also aborts compilation somewhat unnecessarily.
>>>>
>>>
>>> AFAIK (except for unbounded resource arrays) the native compiler doesn't allow implicit size arrays
>>> without initializer, so aborting seemed logical.
>>
>> Failing compilation is fine, but in general I think we want to avoid aborting if we can help it. We
>> should only abort if we really can't continue parsing, e.g. if we encounter a syntax error. That
>> way, if there are multiple errors, the (HLSL) programmer can deal with them all at once.
>>
>>> (As a side note, perhaps we should use zero instead of UINT_MAX. Unbounded resources in shader
>>>> model 5.1 are encoded as zero in the reflection data, whereas declaring a resource array as e.g.
>>>> "Texture2D t[0xffffffff]" yields 0xffffffff instead, although the shader bytecode is identical.)
>>>>
>>>> As a special extra, this code is apparently valid, and I think deserves to be a test case:
>>>>
>>>> struct apple
>>>> {
>>>> Texture2D t[];
>>>> };
>>>
>>> Hmm, this is interesting. Maybe it is intended for input semantics?
>>> I will investigate.
>>
>> Not for input semantics, but rather for bound resources. Shader model 5.0 allows declaring arrays
>> of textures, and shader model 5.1 (and Direct3D 12) allows declaring "unbounded" arrays, as e.g.
>> "Texture2D t[]" or "Texture2D t[0]".
>>
>> Normally textures aren't declared as part of a struct, but I was curious to see if it was legal,
>> and it turns out it is. (Although the reflection type information that's generated isn't quite
>> correct.)
>
> Hmm, I will see where I can add a FIXME for those cases for now then.
It's not something that this patch necessarily needs to handle, even
with a FIXME, but it should inform how the code is structured, which is
why I bring it up.
April 28, 2022
Re: [PATCH v5 1/2] include: Add winusb.h file.
by Mohamad Al-Jaf
On Thu, Apr 28, 2022 at 12:15 PM Zebediah Figura
<zfigura(a)codeweavers.com> wrote:
> > I think the struct packing is wrong, the SDK uses #pragma pack(1) here,
> > which probably should use #include "pshpack1.h" / "poppack.h".
>
> It doesn't make a difference in this case, though.
Hi,
Thanks for the review.
In the previous version pshpack1.h / poppack.h were included in
usb100.h and ddk/wdm.h which was needed by ddk/winusbio.h. Since it
doesn't make a difference in this case I suppose there's no reason to
add it.
On Thu, Apr 28, 2022 at 8:33 AM Rémi Bernon <rbernon(a)codeweavers.com> wrote:
> I have no idea what's the usual policy for how to write these. The
> signatures look alright, but are we still using LP*/P* types, or should
> it be expanded to pointers?
I'm not sure either, Wine is inconsistent in this regard. At least in
winbase.h there's a lot of LP*/P* types.
Just curious what difference it makes compared to being expanded to pointers?
> I see the previous patch version had more functions, and I can't really
> tell if it's better or not. Imho either add only the functions that are
> going to be used (so WinUsb_Free) or required by third-party programs to
> build, or add everything that the SDK public headers declare?
>
> Anything in between seems arbitrary to me, but I have not much
> experience in adding stuff like this.
Yeah, I agree that either all the functions or just the one that's
going to be used would be better.
The reason I removed the ddk/winusbio.h dependent functions is that
some copyright concerns were raised for the winusbio.h file. It's a
rather small file and I'm not sure how to add it in this case. Would
rearranging the flags and structs be sufficient?
I'm not sure what the policy for headers is. IMO, I find it useful for public
headers to be fully added, well the relevant parts to Wine that is. It
saves developers time from having to add them later. Still, I'm fine
with doing it either way.
--
Kind regards,
Mohamad
April 28, 2022
Re: [PATCH vkd3d 4/8] vkd3d-shader/hlsl: Support initialization of implicit size arrays.
by Francisco Casas
April 28, 2022 4:22 PM, "Zebediah Figura" <zfigura(a)codeweavers.com> wrote:
> On 4/28/22 14:45, Francisco Casas wrote:
>
>> diff --git a/libs/vkd3d-shader/hlsl.y b/libs/vkd3d-shader/hlsl.y
>> index 905dbfc5..e7fe74d8 100644
>> --- a/libs/vkd3d-shader/hlsl.y
>> +++ b/libs/vkd3d-shader/hlsl.y
>> @@ -1606,7 +1606,27 @@ static struct list *declare_vars(struct hlsl_ctx *ctx, struct hlsl_type
>> *basic_t
>> type = basic_type;
>> for (i = 0; i < v->arrays.count; ++i)
>> + {
>> + if (v->arrays.sizes[i] == HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT)
>> + {
>> + unsigned int size = initializer_size(&v->initializer);
>> + unsigned int elem_components = hlsl_type_component_count(type);
>> +
>> + assert(v->initializer.args_count);
>> +
>> + v->arrays.sizes[i] = (size + elem_components - 1)/elem_components;
>> +
>> + if (size % elem_components != 0)
>> + {
>> + hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_WRONG_PARAMETER_COUNT,
>> + "Cannot initialize implicit array with %u components, expected a multiple of %u.",
>> + size, elem_components);
>> + free_parse_initializer(&v->initializer);
>> + v->initializer.args_count = 0;
>> + }
>>> This doesn't seem like it'll do the right thing for implicit sizes on an inner array, especially
>>> considering that the right thing is probably to fail compilation.
>>>
>>> It also won't do the right thing for initializers without braces (viz. also fail compilation).
>>
>> These cases are checked in the parse rules introduced in this patch.
>
> Oh, I see, I wasn't actually correctly reading the parser rule. That makes sense.
>
> On the other hand, handling IMPLICIT inside of the loop, without any checks, was one of the things
> that made me think "we're not handling inner arrays correctly", so arguably something deserves
> changing here :-)
>
Well, the structure of the parsing rule ensures that only the most external array could have
IMPLICIT side. This is related to the
assert(i == v->arrays.count - 1);
suggested by Giovanni.
> I don't see any handling for braceless initializers in this patch, though; am I missing something?
>
The error is added to the "variable_def:" rule.
But then again, maybe it is not so clear that the parse rule ensures that only the most external array
can have IMPLICIT size. I could add the same assertion here too.
By the way, I am not sure if you changed your stance on checking the implicit size arrays at the parse
level. I would prefer to keep it there... if I find a way of also handling these unbounded resource
arrays nicely.
>> I think we need tests for all of these corner cases, and also a test for missing initializers.
>>>
>>
>> Okay, adding them.
>> + }
>> type = hlsl_new_array_type(ctx, type, v->arrays.sizes[i]);
>> + }
>> vkd3d_free(v->arrays.sizes);
>> if (type->type != HLSL_CLASS_MATRIX)
>> @@ -2464,6 +2484,7 @@ static bool add_method_call(struct hlsl_ctx *ctx, struct list *instrs, struct
>> hl
>> %token <name> TYPE_IDENTIFIER
>> %type <arrays> arrays
>> +%type <arrays> implicit_arrays
>> %type <assign_op> assign_op
>> @@ -3108,7 +3129,7 @@ variables_def:
>> }
>> variable_decl:
>> - any_identifier arrays colon_attribute
>> + any_identifier implicit_arrays colon_attribute
>> {
>> $$ = hlsl_alloc(ctx, sizeof(*$$));
>> $$->loc = @1;
>> @@ -3137,6 +3158,15 @@ state_block:
>> variable_def:
>> variable_decl
>> + {
>> + if ($$->arrays.sizes && $$->arrays.sizes[$$->arrays.count - 1] ==
>> HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT)
>> + {
>> + hlsl_error(ctx, &@1, VKD3D_SHADER_ERROR_HLSL_MISSING_INITIALIZER,
>> + "Implicit array requires initializer.");
Here ^
>> + free_parse_variable_def($$);
>> + YYABORT;
>> + }
>> + }
>>> This won't work for unbounded resource arrays, which can be declared with an empty pair of brackets
>>> (and no initializer).
>>
>> I see, I didn't knew about those.
>> It also aborts compilation somewhat unnecessarily.
>>>
>>
>> AFAIK (except for unbounded resource arrays) the native compiler doesn't allow implicit size arrays
>> without initializer, so aborting seemed logical.
>
> Failing compilation is fine, but in general I think we want to avoid aborting if we can help it. We
> should only abort if we really can't continue parsing, e.g. if we encounter a syntax error. That
> way, if there are multiple errors, the (HLSL) programmer can deal with them all at once.
>
>> (As a side note, perhaps we should use zero instead of UINT_MAX. Unbounded resources in shader
>>> model 5.1 are encoded as zero in the reflection data, whereas declaring a resource array as e.g.
>>> "Texture2D t[0xffffffff]" yields 0xffffffff instead, although the shader bytecode is identical.)
>>>
>>> As a special extra, this code is apparently valid, and I think deserves to be a test case:
>>>
>>> struct apple
>>> {
>>> Texture2D t[];
>>> };
>>
>> Hmm, this is interesting. Maybe it is intended for input semantics?
>> I will investigate.
>
> Not for input semantics, but rather for bound resources. Shader model 5.0 allows declaring arrays
> of textures, and shader model 5.1 (and Direct3D 12) allows declaring "unbounded" arrays, as e.g.
> "Texture2D t[]" or "Texture2D t[0]".
>
> Normally textures aren't declared as part of a struct, but I was curious to see if it was legal,
> and it turns out it is. (Although the reflection type information that's generated isn't quite
> correct.)
Hmm, I will see where I can add a FIXME for those cases for now then.
April 28, 2022
Re: [PATCH vkd3d 4/8] vkd3d-shader/hlsl: Support initialization of implicit size arrays.
by Francisco Casas
April 28, 2022 4:22 PM, "Zebediah Figura" <zfigura(a)codeweavers.com (mailto:zfigura(a)codeweavers.com)> wrote:
On 4/28/22 14:45, Francisco Casas wrote:
diff --git a/libs/vkd3d-shader/hlsl.y b/libs/vkd3d-shader/hlsl.y
index 905dbfc5..e7fe74d8 100644
--- a/libs/vkd3d-shader/hlsl.y
+++ b/libs/vkd3d-shader/hlsl.y
@@ -1606,7 +1606,27 @@ static struct list *declare_vars(struct hlsl_ctx *ctx, struct hlsl_type
*basic_t
type = basic_type;
for (i = 0; i < v->arrays.count; ++i)
+ {
+ if (v->arrays.sizes[i] == HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT)
+ {
+ unsigned int size = initializer_size(&v->initializer);
+ unsigned int elem_components = hlsl_type_component_count(type);
+
+ assert(v->initializer.args_count);
+
+ v->arrays.sizes[i] = (size + elem_components - 1)/elem_components;
+
+ if (size % elem_components != 0)
+ {
+ hlsl_error(ctx, &v->loc, VKD3D_SHADER_ERROR_HLSL_WRONG_PARAMETER_COUNT,
+ "Cannot initialize implicit array with %u components, expected a multiple of %u.",
+ size, elem_components);
+ free_parse_initializer(&v->initializer);
+ v->initializer.args_count = 0;
+ }
This doesn't seem like it'll do the right thing for implicit sizes on an inner array, especially
considering that the right thing is probably to fail compilation.
It also won't do the right thing for initializers without braces (viz. also fail compilation).These cases are checked in the parse rules introduced in this patch.
Oh, I see, I wasn't actually correctly reading the parser rule. That makes sense.
On the other hand, handling IMPLICIT inside of the loop, without any checks, was one of the things
that made me think "we're not handling inner arrays correctly", so arguably something deserves
changing here :-)
Well, the structure of the parsing rule ensures that only the most external array could have
IMPLICIT side. This is related to the
assert(i == v->arrays.count - 1);
suggested by Giovanni.
I don't see any handling for braceless initializers in this patch, though; am I missing something?
The error is added to the "variable_def:" rule.
But then again, maybe it is not so clear that the parse rule ensures that only the most external array
can have IMPLICIT size. I could add the same assertion here too.
By the way, I am not sure if you changed your stance on checking the implicit size arrays at the parse
level. I would prefer to keep it there... if I find a way of also handling these unbounded resource
arrays nicely.
I think we need tests for all of these corner cases, and also a test for missing initializers. Okay, adding them.
+ }
type = hlsl_new_array_type(ctx, type, v->arrays.sizes[i]);
+ }
vkd3d_free(v->arrays.sizes);
if (type->type != HLSL_CLASS_MATRIX)
@@ -2464,6 +2484,7 @@ static bool add_method_call(struct hlsl_ctx *ctx, struct list *instrs, struct
hl
%token <name> TYPE_IDENTIFIER
%type <arrays> arrays
+%type <arrays> implicit_arrays
%type <assign_op> assign_op
@@ -3108,7 +3129,7 @@ variables_def:
}
variable_decl:
- any_identifier arrays colon_attribute
+ any_identifier implicit_arrays colon_attribute
{
$$ = hlsl_alloc(ctx, sizeof(*$$));
$$->loc = @1;
@@ -3137,6 +3158,15 @@ state_block:
variable_def:
variable_decl
+ {
+ if ($$->arrays.sizes && $$->arrays.sizes[$$->arrays.count - 1] ==
HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT)
+ {
+ hlsl_error(ctx, &@1, VKD3D_SHADER_ERROR_HLSL_MISSING_INITIALIZER,
+ "Implicit array requires initializer.");
+ free_parse_variable_def($$);
+ YYABORT;
+ }
+ }
This won't work for unbounded resource arrays, which can be declared with an empty pair of brackets
(and no initializer).I see, I didn't knew about those.
It also aborts compilation somewhat unnecessarily. AFAIK (except for unbounded resource arrays) the native compiler doesn't allow implicit size arrays
without initializer, so aborting seemed logical.
Failing compilation is fine, but in general I think we want to avoid aborting if we can help it. We
should only abort if we really can't continue parsing, e.g. if we encounter a syntax error. That
way, if there are multiple errors, the (HLSL) programmer can deal with them all at once.
(As a side note, perhaps we should use zero instead of UINT_MAX. Unbounded resources in shader
model 5.1 are encoded as zero in the reflection data, whereas declaring a resource array as e.g.
"Texture2D t[0xffffffff]" yields 0xffffffff instead, although the shader bytecode is identical.)
As a special extra, this code is apparently valid, and I think deserves to be a test case:
struct apple
{
Texture2D t[];
};Hmm, this is interesting. Maybe it is intended for input semantics?
I will investigate.
Not for input semantics, but rather for bound resources. Shader model 5.0 allows declaring arrays
of textures, and shader model 5.1 (and Direct3D 12) allows declaring "unbounded" arrays, as e.g.
"Texture2D t[]" or "Texture2D t[0]".
Normally textures aren't declared as part of a struct, but I was curious to see if it was legal,
and it turns out it is. (Although the reflection type information that's generated isn't quite
correct.)
Hmm, I will see where I can add a FIXME for those cases for now then.
April 28, 2022
Re: [PATCH v2 3/3] winegstreamer: Check whether transforms are supported at creation time.
by Zebediah Figura
Signed-off-by: Zebediah Figura <zfigura(a)codeweavers.com>
April 28, 2022