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: [PATCH v2 2/3] winegstreamer: Check caps pointer in wg_format_to_caps_h264.
by Zebediah Figura
Signed-off-by: Zebediah Figura <zfigura(a)codeweavers.com>
April 28, 2022
Re: [PATCH v2 1/3] winegstreamer: Ignore unset H264 format profile and level.
by Zebediah Figura
Signed-off-by: Zebediah Figura <zfigura(a)codeweavers.com>
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: Reject incompatible output types in MPEG audio decoder.
by Zebediah Figura
Signed-off-by: Zebediah Figura <zfigura(a)codeweavers.com>
April 28, 2022
Re: [PATCH v2 2/3] quartz/tests: Add tests for MPEG audio decoder sink connection.
by Zebediah Figura
Signed-off-by: Zebediah Figura <zfigura(a)codeweavers.com>
April 28, 2022
Re: [PATCH v2 1/3] winegstreamer: Reject incompatible input media types in MPEG audio decoder.
by Zebediah Figura
Signed-off-by: Zebediah Figura <zfigura(a)codeweavers.com>
April 28, 2022
Re: [PATCH vkd3d 4/8] vkd3d-shader/hlsl: Support initialization of implicit size arrays.
by Zebediah Figura
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 :-)
I don't see any handling for braceless initializers in this patch,
though; am I missing something?
>
>> 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.)
April 28, 2022
Re: [PATCH vkd3d 4/8] vkd3d-shader/hlsl: Support initialization of implicit size arrays.
by Francisco Casas
Hello,
April 28, 2022 2:22 PM, "Zebediah Figura" <zfigura(a)codeweavers.com> wrote:
> On 4/28/22 08:32, Giovanni Mascellani wrote:
>
>> @@ -328,6 +330,9 @@ struct hlsl_type *hlsl_new_array_type(struct hlsl_ctx *ctx, struct hlsl_type
>> *ba
>>> list_add_tail(&ctx->types, &type->entry);
>>> + if (type->e.array.elements_count == HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT)
>> + TRACE("Implicit element count.\n");
>> +
>> return type;
>> }
>
> When can this currently happen?
>
Huh, actually this shouldn't happen in the current patch as it is at least. I will remove it.
>> 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.
> 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.
> (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.
> uniform struct apple a;
>
> float4 main() : sv_target
> {
> return a.t[0].Load(0);
> }
>
> [We don't currently have support for unbounded arrays in the shader_runner infrastructure, though,
> so it doesn't urgently need to be a test case. Alternatively it could be written in C.]
>
Okay, I will see if I can write a C test, if not now, later.
>> | variable_decl '=' complex_initializer
>> {
>> $$ = $1;
>> @@ -3188,6 +3218,24 @@ arrays:
>> $$.sizes[$$.count++] = size;
>> }
>>> +implicit_arrays:
>> + arrays
>> + | '[' ']' arrays
>> + {
>> + uint32_t *new_array;
>> +
>> + $$ = $3;
>> +
>> + if (!(new_array = hlsl_realloc(ctx, $$.sizes, ($$.count + 1) * sizeof(*new_array))))
>> + {
>> + vkd3d_free($$.sizes);
>> + YYABORT;
>> + }
>> +
>> + $$.sizes = new_array;
>> + $$.sizes[$$.count++] = HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT;
>> + }
>> +
>
> I think this should be part of the "arrays" rule, actually. Besides being simpler YACC, it allows
> us to generate more helpful messages than "syntax error" if someone tries to use an empty array
> size in an invalid context.
Okay, I will try moving everything outside the parser rules. But we will need to introduce several
checks to ensure that all the array sizes are defined where they are needed, including typedefs,
casts, struct members.
Now that I think about it, it may also be worth testing if implicit size arrays are allowed in function
argument and return types.
April 28, 2022
Re: [PATCH vkd3d 6/8] vkd3d-shader/hlsl: Use a more sensible source location in add_constructor().
by Zebediah Figura
On 4/28/22 08:32, Giovanni Mascellani wrote:
> Signed-off-by: Giovanni Mascellani <gmascellani(a)codeweavers.com>
> ---
> libs/vkd3d-shader/hlsl.y | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/libs/vkd3d-shader/hlsl.y b/libs/vkd3d-shader/hlsl.y
> index 3fff8fc6..b68d9700 100644
> --- a/libs/vkd3d-shader/hlsl.y
> +++ b/libs/vkd3d-shader/hlsl.y
> @@ -2077,7 +2077,7 @@ static struct list *add_constructor(struct hlsl_ctx *ctx, struct hlsl_type *type
> continue;
> }
>
> - initialize_var_components(ctx, params->instrs, var, &idx, arg, &loc);
> + initialize_var_components(ctx, params->instrs, var, &idx, arg, &arg->loc);
> }
>
> if (!(load = hlsl_new_var_load(ctx, var, loc)))
Note that this makes the "loc" parameter to initialize_var_components()
redundant.
I think this patch makes sense, although do you happen to have an
example of an error message that's improved this way?
April 28, 2022
Re: [PATCH vkd3d 5/8] vkd3d-shader/hlsl: Check argument count for constructors.
by Zebediah Figura
On 4/28/22 08:32, Giovanni Mascellani wrote:
> Based on a patch by Francisco Casas.
>
> Signed-off-by: Giovanni Mascellani <gmascellani(a)codeweavers.com>
> ---
> libs/vkd3d-shader/hlsl.y | 8 ++++++++
> 1 file changed, 8 insertions(+)
>
> diff --git a/libs/vkd3d-shader/hlsl.y b/libs/vkd3d-shader/hlsl.y
> index e7fe74d8..3fff8fc6 100644
> --- a/libs/vkd3d-shader/hlsl.y
> +++ b/libs/vkd3d-shader/hlsl.y
> @@ -2044,12 +2044,20 @@ static struct list *add_call(struct hlsl_ctx *ctx, const char *name,
> static struct list *add_constructor(struct hlsl_ctx *ctx, struct hlsl_type *type,
> struct parse_initializer *params, struct vkd3d_shader_location loc)
> {
> + unsigned int size = initializer_size(params);
> static unsigned int counter;
> struct hlsl_ir_load *load;
> struct hlsl_ir_var *var;
> unsigned int i, idx = 0;
> char name[23];
>
> + if (hlsl_type_component_count(type) != size)
> + {
> + hlsl_error(ctx, &loc, VKD3D_SHADER_ERROR_HLSL_WRONG_PARAMETER_COUNT,
> + "Expected %u components in constructor, but got %u.", hlsl_type_component_count(type), size);
> + return NULL;
> + }
> +
> sprintf(name, "<constructor-%x>", counter++);
> if (!(var = hlsl_new_synthetic_var(ctx, name, type, loc)))
> return NULL;
We already handle this in the caller.
April 28, 2022
Re: [PATCH vkd3d 4/8] vkd3d-shader/hlsl: Support initialization of implicit size arrays.
by Zebediah Figura
On 4/28/22 08:32, Giovanni Mascellani wrote:
> @@ -328,6 +330,9 @@ struct hlsl_type *hlsl_new_array_type(struct hlsl_ctx *ctx, struct hlsl_type *ba
>
> list_add_tail(&ctx->types, &type->entry);
>
> + if (type->e.array.elements_count == HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT)
> + TRACE("Implicit element count.\n");
> +
> return type;
> }
>
When can this currently happen?
> 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).
I think we need tests for all of these corner cases, and also a test for
missing initializers.
> + }
> 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).
It also aborts compilation somewhat unnecessarily.
(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[];
};
uniform struct apple a;
float4 main() : sv_target
{
return a.t[0].Load(0);
}
[We don't currently have support for unbounded arrays in the
shader_runner infrastructure, though, so it doesn't urgently need to be
a test case. Alternatively it could be written in C.]
> | variable_decl '=' complex_initializer
> {
> $$ = $1;
> @@ -3188,6 +3218,24 @@ arrays:
> $$.sizes[$$.count++] = size;
> }
>
> +implicit_arrays:
> + arrays
> + | '[' ']' arrays
> + {
> + uint32_t *new_array;
> +
> + $$ = $3;
> +
> + if (!(new_array = hlsl_realloc(ctx, $$.sizes, ($$.count + 1) * sizeof(*new_array))))
> + {
> + vkd3d_free($$.sizes);
> + YYABORT;
> + }
> +
> + $$.sizes = new_array;
> + $$.sizes[$$.count++] = HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT;
> + }
> +
I think this should be part of the "arrays" rule, actually. Besides
being simpler YACC, it allows us to generate more helpful messages than
"syntax error" if someone tries to use an empty array size in an invalid
context.
April 28, 2022
Re: [PATCH vkd3d 1/8] vkd3d-shader/hlsl: Detect missing loads on rhs when splitting copies of non-numeric types.
by Zebediah Figura
On 4/28/22 08:31, Giovanni Mascellani wrote:
> From: Francisco Casas <fcasas(a)codeweavers.com>
>
> Otherwise we can get failed assertions:
> assert(node->type == HLSL_IR_LOAD);
> because broadcasts to these types are not implemented yet.
>
> Signed-off-by: Francisco Casas <fcasas(a)codeweavers.com>
> Signed-off-by: Giovanni Mascellani <gmascellani(a)codeweavers.com>
> ---
> libs/vkd3d-shader/hlsl_codegen.c | 12 ++++++++++++
> 1 file changed, 12 insertions(+)
>
> diff --git a/libs/vkd3d-shader/hlsl_codegen.c b/libs/vkd3d-shader/hlsl_codegen.c
> index e945b94d..f7396a96 100644
> --- a/libs/vkd3d-shader/hlsl_codegen.c
> +++ b/libs/vkd3d-shader/hlsl_codegen.c
> @@ -637,6 +637,12 @@ static bool split_array_copies(struct hlsl_ctx *ctx, struct hlsl_ir_node *instr,
> element_type = type->e.array.type;
> element_size = hlsl_type_get_array_element_reg_size(element_type);
>
> + if (rhs->type != HLSL_IR_LOAD)
> + {
> + hlsl_fixme(ctx, &instr->loc, "Array store rhs is not HLSL_IR_LOAD. Broadcast may be missing.");
> + return false;
> + }
> +
> for (i = 0; i < type->e.array.elements_count; ++i)
> {
> if (!split_copy(ctx, store, hlsl_ir_load(rhs), i * element_size, element_type))
In the future, though, I think we want to try to handle this in the
place where we generate the invalid IR, if that's feasible. That makes
it more inherently clear why we're generating invalid IR, and it also
ensures we don't forget to remove the hlsl_fixme later. In this case I
believe it'd be a matter of handling it for explicit and implicit casts.
April 28, 2022
Re: [PATCH vkd3d 2/8] tests: Test complex broadcasts.
by Zebediah Figura
Signed-off-by: Zebediah Figura <zfigura(a)codeweavers.com>
---
As a follow-up, adding a test for scalar-to-array casts would also be
helpful.
April 28, 2022
Re: [PATCH vkd3d 1/8] vkd3d-shader/hlsl: Detect missing loads on rhs when splitting copies of non-numeric types.
by Zebediah Figura
Signed-off-by: Zebediah Figura <zfigura(a)codeweavers.com>
April 28, 2022
Re: [PATCH vkd3d 3/8] tests: Test initialization of implicit size arrays.
by Zebediah Figura
Signed-off-by: Zebediah Figura <zfigura(a)codeweavers.com>
April 28, 2022
Re: [PATCH vkd3d 7/8] vkd3d-shader/hlsl: Allow storing to matrices.
by Zebediah Figura
Signed-off-by: Zebediah Figura <zfigura(a)codeweavers.com>
April 28, 2022
Re: [PATCH vkd3d 8/8] vkd3d-shader/hlsl: Handle branches in copy propagation.
by Zebediah Figura
Signed-off-by: Zebediah Figura <zfigura(a)codeweavers.com>
April 28, 2022
Re: MR6v2 - Avoid performance degradation due to vDSO unmapping (BZ#52313)
by Jinoh Kang (@iamahuman)
I have cleaned up the code a bit. Also, I clarified the rationale in d45761a20b471b3ccb64df5fe7509d4cce753a92 (loader: Don't clobber existing memory mappings when reserving addresses.).
--
https://gitlab.winehq.org/wine/wine/-/merge_requests/6#note_500
April 28, 2022
[PATCH v2 10/10] loader: Enable all remap logic by default.
by Jinoh Kang
From: Jinoh Kang <jinoh.kang.kr(a)gmail.com>
Signed-off-by: Jinoh Kang <jinoh.kang.kr(a)gmail.com>
---
loader/preloader.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/loader/preloader.c b/loader/preloader.c
index 6ee15f37514..1bc90da1d19 100644
--- a/loader/preloader.c
+++ b/loader/preloader.c
@@ -291,11 +291,11 @@ enum remap_policy
REMAP_POLICY_SKIP = 2,
LAST_REMAP_POLICY,
- REMAP_POLICY_DEFAULT_VDSO = REMAP_POLICY_SKIP,
+ REMAP_POLICY_DEFAULT_VDSO = REMAP_POLICY_ON_CONFLICT,
#ifdef __arm__
- REMAP_POLICY_DEFAULT_SIGPAGE = REMAP_POLICY_SKIP,
+ REMAP_POLICY_DEFAULT_SIGPAGE = REMAP_POLICY_ON_CONFLICT,
#endif
- REMAP_POLICY_DEFAULT_STACK = REMAP_POLICY_SKIP,
+ REMAP_POLICY_DEFAULT_STACK = REMAP_POLICY_ON_CONFLICT,
};
/*
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/6
April 28, 2022
[PATCH v2 09/10] 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 | 146 ++++++++++++++++++++++++++++++++++++++-------
1 file changed, 125 insertions(+), 21 deletions(-)
diff --git a/loader/preloader.c b/loader/preloader.c
index 091fb16a2c0..6ee15f37514 100644
--- a/loader/preloader.c
+++ b/loader/preloader.c
@@ -258,6 +258,7 @@ enum vma_type_flags
#ifdef __arm__
VMA_SIGPAGE = 0x08,
#endif
+ VMA_STACK = 0x10,
};
struct vma_area
@@ -294,6 +295,7 @@ enum remap_policy
#ifdef __arm__
REMAP_POLICY_DEFAULT_SIGPAGE = REMAP_POLICY_SKIP,
#endif
+ REMAP_POLICY_DEFAULT_STACK = REMAP_POLICY_SKIP,
};
/*
@@ -1320,6 +1322,75 @@ static struct wld_auxv *delete_auxv_entries( struct wld_auxv *auxv,
return dstp;
}
+/*
+ * 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 )
+{
+ stackargs_switch_stack( newinfo, oldinfo, newstack );
+
+ *(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_last - newinfo->auxv );
+ wld_memmove( newinfo->auxv_last, oldinfo->auxv_last,
+ (unsigned long)newstackend - (unsigned long)newinfo->auxv_last );
+}
+
/*
* set_auxiliary_values
*
@@ -2167,7 +2238,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 *act_count )
+static void scan_vma( struct vma_area_list *list, size_t *act_count, void *stack_ptr )
{
int fd;
size_t n = 0;
@@ -2191,6 +2262,9 @@ static void scan_vma( struct vma_area_list *list, size_t *act_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++;
}
@@ -2221,7 +2295,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;
@@ -2236,7 +2310,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));
@@ -2493,7 +2567,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:
@@ -2541,7 +2615,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:
@@ -2552,29 +2626,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
@@ -2637,15 +2740,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 28, 2022
[PATCH v2 08/10] 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 f067523d164..091fb16a2c0 100644
--- a/loader/preloader.c
+++ b/loader/preloader.c
@@ -252,9 +252,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
@@ -287,7 +290,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
};
/*
@@ -1981,6 +1987,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;
@@ -2493,6 +2503,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
*
@@ -2548,6 +2607,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 );
@@ -2580,7 +2640,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 28, 2022
[PATCH v2 07/10] 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 | 580 ++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 577 insertions(+), 3 deletions(-)
diff --git a/loader/preloader.c b/loader/preloader.c
index 0af25394830..f067523d164 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,18 @@ 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.
*/
@@ -193,10 +214,55 @@ struct linebuffer
int truncated;
};
+/*
+ * Flags that specify the kind of each VMA entry read from /proc/self/maps.
+ *
+ * On Linux, Reading /proc/self/maps is the only reliable way to identify the
+ * exact range of vDSO/vvar mapping. The reason is twofold:
+ *
+ * 1. vDSO usually hard-codes vvar's offset relative to vDSO. Therefore,
+ * remapping vDSO requires vvar to be also remapped as well. However, vvar's
+ * size and its location relative to vDSO is *not* guaranteed by 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, [vvar] was a single page [6].
+ *
+ * 2. It's very difficult to deduce vDSO and vvar's size and offset relative to
+ * each other. Since vvar's symbol does not exist in vDSO's symtab,
+ * determining the layout would require parsing vDSO's code.
+ *
+ * Also note that CRIU (Checkpoint Restore In Userspace) has maps parsing code
+ * just 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/a315774e11b4da1eb36446ae996…
+ */
+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
@@ -209,6 +275,57 @@ 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 a 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;
+
+ struct vma_area_list *vma_list;
+
+ /*
+ * Difference between the base address of the new mapping and the old mapping.
+ *
+ * Set to zero if the handler reverted mappings to old state before returning
+ * in order to safely return when it detects failed remapping.
+ */
+ unsigned long delta;
+
+ /*
+ * Whether remapping was successfully recognised by the kernel.
+ *
+ * If the signal handler is never called (due to e.g. being blocked), it is counted
+ * as being unsuccessful.
+ */
+ unsigned char is_successful;
+
+ /*
+ * Whether remapping could not be recognised by the kernel.
+ *
+ * If both is_successful and is_failed are set, is_failed takes precedence.
+ * The flags are intentionally made redundant to detect multiple successive
+ * invocation of the signal handler due to external signal delivery. */
+ 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
@@ -244,6 +361,15 @@ 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
@@ -381,6 +507,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;
@@ -389,6 +525,64 @@ 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];
@@ -467,9 +661,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 */ );
@@ -577,9 +777,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 */ );
@@ -679,9 +896,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 */ );
@@ -1695,6 +1929,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;
@@ -1725,11 +1960,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++;
@@ -1737,6 +1972,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;
}
@@ -1836,6 +2082,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
*
@@ -1921,6 +2237,262 @@ 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;
+ }
+
+ /* Signal handler might be called several times externally,
+ * 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 on i386
+ * the fast syscall gate (which uses SYSENTER on Intel CPUs). The kernel keeps
+ * track of the addresses of both of these things per process, and those
+ * addresses need to be updated accordingly if the vDSO address changes.
+ * Without proper support, mremap() on vDSO still succeeds, 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 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). On
+ * i386, we additionally check for the syscall gate. 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 can no longer be called,
+ * we can safely access the result data. */
+ 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
*
@@ -2008,6 +2580,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)
@@ -2037,7 +2611,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 28, 2022
[PATCH v2 06/10] 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 6c3c21f5978..0af25394830 100644
--- a/loader/preloader.c
+++ b/loader/preloader.c
@@ -1137,7 +1137,7 @@ static void set_auxiliary_values( struct preloader_state *state,
*
* 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 28, 2022
[PATCH v2 05/10] 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 of 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
call 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 skipped.
Meanwhile, if any part of the preloader overlaps with a reserved
range, it is unmapped. In other words, we forcibly perform (1) here.
This could lead to crash if it ever touched code or data that are
still being used.
- vDSO/vvar. This is overwritten and ignored completely when it
overlaps 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 overlaps 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 treatments required for existing
VMAs are either (2) or (3), not (1).
2. (1) irrevocably overwrites some useful preexisting useful VMAs such
as vDSO if they overlap with any reserved ranges. It's more useful
to relocate vDSO to a safe address if possible, which is supported by
newer versions of Linux kernel. 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 | 392 ++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 369 insertions(+), 23 deletions(-)
diff --git a/loader/preloader.c b/loader/preloader.c
index b46187dbf8e..6c3c21f5978 100644
--- a/loader/preloader.c
+++ b/loader/preloader.c
@@ -184,6 +184,31 @@ struct preloader_state
struct stackarg_info s;
};
+struct linebuffer
+{
+ char *base;
+ char *limit;
+ char *head;
+ char *tail;
+ int truncated;
+};
+
+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
@@ -742,6 +767,17 @@ static inline void *wld_memmove( void *dest, const void *src, size_t len )
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
*
@@ -1596,6 +1632,333 @@ 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); /* extra NULL byte */
+ 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.
+ * In this case, 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 )))
+ {
+ line = lbuf->tail;
+ lbuf->tail = lnp + 1;
+ if (!lbuf->truncated)
+ {
+ *lnp = '\0';
+ return line;
+ }
+ lbuf->truncated = 0;
+ }
+
+ if (lbuf->base == lbuf->tail)
+ {
+ if (lbuf->head == lbuf->limit)
+ {
+ line = lbuf->tail;
+ lbuf->tail = lbuf->head;
+ lbuf->truncated = 1;
+ *lbuf->head = '\0';
+ return line;
+ }
+ }
+ else 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, 16, 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->start < item->start) 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 *act_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);
+ *act_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
@@ -1612,6 +1975,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 );
@@ -1640,29 +2004,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 */
@@ -1723,6 +2067,8 @@ void* wld_start( void **stack )
}
#endif
+ free_vma_list( &vma_list );
+
*stack = state.s.stack;
return (void *)ld_so_map.l_entry;
}
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/6
April 28, 2022
[PATCH v2 04/10] 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 7584de51eca..b46187dbf8e 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 */ );
@@ -1601,6 +1623,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 28, 2022
[PATCH v2 03/10] 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 13406984c99..7584de51eca 100644
--- a/loader/preloader.c
+++ b/loader/preloader.c
@@ -1503,18 +1503,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 - 1)
+ return i;
}
- return 0;
+ return -1;
}
/* remove a range from the preload list */
@@ -1537,7 +1548,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;
@@ -1625,7 +1636,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 28, 2022
[PATCH v2 02/10] 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 7b69046238a..13406984c99 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>
@@ -719,6 +720,42 @@ static inline void *wld_memmove( void *dest, const void *src, size_t len )
return dest;
}
+/*
+ * 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
*
@@ -1419,27 +1456,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 28, 2022
[PATCH v2 01/10] 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 | 309 ++++++++++++++++++++++++++++++++++-----------
1 file changed, 236 insertions(+), 73 deletions(-)
diff --git a/loader/preloader.c b/loader/preloader.c
index 585be50624f..7b69046238a 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_last;
+};
+
+/* 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,32 @@ 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;
+}
+
+static inline void *wld_memmove( void *dest, const void *src, size_t len )
+{
+ unsigned char *destp = dest;
+ const unsigned char *srcp = src;
+
+ if ((unsigned long)dest - (unsigned long)src < len)
+ {
+ destp += len;
+ srcp += len;
+ while (len--) *--destp = *--srcp;
+ }
+ else
+ {
+ while (len--) *destp++ = *srcp++;
+ }
+
+ return dest;
+}
+
/*
* wld_printf - just the basics
*
@@ -794,72 +839,201 @@ static void dump_auxiliary( struct wld_auxv *av )
}
#endif
-/*
- * set_auxiliary_values
+/* find_auxv_last_entry
*
- * Set the new auxiliary values
+ * Get the last auxv entry (AT_NULL).
*/
-static void set_auxiliary_values( struct wld_auxv *av, const struct wld_auxv *new_av,
- const struct wld_auxv *delete_av, void **stack )
+struct wld_auxv *find_auxv_last_entry( const struct wld_auxv *auxv )
{
- int i, j, av_count = 0, new_count = 0, delete_count = 0;
- char *src, *dst;
+ const struct wld_auxv *ptr;
- /* count how many aux values we have already */
- while (av[av_count].a_type != AT_NULL) av_count++;
+ for (ptr = auxv; ptr->a_type != AT_NULL; ptr++)
+ ;
- /* delete unwanted values */
- for (j = 0; delete_av[j].a_type != AT_NULL; j++)
+ return (struct wld_auxv *)ptr;
+}
+
+/* find_auxv_entry
+ *
+ * Find the first auxv entry that has the given type, or NULL if no such entry
+ * is found.
+ */
+static struct wld_auxv *find_auxv_entry( const struct wld_auxv *auxv,
+ const struct wld_auxv *auxv_last,
+ ElfW(Addr) type )
+{
+ const struct wld_auxv *ptr;
+
+ for (ptr = auxv; ptr != auxv_last; ptr++)
{
- for (i = 0; i < av_count; i++) if (av[i].a_type == delete_av[j].a_type)
- {
- av[i].a_type = av[av_count-1].a_type;
- av[i].a_un.a_val = av[av_count-1].a_un.a_val;
- av[--av_count].a_type = AT_NULL;
- delete_count++;
- break;
- }
+ if (ptr->a_type == type) return (struct wld_auxv *)ptr;
}
- /* count how many values we have in new_av that aren't in av */
- for (j = 0; new_av[j].a_type != AT_NULL; j++)
+ return NULL;
+}
+
+/*
+ * 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;
+
+ 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;
+
+ outinfo->stack = stack;
+ outinfo->argc = argc;
+ outinfo->argv = argv;
+ outinfo->envp = envp;
+ outinfo->auxv = auxv;
+ outinfo->auxv_last = find_auxv_last_entry( auxv );
+}
+
+/*
+ * stackargs_getenv
+ *
+ * Retrieve the value of an environment variable from stackarg_info.
+ */
+static char *stackargs_getenv( const struct stackarg_info *info, const char *name )
+{
+ char **envp = info->envp;
+ size_t namelen = wld_strlen( name );
+
+ while (*envp)
{
- for (i = 0; i < av_count; i++) if (av[i].a_type == new_av[j].a_type) break;
- if (i == av_count) new_count++;
+ if (wld_strncmp( *envp, name, namelen ) == 0 &&
+ (*envp)[namelen] == '=') return *envp + namelen + 1;
+ envp++;
}
+ 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;
+}
+
+/*
+ * stackargs_switch_stack
+ *
+ * Fix up variables in oldinfo to the given stack base, and return
+ * the new information to newinfo (does not modify oldinfo).
+ */
+static void stackargs_switch_stack( struct stackarg_info *newinfo, struct stackarg_info *oldinfo, void *newstack )
+{
+ unsigned long delta = (unsigned long)newstack - (unsigned long)oldinfo->stack;
+
+ /* NOTE it is legal that newinfo == oldinfo */
+ 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_last = (void *)((unsigned long)oldinfo->auxv_last + delta);
+}
+
+/* delete_auxv_entries
+ *
+ * Delete specified types from the given auxiliary vector.
+ * Returns the address of the last entry of the vector.
+ */
+static struct wld_auxv *delete_auxv_entries( struct wld_auxv *auxv,
+ struct wld_auxv *auxv_last,
+ const struct wld_auxv *delete_av )
+{
+ struct wld_auxv *dstp = auxv, *srcp;
+ struct wld_auxv *delete_av_end = find_auxv_last_entry( delete_av );
- src = (char *)*stack;
- dst = src - (new_count - delete_count) * sizeof(*av);
- dst = (char *)((unsigned long)dst & ~15);
- if (dst < src) /* need to make room for the extra values */
+ for (srcp = auxv; srcp != auxv_last; srcp++)
{
- int len = (char *)(av + av_count + 1) - src;
- for (i = 0; i < len; i++) dst[i] = src[i];
+ if (!find_auxv_entry( delete_av, delete_av_end, srcp->a_type ))
+ {
+ if (dstp != srcp)
+ {
+ dstp->a_type = srcp->a_type;
+ dstp->a_un.a_val = srcp->a_un.a_val;
+ }
+ dstp++;
+ }
}
- else if (dst > src) /* get rid of unused values */
+
+ dstp->a_type = AT_NULL;
+ dstp->a_un.a_val = 0;
+ return dstp;
+}
+
+/*
+ * set_auxiliary_values
+ *
+ * Set the new auxiliary values
+ */
+static void set_auxiliary_values( struct preloader_state *state,
+ const struct wld_auxv *new_av,
+ const struct wld_auxv *delete_av )
+{
+ size_t i, new_count = 0, delete_count;
+ unsigned long dst;
+ struct wld_auxv *avp, *new_auxv_last;
+
+ /* delete unwanted values */
+ new_auxv_last = delete_auxv_entries( state->s.auxv, state->s.auxv_last, delete_av );
+ delete_count = state->s.auxv_last - new_auxv_last;
+ state->s.auxv_last = new_auxv_last;
+
+ /* count how many values we have in new_av that aren't in av */
+ for (i = 0; new_av[i].a_type != AT_NULL; i++)
{
- int len = (char *)(av + av_count + 1) - src;
- for (i = len - 1; i >= 0; i--) dst[i] = src[i];
+ if (find_auxv_entry( state->s.auxv, state->s.auxv_last, new_av[i].a_type )) new_count++;
}
- *stack = dst;
- av = (struct wld_auxv *)((char *)av + (dst - src));
+
+ dst = ((unsigned long)state->s.stack -
+ (new_count - delete_count) * sizeof(struct wld_auxv)) & ~15;
+ wld_memmove( (void *)dst, state->s.stack,
+ (unsigned long)(state->s.auxv_last + 1) -
+ (unsigned long)state->s.stack );
+ stackargs_switch_stack( &state->s, &state->s, (void *)dst );
/* now set the values */
- for (j = 0; new_av[j].a_type != AT_NULL; j++)
+ for (i = 0; new_av[i].a_type != AT_NULL; i++)
{
- for (i = 0; i < av_count; i++) if (av[i].a_type == new_av[j].a_type) break;
- if (i < av_count) av[i].a_un.a_val = new_av[j].a_un.a_val;
+ avp = find_auxv_entry( state->s.auxv, state->s.auxv_last, new_av[i].a_type );
+ if (avp) avp->a_un.a_val = new_av[i].a_un.a_val;
else
{
- av[av_count].a_type = new_av[j].a_type;
- av[av_count].a_un.a_val = new_av[j].a_un.a_val;
- av_count++;
+ state->s.auxv_last->a_type = new_av[i].a_type;
+ state->s.auxv_last->a_un.a_val = new_av[i].a_un.a_val;
+ state->s.auxv_last++;
}
}
+ state->s.auxv_last->a_type = AT_NULL;
+ state->s.auxv_last->a_un.a_val = 0;
#ifdef DUMP_AUX_INFO
wld_printf("New auxiliary info:\n");
- dump_auxiliary( av );
+ dump_auxiliary( state->s.auxv );
#endif
}
@@ -1369,47 +1543,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] );
-
- /* skip over the parameters */
- p = argv + *pargc + 1;
+ parse_stackargs( &state.s, *stack );
- /* 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++;
- }
+ if (state.s.argc < 2) fatal_error( "Usage: %s wine_binary [args]\n", state.s.argv[0] );
- 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 +1599,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 +1616,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,14 +1631,13 @@ 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 );
+ set_auxiliary_values( &state, new_av, delete_av );
#ifdef DUMP_AUX_INFO
- wld_printf("new stack = %p\n", *stack);
+ wld_printf("new stack = %p\n", state.s.stack);
wld_printf("jumping to %p\n", (void *)ld_so_map.l_entry);
#endif
#ifdef DUMP_MAPS
@@ -1490,6 +1652,7 @@ void* wld_start( void **stack )
}
#endif
+ *stack = state.s.stack;
return (void *)ld_so_map.l_entry;
}
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/6
April 28, 2022
[PATCH v2 0/10] 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
```
--
v2: loader: Enable all remap logic by default.
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 28, 2022
[PATCH] uiautomationcore: Make hwnd_host_provider_vtbl static const.
by Connor McAdams
Signed-off-by: Connor McAdams <cmcadams(a)codeweavers.com>
---
dlls/uiautomationcore/uia_main.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/uiautomationcore/uia_main.c b/dlls/uiautomationcore/uia_main.c
index c250d82a1e6..a303e71cf76 100644
--- a/dlls/uiautomationcore/uia_main.c
+++ b/dlls/uiautomationcore/uia_main.c
@@ -247,7 +247,7 @@ HRESULT WINAPI hwnd_host_provider_get_HostRawElementProvider(IRawElementProvider
return S_OK;
}
-IRawElementProviderSimpleVtbl hwnd_host_provider_vtbl = {
+static const IRawElementProviderSimpleVtbl hwnd_host_provider_vtbl = {
hwnd_host_provider_QueryInterface,
hwnd_host_provider_AddRef,
hwnd_host_provider_Release,
--
2.25.1
April 28, 2022
Re: [PATCH] include: Add WINE_ALLOC_SIZE attribute to heap_calloc().
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=113766
Your paranoid android.
=== debian11 (build log) ===
../wine/include/winbase.h:2353:115: error: Too many macro arguments (1)
Task: The win32 Wine build failed
=== debian11 (build log) ===
../wine/include/winbase.h:2353:115: error: Too many macro arguments (1)
Task: The wow64 Wine build failed
April 28, 2022
[PATCH] include: Add WINE_ALLOC_SIZE attribute to heap_calloc().
by Brendan Shanks
Signed-off-by: Brendan Shanks <bshanks(a)codeweavers.com>
---
This is a C99 variadic macro, but I tested with GCC 4.5 and it seemed
supported in all modes (including the default).
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 28, 2022
Re: [PATCH v5 1/2] include: Add winusb.h file.
by Zebediah Figura
On 4/28/22 07:33, Rémi Bernon wrote:
> On 4/14/22 01:36, Mohamad Al-Jaf wrote:
>> +#ifndef _WINUSB_H_
>> +#define _WINUSB_H_
>> +
>> +#ifdef __cplusplus
>> +extern "C" {
>> +#endif
>> +
>> +typedef PVOID WINUSB_INTERFACE_HANDLE, *PWINUSB_INTERFACE_HANDLE;
>> +
>> +typedef struct _WINUSB_SETUP_PACKET {
>> + UCHAR RequestType;
>> + UCHAR Request;
>> + USHORT Value;
>> + USHORT Index;
>> + USHORT Length;
>> +} WINUSB_SETUP_PACKET, *PWINUSB_SETUP_PACKET;
>> +
>
> 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.
April 28, 2022
Re: [PATCH vkd3d 3/4] vkd3d: Replace the signaled semaphore list with a resizable array.
by Henri Verbeet
On Tue, 26 Apr 2022 at 15:48, Conor McCarthy <cmccarthy(a)codeweavers.com> wrote:
> @@ -590,18 +590,19 @@ 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)
> + for (i = 0; i < fence->semaphore_count; ++i)
> {
> 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)
> @@ -612,10 +613,7 @@ static void d3d12_fence_garbage_collect_vk_semaphores_locked(struct d3d12_fence
> 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];
> }
I suppose that works, although decrementing "i" here only to have the
loop control increment it again seems a bit awkward; more so for the
case where "i == 0". It seems tempting to suggest using a while-loop
here, only incrementing "i" when we take the "continue" path, and then
using "*current = fence->semaphores[--fence->semaphore_count];" for
the last line of the block.
> @@ -703,10 +704,8 @@ 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;
> + i = semaphore - fence->semaphores;
> + fence->semaphores[i] = fence->semaphores[--fence->semaphore_count];
I.e., "*semaphore = fence->semaphores[--fence->semaphore_count];", right?
April 28, 2022
Re: [PATCH vkd3d 2/4] vkd3d: Create one fence worker thread per command queue.
by Henri Verbeet
On Tue, 26 Apr 2022 at 15:48, Conor McCarthy <cmccarthy(a)codeweavers.com> wrote:
> @@ -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->vk_info.KHR_timeline_semaphore
> + ? vkd3d_wait_for_gpu_timeline_semaphore : vkd3d_wait_for_gpu_fence;
I think the original "device->use_timeline_semaphores" check was
correct. As-is, this will try to use
vkd3d_wait_for_gpu_timeline_semaphore() on fences without a timeline
semaphore for me, with predictable results.
April 28, 2022
Re: [PATCH vkd3d 1/4] vkd3d: Introduce an internal refcount to d3d12_fence to replace the thread waiting mechanism.
by Henri Verbeet
Signed-off-by: Henri Verbeet <hverbeet(a)codeweavers.com>
April 28, 2022
Re: Access to graphics memory mappings on upcoming WOW64 implementation
by Zebediah Figura
On 4/28/22 03:10, Giovanni Mascellani wrote:
> Hi,
>
> Il 25/04/22 20:20, Zebediah Figura ha scritto:
>> Yes, procfs still displays the mapping in that case. Note that what's
>> listed is the path (and inode), so we would reopen the path and then
>> map it.
>
> Couldn't the driver have deleted the file too?
Indeed, that is possible. I don't think it'd happen in practice, but
it's one of the reasons it might be nice to have a fallback like I had
mentioned.
> Or can you create a new
> link to a file given its inode? I couldn't find how after a brief
> internet search.
I don't think you can do this; it would be a security hole.
April 28, 2022
Re: [PATCH vkd3d 4/8] vkd3d-shader/hlsl: Support initialization of implicit size arrays.
by Francisco Casas
Hello,
April 28, 2022 10:49 AM, "Giovanni Mascellani" <gmascellani(a)codeweavers.com> wrote:
> Hi,
>
> I think the patch generally looks good, but there are a few details to fix.
>
> Il 28/04/22 15:32, Giovanni Mascellani ha scritto:
>
>> @@ -328,6 +330,9 @@ struct hlsl_type *hlsl_new_array_type(struct hlsl_ctx *ctx, struct hlsl_type
>> *ba
>>> list_add_tail(&ctx->types, &type->entry);
>>> + if (type->e.array.elements_count == HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT)
>> + TRACE("Implicit element count.\n");
>> +
>
> I don't understand what this should be useful for, is it perhaps a leftover of development?
>
Yes, probably a leftover. I think I was worried adding the special case of implicit arrays could
generate bugs in other parts of the code if they didn't consider it.
But that shouldn't be the case with this implementation since this special case is not present
after the initialization.
>> return type;
>> }
>>> @@ -410,6 +415,7 @@ unsigned int hlsl_type_component_count(struct hlsl_type *type)
>> }
>> if (type->type == HLSL_CLASS_ARRAY)
>> {
>> + assert(type->e.array.elements_count != HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT);
>> return hlsl_type_component_count(type->e.array.type) * type->e.array.elements_count;
>> }
>> if (type->type != HLSL_CLASS_STRUCT)
>> @@ -1023,7 +1029,12 @@ struct vkd3d_string_buffer *hlsl_type_to_string(struct hlsl_ctx *ctx, const
>> stru
>> }
>>> for (t = type; t->type == HLSL_CLASS_ARRAY; t = t->e.array.type)
>> - vkd3d_string_buffer_printf(string, "[%u]", t->e.array.elements_count);
>> + {
>> + if (t->e.array.elements_count == HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT)
>> + vkd3d_string_buffer_printf(string, "[<undefined>]");
>> + else
>> + vkd3d_string_buffer_printf(string, "[%u]", t->e.array.elements_count);
>> + }
>
> I guess elements_count should never be IMPLICIT here, should it? As soon as the hunk in
> declare_vars() is done, all the counts are known.
>
Yes, but I think we better cover all cases in a function we may be using for debugging purposes.
> At any rate, if it can happen that the IMPLICIT branch can be taken here, then I'd just use "[]"
> instead of "[<undefined>]", which is the standard C way.
>
I think I opted for this verbose alternative since finding an implicit size array in the wild would
be an oddity worth emphasizing. But maybe I was paranoid. I will change it to "[]".
>> return string;
>> }
>>> diff --git a/libs/vkd3d-shader/hlsl.h b/libs/vkd3d-shader/hlsl.h
>> index 28b2ff1b..94597999 100644
>> --- a/libs/vkd3d-shader/hlsl.h
>> +++ b/libs/vkd3d-shader/hlsl.h
>> @@ -227,6 +227,8 @@ struct hlsl_src
>>> #define HLSL_MODIFIERS_MAJORITY_MASK (HLSL_MODIFIER_ROW_MAJOR | HLSL_MODIFIER_COLUMN_MAJOR)
>>> +#define HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT 0xffffffff
>> +
>> struct hlsl_reg_reservation
>> {
>> char type;
>> 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);
>
> Maybe it's just me, but here I would also assert that i == v->arrays.count - 1. This should already
> be forced by the parser, but it's a useful code documentation anyway.
>
Sounds good.
>> + v->arrays.sizes[i] = (size + elem_components - 1)/elem_components;
>
> Why "+ elem_components - 1", given that immediately below we error out if size is not a multiple of
> elem_components?
>
> Also, notice that elem_components could be zero. You should handle that gracefully.
>
These are good points, I will update the patch.
>> +
>> + 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;
>> + }
>> + }
>> type = hlsl_new_array_type(ctx, type, v->arrays.sizes[i]);
>> + }
>> vkd3d_free(v->arrays.sizes);
>
> Thanks, Giovanni.
April 28, 2022
Re: [PATCH 3/3] wined3d: Refuse blits between height-scaled formats in the raw blitter.
by Henri Verbeet
Signed-off-by: Henri Verbeet <hverbeet(a)codeweavers.com>
April 28, 2022
Re: [PATCH 2/3] wined3d: Refuse blits between height-scaled formats in the FBO blitter.
by Henri Verbeet
Signed-off-by: Henri Verbeet <hverbeet(a)codeweavers.com>
April 28, 2022
Re: [PATCH 1/3] wined3d: Refuse blits between height-scaled formats in the CPU blitter.
by Henri Verbeet
Signed-off-by: Henri Verbeet <hverbeet(a)codeweavers.com>
April 28, 2022
Re: [PATCH vkd3d 4/8] vkd3d-shader/hlsl: Support initialization of implicit size arrays.
by Giovanni Mascellani
Hi,
I think the patch generally looks good, but there are a few details to fix.
Il 28/04/22 15:32, Giovanni Mascellani ha scritto:
> @@ -328,6 +330,9 @@ struct hlsl_type *hlsl_new_array_type(struct hlsl_ctx *ctx, struct hlsl_type *ba
>
> list_add_tail(&ctx->types, &type->entry);
>
> + if (type->e.array.elements_count == HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT)
> + TRACE("Implicit element count.\n");
> +
I don't understand what this should be useful for, is it perhaps a
leftover of development?
> return type;
> }
>
> @@ -410,6 +415,7 @@ unsigned int hlsl_type_component_count(struct hlsl_type *type)
> }
> if (type->type == HLSL_CLASS_ARRAY)
> {
> + assert(type->e.array.elements_count != HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT);
> return hlsl_type_component_count(type->e.array.type) * type->e.array.elements_count;
> }
> if (type->type != HLSL_CLASS_STRUCT)
> @@ -1023,7 +1029,12 @@ struct vkd3d_string_buffer *hlsl_type_to_string(struct hlsl_ctx *ctx, const stru
> }
>
> for (t = type; t->type == HLSL_CLASS_ARRAY; t = t->e.array.type)
> - vkd3d_string_buffer_printf(string, "[%u]", t->e.array.elements_count);
> + {
> + if (t->e.array.elements_count == HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT)
> + vkd3d_string_buffer_printf(string, "[<undefined>]");
> + else
> + vkd3d_string_buffer_printf(string, "[%u]", t->e.array.elements_count);
> + }
I guess elements_count should never be IMPLICIT here, should it? As soon
as the hunk in declare_vars() is done, all the counts are known.
At any rate, if it can happen that the IMPLICIT branch can be taken
here, then I'd just use "[]" instead of "[<undefined>]", which is the
standard C way.
> return string;
> }
>
> diff --git a/libs/vkd3d-shader/hlsl.h b/libs/vkd3d-shader/hlsl.h
> index 28b2ff1b..94597999 100644
> --- a/libs/vkd3d-shader/hlsl.h
> +++ b/libs/vkd3d-shader/hlsl.h
> @@ -227,6 +227,8 @@ struct hlsl_src
>
> #define HLSL_MODIFIERS_MAJORITY_MASK (HLSL_MODIFIER_ROW_MAJOR | HLSL_MODIFIER_COLUMN_MAJOR)
>
> +#define HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT 0xffffffff
> +
> struct hlsl_reg_reservation
> {
> char type;
> 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);
Maybe it's just me, but here I would also assert that i ==
v->arrays.count - 1. This should already be forced by the parser, but
it's a useful code documentation anyway.
> + v->arrays.sizes[i] = (size + elem_components - 1)/elem_components;
Why "+ elem_components - 1", given that immediately below we error out
if size is not a multiple of elem_components?
Also, notice that elem_components could be zero. You should handle that
gracefully.
> +
> + 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;
> + }
> + }
> type = hlsl_new_array_type(ctx, type, v->arrays.sizes[i]);
> + }
> vkd3d_free(v->arrays.sizes);
>
Thanks, Giovanni.
April 28, 2022
Re: [PATCH v2 2/2] programs/sc: Allow using separate arguments for parameter name and value.
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=113743
Your paranoid android.
=== w7u_2qxl (32 bit report) ===
sc.exe:
sc.c:336: Test failed: got exit code 1639, expected 0
sc.c:338: Test failed: OpenServiceA failed: 1060
sc.c:339: Test failed: got exit code 1060, expected 0
=== w7u_adm (32 bit report) ===
sc.exe:
sc.c:221: Test failed: got exit code 5, expected 0
sc.c:223: Test failed: OpenServiceA failed: 1060
sc.c:228: Test failed: got exit code 5, expected 1073
sc.c:230: Test failed: OpenServiceA failed: 1060
sc.c:231: Test failed: got exit code 1060, expected 0
sc.c:236: Test failed: got exit code 5, expected 1639
sc.c:240: Test failed: got exit code 5, expected 0
sc.c:242: Test failed: OpenServiceA failed: 1060
sc.c:243: Test failed: got exit code 1060, expected 0
sc.c:246: Test failed: got exit code 5, expected 87
sc.c:250: Test failed: got exit code 5, expected 0
sc.c:252: Test failed: OpenServiceA failed: 1060
sc.c:253: Test failed: got exit code 1060, expected 0
sc.c:258: Test failed: got exit code 5, expected 1639
sc.c:268: Test failed: got exit code 5, expected 0
sc.c:271: Test failed: OpenServiceA failed: 1060
sc.c:272: Test failed: got exit code 1060, expected 0
sc.c:268: Test failed: got exit code 5, expected 0
sc.c:271: Test failed: OpenServiceA failed: 1060
sc.c:272: Test failed: got exit code 1060, expected 0
sc.c:268: Test failed: got exit code 5, expected 0
sc.c:271: Test failed: OpenServiceA failed: 1060
sc.c:272: Test failed: got exit code 1060, expected 0
sc.c:268: Test failed: got exit code 5, expected 0
sc.c:271: Test failed: OpenServiceA failed: 1060
sc.c:272: Test failed: got exit code 1060, expected 0
sc.c:268: Test failed: got exit code 5, expected 0
sc.c:271: Test failed: OpenServiceA failed: 1060
sc.c:272: Test failed: got exit code 1060, expected 0
sc.c:268: Test failed: got exit code 5, expected 0
sc.c:271: Test failed: OpenServiceA failed: 1060
sc.c:272: Test failed: got exit code 1060, expected 0
sc.c:284: Test failed: got exit code 5, expected 0
sc.c:286: Test failed: OpenServiceA failed: 1060
sc.c:287: Test failed: got exit code 1060, expected 0
sc.c:284: Test failed: got exit code 5, expected 0
sc.c:286: Test failed: OpenServiceA failed: 1060
sc.c:287: Test failed: got exit code 1060, expected 0
sc.c:284: Test failed: got exit code 5, expected 0
sc.c:286: Test failed: OpenServiceA failed: 1060
sc.c:287: Test failed: got exit code 1060, expected 0
sc.c:284: Test failed: got exit code 5, expected 0
sc.c:286: Test failed: OpenServiceA failed: 1060
sc.c:287: Test failed: got exit code 1060, expected 0
sc.c:293: Test failed: got exit code 5, expected 87
sc.c:297: Test failed: got exit code 5, expected 0
sc.c:299: Test failed: OpenServiceA failed: 1060
sc.c:300: Test failed: got exit code 1060, expected 0
sc.c:305: Test failed: got exit code 5, expected 1059
sc.c:309: Test failed: got exit code 5, expected 0
sc.c:311: Test failed: OpenServiceA failed: 1060
sc.c:312: Test failed: got exit code 1060, expected 0
sc.c:315: Test failed: got exit code 5, expected 0
sc.c:317: Test failed: got exit code 5, expected 0
sc.c:319: Test failed: OpenServiceA failed: 1060
sc.c:320: Test failed: got exit code 1060, expected 0
sc.c:321: Test failed: got exit code 1060, expected 0
sc.c:327: Test failed: got exit code 5, expected 0
sc.c:329: Test failed: OpenServiceA failed: 1060
sc.c:330: Test failed: got exit code 1060, expected 0
sc.c:336: Test failed: got exit code 5, expected 0
sc.c:338: Test failed: OpenServiceA failed: 1060
sc.c:339: Test failed: got exit code 1060, expected 0
sc.c:345: Test failed: got exit code 5, expected 0
sc.c:347: Test failed: OpenServiceA failed: 1060
sc.c:348: Test failed: got exit code 1060, expected 0
=== w7u_el (32 bit report) ===
sc.exe:
sc.c:336: Test failed: got exit code 1639, expected 0
sc.c:338: Test failed: OpenServiceA failed: 1060
sc.c:339: Test failed: got exit code 1060, expected 0
=== w8adm (32 bit report) ===
sc.exe:
sc.c:221: Test failed: got exit code 5, expected 0
sc.c:223: Test failed: OpenServiceA failed: 1060
sc.c:228: Test failed: got exit code 5, expected 1073
sc.c:230: Test failed: OpenServiceA failed: 1060
sc.c:231: Test failed: got exit code 1060, expected 0
sc.c:236: Test failed: got exit code 5, expected 1639
sc.c:240: Test failed: got exit code 5, expected 0
sc.c:242: Test failed: OpenServiceA failed: 1060
sc.c:243: Test failed: got exit code 1060, expected 0
sc.c:246: Test failed: got exit code 5, expected 87
sc.c:250: Test failed: got exit code 5, expected 0
sc.c:252: Test failed: OpenServiceA failed: 1060
sc.c:253: Test failed: got exit code 1060, expected 0
sc.c:258: Test failed: got exit code 5, expected 1639
sc.c:268: Test failed: got exit code 5, expected 0
sc.c:271: Test failed: OpenServiceA failed: 1060
sc.c:272: Test failed: got exit code 1060, expected 0
sc.c:268: Test failed: got exit code 5, expected 0
sc.c:271: Test failed: OpenServiceA failed: 1060
sc.c:272: Test failed: got exit code 1060, expected 0
sc.c:268: Test failed: got exit code 5, expected 0
sc.c:271: Test failed: OpenServiceA failed: 1060
sc.c:272: Test failed: got exit code 1060, expected 0
sc.c:268: Test failed: got exit code 5, expected 0
sc.c:271: Test failed: OpenServiceA failed: 1060
sc.c:272: Test failed: got exit code 1060, expected 0
sc.c:268: Test failed: got exit code 5, expected 0
sc.c:271: Test failed: OpenServiceA failed: 1060
sc.c:272: Test failed: got exit code 1060, expected 0
sc.c:268: Test failed: got exit code 5, expected 0
sc.c:271: Test failed: OpenServiceA failed: 1060
sc.c:272: Test failed: got exit code 1060, expected 0
sc.c:284: Test failed: got exit code 5, expected 0
sc.c:286: Test failed: OpenServiceA failed: 1060
sc.c:287: Test failed: got exit code 1060, expected 0
sc.c:284: Test failed: got exit code 5, expected 0
sc.c:286: Test failed: OpenServiceA failed: 1060
sc.c:287: Test failed: got exit code 1060, expected 0
sc.c:284: Test failed: got exit code 5, expected 0
sc.c:286: Test failed: OpenServiceA failed: 1060
sc.c:287: Test failed: got exit code 1060, expected 0
sc.c:284: Test failed: got exit code 5, expected 0
sc.c:286: Test failed: OpenServiceA failed: 1060
sc.c:287: Test failed: got exit code 1060, expected 0
sc.c:293: Test failed: got exit code 5, expected 87
sc.c:297: Test failed: got exit code 5, expected 0
sc.c:299: Test failed: OpenServiceA failed: 1060
sc.c:300: Test failed: got exit code 1060, expected 0
sc.c:305: Test failed: got exit code 5, expected 1059
sc.c:309: Test failed: got exit code 5, expected 0
sc.c:311: Test failed: OpenServiceA failed: 1060
sc.c:312: Test failed: got exit code 1060, expected 0
sc.c:315: Test failed: got exit code 5, expected 0
sc.c:317: Test failed: got exit code 5, expected 0
sc.c:319: Test failed: OpenServiceA failed: 1060
sc.c:320: Test failed: got exit code 1060, expected 0
sc.c:321: Test failed: got exit code 1060, expected 0
sc.c:327: Test failed: got exit code 5, expected 0
sc.c:329: Test failed: OpenServiceA failed: 1060
sc.c:330: Test failed: got exit code 1060, expected 0
sc.c:336: Test failed: got exit code 5, expected 0
sc.c:338: Test failed: OpenServiceA failed: 1060
sc.c:339: Test failed: got exit code 1060, expected 0
sc.c:345: Test failed: got exit code 5, expected 0
sc.c:347: Test failed: OpenServiceA failed: 1060
sc.c:348: Test failed: got exit code 1060, expected 0
April 28, 2022
[PATCH v2 6/6] wineoss: Combine midReceiveChar() into handle_midi_data().
by Andrew Eikum
From: Huw Davies <huw(a)codeweavers.com>
Signed-off-by: Huw Davies <huw(a)codeweavers.com>
Signed-off-by: Andrew Eikum <aeikum(a)codeweavers.com>
---
dlls/wineoss.drv/midi.c | 39 ++++++++++++++-------------------------
1 file changed, 14 insertions(+), 25 deletions(-)
diff --git a/dlls/wineoss.drv/midi.c b/dlls/wineoss.drv/midi.c
index 564a2431472..e36a737624a 100644
--- a/dlls/wineoss.drv/midi.c
+++ b/dlls/wineoss.drv/midi.c
@@ -304,39 +304,28 @@ static void handle_regular_data(struct midi_src *src, unsigned char value, UINT
}
}
-/**************************************************************************
- * midReceiveChar [internal]
- */
-static void midReceiveChar(WORD wDevID, unsigned char value, DWORD dwTime)
-{
- TRACE("Adding %02xh to %d[%d]\n", value, wDevID, MidiInDev[wDevID].incLen);
-
- if (wDevID >= MIDM_NumDevs) {
- WARN("bad devID\n");
- return;
- }
- if (MidiInDev[wDevID].state <= 0) {
- TRACE("disabled or input not started, thrown away\n");
- return;
- }
-
- dwTime -= MidiInDev[wDevID].startTime;
-
- if (value == 0xf0 || MidiInDev[wDevID].state & 2) /* system exclusive */
- handle_sysex_data(MidiInDev + wDevID, value, dwTime);
- else
- handle_regular_data(MidiInDev + wDevID, value, dwTime);
-}
-
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;
for (i = 0; i < len; i += (buffer[i] & 0x80) ? 8 : 4)
{
if (buffer[i] != SEQ_MIDIPUTC) continue;
- midReceiveChar(buffer[i + 2], buffer[i + 1], time);
+ 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);
}
}
--
2.36.0
April 28, 2022
[PATCH v2 5/6] wineoss: Make the MIM_LONGDATA time relative to the start time.
by Andrew Eikum
From: Huw Davies <huw(a)codeweavers.com>
Signed-off-by: Huw Davies <huw(a)codeweavers.com>
Signed-off-by: Andrew Eikum <aeikum(a)codeweavers.com>
---
dlls/wineoss.drv/midi.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/dlls/wineoss.drv/midi.c b/dlls/wineoss.drv/midi.c
index 77bd37d3f2a..564a2431472 100644
--- a/dlls/wineoss.drv/midi.c
+++ b/dlls/wineoss.drv/midi.c
@@ -300,7 +300,6 @@ static void handle_regular_data(struct midi_src *src, unsigned char value, UINT
if (to_send)
{
src->incLen = 0;
- time -= src->startTime;
MIDI_NotifyClient(src - MidiInDev, MIM_DATA, to_send, time);
}
}
@@ -321,6 +320,8 @@ static void midReceiveChar(WORD wDevID, unsigned char value, DWORD dwTime)
return;
}
+ dwTime -= MidiInDev[wDevID].startTime;
+
if (value == 0xf0 || MidiInDev[wDevID].state & 2) /* system exclusive */
handle_sysex_data(MidiInDev + wDevID, value, dwTime);
else
--
2.36.0
April 28, 2022
[PATCH v2 4/6] wineoss: Move the regular data handling to a separate helper.
by Andrew Eikum
From: Huw Davies <huw(a)codeweavers.com>
Signed-off-by: Huw Davies <huw(a)codeweavers.com>
Signed-off-by: Andrew Eikum <aeikum(a)codeweavers.com>
---
dlls/wineoss.drv/midi.c | 122 ++++++++++++++++++++--------------------
1 file changed, 60 insertions(+), 62 deletions(-)
diff --git a/dlls/wineoss.drv/midi.c b/dlls/wineoss.drv/midi.c
index 2c1e8b3a4d7..77bd37d3f2a 100644
--- a/dlls/wineoss.drv/midi.c
+++ b/dlls/wineoss.drv/midi.c
@@ -247,86 +247,84 @@ static void handle_sysex_data(struct midi_src *src, unsigned char value, UINT ti
in_buffer_unlock();
}
-/**************************************************************************
- * midReceiveChar [internal]
- */
-static void midReceiveChar(WORD wDevID, unsigned char value, DWORD dwTime)
+static void handle_regular_data(struct midi_src *src, unsigned char value, UINT time)
{
- DWORD toSend = 0;
+ UINT to_send = 0;
- TRACE("Adding %02xh to %d[%d]\n", value, wDevID, MidiInDev[wDevID].incLen);
+#define IS_CMD(_x) (((_x) & 0x80) == 0x80)
+#define IS_SYS_CMD(_x) (((_x) & 0xF0) == 0xF0)
- if (wDevID >= MIDM_NumDevs) {
- WARN("bad devID\n");
- return;
- }
- if (MidiInDev[wDevID].state <= 0) {
- TRACE("disabled or input not started, thrown away\n");
- return;
- }
-
- if (value == 0xf0 || MidiInDev[wDevID].state & 2) /* system exclusive */
+ if (!IS_CMD(value) && src->incLen == 0) /* try to reuse old cmd */
{
- handle_sysex_data(MidiInDev + wDevID, value, dwTime);
- return;
- }
-
-#define IS_CMD(_x) (((_x) & 0x80) == 0x80)
-#define IS_SYS_CMD(_x) (((_x) & 0xF0) == 0xF0)
-
- if (!IS_CMD(value) && MidiInDev[wDevID].incLen == 0) { /* try to reuse old cmd */
- if (IS_CMD(MidiInDev[wDevID].incPrev) && !IS_SYS_CMD(MidiInDev[wDevID].incPrev)) {
- MidiInDev[wDevID].incoming[0] = MidiInDev[wDevID].incPrev;
- MidiInDev[wDevID].incLen = 1;
- TRACE("Reusing old command %02xh\n", MidiInDev[wDevID].incPrev);
- } else {
- FIXME("error for midi-in, should generate MIM_ERROR notification:"
- " prev=%02Xh, incLen=%02Xh\n",
- MidiInDev[wDevID].incPrev, MidiInDev[wDevID].incLen);
- return;
- }
- }
- MidiInDev[wDevID].incoming[(int)(MidiInDev[wDevID].incLen++)] = value;
- if (MidiInDev[wDevID].incLen == 1 && !IS_SYS_CMD(MidiInDev[wDevID].incoming[0])) {
- /* store new cmd, just in case */
- MidiInDev[wDevID].incPrev = MidiInDev[wDevID].incoming[0];
+ 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 (MidiInDev[wDevID].incoming[0] & 0xF0) {
+ switch (src->incoming[0] & 0xF0)
+ {
case MIDI_NOTEOFF:
case MIDI_NOTEON:
case MIDI_KEY_PRESSURE:
case MIDI_CTL_CHANGE:
case MIDI_PITCH_BEND:
- if (MidiInDev[wDevID].incLen == 3) {
- toSend = (MidiInDev[wDevID].incoming[2] << 16) |
- (MidiInDev[wDevID].incoming[1] << 8) |
- (MidiInDev[wDevID].incoming[0] << 0);
- }
- break;
+ 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 (MidiInDev[wDevID].incLen == 2) {
- toSend = (MidiInDev[wDevID].incoming[1] << 8) |
- (MidiInDev[wDevID].incoming[0] << 0);
- }
- break;
+ if (src->incLen == 2)
+ to_send = (src->incoming[1] << 8) | src->incoming[0];
+ break;
case MIDI_SYSTEM_PREFIX:
- if (MidiInDev[wDevID].incLen == 1)
- toSend = (MidiInDev[wDevID].incoming[0] << 0);
- break;
- default:
- WARN("This shouldn't happen (%02X)\n", MidiInDev[wDevID].incoming[0]);
- }
- if (toSend != 0) {
- TRACE("Sending event %08x\n", toSend);
- MidiInDev[wDevID].incLen = 0;
- dwTime -= MidiInDev[wDevID].startTime;
- MIDI_NotifyClient(wDevID, MIM_DATA, toSend, dwTime);
+ if (src->incLen == 1)
+ to_send = src->incoming[0];
+ break;
}
+
+ if (to_send)
+ {
+ src->incLen = 0;
+ time -= src->startTime;
+ MIDI_NotifyClient(src - MidiInDev, MIM_DATA, to_send, time);
+ }
+}
+
+/**************************************************************************
+ * midReceiveChar [internal]
+ */
+static void midReceiveChar(WORD wDevID, unsigned char value, DWORD dwTime)
+{
+ TRACE("Adding %02xh to %d[%d]\n", value, wDevID, MidiInDev[wDevID].incLen);
+
+ if (wDevID >= MIDM_NumDevs) {
+ WARN("bad devID\n");
+ return;
+ }
+ if (MidiInDev[wDevID].state <= 0) {
+ TRACE("disabled or input not started, thrown away\n");
+ return;
+ }
+
+ if (value == 0xf0 || MidiInDev[wDevID].state & 2) /* system exclusive */
+ handle_sysex_data(MidiInDev + wDevID, value, dwTime);
+ else
+ handle_regular_data(MidiInDev + wDevID, value, dwTime);
}
static void handle_midi_data(unsigned char *buffer, unsigned int len)
--
2.36.0
April 28, 2022
[PATCH v2 3/6] wineoss: Move the sysex handling to a separate helper.
by Andrew Eikum
From: Huw Davies <huw(a)codeweavers.com>
Signed-off-by: Huw Davies <huw(a)codeweavers.com>
Signed-off-by: Andrew Eikum <aeikum(a)codeweavers.com>
---
dlls/wineoss.drv/midi.c | 67 ++++++++++++++++++++++++-----------------
1 file changed, 40 insertions(+), 27 deletions(-)
diff --git a/dlls/wineoss.drv/midi.c b/dlls/wineoss.drv/midi.c
index 462fd3625f0..2c1e8b3a4d7 100644
--- a/dlls/wineoss.drv/midi.c
+++ b/dlls/wineoss.drv/midi.c
@@ -210,6 +210,43 @@ 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();
+}
+
/**************************************************************************
* midReceiveChar [internal]
*/
@@ -228,33 +265,9 @@ static void midReceiveChar(WORD wDevID, unsigned char value, DWORD dwTime)
return;
}
- if (value == 0xf0 || MidiInDev[wDevID].state & 2) { /* system exclusive */
- LPMIDIHDR lpMidiHdr;
- BOOL sbfb = FALSE;
-
- MidiInDev[wDevID].state |= 2;
- MidiInDev[wDevID].incLen = 0;
- in_buffer_lock();
- if ((lpMidiHdr = MidiInDev[wDevID].lpQueueHdr) != NULL) {
- LPBYTE lpData = (LPBYTE) lpMidiHdr->lpData;
-
- lpData[lpMidiHdr->dwBytesRecorded++] = value;
- if (lpMidiHdr->dwBytesRecorded == lpMidiHdr->dwBufferLength) {
- sbfb = TRUE;
- }
- }
- if (value == 0xF7) { /* then end */
- MidiInDev[wDevID].state &= ~2;
- sbfb = TRUE;
- }
- if (sbfb && lpMidiHdr != NULL) {
- lpMidiHdr = MidiInDev[wDevID].lpQueueHdr;
- MidiInDev[wDevID].lpQueueHdr = lpMidiHdr->lpNext;
- lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
- lpMidiHdr->dwFlags |= MHDR_DONE;
- MIDI_NotifyClient(wDevID, MIM_LONGDATA, (DWORD_PTR)lpMidiHdr, dwTime);
- }
- in_buffer_unlock();
+ if (value == 0xf0 || MidiInDev[wDevID].state & 2) /* system exclusive */
+ {
+ handle_sysex_data(MidiInDev + wDevID, value, dwTime);
return;
}
--
2.36.0
April 28, 2022
[PATCH v2 2/6] wineoss: Add the leading 0xf0 to the sysex data.
by Andrew Eikum
From: Huw Davies <huw(a)codeweavers.com>
Signed-off-by: Huw Davies <huw(a)codeweavers.com>
Signed-off-by: Andrew Eikum <aeikum(a)codeweavers.com>
---
dlls/wineoss.drv/midi.c | 14 +++++---------
1 file changed, 5 insertions(+), 9 deletions(-)
diff --git a/dlls/wineoss.drv/midi.c b/dlls/wineoss.drv/midi.c
index 1a5b348da9f..462fd3625f0 100644
--- a/dlls/wineoss.drv/midi.c
+++ b/dlls/wineoss.drv/midi.c
@@ -228,10 +228,12 @@ static void midReceiveChar(WORD wDevID, unsigned char value, DWORD dwTime)
return;
}
- if (MidiInDev[wDevID].state & 2) { /* system exclusive */
+ if (value == 0xf0 || MidiInDev[wDevID].state & 2) { /* system exclusive */
LPMIDIHDR lpMidiHdr;
BOOL sbfb = FALSE;
+ MidiInDev[wDevID].state |= 2;
+ MidiInDev[wDevID].incLen = 0;
in_buffer_lock();
if ((lpMidiHdr = MidiInDev[wDevID].lpQueueHdr) != NULL) {
LPBYTE lpData = (LPBYTE) lpMidiHdr->lpData;
@@ -300,14 +302,8 @@ static void midReceiveChar(WORD wDevID, unsigned char value, DWORD dwTime)
}
break;
case MIDI_SYSTEM_PREFIX:
- if (MidiInDev[wDevID].incoming[0] == 0xF0) {
- MidiInDev[wDevID].state |= 2;
- MidiInDev[wDevID].incLen = 0;
- } else {
- if (MidiInDev[wDevID].incLen == 1) {
- toSend = (MidiInDev[wDevID].incoming[0] << 0);
- }
- }
+ if (MidiInDev[wDevID].incLen == 1)
+ toSend = (MidiInDev[wDevID].incoming[0] << 0);
break;
default:
WARN("This shouldn't happen (%02X)\n", MidiInDev[wDevID].incoming[0]);
--
2.36.0
April 28, 2022
[PATCH v2 1/6] wineoss: Simplify the midi in dispatcher.
by Andrew Eikum
From: Huw Davies <huw(a)codeweavers.com>
The continue statement looks a bit awkward at the moment,
but a future patch will introduce more code into the for
loop.
Signed-off-by: Huw Davies <huw(a)codeweavers.com>
Signed-off-by: Andrew Eikum <aeikum(a)codeweavers.com>
---
v2: Clean up some wonky indentation in 1/6.
dlls/wineoss.drv/midi.c | 42 ++++++++++++++---------------------------
1 file changed, 14 insertions(+), 28 deletions(-)
diff --git a/dlls/wineoss.drv/midi.c b/dlls/wineoss.drv/midi.c
index fe73d97f5ab..1a5b348da9f 100644
--- a/dlls/wineoss.drv/midi.c
+++ b/dlls/wineoss.drv/midi.c
@@ -320,12 +320,23 @@ static void midReceiveChar(WORD wDevID, unsigned char value, DWORD dwTime)
}
}
+static void handle_midi_data(unsigned char *buffer, unsigned int len)
+{
+ unsigned int time = GetTickCount(), i;
+
+ for (i = 0; i < len; i += (buffer[i] & 0x80) ? 8 : 4)
+ {
+ if (buffer[i] != SEQ_MIDIPUTC) continue;
+
+ midReceiveChar(buffer[i + 2], buffer[i + 1], time);
+ }
+}
+
static DWORD WINAPI midRecThread(void *arg)
{
int fd = (int)(INT_PTR)arg;
unsigned char buffer[256];
- int len, idx;
- DWORD dwTime;
+ int len;
struct pollfd pfd;
TRACE("Thread startup\n");
@@ -349,32 +360,7 @@ static DWORD WINAPI midRecThread(void *arg)
continue;
}
- dwTime = GetTickCount();
-
- for (idx = 0; idx < len; ) {
- if (buffer[idx] & 0x80) {
- TRACE(
- "Reading<8> %02x %02x %02x %02x %02x %02x %02x %02x\n",
- buffer[idx + 0], buffer[idx + 1],
- buffer[idx + 2], buffer[idx + 3],
- buffer[idx + 4], buffer[idx + 5],
- buffer[idx + 6], buffer[idx + 7]);
- idx += 8;
- } else {
- switch (buffer[idx + 0]) {
- case SEQ_WAIT:
- case SEQ_ECHO:
- break;
- case SEQ_MIDIPUTC:
- midReceiveChar(buffer[idx + 2], buffer[idx + 1], dwTime);
- break;
- default:
- TRACE("Unsupported event %d\n", buffer[idx + 0]);
- break;
- }
- idx += 4;
- }
- }
+ handle_midi_data(buffer, len);
}
return 0;
}
--
2.36.0
April 28, 2022
Re: [PATCH v2 1/2] programs/sc: Add tests.
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=113742
Your paranoid android.
=== w7u_2qxl (32 bit report) ===
sc.exe:
sc.c:329: Test failed: got exit code 1639, expected 0
sc.c:331: Test failed: OpenServiceA failed: 1060
sc.c:332: Test failed: got exit code 1060, expected 0
=== w7u_adm (32 bit report) ===
sc.exe:
sc.c:214: Test failed: got exit code 5, expected 0
sc.c:216: Test failed: OpenServiceA failed: 1060
sc.c:221: Test failed: got exit code 5, expected 1073
sc.c:223: Test failed: OpenServiceA failed: 1060
sc.c:224: Test failed: got exit code 1060, expected 0
sc.c:229: Test failed: got exit code 5, expected 1639
sc.c:233: Test failed: got exit code 5, expected 0
sc.c:235: Test failed: OpenServiceA failed: 1060
sc.c:236: Test failed: got exit code 1060, expected 0
sc.c:239: Test failed: got exit code 5, expected 87
sc.c:243: Test failed: got exit code 5, expected 0
sc.c:245: Test failed: OpenServiceA failed: 1060
sc.c:246: Test failed: got exit code 1060, expected 0
sc.c:251: Test failed: got exit code 5, expected 1639
sc.c:261: Test failed: got exit code 5, expected 0
sc.c:264: Test failed: OpenServiceA failed: 1060
sc.c:265: Test failed: got exit code 1060, expected 0
sc.c:261: Test failed: got exit code 5, expected 0
sc.c:264: Test failed: OpenServiceA failed: 1060
sc.c:265: Test failed: got exit code 1060, expected 0
sc.c:261: Test failed: got exit code 5, expected 0
sc.c:264: Test failed: OpenServiceA failed: 1060
sc.c:265: Test failed: got exit code 1060, expected 0
sc.c:261: Test failed: got exit code 5, expected 0
sc.c:264: Test failed: OpenServiceA failed: 1060
sc.c:265: Test failed: got exit code 1060, expected 0
sc.c:261: Test failed: got exit code 5, expected 0
sc.c:264: Test failed: OpenServiceA failed: 1060
sc.c:265: Test failed: got exit code 1060, expected 0
sc.c:261: Test failed: got exit code 5, expected 0
sc.c:264: Test failed: OpenServiceA failed: 1060
sc.c:265: Test failed: got exit code 1060, expected 0
sc.c:277: Test failed: got exit code 5, expected 0
sc.c:279: Test failed: OpenServiceA failed: 1060
sc.c:280: Test failed: got exit code 1060, expected 0
sc.c:277: Test failed: got exit code 5, expected 0
sc.c:279: Test failed: OpenServiceA failed: 1060
sc.c:280: Test failed: got exit code 1060, expected 0
sc.c:277: Test failed: got exit code 5, expected 0
sc.c:279: Test failed: OpenServiceA failed: 1060
sc.c:280: Test failed: got exit code 1060, expected 0
sc.c:277: Test failed: got exit code 5, expected 0
sc.c:279: Test failed: OpenServiceA failed: 1060
sc.c:280: Test failed: got exit code 1060, expected 0
sc.c:286: Test failed: got exit code 5, expected 87
sc.c:290: Test failed: got exit code 5, expected 0
sc.c:292: Test failed: OpenServiceA failed: 1060
sc.c:293: Test failed: got exit code 1060, expected 0
sc.c:298: Test failed: got exit code 5, expected 1059
sc.c:302: Test failed: got exit code 5, expected 0
sc.c:304: Test failed: OpenServiceA failed: 1060
sc.c:305: Test failed: got exit code 1060, expected 0
sc.c:308: Test failed: got exit code 5, expected 0
sc.c:310: Test failed: got exit code 5, expected 0
sc.c:312: Test failed: OpenServiceA failed: 1060
sc.c:313: Test failed: got exit code 1060, expected 0
sc.c:314: Test failed: got exit code 1060, expected 0
sc.c:320: Test failed: got exit code 5, expected 0
sc.c:322: Test failed: OpenServiceA failed: 1060
sc.c:323: Test failed: got exit code 1060, expected 0
sc.c:329: Test failed: got exit code 5, expected 0
sc.c:331: Test failed: OpenServiceA failed: 1060
sc.c:332: Test failed: got exit code 1060, expected 0
sc.c:338: Test failed: got exit code 5, expected 0
sc.c:340: Test failed: OpenServiceA failed: 1060
sc.c:341: Test failed: got exit code 1060, expected 0
=== w7u_el (32 bit report) ===
sc.exe:
sc.c:329: Test failed: got exit code 1639, expected 0
sc.c:331: Test failed: OpenServiceA failed: 1060
sc.c:332: Test failed: got exit code 1060, expected 0
=== w8adm (32 bit report) ===
sc.exe:
sc.c:214: Test failed: got exit code 5, expected 0
sc.c:216: Test failed: OpenServiceA failed: 1060
sc.c:221: Test failed: got exit code 5, expected 1073
sc.c:223: Test failed: OpenServiceA failed: 1060
sc.c:224: Test failed: got exit code 1060, expected 0
sc.c:229: Test failed: got exit code 5, expected 1639
sc.c:233: Test failed: got exit code 5, expected 0
sc.c:235: Test failed: OpenServiceA failed: 1060
sc.c:236: Test failed: got exit code 1060, expected 0
sc.c:239: Test failed: got exit code 5, expected 87
sc.c:243: Test failed: got exit code 5, expected 0
sc.c:245: Test failed: OpenServiceA failed: 1060
sc.c:246: Test failed: got exit code 1060, expected 0
sc.c:251: Test failed: got exit code 5, expected 1639
sc.c:261: Test failed: got exit code 5, expected 0
sc.c:264: Test failed: OpenServiceA failed: 1060
sc.c:265: Test failed: got exit code 1060, expected 0
sc.c:261: Test failed: got exit code 5, expected 0
sc.c:264: Test failed: OpenServiceA failed: 1060
sc.c:265: Test failed: got exit code 1060, expected 0
sc.c:261: Test failed: got exit code 5, expected 0
sc.c:264: Test failed: OpenServiceA failed: 1060
sc.c:265: Test failed: got exit code 1060, expected 0
sc.c:261: Test failed: got exit code 5, expected 0
sc.c:264: Test failed: OpenServiceA failed: 1060
sc.c:265: Test failed: got exit code 1060, expected 0
sc.c:261: Test failed: got exit code 5, expected 0
sc.c:264: Test failed: OpenServiceA failed: 1060
sc.c:265: Test failed: got exit code 1060, expected 0
sc.c:261: Test failed: got exit code 5, expected 0
sc.c:264: Test failed: OpenServiceA failed: 1060
sc.c:265: Test failed: got exit code 1060, expected 0
sc.c:277: Test failed: got exit code 5, expected 0
sc.c:279: Test failed: OpenServiceA failed: 1060
sc.c:280: Test failed: got exit code 1060, expected 0
sc.c:277: Test failed: got exit code 5, expected 0
sc.c:279: Test failed: OpenServiceA failed: 1060
sc.c:280: Test failed: got exit code 1060, expected 0
sc.c:277: Test failed: got exit code 5, expected 0
sc.c:279: Test failed: OpenServiceA failed: 1060
sc.c:280: Test failed: got exit code 1060, expected 0
sc.c:277: Test failed: got exit code 5, expected 0
sc.c:279: Test failed: OpenServiceA failed: 1060
sc.c:280: Test failed: got exit code 1060, expected 0
sc.c:286: Test failed: got exit code 5, expected 87
sc.c:290: Test failed: got exit code 5, expected 0
sc.c:292: Test failed: OpenServiceA failed: 1060
sc.c:293: Test failed: got exit code 1060, expected 0
sc.c:298: Test failed: got exit code 5, expected 1059
sc.c:302: Test failed: got exit code 5, expected 0
sc.c:304: Test failed: OpenServiceA failed: 1060
sc.c:305: Test failed: got exit code 1060, expected 0
sc.c:308: Test failed: got exit code 5, expected 0
sc.c:310: Test failed: got exit code 5, expected 0
sc.c:312: Test failed: OpenServiceA failed: 1060
sc.c:313: Test failed: got exit code 1060, expected 0
sc.c:314: Test failed: got exit code 1060, expected 0
sc.c:320: Test failed: got exit code 5, expected 0
sc.c:322: Test failed: OpenServiceA failed: 1060
sc.c:323: Test failed: got exit code 1060, expected 0
sc.c:329: Test failed: got exit code 5, expected 0
sc.c:331: Test failed: OpenServiceA failed: 1060
sc.c:332: Test failed: got exit code 1060, expected 0
sc.c:338: Test failed: got exit code 5, expected 0
sc.c:340: Test failed: OpenServiceA failed: 1060
sc.c:341: Test failed: got exit code 1060, expected 0
April 28, 2022
[PATCH vkd3d 8/8] vkd3d-shader/hlsl: Handle branches in copy propagation.
by Giovanni Mascellani
Signed-off-by: Giovanni Mascellani <gmascellani(a)codeweavers.com>
---
libs/vkd3d-shader/hlsl_codegen.c | 179 +++++++++++++++++++++++++++----
1 file changed, 159 insertions(+), 20 deletions(-)
diff --git a/libs/vkd3d-shader/hlsl_codegen.c b/libs/vkd3d-shader/hlsl_codegen.c
index f7396a96..b967a2a3 100644
--- a/libs/vkd3d-shader/hlsl_codegen.c
+++ b/libs/vkd3d-shader/hlsl_codegen.c
@@ -273,8 +273,16 @@ static bool lower_broadcasts(struct hlsl_ctx *ctx, struct hlsl_ir_node *instr, v
return false;
}
+enum copy_propagation_value_state
+{
+ VALUE_STATE_NOT_WRITTEN = 0,
+ VALUE_STATE_STATICALLY_WRITTEN,
+ VALUE_STATE_DYNAMICALLY_WRITTEN,
+};
+
struct copy_propagation_value
{
+ enum copy_propagation_value_state state;
struct hlsl_ir_node *node;
unsigned int component;
};
@@ -289,6 +297,7 @@ struct copy_propagation_var_def
struct copy_propagation_state
{
struct rb_tree var_defs;
+ struct copy_propagation_state *parent;
};
static int copy_propagation_var_def_compare(const void *key, const struct rb_entry *entry)
@@ -306,15 +315,32 @@ static void copy_propagation_var_def_destroy(struct rb_entry *entry, void *conte
vkd3d_free(var_def);
}
-static struct copy_propagation_var_def *copy_propagation_get_var_def(const struct copy_propagation_state *state,
- const struct hlsl_ir_var *var)
+static struct copy_propagation_value *copy_propagation_get_value(const struct copy_propagation_state *state,
+ const struct hlsl_ir_var *var, unsigned component)
{
- struct rb_entry *entry = rb_get(&state->var_defs, var);
+ for (; state; state = state->parent)
+ {
+ struct rb_entry *entry = rb_get(&state->var_defs, var);
+ if (entry)
+ {
+ struct copy_propagation_var_def *var_def = RB_ENTRY_VALUE(entry, struct copy_propagation_var_def, entry);
+ enum copy_propagation_value_state state = var_def->values[component].state;
- if (entry)
- return RB_ENTRY_VALUE(entry, struct copy_propagation_var_def, entry);
- else
- return NULL;
+ assert(component < var_def->var->data_type->reg_size);
+
+ switch (state)
+ {
+ case VALUE_STATE_STATICALLY_WRITTEN:
+ return &var_def->values[component];
+ case VALUE_STATE_DYNAMICALLY_WRITTEN:
+ return NULL;
+ case VALUE_STATE_NOT_WRITTEN:
+ break;
+ }
+ }
+ }
+
+ return NULL;
}
static struct copy_propagation_var_def *copy_propagation_create_var_def(struct hlsl_ctx *ctx,
@@ -338,10 +364,28 @@ static struct copy_propagation_var_def *copy_propagation_create_var_def(struct h
return var_def;
}
+static void copy_propagation_invalidate_variable(struct copy_propagation_var_def *var_def,
+ unsigned offset, unsigned char writemask)
+{
+ unsigned i;
+
+ TRACE("Invalidate variable %s[%u]%s.\n", var_def->var->name, offset, debug_hlsl_writemask(writemask));
+
+ for (i = 0; i < 4; ++i)
+ {
+ if (writemask & (1u << i))
+ var_def->values[offset + i].state = VALUE_STATE_DYNAMICALLY_WRITTEN;
+ }
+}
+
static void copy_propagation_invalidate_whole_variable(struct copy_propagation_var_def *var_def)
{
+ unsigned i;
+
TRACE("Invalidate variable %s.\n", var_def->var->name);
- memset(var_def->values, 0, sizeof(*var_def->values) * var_def->var->data_type->reg_size);
+
+ for (i = 0; i < var_def->var->data_type->reg_size; ++i)
+ var_def->values[i].state = VALUE_STATE_DYNAMICALLY_WRITTEN;
}
static void copy_propagation_set_value(struct copy_propagation_var_def *var_def, unsigned int offset,
@@ -355,6 +399,7 @@ static void copy_propagation_set_value(struct copy_propagation_var_def *var_def,
{
TRACE("Variable %s[%u] is written by instruction %p%s.\n",
var_def->var->name, offset + i, node, debug_hlsl_writemask(1u << i));
+ var_def->values[offset + i].state = VALUE_STATE_STATICALLY_WRITTEN;
var_def->values[offset + i].node = node;
var_def->values[offset + i].component = j++;
}
@@ -366,32 +411,34 @@ static struct hlsl_ir_node *copy_propagation_compute_replacement(struct hlsl_ctx
unsigned int count, unsigned int *swizzle)
{
const struct hlsl_ir_var *var = deref->var;
- struct copy_propagation_var_def *var_def;
struct hlsl_ir_node *node = NULL;
unsigned int offset, i;
if (!hlsl_offset_from_deref(ctx, deref, &offset))
return NULL;
- if (!(var_def = copy_propagation_get_var_def(state, var)))
- return NULL;
-
- assert(offset + count <= var_def->var->data_type->reg_size);
+ if (var->data_type->type != HLSL_CLASS_OBJECT)
+ assert(offset + count <= var->data_type->reg_size);
*swizzle = 0;
for (i = 0; i < count; ++i)
{
+ struct copy_propagation_value *value = copy_propagation_get_value(state, var, offset + i);
+
+ if (!value)
+ return NULL;
+
if (!node)
{
- node = var_def->values[offset + i].node;
+ node = value->node;
}
- else if (node != var_def->values[offset + i].node)
+ else if (node != value->node)
{
TRACE("No single source for propagating load from %s[%u-%u].\n", var->name, offset, offset + count);
return NULL;
}
- *swizzle |= var_def->values[offset + i].component << i * 2;
+ *swizzle |= value->component << i * 2;
}
TRACE("Load from %s[%u-%u] propagated as instruction %p%s.\n",
@@ -495,6 +542,98 @@ static void copy_propagation_record_store(struct hlsl_ctx *ctx, struct hlsl_ir_s
}
}
+static void copy_propagation_state_init(struct hlsl_ctx *ctx, struct copy_propagation_state *state,
+ struct copy_propagation_state *parent)
+{
+ rb_init(&state->var_defs, copy_propagation_var_def_compare);
+ state->parent = parent;
+}
+
+static void copy_propagation_state_destroy(struct copy_propagation_state *state)
+{
+ rb_destroy(&state->var_defs, copy_propagation_var_def_destroy, NULL);
+}
+
+static void copy_propagation_invalidate_from_block(struct hlsl_ctx *ctx, struct copy_propagation_state *state,
+ struct hlsl_block *block)
+{
+ struct hlsl_ir_node *instr;
+
+ LIST_FOR_EACH_ENTRY(instr, &block->instrs, struct hlsl_ir_node, entry)
+ {
+ switch (instr->type)
+ {
+ case HLSL_IR_STORE:
+ {
+ struct hlsl_ir_store *store = hlsl_ir_store(instr);
+ struct copy_propagation_var_def *var_def;
+ struct hlsl_deref *lhs = &store->lhs;
+ struct hlsl_ir_var *var = lhs->var;
+ unsigned int offset;
+
+ if (!(var_def = copy_propagation_create_var_def(ctx, state, var)))
+ continue;
+
+ if (hlsl_offset_from_deref(ctx, lhs, &offset))
+ copy_propagation_invalidate_variable(var_def, offset, store->writemask);
+ else
+ copy_propagation_invalidate_whole_variable(var_def);
+
+ break;
+ }
+
+ case HLSL_IR_IF:
+ {
+ struct hlsl_ir_if *iff = hlsl_ir_if(instr);
+
+ copy_propagation_invalidate_from_block(ctx, state, &iff->then_instrs);
+ copy_propagation_invalidate_from_block(ctx, state, &iff->else_instrs);
+
+ break;
+ }
+
+ case HLSL_IR_LOOP:
+ {
+ struct hlsl_ir_loop *loop = hlsl_ir_loop(instr);
+
+ copy_propagation_invalidate_from_block(ctx, state, &loop->body);
+
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+}
+
+static bool copy_propagation_transform_block(struct hlsl_ctx *ctx, struct hlsl_block *block,
+ struct copy_propagation_state *state);
+
+static bool copy_propagation_process_if(struct hlsl_ctx *ctx, struct hlsl_ir_if *iff,
+ struct copy_propagation_state *state)
+{
+ struct copy_propagation_state inner_state;
+ bool progress = false;
+
+ copy_propagation_state_init(ctx, &inner_state, state);
+ progress |= copy_propagation_transform_block(ctx, &iff->then_instrs, &inner_state);
+ copy_propagation_state_destroy(&inner_state);
+
+ copy_propagation_state_init(ctx, &inner_state, state);
+ progress |= copy_propagation_transform_block(ctx, &iff->else_instrs, &inner_state);
+ copy_propagation_state_destroy(&inner_state);
+
+ /* Ideally we'd invalidate the outer state looking at what was
+ * touched in the two inner states, but this doesn't work for
+ * loops (because we need to know what is invalidated in advance),
+ * so we need copy_propagation_invalidate_from_block() anyway. */
+ copy_propagation_invalidate_from_block(ctx, state, &iff->then_instrs);
+ copy_propagation_invalidate_from_block(ctx, state, &iff->else_instrs);
+
+ return progress;
+}
+
static bool copy_propagation_transform_block(struct hlsl_ctx *ctx, struct hlsl_block *block,
struct copy_propagation_state *state)
{
@@ -518,8 +657,8 @@ static bool copy_propagation_transform_block(struct hlsl_ctx *ctx, struct hlsl_b
break;
case HLSL_IR_IF:
- FIXME("Copy propagation doesn't support conditionals yet, leaving.\n");
- return progress;
+ progress |= copy_propagation_process_if(ctx, hlsl_ir_if(instr), state);
+ break;
case HLSL_IR_LOOP:
FIXME("Copy propagation doesn't support loops yet, leaving.\n");
@@ -538,11 +677,11 @@ static bool copy_propagation_execute(struct hlsl_ctx *ctx, struct hlsl_block *bl
struct copy_propagation_state state;
bool progress;
- rb_init(&state.var_defs, copy_propagation_var_def_compare);
+ copy_propagation_state_init(ctx, &state, NULL);
progress = copy_propagation_transform_block(ctx, block, &state);
- rb_destroy(&state.var_defs, copy_propagation_var_def_destroy, NULL);
+ copy_propagation_state_destroy(&state);
return progress;
}
--
2.36.0
April 28, 2022
[PATCH vkd3d 7/8] vkd3d-shader/hlsl: Allow storing to matrices.
by Giovanni Mascellani
Signed-off-by: Giovanni Mascellani <gmascellani(a)codeweavers.com>
---
libs/vkd3d-shader/hlsl_sm4.c | 6 ------
tests/hlsl-initializer-matrix.shader_test | 2 +-
tests/hlsl-matrix-indexing.shader_test | 2 +-
tests/hlsl-numeric-types.shader_test | 4 ++--
4 files changed, 4 insertions(+), 10 deletions(-)
diff --git a/libs/vkd3d-shader/hlsl_sm4.c b/libs/vkd3d-shader/hlsl_sm4.c
index 2dcfdb88..6b98ca5d 100644
--- a/libs/vkd3d-shader/hlsl_sm4.c
+++ b/libs/vkd3d-shader/hlsl_sm4.c
@@ -2048,12 +2048,6 @@ static void write_sm4_store(struct hlsl_ctx *ctx,
struct sm4_instruction instr;
unsigned int writemask;
- if (store->lhs.var->data_type->type == HLSL_CLASS_MATRIX)
- {
- hlsl_fixme(ctx, &store->node.loc, "Store to a matrix variable.");
- return;
- }
-
memset(&instr, 0, sizeof(instr));
instr.opcode = VKD3D_SM4_OP_MOV;
diff --git a/tests/hlsl-initializer-matrix.shader_test b/tests/hlsl-initializer-matrix.shader_test
index 790d402d..ea9de9c0 100644
--- a/tests/hlsl-initializer-matrix.shader_test
+++ b/tests/hlsl-initializer-matrix.shader_test
@@ -68,5 +68,5 @@ float4 main() : SV_TARGET
}
[test]
-todo draw quad
+draw quad
probe all rgba (21, 22, 31, 32)
diff --git a/tests/hlsl-matrix-indexing.shader_test b/tests/hlsl-matrix-indexing.shader_test
index 9336a67a..716b43d4 100644
--- a/tests/hlsl-matrix-indexing.shader_test
+++ b/tests/hlsl-matrix-indexing.shader_test
@@ -74,5 +74,5 @@ float4 main() : SV_TARGET
}
[test]
-todo draw quad
+draw quad
probe all rgba (1.0, 5.0, 7.0, 12.0)
diff --git a/tests/hlsl-numeric-types.shader_test b/tests/hlsl-numeric-types.shader_test
index 58612191..7504f95a 100644
--- a/tests/hlsl-numeric-types.shader_test
+++ b/tests/hlsl-numeric-types.shader_test
@@ -56,7 +56,7 @@ float4 main() : sv_target
}
[test]
-todo draw quad
+draw quad
probe all rgba (5.0, 6.0, 7.0, 8.0)
[pixel shader]
@@ -68,7 +68,7 @@ float4 main() : sv_target
}
[test]
-todo draw quad
+draw quad
probe all rgba (5.0, 6.0, 7.0, 0.0)
[pixel shader fail]
--
2.36.0
April 28, 2022
[PATCH vkd3d 6/8] vkd3d-shader/hlsl: Use a more sensible source location in add_constructor().
by Giovanni Mascellani
Signed-off-by: Giovanni Mascellani <gmascellani(a)codeweavers.com>
---
libs/vkd3d-shader/hlsl.y | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libs/vkd3d-shader/hlsl.y b/libs/vkd3d-shader/hlsl.y
index 3fff8fc6..b68d9700 100644
--- a/libs/vkd3d-shader/hlsl.y
+++ b/libs/vkd3d-shader/hlsl.y
@@ -2077,7 +2077,7 @@ static struct list *add_constructor(struct hlsl_ctx *ctx, struct hlsl_type *type
continue;
}
- initialize_var_components(ctx, params->instrs, var, &idx, arg, &loc);
+ initialize_var_components(ctx, params->instrs, var, &idx, arg, &arg->loc);
}
if (!(load = hlsl_new_var_load(ctx, var, loc)))
--
2.36.0
April 28, 2022
[PATCH vkd3d 5/8] vkd3d-shader/hlsl: Check argument count for constructors.
by Giovanni Mascellani
Based on a patch by Francisco Casas.
Signed-off-by: Giovanni Mascellani <gmascellani(a)codeweavers.com>
---
libs/vkd3d-shader/hlsl.y | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/libs/vkd3d-shader/hlsl.y b/libs/vkd3d-shader/hlsl.y
index e7fe74d8..3fff8fc6 100644
--- a/libs/vkd3d-shader/hlsl.y
+++ b/libs/vkd3d-shader/hlsl.y
@@ -2044,12 +2044,20 @@ static struct list *add_call(struct hlsl_ctx *ctx, const char *name,
static struct list *add_constructor(struct hlsl_ctx *ctx, struct hlsl_type *type,
struct parse_initializer *params, struct vkd3d_shader_location loc)
{
+ unsigned int size = initializer_size(params);
static unsigned int counter;
struct hlsl_ir_load *load;
struct hlsl_ir_var *var;
unsigned int i, idx = 0;
char name[23];
+ if (hlsl_type_component_count(type) != size)
+ {
+ hlsl_error(ctx, &loc, VKD3D_SHADER_ERROR_HLSL_WRONG_PARAMETER_COUNT,
+ "Expected %u components in constructor, but got %u.", hlsl_type_component_count(type), size);
+ return NULL;
+ }
+
sprintf(name, "<constructor-%x>", counter++);
if (!(var = hlsl_new_synthetic_var(ctx, name, type, loc)))
return NULL;
--
2.36.0
April 28, 2022
[PATCH vkd3d 4/8] vkd3d-shader/hlsl: Support initialization of implicit size arrays.
by Giovanni Mascellani
From: Francisco Casas <fcasas(a)codeweavers.com>
HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT is used as a temporal value for
elements_count for implicit size arrays. It is replaced by the
correct value after parsing the initializer.
Signed-off-by: Francisco Casas <fcasas(a)codeweavers.com>
---
libs/vkd3d-shader/hlsl.c | 13 ++++-
libs/vkd3d-shader/hlsl.h | 2 +
libs/vkd3d-shader/hlsl.y | 50 ++++++++++++++++++-
libs/vkd3d-shader/hlsl_sm1.c | 1 +
libs/vkd3d-shader/hlsl_sm4.c | 1 +
...lsl-initializer-implicit-array.shader_test | 4 +-
6 files changed, 67 insertions(+), 4 deletions(-)
diff --git a/libs/vkd3d-shader/hlsl.c b/libs/vkd3d-shader/hlsl.c
index 7239b183..1d272866 100644
--- a/libs/vkd3d-shader/hlsl.c
+++ b/libs/vkd3d-shader/hlsl.c
@@ -126,6 +126,7 @@ static bool hlsl_type_is_row_major(const struct hlsl_type *type)
static unsigned int get_array_size(const struct hlsl_type *type)
{
+ assert(type->e.array.elements_count != HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT);
if (type->type == HLSL_CLASS_ARRAY)
return get_array_size(type->e.array.type) * type->e.array.elements_count;
return 1;
@@ -166,6 +167,7 @@ static void hlsl_type_calculate_reg_size(struct hlsl_ctx *ctx, struct hlsl_type
unsigned int element_size = type->e.array.type->reg_size;
assert(element_size);
+ assert(type->e.array.elements_count != HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT);
if (is_sm4)
type->reg_size = (type->e.array.elements_count - 1) * align(element_size, 4) + element_size;
else
@@ -328,6 +330,9 @@ struct hlsl_type *hlsl_new_array_type(struct hlsl_ctx *ctx, struct hlsl_type *ba
list_add_tail(&ctx->types, &type->entry);
+ if (type->e.array.elements_count == HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT)
+ TRACE("Implicit element count.\n");
+
return type;
}
@@ -410,6 +415,7 @@ unsigned int hlsl_type_component_count(struct hlsl_type *type)
}
if (type->type == HLSL_CLASS_ARRAY)
{
+ assert(type->e.array.elements_count != HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT);
return hlsl_type_component_count(type->e.array.type) * type->e.array.elements_count;
}
if (type->type != HLSL_CLASS_STRUCT)
@@ -1023,7 +1029,12 @@ struct vkd3d_string_buffer *hlsl_type_to_string(struct hlsl_ctx *ctx, const stru
}
for (t = type; t->type == HLSL_CLASS_ARRAY; t = t->e.array.type)
- vkd3d_string_buffer_printf(string, "[%u]", t->e.array.elements_count);
+ {
+ if (t->e.array.elements_count == HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT)
+ vkd3d_string_buffer_printf(string, "[<undefined>]");
+ else
+ vkd3d_string_buffer_printf(string, "[%u]", t->e.array.elements_count);
+ }
return string;
}
diff --git a/libs/vkd3d-shader/hlsl.h b/libs/vkd3d-shader/hlsl.h
index 28b2ff1b..94597999 100644
--- a/libs/vkd3d-shader/hlsl.h
+++ b/libs/vkd3d-shader/hlsl.h
@@ -227,6 +227,8 @@ struct hlsl_src
#define HLSL_MODIFIERS_MAJORITY_MASK (HLSL_MODIFIER_ROW_MAJOR | HLSL_MODIFIER_COLUMN_MAJOR)
+#define HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT 0xffffffff
+
struct hlsl_reg_reservation
{
char type;
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;
+ }
+ }
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;
+ }
+ }
| variable_decl '=' complex_initializer
{
$$ = $1;
@@ -3188,6 +3218,24 @@ arrays:
$$.sizes[$$.count++] = size;
}
+implicit_arrays:
+ arrays
+ | '[' ']' arrays
+ {
+ uint32_t *new_array;
+
+ $$ = $3;
+
+ if (!(new_array = hlsl_realloc(ctx, $$.sizes, ($$.count + 1) * sizeof(*new_array))))
+ {
+ vkd3d_free($$.sizes);
+ YYABORT;
+ }
+
+ $$.sizes = new_array;
+ $$.sizes[$$.count++] = HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT;
+ }
+
var_modifiers:
%empty
{
diff --git a/libs/vkd3d-shader/hlsl_sm1.c b/libs/vkd3d-shader/hlsl_sm1.c
index 0cdd3917..30380fa2 100644
--- a/libs/vkd3d-shader/hlsl_sm1.c
+++ b/libs/vkd3d-shader/hlsl_sm1.c
@@ -234,6 +234,7 @@ static const struct hlsl_type *get_array_type(const struct hlsl_type *type)
static unsigned int get_array_size(const struct hlsl_type *type)
{
+ assert(type->e.array.elements_count != HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT);
if (type->type == HLSL_CLASS_ARRAY)
return get_array_size(type->e.array.type) * type->e.array.elements_count;
return 1;
diff --git a/libs/vkd3d-shader/hlsl_sm4.c b/libs/vkd3d-shader/hlsl_sm4.c
index 8afa9333..2dcfdb88 100644
--- a/libs/vkd3d-shader/hlsl_sm4.c
+++ b/libs/vkd3d-shader/hlsl_sm4.c
@@ -252,6 +252,7 @@ static const struct hlsl_type *get_array_type(const struct hlsl_type *type)
static unsigned int get_array_size(const struct hlsl_type *type)
{
+ assert(type->e.array.elements_count != HLSL_ARRAY_ELEMENTS_COUNT_IMPLICIT);
if (type->type == HLSL_CLASS_ARRAY)
return get_array_size(type->e.array.type) * type->e.array.elements_count;
return 1;
diff --git a/tests/hlsl-initializer-implicit-array.shader_test b/tests/hlsl-initializer-implicit-array.shader_test
index d2b94da4..91b6f049 100644
--- a/tests/hlsl-initializer-implicit-array.shader_test
+++ b/tests/hlsl-initializer-implicit-array.shader_test
@@ -6,8 +6,8 @@ float4 main() : SV_TARGET
}
[test]
-todo draw quad
-todo probe all rgba (50, 60, 70, 80)
+draw quad
+probe all rgba (50, 60, 70, 80)
[pixel shader fail]
--
2.36.0
April 28, 2022
[PATCH vkd3d 3/8] tests: Test initialization of implicit size arrays.
by Giovanni Mascellani
From: Francisco Casas <fcasas(a)codeweavers.com>
Signed-off-by: Francisco Casas <fcasas(a)codeweavers.com>
Signed-off-by: Giovanni Mascellani <gmascellani(a)codeweavers.com>
---
Makefile.am | 1 +
...hlsl-initializer-implicit-array.shader_test | 18 ++++++++++++++++++
2 files changed, 19 insertions(+)
create mode 100644 tests/hlsl-initializer-implicit-array.shader_test
diff --git a/Makefile.am b/Makefile.am
index fa3c2a6d..2742e77c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -77,6 +77,7 @@ vkd3d_shader_tests = \
tests/hlsl-gather.shader_test \
tests/hlsl-initializer-flatten.shader_test \
tests/hlsl-initializer-invalid-arg-count.shader_test \
+ tests/hlsl-initializer-implicit-array.shader_test \
tests/hlsl-initializer-local-array.shader_test \
tests/hlsl-initializer-nested.shader_test \
tests/hlsl-initializer-numeric.shader_test \
diff --git a/tests/hlsl-initializer-implicit-array.shader_test b/tests/hlsl-initializer-implicit-array.shader_test
new file mode 100644
index 00000000..d2b94da4
--- /dev/null
+++ b/tests/hlsl-initializer-implicit-array.shader_test
@@ -0,0 +1,18 @@
+[pixel shader]
+float4 main() : SV_TARGET
+{
+ float4 arr[] = {10, 20, 30, 40, 50, 60, 70, 80};
+ return arr[1];
+}
+
+[test]
+todo draw quad
+todo probe all rgba (50, 60, 70, 80)
+
+
+[pixel shader fail]
+float4 main() : SV_TARGET
+{
+ float4 arr[] = {10, 20, 30, 40, 50, 60, 70};
+ return arr[0];
+}
--
2.36.0
April 28, 2022
[PATCH vkd3d 2/8] tests: Test complex broadcasts.
by Giovanni Mascellani
From: Francisco Casas <fcasas(a)codeweavers.com>
Signed-off-by: Francisco Casas <fcasas(a)codeweavers.com>
Signed-off-by: Giovanni Mascellani <gmascellani(a)codeweavers.com>
---
Makefile.am | 1 +
tests/cast-broadcast.shader_test | 24 ++++++++++++++++++++++++
2 files changed, 25 insertions(+)
create mode 100644 tests/cast-broadcast.shader_test
diff --git a/Makefile.am b/Makefile.am
index 26aa3f84..fa3c2a6d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -56,6 +56,7 @@ vkd3d_shader_tests = \
tests/arithmetic-int.shader_test \
tests/arithmetic-uint.shader_test \
tests/bitwise.shader_test \
+ tests/cast-broadcast.shader_test \
tests/cast-to-float.shader_test \
tests/cast-to-half.shader_test \
tests/cast-to-int.shader_test \
diff --git a/tests/cast-broadcast.shader_test b/tests/cast-broadcast.shader_test
new file mode 100644
index 00000000..02d14c0b
--- /dev/null
+++ b/tests/cast-broadcast.shader_test
@@ -0,0 +1,24 @@
+[pixel shader]
+
+struct foo
+{
+ float3 aa;
+ float4 bb;
+};
+
+struct bar
+{
+ struct foo aa;
+ int2 bb;
+ int4 cc[8];
+};
+
+float4 main() : SV_TARGET
+{
+ struct bar p = (struct bar)42;
+ return p.aa.bb + p.cc[5];
+}
+
+[test]
+todo draw quad
+todo probe all rgba (84.0, 84.0, 84.0, 84.0)
--
2.36.0
April 28, 2022
[PATCH vkd3d 1/8] vkd3d-shader/hlsl: Detect missing loads on rhs when splitting copies of non-numeric types.
by Giovanni Mascellani
From: Francisco Casas <fcasas(a)codeweavers.com>
Otherwise we can get failed assertions:
assert(node->type == HLSL_IR_LOAD);
because broadcasts to these types are not implemented yet.
Signed-off-by: Francisco Casas <fcasas(a)codeweavers.com>
Signed-off-by: Giovanni Mascellani <gmascellani(a)codeweavers.com>
---
libs/vkd3d-shader/hlsl_codegen.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/libs/vkd3d-shader/hlsl_codegen.c b/libs/vkd3d-shader/hlsl_codegen.c
index e945b94d..f7396a96 100644
--- a/libs/vkd3d-shader/hlsl_codegen.c
+++ b/libs/vkd3d-shader/hlsl_codegen.c
@@ -637,6 +637,12 @@ static bool split_array_copies(struct hlsl_ctx *ctx, struct hlsl_ir_node *instr,
element_type = type->e.array.type;
element_size = hlsl_type_get_array_element_reg_size(element_type);
+ if (rhs->type != HLSL_IR_LOAD)
+ {
+ hlsl_fixme(ctx, &instr->loc, "Array store rhs is not HLSL_IR_LOAD. Broadcast may be missing.");
+ return false;
+ }
+
for (i = 0; i < type->e.array.elements_count; ++i)
{
if (!split_copy(ctx, store, hlsl_ir_load(rhs), i * element_size, element_type))
@@ -667,6 +673,12 @@ static bool split_struct_copies(struct hlsl_ctx *ctx, struct hlsl_ir_node *instr
if (type->type != HLSL_CLASS_STRUCT)
return false;
+ if (rhs->type != HLSL_IR_LOAD)
+ {
+ hlsl_fixme(ctx, &instr->loc, "Struct store rhs is not HLSL_IR_LOAD. Broadcast may be missing.");
+ return false;
+ }
+
LIST_FOR_EACH_ENTRY(field, type->e.elements, struct hlsl_struct_field, entry)
{
if (!split_copy(ctx, store, hlsl_ir_load(rhs), field->reg_offset, field->type))
--
2.36.0
April 28, 2022
[PATCH v2 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 7d41ac14cb27..be91cf550fc7 100644
--- a/programs/sc/tests/sc.c
+++ b/programs/sc/tests/sc.c
@@ -168,13 +168,20 @@ static void test_create_service(void)
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;
@@ -211,45 +218,45 @@ static void test_create_service(void)
/* 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++)
{
@@ -258,11 +265,11 @@ static void test_create_service(void)
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= */
@@ -274,53 +281,53 @@ static void test_create_service(void)
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 */
@@ -335,10 +342,10 @@ static void test_create_service(void)
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 28, 2022
[PATCH v2 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 | 363 ++++++++++++++++++++++++++++++++++
3 files changed, 369 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..7d41ac14cb27
--- /dev/null
+++ b/programs/sc/tests/sc.c
@@ -0,0 +1,363 @@
+/*
+ * 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(void)
+{
+ 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;
+
+#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);
+ 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_DEPEND);
+ delete_test_service2(TRUE, 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
+}
+
+START_TEST(sc)
+{
+ SECURITY_ATTRIBUTES secattr = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
+ 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();
+
+ CloseServiceHandle(scmgr);
+ CloseHandle(nul_file);
+}
--
2.36.0
April 28, 2022
[PATCH resend 3/3] comctl32/tests: Add change sequence for ownerdata listviews.
by Angelo Haller
From: Angelo Haller <angelo(a)szanni.org>
Add a new test sequence for ownerdata listviews that logs solely
item change notifications for single and multiple items changing.
Use it in the ownerdata multiselect tests.
Signed-off-by: Angelo Haller <angelo(a)szanni.org>
---
dlls/comctl32/tests/listview.c | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/dlls/comctl32/tests/listview.c b/dlls/comctl32/tests/listview.c
index 124f463908e..6ac7f53137d 100644
--- a/dlls/comctl32/tests/listview.c
+++ b/dlls/comctl32/tests/listview.c
@@ -37,6 +37,7 @@ enum seq_index {
PARENT_SEQ_INDEX,
PARENT_FULL_SEQ_INDEX,
PARENT_CD_SEQ_INDEX,
+ PARENT_ODSTATECHANGED_SEQ_INDEX,
LISTVIEW_SEQ_INDEX,
EDITBOX_SEQ_INDEX,
COMBINED_SEQ_INDEX,
@@ -254,6 +255,14 @@ static const struct message ownerdata_deselect_all_parent_seq[] = {
{ 0 }
};
+static const struct message ownerdata_multiselect_odstatechanged_seq[] = {
+ { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGED },
+ { WM_NOTIFY, sent|id, 0, 0, LVN_ODSTATECHANGED },
+ { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGED },
+ { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGED },
+ { 0 }
+};
+
static const struct message change_all_parent_seq[] = {
{ WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGING },
{ WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGED },
@@ -523,6 +532,10 @@ static LRESULT WINAPI parent_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LP
add_message(sequences, PARENT_SEQ_INDEX, &msg);
add_message(sequences, COMBINED_SEQ_INDEX, &msg);
}
+ /* log change messages for single and multiple items changing in ownerdata listviews */
+ if (message == WM_NOTIFY && (msg.id == LVN_ITEMCHANGED || msg.id == LVN_ODSTATECHANGED))
+ add_message(sequences, PARENT_ODSTATECHANGED_SEQ_INDEX, &msg);
+
add_message(sequences, PARENT_FULL_SEQ_INDEX, &msg);
switch (message)
@@ -3556,8 +3569,15 @@ static void test_ownerdata_multiselect(void)
hold_key(VK_SHIFT);
+ flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
res = SendMessageA(hwnd, WM_KEYDOWN, VK_DOWN, 0);
expect(0, res);
+
+ ok_sequence(sequences, PARENT_ODSTATECHANGED_SEQ_INDEX,
+ ownerdata_multiselect_odstatechanged_seq,
+ "ownerdata select multiple notification", TRUE);
+
res = SendMessageA(hwnd, WM_KEYUP, VK_DOWN, 0);
expect(0, res);
@@ -3566,8 +3586,15 @@ static void test_ownerdata_multiselect(void)
hold_key(VK_CONTROL);
+ flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
res = SendMessageA(hwnd, WM_KEYDOWN, VK_DOWN, 0);
expect(0, res);
+
+ ok_sequence(sequences, PARENT_ODSTATECHANGED_SEQ_INDEX,
+ ownerdata_multiselect_odstatechanged_seq,
+ "ownerdata select multiple notification", TRUE);
+
res = SendMessageA(hwnd, WM_KEYUP, VK_DOWN, 0);
expect(0, res);
--
2.36.0
April 28, 2022
[PATCH resend 2/3] comctl32/tests: Add multi select tests for ownerdata listviews.
by Angelo Haller
From: Angelo Haller <angelo(a)szanni.org>
Add tests for selecting multiple items in ownerdata listviews by
using SHIFT/CTRL and arrow keys.
Signed-off-by: Angelo Haller <angelo(a)szanni.org>
---
dlls/comctl32/tests/listview.c | 52 ++++++++++++++++++++++++++++++++++
1 file changed, 52 insertions(+)
diff --git a/dlls/comctl32/tests/listview.c b/dlls/comctl32/tests/listview.c
index 160925a073a..124f463908e 100644
--- a/dlls/comctl32/tests/listview.c
+++ b/dlls/comctl32/tests/listview.c
@@ -3530,6 +3530,56 @@ static void test_ownerdata(void)
DestroyWindow(hwnd);
}
+static void test_ownerdata_multiselect(void)
+{
+ HWND hwnd;
+ DWORD res;
+ LVITEMA item;
+
+ hwnd = create_listview_control(LVS_OWNERDATA | LVS_REPORT);
+ ok(hwnd != NULL, "failed to create a listview window\n");
+ res = SendMessageA(hwnd, LVM_SETITEMCOUNT, 20, 0);
+ expect(1, res);
+ res = SendMessageA(hwnd, LVM_GETSELECTEDCOUNT, 0, 0);
+ expect(0, res);
+
+ memset(&item, 0, sizeof(item));
+ item.state = LVIS_SELECTED | LVIS_FOCUSED;
+ item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
+ res = SendMessageA(hwnd, LVM_SETITEMSTATE, 0, (LPARAM)&item);
+ expect(TRUE, res);
+ res = SendMessageA(hwnd, LVM_GETSELECTEDCOUNT, 0, 0);
+ expect(1, res);
+
+ res = SendMessageA(hwnd, LVM_SETSELECTIONMARK, 0, 0);
+ expect(0, res);
+
+ hold_key(VK_SHIFT);
+
+ res = SendMessageA(hwnd, WM_KEYDOWN, VK_DOWN, 0);
+ expect(0, res);
+ res = SendMessageA(hwnd, WM_KEYUP, VK_DOWN, 0);
+ expect(0, res);
+
+ res = SendMessageA(hwnd, LVM_GETSELECTEDCOUNT, 0, 0);
+ expect(2, res);
+
+ hold_key(VK_CONTROL);
+
+ res = SendMessageA(hwnd, WM_KEYDOWN, VK_DOWN, 0);
+ expect(0, res);
+ res = SendMessageA(hwnd, WM_KEYUP, VK_DOWN, 0);
+ expect(0, res);
+
+ release_key(VK_CONTROL);
+ release_key(VK_SHIFT);
+
+ res = SendMessageA(hwnd, LVM_GETSELECTEDCOUNT, 0, 0);
+ expect(3, res);
+
+ DestroyWindow(hwnd);
+}
+
static void test_norecompute(void)
{
static CHAR testA[] = "test";
@@ -6878,6 +6928,7 @@ START_TEST(listview)
test_subitem_rect();
test_sorting();
test_ownerdata();
+ test_ownerdata_multiselect();
test_norecompute();
test_nosortheader();
test_setredraw();
@@ -6937,6 +6988,7 @@ START_TEST(listview)
test_columns();
test_sorting();
test_ownerdata();
+ test_ownerdata_multiselect();
test_norecompute();
test_nosortheader();
test_indentation();
--
2.36.0
April 28, 2022
[PATCH resend 1/3] comctl32/tests: Add hold_key and release_key functions.
by Angelo Haller
From: Angelo Haller <angelo(a)szanni.org>
Add functions to simulate the holding of keys like SHIFT, CTRL,...
Move existing SHIFT key press emulation to these functions.
Signed-off-by: Angelo Haller <angelo(a)szanni.org>
---
dlls/comctl32/tests/listview.c | 35 +++++++++++++++++++++++++---------
1 file changed, 26 insertions(+), 9 deletions(-)
diff --git a/dlls/comctl32/tests/listview.c b/dlls/comctl32/tests/listview.c
index ca263cd644a..160925a073a 100644
--- a/dlls/comctl32/tests/listview.c
+++ b/dlls/comctl32/tests/listview.c
@@ -462,6 +462,30 @@ static const struct message listview_end_label_edit_kill_focus[] = {
{ 0 }
};
+static void hold_key(int vk)
+{
+ BYTE kstate[256];
+ BOOL res;
+
+ res = GetKeyboardState(kstate);
+ ok(res, "GetKeyboardState failed.\n");
+ kstate[vk] |= 0x80;
+ res = SetKeyboardState(kstate);
+ ok(res, "SetKeyboardState failed.\n");
+}
+
+static void release_key(int vk)
+{
+ BYTE kstate[256];
+ BOOL res;
+
+ res = GetKeyboardState(kstate);
+ ok(res, "GetKeyboardState failed.\n");
+ kstate[vk] &= ~0x80;
+ res = SetKeyboardState(kstate);
+ ok(res, "SetKeyboardState failed.\n");
+}
+
static LRESULT WINAPI parent_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static LONG defwndproc_counter = 0;
@@ -2356,7 +2380,6 @@ static void test_multiselect(void)
int i, j;
static const int items=5;
DWORD item_count;
- BYTE kstate[256];
select_task task;
LONG_PTR style;
LVITEMA item;
@@ -2423,10 +2446,7 @@ static void test_multiselect(void)
selected_count = SendMessageA(hwnd, LVM_GETSELECTEDCOUNT, 0, 0);
ok(selected_count == 1, "expected 1, got %ld\n", selected_count);
- /* Set SHIFT key pressed */
- GetKeyboardState(kstate);
- kstate[VK_SHIFT]=0x80;
- SetKeyboardState(kstate);
+ hold_key(VK_SHIFT);
for (j=1;j<=(task.count == -1 ? item_count : task.count);j++) {
r = SendMessageA(hwnd, WM_KEYDOWN, task.loopVK, 0);
@@ -2441,10 +2461,7 @@ static void test_multiselect(void)
"Failed multiple selection %s. There should be %ld selected items (is %ld)\n",
task.descr, item_count, selected_count);
- /* Set SHIFT key released */
- GetKeyboardState(kstate);
- kstate[VK_SHIFT]=0x00;
- SetKeyboardState(kstate);
+ release_key(VK_SHIFT);
}
DestroyWindow(hwnd);
--
2.36.0
April 28, 2022
[PATCH resend 0/3] Add ownerdata listview multiselect tests.
by Angelo Haller
From: Angelo Haller <angelo(a)szanni.org>
This is a resend of the follow up on the listview LVN_ODSTATECHANGED
fixes I sent in a while ago. These are the missing tests that were
requested for the fixes that I will resubmit once this gets merged.
The first two patches are fairly straight forward: refactoring and
adding basic tests.
The third one adds a new message test sequence.
I have tried to use one of the existing PARENT_SEQ_INDEX sequences,
it just turns out that that particular sequence gets spammed with many
unrelated messages.
Many messages that are being sent out of order and/or that concern other
listview aspects: LVN_GETDISPINFOA NM_CUSTOMDRAW WM_CHANGEUISTATE
Hence I introduced a new PARENT_ODSTATECHANGED_SEQ_INDEX to enable
better testing of ownerdata listviews.
Warning: I have had access to the Windows Research Kernel (WRK) 1.2
~10 years ago. These changes are regarding comctrl32 & tests which are NOT
part of the WRK. As outlined in https://wiki.winehq.org/Developer_FAQ this
should therefore satisfy the requirement of ONLY submitting patches to
components I have NOT had access to.
Angelo Haller (3):
comctl32/tests: Add hold_key and release_key functions.
comctl32/tests: Add multi select tests for ownerdata listviews.
comctl32/tests: Add change sequence for ownerdata listviews.
dlls/comctl32/tests/listview.c | 114 ++++++++++++++++++++++++++++++---
1 file changed, 105 insertions(+), 9 deletions(-)
Signed-off-by: Angelo Haller <angelo(a)szanni.org>
--
2.36.0
April 28, 2022
Re: MR11v2 - winex11: PE conversion preparation
by Huw Davies (@huw)
Yes, I guess you have to remember to add them while committing, rather than rely on git send-email.
--
https://gitlab.winehq.org/wine/wine/-/merge_requests/11#note_481
April 28, 2022
Re: [PATCH v5 1/2] include: Add winusb.h file.
by Rémi Bernon
On 4/14/22 01:36, Mohamad Al-Jaf wrote:
> +#ifndef _WINUSB_H_
> +#define _WINUSB_H_
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +typedef PVOID WINUSB_INTERFACE_HANDLE, *PWINUSB_INTERFACE_HANDLE;
> +
> +typedef struct _WINUSB_SETUP_PACKET {
> + UCHAR RequestType;
> + UCHAR Request;
> + USHORT Value;
> + USHORT Index;
> + USHORT Length;
> +} WINUSB_SETUP_PACKET, *PWINUSB_SETUP_PACKET;
> +
I think the struct packing is wrong, the SDK uses #pragma pack(1) here,
which probably should use #include "pshpack1.h" / "poppack.h".
> +BOOL WINAPI WinUsb_AbortPipe(WINUSB_INTERFACE_HANDLE,UCHAR);
> +BOOL WINAPI WinUsb_ControlTransfer(WINUSB_INTERFACE_HANDLE,WINUSB_SETUP_PACKET,PUCHAR,ULONG,PULONG,LPOVERLAPPED);
> +BOOL WINAPI WinUsb_FlushPipe(WINUSB_INTERFACE_HANDLE,UCHAR);
> +BOOL WINAPI WinUsb_Free(WINUSB_INTERFACE_HANDLE);
> +BOOL WINAPI WinUsb_GetAssociatedInterface(WINUSB_INTERFACE_HANDLE,UCHAR,PWINUSB_INTERFACE_HANDLE);
> +BOOL WINAPI WinUsb_GetCurrentAlternateSetting(WINUSB_INTERFACE_HANDLE,PUCHAR);
> +BOOL WINAPI WinUsb_GetDescriptor(WINUSB_INTERFACE_HANDLE,UCHAR,UCHAR,USHORT,PUCHAR,ULONG,PULONG);
> +BOOL WINAPI WinUsb_GetOverlappedResult(WINUSB_INTERFACE_HANDLE,LPOVERLAPPED,LPDWORD,BOOL);
> +BOOL WINAPI WinUsb_GetPipePolicy(WINUSB_INTERFACE_HANDLE,UCHAR,ULONG,PULONG,PVOID);
> +BOOL WINAPI WinUsb_GetPowerPolicy(WINUSB_INTERFACE_HANDLE,ULONG,PULONG,PVOID);
> +BOOL WINAPI WinUsb_Initialize(HANDLE,PWINUSB_INTERFACE_HANDLE);
> +BOOL WINAPI WinUsb_QueryDeviceInformation(WINUSB_INTERFACE_HANDLE,ULONG,PULONG,PVOID);
> +BOOL WINAPI WinUsb_ReadPipe(WINUSB_INTERFACE_HANDLE,UCHAR,PUCHAR,ULONG,PULONG,LPOVERLAPPED);
> +BOOL WINAPI WinUsb_ResetPipe(WINUSB_INTERFACE_HANDLE,UCHAR);
> +BOOL WINAPI WinUsb_SetCurrentAlternateSetting(WINUSB_INTERFACE_HANDLE,UCHAR);
> +BOOL WINAPI WinUsb_SetPipePolicy(WINUSB_INTERFACE_HANDLE,UCHAR,ULONG,ULONG,PVOID);
> +BOOL WINAPI WinUsb_SetPowerPolicy(WINUSB_INTERFACE_HANDLE,ULONG,ULONG,PVOID);
> +BOOL WINAPI WinUsb_WritePipe(WINUSB_INTERFACE_HANDLE,UCHAR,PUCHAR,ULONG,PULONG,LPOVERLAPPED);
> +
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 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.
--
Rémi Bernon <rbernon(a)codeweavers.com>
April 28, 2022
Re: MR11v2 - winex11: PE conversion preparation
by Jacek Caban (@jacek)
Oh, right, this wasn't intentional.
--
https://gitlab.winehq.org/wine/wine/-/merge_requests/11#note_480
April 28, 2022
[PATCH v2 8/8] winex11: Directly use ntdll for utf8 conversion.
by Jacek Caban
From: Jacek Caban <jacek(a)codeweavers.com>
Signed-off-by: Jacek Caban <jacek(a)codeweavers.com>
---
dlls/winex11.drv/clipboard.c | 11 +++++------
dlls/winex11.drv/window.c | 7 ++++---
dlls/winex11.drv/xrandr.c | 8 ++++++--
3 files changed, 15 insertions(+), 11 deletions(-)
diff --git a/dlls/winex11.drv/clipboard.c b/dlls/winex11.drv/clipboard.c
index 1dcd5362c8c..f9e6764fc0a 100644
--- a/dlls/winex11.drv/clipboard.c
+++ b/dlls/winex11.drv/clipboard.c
@@ -914,12 +914,11 @@ static void *import_text_html( Atom type, const void *data, size_t size, size_t
/* Firefox uses UTF-16LE with byte order mark. Convert to UTF-8 without the BOM. */
if (size >= sizeof(WCHAR) && ((const WCHAR *)data)[0] == 0xfeff)
{
- len = WideCharToMultiByte( CP_UTF8, 0, (const WCHAR *)data + 1, size / sizeof(WCHAR) - 1,
- NULL, 0, NULL, NULL );
- if (!(text = malloc( len ))) return 0;
- WideCharToMultiByte( CP_UTF8, 0, (const WCHAR *)data + 1, size / sizeof(WCHAR) - 1,
- text, len, NULL, NULL );
- size = len;
+ DWORD str_len;
+ RtlUnicodeToUTF8N( NULL, 0, &str_len, (const WCHAR *)data + 1, size - sizeof(WCHAR) );
+ if (!(text = malloc( str_len ))) return NULL;
+ RtlUnicodeToUTF8N( text, str_len, &str_len, (const WCHAR *)data + 1, size - sizeof(WCHAR) );
+ size = str_len;
data = text;
}
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c
index d7027032465..3b0ccd8d22a 100644
--- a/dlls/winex11.drv/window.c
+++ b/dlls/winex11.drv/window.c
@@ -437,22 +437,23 @@ static void sync_window_opacity( Display *display, Window win,
*/
static void sync_window_text( Display *display, Window win, const WCHAR *text )
{
- UINT count;
+ DWORD count, len;
char *buffer, *utf8_buffer;
XTextProperty prop;
/* allocate new buffer for window text */
+ len = lstrlenW( text );
count = WideCharToMultiByte(CP_UNIXCP, 0, text, -1, NULL, 0, NULL, NULL);
if (!(buffer = malloc( count ))) return;
WideCharToMultiByte(CP_UNIXCP, 0, text, -1, buffer, count, NULL, NULL);
- count = WideCharToMultiByte(CP_UTF8, 0, text, strlenW(text), NULL, 0, NULL, NULL);
+ RtlUnicodeToUTF8N( NULL, 0, &count, text, len * sizeof(WCHAR) );
if (!(utf8_buffer = malloc( count )))
{
free( buffer );
return;
}
- WideCharToMultiByte(CP_UTF8, 0, text, strlenW(text), utf8_buffer, count, NULL, NULL);
+ RtlUnicodeToUTF8N( utf8_buffer, count, &count, text, len * sizeof(WCHAR) );
if (XmbTextListToTextProperty( display, &buffer, 1, XStdICCTextStyle, &prop ) == Success)
{
diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c
index b0619c0abcd..6ede776d76b 100644
--- a/dlls/winex11.drv/xrandr.c
+++ b/dlls/winex11.drv/xrandr.c
@@ -652,6 +652,7 @@ static BOOL get_gpu_properties_from_vulkan( struct gdi_gpu *gpu, const XRRProvid
VkPhysicalDeviceIDProperties id;
VkInstance vk_instance = NULL;
VkDisplayKHR vk_display;
+ DWORD len;
BOOL ret = FALSE;
VkResult vr;
@@ -723,7 +724,8 @@ static BOOL get_gpu_properties_from_vulkan( struct gdi_gpu *gpu, const XRRProvid
gpu->vendor_id = properties2.properties.vendorID;
gpu->device_id = properties2.properties.deviceID;
}
- MultiByteToWideChar( CP_UTF8, 0, properties2.properties.deviceName, -1, gpu->name, ARRAY_SIZE(gpu->name) );
+ RtlUTF8ToUnicodeN( gpu->name, sizeof(gpu->name), &len, properties2.properties.deviceName,
+ strlen( properties2.properties.deviceName ) + 1 );
ret = TRUE;
goto done;
}
@@ -749,6 +751,7 @@ static BOOL xrandr14_get_gpus2( struct gdi_gpu **new_gpus, int *count, BOOL get_
INT primary_provider = -1;
RECT primary_rect;
BOOL ret = FALSE;
+ DWORD len;
INT i, j;
screen_resources = xrandr_get_screen_resources();
@@ -803,7 +806,8 @@ static BOOL xrandr14_get_gpus2( struct gdi_gpu **new_gpus, int *count, BOOL get_
if (get_properties)
{
if (!get_gpu_properties_from_vulkan( &gpus[i], provider_info ))
- MultiByteToWideChar( CP_UTF8, 0, provider_info->name, -1, gpus[i].name, ARRAY_SIZE(gpus[i].name) );
+ RtlUTF8ToUnicodeN( gpus[i].name, sizeof(gpus[i].name), &len, provider_info->name,
+ strlen( provider_info->name ) + 1 );
/* FIXME: Add an alternate method of getting PCI IDs, for systems that don't support Vulkan */
}
pXRRFreeProviderInfo( provider_info );
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/11
April 28, 2022
[PATCH v2 7/8] winex11: Move Unicode conversion out of string_from_unicode_text.
by Jacek Caban
From: Jacek Caban <jacek(a)codeweavers.com>
Signed-off-by: Jacek Caban <jacek(a)codeweavers.com>
---
dlls/winex11.drv/clipboard.c | 68 +++++++++++++++++++-----------------
1 file changed, 36 insertions(+), 32 deletions(-)
diff --git a/dlls/winex11.drv/clipboard.c b/dlls/winex11.drv/clipboard.c
index ce936f9012e..1dcd5362c8c 100644
--- a/dlls/winex11.drv/clipboard.c
+++ b/dlls/winex11.drv/clipboard.c
@@ -729,9 +729,11 @@ static void *import_utf8_string( Atom type, const void *data, size_t size, size_
DWORD str_size;
WCHAR *ret;
- if (!(ret = malloc( (size * 2 + 1) * sizeof(WCHAR) ))) return NULL;
- RtlUTF8ToUnicodeN( ret + size, size * sizeof(WCHAR), &str_size, data, size );
- return unicode_text_from_string( ret, ret + size, str_size / sizeof(WCHAR), ret_size );
+ RtlUTF8ToUnicodeN( NULL, 0, &str_size, data, size );
+ if (!(ret = malloc( str_size * 2 + sizeof(WCHAR) ))) return NULL;
+ RtlUTF8ToUnicodeN( ret + str_size / sizeof(WCHAR), str_size, &str_size, data, size );
+ return unicode_text_from_string( ret, ret + str_size / sizeof(WCHAR),
+ str_size / sizeof(WCHAR), ret_size );
}
@@ -1198,31 +1200,19 @@ static BOOL export_data( Display *display, Window win, Atom prop, Atom target, v
*
* Convert CF_UNICODETEXT data to a string in the specified codepage.
*/
-static char *string_from_unicode_text( UINT codepage, const WCHAR *string, size_t string_size, size_t *size )
+static void string_from_unicode_text( char *str, size_t len, DWORD *size )
{
- UINT i, j;
- char *str;
- UINT lenW = string_size / sizeof(WCHAR);
- DWORD len;
-
- if (!string_size) return NULL;
+ DWORD i, j;
- len = WideCharToMultiByte( codepage, 0, string, lenW, NULL, 0, NULL, NULL );
- if ((str = malloc( len )))
+ /* remove carriage returns */
+ for (i = j = 0; i < len; i++)
{
- WideCharToMultiByte( codepage, 0, string, lenW, str, len, NULL, NULL);
-
- /* remove carriage returns */
- for (i = j = 0; i < len; i++)
- {
- if (str[i] == '\r' && (i == len - 1 || str[i + 1] == '\n')) continue;
- str[j++] = str[i];
- }
- while (j && !str[j - 1]) j--; /* remove trailing nulls */
- *size = j;
- TRACE( "returning %s\n", debugstr_an( str, j ));
+ if (str[i] == '\r' && (i == len - 1 || str[i + 1] == '\n')) continue;
+ str[j++] = str[i];
}
- return str;
+ while (j && !str[j - 1]) j--; /* remove trailing nulls */
+ TRACE( "returning %s\n", debugstr_an( str, j ));
+ *size = j;
}
@@ -1233,10 +1223,14 @@ static char *string_from_unicode_text( UINT codepage, const WCHAR *string, size_
*/
static BOOL export_string( Display *display, Window win, Atom prop, Atom target, void *data, size_t size )
{
- char *text = string_from_unicode_text( 28591, data, size, &size );
+ DWORD len;
+ char *text;
+
+ if (!(text = malloc( size ))) return FALSE;
+ len = WideCharToMultiByte( 28591, 0, data, size / sizeof(WCHAR), text, size, NULL, NULL );
+ string_from_unicode_text( text, len, &len );
- if (!text) return FALSE;
- put_property( display, win, prop, target, 8, text, size );
+ put_property( display, win, prop, target, 8, text, len );
free( text );
return TRUE;
}
@@ -1250,10 +1244,14 @@ static BOOL export_string( Display *display, Window win, Atom prop, Atom target,
static BOOL export_utf8_string( Display *display, Window win, Atom prop, Atom target,
void *data, size_t size )
{
- char *text = string_from_unicode_text( CP_UTF8, data, size, &size );
+ DWORD len;
+ char *text;
+
+ if (!(text = malloc( size / sizeof(WCHAR) * 3 ))) return FALSE;
+ RtlUnicodeToUTF8N( text, size / sizeof(WCHAR) * 3, &len, data, size );
+ string_from_unicode_text( text, len, &len );
- if (!text) return FALSE;
- put_property( display, win, prop, target, 8, text, size );
+ put_property( display, win, prop, target, 8, text, len );
free( text );
return TRUE;
}
@@ -1280,9 +1278,15 @@ static BOOL export_compound_text( Display *display, Window win, Atom prop, Atom
{
XTextProperty textprop;
XICCEncodingStyle style;
- char *text = string_from_unicode_text( CP_UNIXCP, data, size, &size );
+ DWORD len;
+ char *text;
+
+
+ if (!(text = malloc( size / sizeof(WCHAR) * 3 ))) return FALSE;
+ len = WideCharToMultiByte( CP_UNIXCP, 0, data, size / sizeof(WCHAR),
+ text, size / sizeof(WCHAR) * 3, NULL, NULL );
+ string_from_unicode_text( text, len, &len );
- if (!text) return FALSE;
if (target == x11drv_atom(COMPOUND_TEXT))
style = XCompoundTextStyle;
else
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/11
April 28, 2022
[PATCH v2 6/8] winex11: Move Unicode conversion out of unicode_text_from_string.
by Jacek Caban
From: Jacek Caban <jacek(a)codeweavers.com>
Signed-off-by: Jacek Caban <jacek(a)codeweavers.com>
---
dlls/winex11.drv/clipboard.c | 45 +++++++++++++++++++++---------------
1 file changed, 27 insertions(+), 18 deletions(-)
diff --git a/dlls/winex11.drv/clipboard.c b/dlls/winex11.drv/clipboard.c
index 6baa87c5ce1..ce936f9012e 100644
--- a/dlls/winex11.drv/clipboard.c
+++ b/dlls/winex11.drv/clipboard.c
@@ -687,25 +687,19 @@ static WCHAR* uri_to_dos(char *encodedURI)
*
* Convert a string in the specified encoding to CF_UNICODETEXT format.
*/
-static WCHAR *unicode_text_from_string( UINT codepage, const void *data, size_t size, size_t *ret_size )
+static void *unicode_text_from_string( WCHAR *ret, const WCHAR *string, DWORD count, size_t *size )
{
- DWORD i, j, count;
- WCHAR *strW;
+ DWORD i, j;
- count = MultiByteToWideChar( codepage, 0, data, size, NULL, 0);
-
- if (!(strW = malloc( (count * 2 + 1) * sizeof(WCHAR) ))) return 0;
-
- MultiByteToWideChar( codepage, 0, data, size, strW + count, count );
for (i = j = 0; i < count; i++)
{
- if (strW[i + count] == '\n' && (!i || strW[i + count - 1] != '\r')) strW[j++] = '\r';
- strW[j++] = strW[i + count];
+ if (string[i] == '\n' && (!i || string[i - 1] != '\r')) ret[j++] = '\r';
+ ret[j++] = string[i];
}
- strW[j++] = 0;
- *ret_size = j * sizeof(WCHAR);
- TRACE( "returning %s\n", debugstr_wn( strW, j - 1 ));
- return strW;
+ ret[j++] = 0;
+ *size = j * sizeof(WCHAR);
+ TRACE( "returning %s\n", debugstr_wn( ret, j - 1 ));
+ return ret;
}
@@ -716,7 +710,12 @@ static WCHAR *unicode_text_from_string( UINT codepage, const void *data, size_t
*/
static void *import_string( Atom type, const void *data, size_t size, size_t *ret_size )
{
- return unicode_text_from_string( 28591, data, size, ret_size );
+ DWORD str_size;
+ WCHAR *ret;
+
+ if (!(ret = malloc( (size * 2 + 1) * sizeof(WCHAR) ))) return NULL;
+ str_size = MultiByteToWideChar( 28591, 0, data, size, ret + size, size );
+ return unicode_text_from_string( ret, ret + size, str_size, ret_size );
}
@@ -727,7 +726,12 @@ static void *import_string( Atom type, const void *data, size_t size, size_t *re
*/
static void *import_utf8_string( Atom type, const void *data, size_t size, size_t *ret_size )
{
- return unicode_text_from_string( CP_UTF8, data, size, ret_size );
+ DWORD str_size;
+ WCHAR *ret;
+
+ if (!(ret = malloc( (size * 2 + 1) * sizeof(WCHAR) ))) return NULL;
+ RtlUTF8ToUnicodeN( ret + size, size * sizeof(WCHAR), &str_size, data, size );
+ return unicode_text_from_string( ret, ret + size, str_size / sizeof(WCHAR), ret_size );
}
@@ -741,7 +745,8 @@ static void *import_compound_text( Atom type, const void *data, size_t size, siz
char** srcstr;
int count;
XTextProperty txtprop;
- void *ret;
+ DWORD len;
+ WCHAR *ret;
txtprop.value = (BYTE *)data;
txtprop.nitems = size;
@@ -750,7 +755,11 @@ static void *import_compound_text( Atom type, const void *data, size_t size, siz
if (XmbTextPropertyToTextList( thread_display(), &txtprop, &srcstr, &count ) != Success) return 0;
if (!count) return 0;
- ret = unicode_text_from_string( CP_UNIXCP, srcstr[0], strlen(srcstr[0]) + 1, ret_size );
+ len = strlen(srcstr[0]) + 1;
+ if (!(ret = malloc( (len * 2 + 1) * sizeof(WCHAR) ))) return NULL;
+ count = MultiByteToWideChar( CP_UNIXCP, 0, srcstr[0], len, ret + len, len );
+ ret = unicode_text_from_string( ret, ret + len, count, ret_size );
+
XFreeStringList(srcstr);
return ret;
}
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/11
April 28, 2022
[PATCH v2 5/8] winex11: Use inline intersect_rect helper instead of IntersectRect.
by Jacek Caban
From: Jacek Caban <jacek(a)codeweavers.com>
Signed-off-by: Jacek Caban <jacek(a)codeweavers.com>
---
dlls/winex11.drv/bitblt.c | 2 +-
dlls/winex11.drv/display.c | 4 ++--
dlls/winex11.drv/init.c | 2 +-
dlls/winex11.drv/settings.c | 2 +-
dlls/winex11.drv/window.c | 4 ++--
dlls/winex11.drv/x11drv.h | 9 +++++++++
6 files changed, 16 insertions(+), 7 deletions(-)
diff --git a/dlls/winex11.drv/bitblt.c b/dlls/winex11.drv/bitblt.c
index 550c5f06f37..e21f975ef4a 100644
--- a/dlls/winex11.drv/bitblt.c
+++ b/dlls/winex11.drv/bitblt.c
@@ -1912,7 +1912,7 @@ static void x11drv_surface_flush( struct window_surface *window_surface )
coords.width = surface->header.rect.right - surface->header.rect.left;
coords.height = surface->header.rect.bottom - surface->header.rect.top;
SetRect( &coords.visrect, 0, 0, coords.width, coords.height );
- if (IntersectRect( &coords.visrect, &coords.visrect, &surface->bounds ))
+ if (intersect_rect( &coords.visrect, &coords.visrect, &surface->bounds ))
{
TRACE( "flushing %p %dx%d bounds %s bits %p\n",
surface, coords.width, coords.height,
diff --git a/dlls/winex11.drv/display.c b/dlls/winex11.drv/display.c
index f90cc455c36..aef4ed0b5b0 100644
--- a/dlls/winex11.drv/display.c
+++ b/dlls/winex11.drv/display.c
@@ -124,7 +124,7 @@ RECT get_work_area(const RECT *monitor_rect)
work_rect.right = work_rect.left + work_area[i * 4 + 2];
work_rect.bottom = work_rect.top + work_area[i * 4 + 3];
- if (IntersectRect(&work_rect, &work_rect, monitor_rect))
+ if (intersect_rect( &work_rect, &work_rect, monitor_rect ))
{
TRACE("work_rect:%s.\n", wine_dbgstr_rect(&work_rect));
XFree(work_area);
@@ -146,7 +146,7 @@ RECT get_work_area(const RECT *monitor_rect)
SetRect(&work_rect, work_area[0], work_area[1], work_area[0] + work_area[2],
work_area[1] + work_area[3]);
- if (IntersectRect(&work_rect, &work_rect, monitor_rect))
+ if (intersect_rect( &work_rect, &work_rect, monitor_rect ))
{
TRACE("work_rect:%s.\n", wine_dbgstr_rect(&work_rect));
XFree(work_area);
diff --git a/dlls/winex11.drv/init.c b/dlls/winex11.drv/init.c
index 9c5c394581b..27e070ef9b4 100644
--- a/dlls/winex11.drv/init.c
+++ b/dlls/winex11.drv/init.c
@@ -147,7 +147,7 @@ void add_device_bounds( X11DRV_PDEVICE *dev, const RECT *rect )
if (!dev->bounds) return;
if (dev->region && NtGdiGetRgnBox( dev->region, &rc ))
{
- if (IntersectRect( &rc, &rc, rect )) add_bounds_rect( dev->bounds, &rc );
+ if (intersect_rect( &rc, &rc, rect )) add_bounds_rect( dev->bounds, &rc );
}
else add_bounds_rect( dev->bounds, rect );
}
diff --git a/dlls/winex11.drv/settings.c b/dlls/winex11.drv/settings.c
index 652dedce601..44c61776b4b 100644
--- a/dlls/winex11.drv/settings.c
+++ b/dlls/winex11.drv/settings.c
@@ -709,7 +709,7 @@ static BOOL overlap_placed_displays(const RECT *rect, const struct x11drv_displa
for (display_idx = 0; display_idx < display_count; ++display_idx)
{
if (displays[display_idx].placed &&
- IntersectRect(&intersect, &displays[display_idx].new_rect, rect))
+ intersect_rect(&intersect, &displays[display_idx].new_rect, rect))
return TRUE;
}
return FALSE;
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c
index 6771368671a..d7027032465 100644
--- a/dlls/winex11.drv/window.c
+++ b/dlls/winex11.drv/window.c
@@ -2309,7 +2309,7 @@ static inline BOOL get_surface_rect( const RECT *visible_rect, RECT *surface_rec
{
*surface_rect = NtUserGetVirtualScreenRect();
- if (!IntersectRect( surface_rect, surface_rect, visible_rect )) return FALSE;
+ if (!intersect_rect( surface_rect, surface_rect, visible_rect )) return FALSE;
OffsetRect( surface_rect, -visible_rect->left, -visible_rect->top );
surface_rect->left &= ~31;
surface_rect->top &= ~31;
@@ -2761,7 +2761,7 @@ BOOL X11DRV_UpdateLayeredWindow( HWND hwnd, const UPDATELAYEREDWINDOWINFO *info,
if (info->prcDirty)
{
- IntersectRect( &rect, &rect, info->prcDirty );
+ intersect_rect( &rect, &rect, info->prcDirty );
memcpy( src_bits, dst_bits, bmi->bmiHeader.biSizeImage );
NtGdiPatBlt( hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, BLACKNESS );
}
diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h
index 69aa69404f4..4576564fda3 100644
--- a/dlls/winex11.drv/x11drv.h
+++ b/dlls/winex11.drv/x11drv.h
@@ -885,6 +885,15 @@ static inline HWND get_active_window(void)
return NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ) ? info.hwndActive : 0;
}
+static inline BOOL intersect_rect( RECT *dst, const RECT *src1, const RECT *src2 )
+{
+ dst->left = max( src1->left, src2->left );
+ dst->top = max( src1->top, src2->top );
+ dst->right = min( src1->right, src2->right );
+ dst->bottom = min( src1->bottom, src2->bottom );
+ return !IsRectEmpty( dst );
+}
+
/* registry helpers */
extern HKEY open_hkcu_key( const char *name ) DECLSPEC_HIDDEN;
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/11
April 28, 2022
[PATCH v2 4/8] winex11: Move XdndLeave event handler to event.c.
by Jacek Caban
From: Jacek Caban <jacek(a)codeweavers.com>
Signed-off-by: Jacek Caban <jacek(a)codeweavers.com>
---
dlls/winex11.drv/event.c | 9 ++++++++-
dlls/winex11.drv/unixlib.h | 1 +
dlls/winex11.drv/x11drv.h | 1 -
dlls/winex11.drv/xdnd.c | 6 +++++-
4 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c
index a02f0340010..729a0a7eab6 100644
--- a/dlls/winex11.drv/event.c
+++ b/dlls/winex11.drv/event.c
@@ -1920,6 +1920,13 @@ static void handle_xdnd_drop_event( HWND hwnd, XClientMessageEvent *event )
}
+static void handle_xdnd_leave_event( HWND hwnd, XClientMessageEvent *event )
+{
+ UINT type = DND_LEAVE_EVENT;
+ handle_dnd_event( &type );
+}
+
+
struct client_message_handler
{
int atom; /* protocol atom */
@@ -1935,7 +1942,7 @@ static const struct client_message_handler client_messages[] =
{ XATOM_XdndEnter, handle_xdnd_enter_event },
{ XATOM_XdndPosition, handle_xdnd_position_event },
{ XATOM_XdndDrop, handle_xdnd_drop_event },
- { XATOM_XdndLeave, X11DRV_XDND_LeaveEvent }
+ { XATOM_XdndLeave, handle_xdnd_leave_event }
};
diff --git a/dlls/winex11.drv/unixlib.h b/dlls/winex11.drv/unixlib.h
index 41120385e60..537a8e0326a 100644
--- a/dlls/winex11.drv/unixlib.h
+++ b/dlls/winex11.drv/unixlib.h
@@ -31,6 +31,7 @@ struct format_entry
enum dnd_event_type
{
DND_DROP_EVENT,
+ DND_LEAVE_EVENT,
DND_POSITION_EVENT,
};
diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h
index c17dc45594c..69aa69404f4 100644
--- a/dlls/winex11.drv/x11drv.h
+++ b/dlls/winex11.drv/x11drv.h
@@ -294,7 +294,6 @@ extern BOOL IME_SetCompositionString(DWORD dwIndex, LPCVOID lpComp,
DWORD dwReadLen) DECLSPEC_HIDDEN;
extern void IME_SetResultString(LPWSTR lpResult, DWORD dwResultlen) DECLSPEC_HIDDEN;
-extern void X11DRV_XDND_LeaveEvent( HWND hWnd, XClientMessageEvent *event ) DECLSPEC_HIDDEN;
extern void handle_dnd_enter_event( struct format_entry *formats, ULONG size ) DECLSPEC_HIDDEN;
extern UINT handle_dnd_event( void *params ) DECLSPEC_HIDDEN;
diff --git a/dlls/winex11.drv/xdnd.c b/dlls/winex11.drv/xdnd.c
index 9de0b60d0cd..8240bbbf5d6 100644
--- a/dlls/winex11.drv/xdnd.c
+++ b/dlls/winex11.drv/xdnd.c
@@ -342,7 +342,7 @@ static DWORD handle_drop_event( struct dnd_drop_event_params *params )
*
* Handle an XdndLeave event.
*/
-void X11DRV_XDND_LeaveEvent( HWND hWnd, XClientMessageEvent *event )
+static NTSTATUS handle_leave_event(void)
{
IDropTarget *dropTarget;
@@ -362,6 +362,7 @@ void X11DRV_XDND_LeaveEvent( HWND hWnd, XClientMessageEvent *event )
}
X11DRV_XDND_FreeDragDropOp();
+ return 0;
}
@@ -736,6 +737,9 @@ UINT handle_dnd_event( void *params )
case DND_DROP_EVENT:
return handle_drop_event( params );
+ case DND_LEAVE_EVENT:
+ return handle_leave_event();
+
case DND_POSITION_EVENT:
return handle_position_event( params );
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/11
April 28, 2022
[PATCH v2 3/8] winex11: Move XdndDrop event handler to event.c.
by Jacek Caban
From: Jacek Caban <jacek(a)codeweavers.com>
Signed-off-by: Jacek Caban <jacek(a)codeweavers.com>
---
dlls/winex11.drv/event.c | 26 ++++++++++++++++++++++-
dlls/winex11.drv/unixlib.h | 8 ++++++++
dlls/winex11.drv/x11drv.h | 1 -
dlls/winex11.drv/xdnd.c | 42 ++++++--------------------------------
4 files changed, 39 insertions(+), 38 deletions(-)
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c
index 2bfcd3211ae..a02f0340010 100644
--- a/dlls/winex11.drv/event.c
+++ b/dlls/winex11.drv/event.c
@@ -1896,6 +1896,30 @@ static void handle_xdnd_position_event( HWND hwnd, XClientMessageEvent *event )
}
+static void handle_xdnd_drop_event( HWND hwnd, XClientMessageEvent *event )
+{
+ struct dnd_drop_event_params params;
+ XClientMessageEvent e;
+ DWORD effect;
+
+ params.type = DND_DROP_EVENT;
+ params.hwnd = hwnd;
+ effect = handle_dnd_event( ¶ms );
+
+ /* Tell the target we are finished. */
+ memset( &e, 0, sizeof(e) );
+ e.type = ClientMessage;
+ e.display = event->display;
+ e.window = event->data.l[0];
+ e.message_type = x11drv_atom(XdndFinished);
+ e.format = 32;
+ e.data.l[0] = event->window;
+ e.data.l[1] = !!effect;
+ e.data.l[2] = drop_effect_to_xdnd_action( effect );
+ XSendEvent( event->display, event->data.l[0], False, NoEventMask, (XEvent *)&e );
+}
+
+
struct client_message_handler
{
int atom; /* protocol atom */
@@ -1910,7 +1934,7 @@ static const struct client_message_handler client_messages[] =
{ XATOM_DndProtocol, handle_dnd_protocol },
{ XATOM_XdndEnter, handle_xdnd_enter_event },
{ XATOM_XdndPosition, handle_xdnd_position_event },
- { XATOM_XdndDrop, X11DRV_XDND_DropEvent },
+ { XATOM_XdndDrop, handle_xdnd_drop_event },
{ XATOM_XdndLeave, X11DRV_XDND_LeaveEvent }
};
diff --git a/dlls/winex11.drv/unixlib.h b/dlls/winex11.drv/unixlib.h
index 7f8ec28b7cf..41120385e60 100644
--- a/dlls/winex11.drv/unixlib.h
+++ b/dlls/winex11.drv/unixlib.h
@@ -30,9 +30,17 @@ struct format_entry
enum dnd_event_type
{
+ DND_DROP_EVENT,
DND_POSITION_EVENT,
};
+/* DND_DROP_EVENT params */
+struct dnd_drop_event_params
+{
+ UINT type;
+ HWND hwnd;
+};
+
/* DND_POSITION_EVENT params */
struct dnd_position_event_params
{
diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h
index d49643a890b..c17dc45594c 100644
--- a/dlls/winex11.drv/x11drv.h
+++ b/dlls/winex11.drv/x11drv.h
@@ -294,7 +294,6 @@ extern BOOL IME_SetCompositionString(DWORD dwIndex, LPCVOID lpComp,
DWORD dwReadLen) DECLSPEC_HIDDEN;
extern void IME_SetResultString(LPWSTR lpResult, DWORD dwResultlen) DECLSPEC_HIDDEN;
-extern void X11DRV_XDND_DropEvent( HWND hWnd, XClientMessageEvent *event ) DECLSPEC_HIDDEN;
extern void X11DRV_XDND_LeaveEvent( HWND hWnd, XClientMessageEvent *event ) DECLSPEC_HIDDEN;
extern void handle_dnd_enter_event( struct format_entry *formats, ULONG size ) DECLSPEC_HIDDEN;
diff --git a/dlls/winex11.drv/xdnd.c b/dlls/winex11.drv/xdnd.c
index 7633738917f..9de0b60d0cd 100644
--- a/dlls/winex11.drv/xdnd.c
+++ b/dlls/winex11.drv/xdnd.c
@@ -142,20 +142,6 @@ static IDropTarget* get_droptarget_pointer(HWND hwnd)
return droptarget;
}
-/**************************************************************************
- * X11DRV_XDND_DROPEFFECTToXdndAction
- */
-static long X11DRV_XDND_DROPEFFECTToXdndAction(DWORD effect)
-{
- if (effect == DROPEFFECT_COPY)
- return x11drv_atom(XdndActionCopy);
- else if (effect == DROPEFFECT_MOVE)
- return x11drv_atom(XdndActionMove);
- else if (effect == DROPEFFECT_LINK)
- return x11drv_atom(XdndActionLink);
- FIXME("unknown drop effect %u, assuming XdndActionCopy\n", effect);
- return x11drv_atom(XdndActionCopy);
-}
/* Recursively searches for a window on given coordinates in a drag&drop specific manner.
*
@@ -278,14 +264,8 @@ static BOOL handle_position_event( struct dnd_position_event_params *params )
return accept ? effect : 0;
}
-/**************************************************************************
- * X11DRV_XDND_DropEvent
- *
- * Handle an XdndDrop event.
- */
-void X11DRV_XDND_DropEvent( HWND hWnd, XClientMessageEvent *event )
+static DWORD handle_drop_event( struct dnd_drop_event_params *params )
{
- XClientMessageEvent e;
IDropTarget *dropTarget;
DWORD effect = XDNDDropEffect;
int accept = 0; /* Assume we're not accepting */
@@ -338,7 +318,7 @@ void X11DRV_XDND_DropEvent( HWND hWnd, XClientMessageEvent *event )
/* Only send WM_DROPFILES if Drop didn't succeed or DROPEFFECT_NONE was set.
* Doing both causes winamp to duplicate the dropped files (#29081) */
- HWND hwnd_drop = window_accepting_files(window_from_point_dnd(hWnd, XDNDxy));
+ HWND hwnd_drop = window_accepting_files(window_from_point_dnd( params->hwnd, XDNDxy ));
if (hwnd_drop && X11DRV_XDND_HasHDROP())
{
@@ -354,20 +334,7 @@ void X11DRV_XDND_DropEvent( HWND hWnd, XClientMessageEvent *event )
TRACE("effectRequested(0x%x) accept(%d) performed(0x%x) at x(%d),y(%d)\n",
XDNDDropEffect, accept, effect, XDNDxy.x, XDNDxy.y);
- /* Tell the target we are finished. */
- memset(&e, 0, sizeof(e));
- e.type = ClientMessage;
- e.display = event->display;
- e.window = event->data.l[0];
- e.message_type = x11drv_atom(XdndFinished);
- e.format = 32;
- e.data.l[0] = event->window;
- e.data.l[1] = accept;
- if (accept)
- e.data.l[2] = X11DRV_XDND_DROPEFFECTToXdndAction(effect);
- else
- e.data.l[2] = None;
- XSendEvent(event->display, event->data.l[0], False, NoEventMask, (XEvent*)&e);
+ return accept ? effect : 0;
}
/**************************************************************************
@@ -766,6 +733,9 @@ UINT handle_dnd_event( void *params )
switch (*(UINT *)params)
{
+ case DND_DROP_EVENT:
+ return handle_drop_event( params );
+
case DND_POSITION_EVENT:
return handle_position_event( params );
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/11
April 28, 2022
[PATCH v2 2/8] winex11: Move XdndPosition event handler to event.c.
by Jacek Caban
From: Jacek Caban <jacek(a)codeweavers.com>
Signed-off-by: Jacek Caban <jacek(a)codeweavers.com>
---
dlls/winex11.drv/event.c | 72 +++++++++++++++++++++++++++++++++++++-
dlls/winex11.drv/unixlib.h | 43 +++++++++++++++++++++++
dlls/winex11.drv/x11drv.h | 10 ++----
dlls/winex11.drv/xdnd.c | 70 ++++++++++--------------------------
4 files changed, 135 insertions(+), 60 deletions(-)
create mode 100644 dlls/winex11.drv/unixlib.h
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c
index c8cd3fd5ef1..2bfcd3211ae 100644
--- a/dlls/winex11.drv/event.c
+++ b/dlls/winex11.drv/event.c
@@ -1826,6 +1826,76 @@ static void handle_xdnd_enter_event( HWND hWnd, XClientMessageEvent *event )
}
+static DWORD xdnd_action_to_drop_effect( long action )
+{
+ /* In Windows, nothing but the given effects is allowed.
+ * In X the given action is just a hint, and you can always
+ * XdndActionCopy and XdndActionPrivate, so be more permissive. */
+ if (action == x11drv_atom(XdndActionCopy))
+ return DROPEFFECT_COPY;
+ else if (action == x11drv_atom(XdndActionMove))
+ return DROPEFFECT_MOVE | DROPEFFECT_COPY;
+ else if (action == x11drv_atom(XdndActionLink))
+ return DROPEFFECT_LINK | DROPEFFECT_COPY;
+ else if (action == x11drv_atom(XdndActionAsk))
+ /* FIXME: should we somehow ask the user what to do here? */
+ return DROPEFFECT_COPY | DROPEFFECT_MOVE | DROPEFFECT_LINK;
+
+ FIXME( "unknown action %ld, assuming DROPEFFECT_COPY\n", action );
+ return DROPEFFECT_COPY;
+}
+
+
+static long drop_effect_to_xdnd_action( DWORD effect )
+{
+ if (effect == DROPEFFECT_COPY)
+ return x11drv_atom(XdndActionCopy);
+ else if (effect == DROPEFFECT_MOVE)
+ return x11drv_atom(XdndActionMove);
+ else if (effect == DROPEFFECT_LINK)
+ return x11drv_atom(XdndActionLink);
+ else if (effect == DROPEFFECT_NONE)
+ return None;
+
+ FIXME( "unknown drop effect %u, assuming XdndActionCopy\n", effect );
+ return x11drv_atom(XdndActionCopy);
+}
+
+
+static void handle_xdnd_position_event( HWND hwnd, XClientMessageEvent *event )
+{
+ struct dnd_position_event_params params;
+ XClientMessageEvent e;
+ DWORD effect;
+
+ params.type = DND_POSITION_EVENT;
+ params.hwnd = hwnd;
+ params.point = root_to_virtual_screen( event->data.l[2] >> 16, event->data.l[2] & 0xFFFF );
+ params.effect = effect = xdnd_action_to_drop_effect( event->data.l[4] );
+
+ effect = handle_dnd_event( ¶ms );
+
+ TRACE( "actionRequested(%ld) chosen(0x%x) at x(%d),y(%d)\n",
+ event->data.l[4], effect, params.point.x, params.point.y );
+
+ /*
+ * Let source know if we're accepting the drop by
+ * sending a status message.
+ */
+ e.type = ClientMessage;
+ e.display = event->display;
+ e.window = event->data.l[0];
+ e.message_type = x11drv_atom(XdndStatus);
+ e.format = 32;
+ e.data.l[0] = event->window;
+ e.data.l[1] = !!effect;
+ e.data.l[2] = 0; /* Empty Rect */
+ e.data.l[3] = 0; /* Empty Rect */
+ e.data.l[4] = drop_effect_to_xdnd_action( effect );
+ XSendEvent( event->display, event->data.l[0], False, NoEventMask, (XEvent *)&e );
+}
+
+
struct client_message_handler
{
int atom; /* protocol atom */
@@ -1839,7 +1909,7 @@ static const struct client_message_handler client_messages[] =
{ XATOM__XEMBED, handle_xembed_protocol },
{ XATOM_DndProtocol, handle_dnd_protocol },
{ XATOM_XdndEnter, handle_xdnd_enter_event },
- { XATOM_XdndPosition, X11DRV_XDND_PositionEvent },
+ { XATOM_XdndPosition, handle_xdnd_position_event },
{ XATOM_XdndDrop, X11DRV_XDND_DropEvent },
{ XATOM_XdndLeave, X11DRV_XDND_LeaveEvent }
};
diff --git a/dlls/winex11.drv/unixlib.h b/dlls/winex11.drv/unixlib.h
new file mode 100644
index 00000000000..7f8ec28b7cf
--- /dev/null
+++ b/dlls/winex11.drv/unixlib.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2022 Jacek Caban 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 "ntuser.h"
+#include "wine/unixlib.h"
+
+/* DnD support */
+
+struct format_entry
+{
+ UINT format;
+ UINT size;
+ char data[1];
+};
+
+enum dnd_event_type
+{
+ DND_POSITION_EVENT,
+};
+
+/* DND_POSITION_EVENT params */
+struct dnd_position_event_params
+{
+ UINT type;
+ HWND hwnd;
+ POINT point;
+ DWORD effect;
+};
diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h
index 9339c1c08e6..d49643a890b 100644
--- a/dlls/winex11.drv/x11drv.h
+++ b/dlls/winex11.drv/x11drv.h
@@ -61,6 +61,7 @@ typedef int Status;
#include "winbase.h"
#include "ntgdi.h"
#include "wine/gdi_driver.h"
+#include "unixlib.h"
#include "wine/list.h"
#define MAX_DASHLEN 16
@@ -293,18 +294,11 @@ extern BOOL IME_SetCompositionString(DWORD dwIndex, LPCVOID lpComp,
DWORD dwReadLen) DECLSPEC_HIDDEN;
extern void IME_SetResultString(LPWSTR lpResult, DWORD dwResultlen) DECLSPEC_HIDDEN;
-extern void X11DRV_XDND_PositionEvent( HWND hWnd, XClientMessageEvent *event ) DECLSPEC_HIDDEN;
extern void X11DRV_XDND_DropEvent( HWND hWnd, XClientMessageEvent *event ) DECLSPEC_HIDDEN;
extern void X11DRV_XDND_LeaveEvent( HWND hWnd, XClientMessageEvent *event ) DECLSPEC_HIDDEN;
-struct format_entry
-{
- UINT format;
- UINT size;
- char data[1];
-};
-
extern void handle_dnd_enter_event( struct format_entry *formats, ULONG size ) DECLSPEC_HIDDEN;
+extern UINT handle_dnd_event( void *params ) DECLSPEC_HIDDEN;
extern struct format_entry *import_xdnd_selection( Display *display, Window win, Atom selection,
Atom *targets, UINT count,
diff --git a/dlls/winex11.drv/xdnd.c b/dlls/winex11.drv/xdnd.c
index e56fc964ada..7633738917f 100644
--- a/dlls/winex11.drv/xdnd.c
+++ b/dlls/winex11.drv/xdnd.c
@@ -142,27 +142,6 @@ static IDropTarget* get_droptarget_pointer(HWND hwnd)
return droptarget;
}
-/**************************************************************************
- * X11DRV_XDND_XdndActionToDROPEFFECT
- */
-static DWORD X11DRV_XDND_XdndActionToDROPEFFECT(long action)
-{
- /* In Windows, nothing but the given effects is allowed.
- * In X the given action is just a hint, and you can always
- * XdndActionCopy and XdndActionPrivate, so be more permissive. */
- if (action == x11drv_atom(XdndActionCopy))
- return DROPEFFECT_COPY;
- else if (action == x11drv_atom(XdndActionMove))
- return DROPEFFECT_MOVE | DROPEFFECT_COPY;
- else if (action == x11drv_atom(XdndActionLink))
- return DROPEFFECT_LINK | DROPEFFECT_COPY;
- else if (action == x11drv_atom(XdndActionAsk))
- /* FIXME: should we somehow ask the user what to do here? */
- return DROPEFFECT_COPY | DROPEFFECT_MOVE | DROPEFFECT_LINK;
- FIXME("unknown action %ld, assuming DROPEFFECT_COPY\n", action);
- return DROPEFFECT_COPY;
-}
-
/**************************************************************************
* X11DRV_XDND_DROPEFFECTToXdndAction
*/
@@ -215,22 +194,17 @@ static HWND window_accepting_files(HWND hwnd)
*
* Handle an XdndPosition event.
*/
-void X11DRV_XDND_PositionEvent( HWND hWnd, XClientMessageEvent *event )
+static BOOL handle_position_event( struct dnd_position_event_params *params )
{
- XClientMessageEvent e;
int accept = 0; /* Assume we're not accepting */
IDropTarget *dropTarget = NULL;
- DWORD effect;
+ DWORD effect = params->effect;
POINTL pointl;
HWND targetWindow;
HRESULT hr;
- XDNDxy = root_to_virtual_screen( event->data.l[2] >> 16, event->data.l[2] & 0xFFFF );
- targetWindow = window_from_point_dnd(hWnd, XDNDxy);
-
- pointl.x = XDNDxy.x;
- pointl.y = XDNDxy.y;
- effect = X11DRV_XDND_XdndActionToDROPEFFECT(event->data.l[4]);
+ XDNDxy = params->point;
+ targetWindow = window_from_point_dnd( params->hwnd, XDNDxy );
if (!XDNDAccepted || XDNDLastTargetWnd != targetWindow)
{
@@ -301,27 +275,7 @@ void X11DRV_XDND_PositionEvent( HWND hWnd, XClientMessageEvent *event )
}
}
- TRACE("actionRequested(%ld) accept(%d) chosen(0x%x) at x(%d),y(%d)\n",
- event->data.l[4], accept, effect, XDNDxy.x, XDNDxy.y);
-
- /*
- * Let source know if we're accepting the drop by
- * sending a status message.
- */
- e.type = ClientMessage;
- e.display = event->display;
- e.window = event->data.l[0];
- e.message_type = x11drv_atom(XdndStatus);
- e.format = 32;
- e.data.l[0] = event->window;
- e.data.l[1] = accept;
- e.data.l[2] = 0; /* Empty Rect */
- e.data.l[3] = 0; /* Empty Rect */
- if (accept)
- e.data.l[4] = X11DRV_XDND_DROPEFFECTToXdndAction(effect);
- else
- e.data.l[4] = None;
- XSendEvent(event->display, event->data.l[0], False, NoEventMask, (XEvent*)&e);
+ return accept ? effect : 0;
}
/**************************************************************************
@@ -806,3 +760,17 @@ static IDataObjectVtbl xdndDataObjectVtbl =
};
static IDataObject XDNDDataObject = { &xdndDataObjectVtbl };
+
+UINT handle_dnd_event( void *params )
+{
+
+ switch (*(UINT *)params)
+ {
+ case DND_POSITION_EVENT:
+ return handle_position_event( params );
+
+ default:
+ ERR( "invalid event\n" );
+ return 0;
+ }
+}
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/11
April 28, 2022
[PATCH v2 1/8] winex11: Move XdndEnter event handler to event.c.
by Jacek Caban
From: Jacek Caban <jacek(a)codeweavers.com>
In preparation for xdnd.c being in PE file.
Signed-off-by: Jacek Caban <jacek(a)codeweavers.com>
---
dlls/winex11.drv/event.c | 76 +++++++++++++++++++++++++++++++++-
dlls/winex11.drv/x11drv.h | 3 +-
dlls/winex11.drv/xdnd.c | 86 ++-------------------------------------
3 files changed, 80 insertions(+), 85 deletions(-)
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c
index e1bcd70d35a..c8cd3fd5ef1 100644
--- a/dlls/winex11.drv/event.c
+++ b/dlls/winex11.drv/event.c
@@ -49,6 +49,7 @@
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(event);
+WINE_DECLARE_DEBUG_CHANNEL(xdnd);
extern BOOL ximInComposeMode;
@@ -1752,6 +1753,79 @@ static void handle_dnd_protocol( HWND hwnd, XClientMessageEvent *event )
}
+/**************************************************************************
+ * handle_xdnd_enter_event
+ *
+ * Handle an XdndEnter event.
+ */
+static void handle_xdnd_enter_event( HWND hWnd, XClientMessageEvent *event )
+{
+ struct format_entry *data;
+ unsigned long count = 0;
+ Atom *xdndtypes;
+ size_t size;
+ int version;
+
+ version = (event->data.l[1] & 0xFF000000) >> 24;
+
+ TRACE( "ver(%d) check-XdndTypeList(%ld) data=%ld,%ld,%ld,%ld,%ld\n",
+ version, (event->data.l[1] & 1),
+ event->data.l[0], event->data.l[1], event->data.l[2],
+ event->data.l[3], event->data.l[4] );
+
+ if (version > WINE_XDND_VERSION)
+ {
+ ERR("ignoring unsupported XDND version %d\n", version);
+ return;
+ }
+
+ /* If the source supports more than 3 data types we retrieve
+ * the entire list. */
+ if (event->data.l[1] & 1)
+ {
+ Atom acttype;
+ int actfmt;
+ unsigned long bytesret;
+
+ /* Request supported formats from source window */
+ XGetWindowProperty( event->display, event->data.l[0], x11drv_atom(XdndTypeList),
+ 0, 65535, FALSE, AnyPropertyType, &acttype, &actfmt, &count,
+ &bytesret, (unsigned char **)&xdndtypes );
+ }
+ else
+ {
+ count = 3;
+ xdndtypes = (Atom *)&event->data.l[2];
+ }
+
+ if (TRACE_ON(xdnd))
+ {
+ unsigned int i;
+
+ for (i = 0; i < count; i++)
+ {
+ if (xdndtypes[i] != 0)
+ {
+ char * pn = XGetAtomName( event->display, xdndtypes[i] );
+ TRACE( "XDNDEnterAtom %ld: %s\n", xdndtypes[i], pn );
+ XFree( pn );
+ }
+ }
+ }
+
+ data = import_xdnd_selection( event->display, event->window, x11drv_atom(XdndSelection),
+ xdndtypes, count, &size );
+ if (data)
+ {
+ handle_dnd_enter_event( data, size );
+ free( data );
+ }
+
+ if (event->data.l[1] & 1)
+ XFree(xdndtypes);
+}
+
+
struct client_message_handler
{
int atom; /* protocol atom */
@@ -1764,7 +1838,7 @@ static const struct client_message_handler client_messages[] =
{ XATOM_WM_PROTOCOLS, handle_wm_protocols },
{ XATOM__XEMBED, handle_xembed_protocol },
{ XATOM_DndProtocol, handle_dnd_protocol },
- { XATOM_XdndEnter, X11DRV_XDND_EnterEvent },
+ { XATOM_XdndEnter, handle_xdnd_enter_event },
{ XATOM_XdndPosition, X11DRV_XDND_PositionEvent },
{ XATOM_XdndDrop, X11DRV_XDND_DropEvent },
{ XATOM_XdndLeave, X11DRV_XDND_LeaveEvent }
diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h
index f6d28122fc3..9339c1c08e6 100644
--- a/dlls/winex11.drv/x11drv.h
+++ b/dlls/winex11.drv/x11drv.h
@@ -293,7 +293,6 @@ extern BOOL IME_SetCompositionString(DWORD dwIndex, LPCVOID lpComp,
DWORD dwReadLen) DECLSPEC_HIDDEN;
extern void IME_SetResultString(LPWSTR lpResult, DWORD dwResultlen) DECLSPEC_HIDDEN;
-extern void X11DRV_XDND_EnterEvent( HWND hWnd, XClientMessageEvent *event ) DECLSPEC_HIDDEN;
extern void X11DRV_XDND_PositionEvent( HWND hWnd, XClientMessageEvent *event ) DECLSPEC_HIDDEN;
extern void X11DRV_XDND_DropEvent( HWND hWnd, XClientMessageEvent *event ) DECLSPEC_HIDDEN;
extern void X11DRV_XDND_LeaveEvent( HWND hWnd, XClientMessageEvent *event ) DECLSPEC_HIDDEN;
@@ -305,6 +304,8 @@ struct format_entry
char data[1];
};
+extern void handle_dnd_enter_event( struct format_entry *formats, ULONG size ) DECLSPEC_HIDDEN;
+
extern struct format_entry *import_xdnd_selection( Display *display, Window win, Atom selection,
Atom *targets, UINT count,
size_t *size ) DECLSPEC_HIDDEN;
diff --git a/dlls/winex11.drv/xdnd.c b/dlls/winex11.drv/xdnd.c
index 34cb2a47081..e56fc964ada 100644
--- a/dlls/winex11.drv/xdnd.c
+++ b/dlls/winex11.drv/xdnd.c
@@ -55,8 +55,6 @@ static HWND XDNDLastTargetWnd;
/* might be an ancestor of XDNDLastTargetWnd */
static HWND XDNDLastDropTargetWnd;
-static void X11DRV_XDND_ResolveProperty(Display *display, Window xwin, Time tm,
- Atom *types, unsigned long count);
static BOOL X11DRV_XDND_HasHDROP(void);
static HRESULT X11DRV_XDND_SendDropFiles(HWND hwnd);
static void X11DRV_XDND_FreeDragDropOp(void);
@@ -180,73 +178,6 @@ static long X11DRV_XDND_DROPEFFECTToXdndAction(DWORD effect)
return x11drv_atom(XdndActionCopy);
}
-/**************************************************************************
- * X11DRV_XDND_EnterEvent
- *
- * Handle an XdndEnter event.
- */
-void X11DRV_XDND_EnterEvent( HWND hWnd, XClientMessageEvent *event )
-{
- int version;
- Atom *xdndtypes;
- unsigned long count = 0;
-
- version = (event->data.l[1] & 0xFF000000) >> 24;
- TRACE("ver(%d) check-XdndTypeList(%ld) data=%ld,%ld,%ld,%ld,%ld\n",
- version, (event->data.l[1] & 1),
- event->data.l[0], event->data.l[1], event->data.l[2],
- event->data.l[3], event->data.l[4]);
-
- if (version > WINE_XDND_VERSION)
- {
- ERR("ignoring unsupported XDND version %d\n", version);
- return;
- }
-
- XDNDAccepted = FALSE;
-
- /* If the source supports more than 3 data types we retrieve
- * the entire list. */
- if (event->data.l[1] & 1)
- {
- Atom acttype;
- int actfmt;
- unsigned long bytesret;
-
- /* Request supported formats from source window */
- XGetWindowProperty(event->display, event->data.l[0], x11drv_atom(XdndTypeList),
- 0, 65535, FALSE, AnyPropertyType, &acttype, &actfmt, &count,
- &bytesret, (unsigned char**)&xdndtypes);
- }
- else
- {
- count = 3;
- xdndtypes = (Atom*) &event->data.l[2];
- }
-
- if (TRACE_ON(xdnd))
- {
- unsigned int i;
-
- for (i = 0; i < count; i++)
- {
- if (xdndtypes[i] != 0)
- {
- char * pn = XGetAtomName(event->display, xdndtypes[i]);
- TRACE("XDNDEnterAtom %ld: %s\n", xdndtypes[i], pn);
- XFree(pn);
- }
- }
- }
-
- /* Do a one-time data read and cache results */
- X11DRV_XDND_ResolveProperty(event->display, event->window,
- event->data.l[1], xdndtypes, count);
-
- if (event->data.l[1] & 1)
- XFree(xdndtypes);
-}
-
/* Recursively searches for a window on given coordinates in a drag&drop specific manner.
*
* Don't use WindowFromPoint instead, because it omits the STATIC and transparent
@@ -514,29 +445,18 @@ void X11DRV_XDND_LeaveEvent( HWND hWnd, XClientMessageEvent *event )
/**************************************************************************
- * X11DRV_XDND_ResolveProperty
- *
- * Resolve all MIME types to windows clipboard formats. All data is cached.
+ * handle_dnd_enter_event
*/
-static void X11DRV_XDND_ResolveProperty(Display *display, Window xwin, Time tm,
- Atom *types, unsigned long count)
+void handle_dnd_enter_event( struct format_entry *formats, ULONG size )
{
- struct format_entry *formats;
- size_t size;
-
- TRACE("count(%ld)\n", count);
-
+ XDNDAccepted = FALSE;
X11DRV_XDND_FreeDragDropOp(); /* Clear previously cached data */
- formats = import_xdnd_selection( display, xwin, x11drv_atom(XdndSelection), types, count, &size );
- if (!formats) return;
-
if ((xdnd_formats = HeapAlloc( GetProcessHeap(), 0, size )))
{
memcpy( xdnd_formats, formats, size );
xdnd_formats_end = (struct format_entry *)((char *)xdnd_formats + size);
}
- free( formats );
}
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/11
April 28, 2022
[PATCH v2 0/8] MR11: winex11: PE conversion preparation
by Jacek Caban (@jacek)
Prepare xdnd.c to be in PE file and use ntdll interface for Unicode conversion.
--
v2: winex11: Directly use ntdll for utf8 conversion.
winex11: Move Unicode conversion out of string_from_unicode_text.
winex11: Move Unicode conversion out of unicode_text_from_string.
winex11: Use inline intersect_rect helper instead of IntersectRect.
winex11: Move XdndLeave event handler to event.c.
winex11: Move XdndDrop event handler to event.c.
winex11: Move XdndPosition event handler to event.c.
winex11: Move XdndEnter event handler to event.c.
https://gitlab.winehq.org/wine/wine/-/merge_requests/11
April 28, 2022
Re: MR11v1 - winex11: PE conversion preparation
by Huw Davies (@huw)
These are missing your SoB (assuming we still care).
--
https://gitlab.winehq.org/wine/wine/-/merge_requests/11#note_478
April 28, 2022
[PATCH v2 2/2] include: Add Windows.Storage.Streams.Buffer runtimeclass declaration.
by Rémi Bernon
From: Biswapriyo Nath <nathbappai(a)gmail.com>
Signed-off-by: Biswapriyo Nath <nathbappai(a)gmail.com>
Signed-off-by: Rémi Bernon <rbernon(a)codeweavers.com>
---
include/windows.storage.streams.idl | 51 +++++++++++++++++++++++++++++
1 file changed, 51 insertions(+)
diff --git a/include/windows.storage.streams.idl b/include/windows.storage.streams.idl
index ba58e74687d..bb107b25976 100644
--- a/include/windows.storage.streams.idl
+++ b/include/windows.storage.streams.idl
@@ -25,11 +25,50 @@ import "eventtoken.idl";
import "windows.foundation.idl";
namespace Windows.Storage.Streams {
+ interface IBuffer;
+ interface IBufferFactory;
+ interface IBufferStatics;
interface IContentTypeProvider;
interface IInputStream;
interface IOutputStream;
interface IRandomAccessStream;
interface IRandomAccessStreamWithContentType;
+ runtimeclass Buffer;
+
+ [
+ contract(Windows.Foundation.UniversalApiContract, 1.0),
+ uuid(905a0fe0-bc53-11df-8c49-001e4fc686da)
+ ]
+ interface IBuffer : IInspectable
+ {
+ [propget] HRESULT Capacity([out, retval] UINT32 *value);
+ [propget] HRESULT Length([out, retval] UINT32 *value);
+ [propput] HRESULT Length([in] UINT32 value);
+ }
+
+ [
+ contract(Windows.Foundation.UniversalApiContract, 1.0),
+ exclusiveto(Windows.Storage.Streams.Buffer),
+ uuid(71af914d-c10f-484b-bc50-14bc623b3a27)
+ ]
+ interface IBufferFactory : IInspectable
+ {
+ HRESULT Create([in, range(0x00000000, 0x7fffffff)] UINT32 capacity,
+ [out, retval] Windows.Storage.Streams.Buffer **value);
+ }
+
+ [
+ contract(Windows.Foundation.UniversalApiContract, 1.0),
+ exclusiveto(Windows.Storage.Streams.Buffer),
+ uuid(e901e65b-d716-475a-a90a-af7229b1e741)
+ ]
+ interface IBufferStatics : IInspectable
+ {
+ HRESULT CreateCopyFromMemoryBuffer([in] Windows.Foundation.IMemoryBuffer *input,
+ [out, retval] Windows.Storage.Streams.Buffer **value);
+ HRESULT CreateMemoryBufferOverIBuffer([in] Windows.Storage.Streams.IBuffer *input,
+ [out, retval] Windows.Foundation.MemoryBuffer **value);
+ }
[
uuid(cc254827-4b3d-438f-9232-10c76bc7e038),
@@ -43,4 +82,16 @@ namespace Windows.Storage.Streams {
Windows.Storage.Streams.IContentTypeProvider
{
}
+
+ [
+ activatable(Windows.Storage.Streams.IBufferFactory, Windows.Foundation.UniversalApiContract, 1.0),
+ contract(Windows.Foundation.UniversalApiContract, 1.0),
+ marshaling_behavior(agile),
+ static(Windows.Storage.Streams.IBufferStatics, Windows.Foundation.UniversalApiContract, 1.0),
+ threading(both)
+ ]
+ runtimeclass Buffer
+ {
+ [default] interface Windows.Storage.Streams.IBuffer;
+ }
}
--
2.35.1
April 28, 2022
[PATCH v2 1/2] include: Use nested namespaces in windows.storage.streams.idl.
by Rémi Bernon
Signed-off-by: Rémi Bernon <rbernon(a)codeweavers.com>
---
Supersedes: 233842
include/windows.storage.streams.idl | 56 +++++++++--------------------
1 file changed, 16 insertions(+), 40 deletions(-)
diff --git a/include/windows.storage.streams.idl b/include/windows.storage.streams.idl
index 59b15761af0..ba58e74687d 100644
--- a/include/windows.storage.streams.idl
+++ b/include/windows.storage.streams.idl
@@ -24,47 +24,23 @@ import "inspectable.idl";
import "eventtoken.idl";
import "windows.foundation.idl";
-namespace Windows
-{
- namespace Foundation
- {
- interface IClosable;
- }
-}
+namespace Windows.Storage.Streams {
+ interface IContentTypeProvider;
+ interface IInputStream;
+ interface IOutputStream;
+ interface IRandomAccessStream;
+ interface IRandomAccessStreamWithContentType;
-namespace Windows
-{
- namespace Storage
+ [
+ uuid(cc254827-4b3d-438f-9232-10c76bc7e038),
+ ]
+ interface IRandomAccessStreamWithContentType : IInspectable
+ requires
+ Windows.Storage.Streams.IRandomAccessStream,
+ Windows.Foundation.IClosable,
+ Windows.Storage.Streams.IInputStream,
+ Windows.Storage.Streams.IOutputStream,
+ Windows.Storage.Streams.IContentTypeProvider
{
- namespace Streams
- {
- interface IContentTypeProvider;
- interface IInputStream;
- interface IOutputStream;
- interface IRandomAccessStream;
- interface IRandomAccessStreamWithContentType;
- }
- }
-}
-
-namespace Windows
-{
- namespace Storage
- {
- namespace Streams
- {
- [
- uuid(cc254827-4b3d-438f-9232-10c76bc7e038),
- ]
- interface IRandomAccessStreamWithContentType : IInspectable
- requires
- Windows.Storage.Streams.IRandomAccessStream,
- Windows.Foundation.IClosable,
- Windows.Storage.Streams.IInputStream,
- Windows.Storage.Streams.IOutputStream,
- Windows.Storage.Streams.IContentTypeProvider
- {
- }
- }
}
}
--
2.35.1
April 28, 2022
[PATCH 8/8] winex11: Directly use ntdll for utf8 conversion.
by Jacek Caban
From: Jacek Caban <jacek(a)codeweavers.com>
---
dlls/winex11.drv/clipboard.c | 11 +++++------
dlls/winex11.drv/window.c | 7 ++++---
dlls/winex11.drv/xrandr.c | 8 ++++++--
3 files changed, 15 insertions(+), 11 deletions(-)
diff --git a/dlls/winex11.drv/clipboard.c b/dlls/winex11.drv/clipboard.c
index 1dcd5362c8c..f9e6764fc0a 100644
--- a/dlls/winex11.drv/clipboard.c
+++ b/dlls/winex11.drv/clipboard.c
@@ -914,12 +914,11 @@ static void *import_text_html( Atom type, const void *data, size_t size, size_t
/* Firefox uses UTF-16LE with byte order mark. Convert to UTF-8 without the BOM. */
if (size >= sizeof(WCHAR) && ((const WCHAR *)data)[0] == 0xfeff)
{
- len = WideCharToMultiByte( CP_UTF8, 0, (const WCHAR *)data + 1, size / sizeof(WCHAR) - 1,
- NULL, 0, NULL, NULL );
- if (!(text = malloc( len ))) return 0;
- WideCharToMultiByte( CP_UTF8, 0, (const WCHAR *)data + 1, size / sizeof(WCHAR) - 1,
- text, len, NULL, NULL );
- size = len;
+ DWORD str_len;
+ RtlUnicodeToUTF8N( NULL, 0, &str_len, (const WCHAR *)data + 1, size - sizeof(WCHAR) );
+ if (!(text = malloc( str_len ))) return NULL;
+ RtlUnicodeToUTF8N( text, str_len, &str_len, (const WCHAR *)data + 1, size - sizeof(WCHAR) );
+ size = str_len;
data = text;
}
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c
index d7027032465..3b0ccd8d22a 100644
--- a/dlls/winex11.drv/window.c
+++ b/dlls/winex11.drv/window.c
@@ -437,22 +437,23 @@ static void sync_window_opacity( Display *display, Window win,
*/
static void sync_window_text( Display *display, Window win, const WCHAR *text )
{
- UINT count;
+ DWORD count, len;
char *buffer, *utf8_buffer;
XTextProperty prop;
/* allocate new buffer for window text */
+ len = lstrlenW( text );
count = WideCharToMultiByte(CP_UNIXCP, 0, text, -1, NULL, 0, NULL, NULL);
if (!(buffer = malloc( count ))) return;
WideCharToMultiByte(CP_UNIXCP, 0, text, -1, buffer, count, NULL, NULL);
- count = WideCharToMultiByte(CP_UTF8, 0, text, strlenW(text), NULL, 0, NULL, NULL);
+ RtlUnicodeToUTF8N( NULL, 0, &count, text, len * sizeof(WCHAR) );
if (!(utf8_buffer = malloc( count )))
{
free( buffer );
return;
}
- WideCharToMultiByte(CP_UTF8, 0, text, strlenW(text), utf8_buffer, count, NULL, NULL);
+ RtlUnicodeToUTF8N( utf8_buffer, count, &count, text, len * sizeof(WCHAR) );
if (XmbTextListToTextProperty( display, &buffer, 1, XStdICCTextStyle, &prop ) == Success)
{
diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c
index b0619c0abcd..6ede776d76b 100644
--- a/dlls/winex11.drv/xrandr.c
+++ b/dlls/winex11.drv/xrandr.c
@@ -652,6 +652,7 @@ static BOOL get_gpu_properties_from_vulkan( struct gdi_gpu *gpu, const XRRProvid
VkPhysicalDeviceIDProperties id;
VkInstance vk_instance = NULL;
VkDisplayKHR vk_display;
+ DWORD len;
BOOL ret = FALSE;
VkResult vr;
@@ -723,7 +724,8 @@ static BOOL get_gpu_properties_from_vulkan( struct gdi_gpu *gpu, const XRRProvid
gpu->vendor_id = properties2.properties.vendorID;
gpu->device_id = properties2.properties.deviceID;
}
- MultiByteToWideChar( CP_UTF8, 0, properties2.properties.deviceName, -1, gpu->name, ARRAY_SIZE(gpu->name) );
+ RtlUTF8ToUnicodeN( gpu->name, sizeof(gpu->name), &len, properties2.properties.deviceName,
+ strlen( properties2.properties.deviceName ) + 1 );
ret = TRUE;
goto done;
}
@@ -749,6 +751,7 @@ static BOOL xrandr14_get_gpus2( struct gdi_gpu **new_gpus, int *count, BOOL get_
INT primary_provider = -1;
RECT primary_rect;
BOOL ret = FALSE;
+ DWORD len;
INT i, j;
screen_resources = xrandr_get_screen_resources();
@@ -803,7 +806,8 @@ static BOOL xrandr14_get_gpus2( struct gdi_gpu **new_gpus, int *count, BOOL get_
if (get_properties)
{
if (!get_gpu_properties_from_vulkan( &gpus[i], provider_info ))
- MultiByteToWideChar( CP_UTF8, 0, provider_info->name, -1, gpus[i].name, ARRAY_SIZE(gpus[i].name) );
+ RtlUTF8ToUnicodeN( gpus[i].name, sizeof(gpus[i].name), &len, provider_info->name,
+ strlen( provider_info->name ) + 1 );
/* FIXME: Add an alternate method of getting PCI IDs, for systems that don't support Vulkan */
}
pXRRFreeProviderInfo( provider_info );
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/11
April 28, 2022
[PATCH 7/8] winex11: Move Unicode conversion out of string_from_unicode_text.
by Jacek Caban
From: Jacek Caban <jacek(a)codeweavers.com>
---
dlls/winex11.drv/clipboard.c | 68 +++++++++++++++++++-----------------
1 file changed, 36 insertions(+), 32 deletions(-)
diff --git a/dlls/winex11.drv/clipboard.c b/dlls/winex11.drv/clipboard.c
index ce936f9012e..1dcd5362c8c 100644
--- a/dlls/winex11.drv/clipboard.c
+++ b/dlls/winex11.drv/clipboard.c
@@ -729,9 +729,11 @@ static void *import_utf8_string( Atom type, const void *data, size_t size, size_
DWORD str_size;
WCHAR *ret;
- if (!(ret = malloc( (size * 2 + 1) * sizeof(WCHAR) ))) return NULL;
- RtlUTF8ToUnicodeN( ret + size, size * sizeof(WCHAR), &str_size, data, size );
- return unicode_text_from_string( ret, ret + size, str_size / sizeof(WCHAR), ret_size );
+ RtlUTF8ToUnicodeN( NULL, 0, &str_size, data, size );
+ if (!(ret = malloc( str_size * 2 + sizeof(WCHAR) ))) return NULL;
+ RtlUTF8ToUnicodeN( ret + str_size / sizeof(WCHAR), str_size, &str_size, data, size );
+ return unicode_text_from_string( ret, ret + str_size / sizeof(WCHAR),
+ str_size / sizeof(WCHAR), ret_size );
}
@@ -1198,31 +1200,19 @@ static BOOL export_data( Display *display, Window win, Atom prop, Atom target, v
*
* Convert CF_UNICODETEXT data to a string in the specified codepage.
*/
-static char *string_from_unicode_text( UINT codepage, const WCHAR *string, size_t string_size, size_t *size )
+static void string_from_unicode_text( char *str, size_t len, DWORD *size )
{
- UINT i, j;
- char *str;
- UINT lenW = string_size / sizeof(WCHAR);
- DWORD len;
-
- if (!string_size) return NULL;
+ DWORD i, j;
- len = WideCharToMultiByte( codepage, 0, string, lenW, NULL, 0, NULL, NULL );
- if ((str = malloc( len )))
+ /* remove carriage returns */
+ for (i = j = 0; i < len; i++)
{
- WideCharToMultiByte( codepage, 0, string, lenW, str, len, NULL, NULL);
-
- /* remove carriage returns */
- for (i = j = 0; i < len; i++)
- {
- if (str[i] == '\r' && (i == len - 1 || str[i + 1] == '\n')) continue;
- str[j++] = str[i];
- }
- while (j && !str[j - 1]) j--; /* remove trailing nulls */
- *size = j;
- TRACE( "returning %s\n", debugstr_an( str, j ));
+ if (str[i] == '\r' && (i == len - 1 || str[i + 1] == '\n')) continue;
+ str[j++] = str[i];
}
- return str;
+ while (j && !str[j - 1]) j--; /* remove trailing nulls */
+ TRACE( "returning %s\n", debugstr_an( str, j ));
+ *size = j;
}
@@ -1233,10 +1223,14 @@ static char *string_from_unicode_text( UINT codepage, const WCHAR *string, size_
*/
static BOOL export_string( Display *display, Window win, Atom prop, Atom target, void *data, size_t size )
{
- char *text = string_from_unicode_text( 28591, data, size, &size );
+ DWORD len;
+ char *text;
+
+ if (!(text = malloc( size ))) return FALSE;
+ len = WideCharToMultiByte( 28591, 0, data, size / sizeof(WCHAR), text, size, NULL, NULL );
+ string_from_unicode_text( text, len, &len );
- if (!text) return FALSE;
- put_property( display, win, prop, target, 8, text, size );
+ put_property( display, win, prop, target, 8, text, len );
free( text );
return TRUE;
}
@@ -1250,10 +1244,14 @@ static BOOL export_string( Display *display, Window win, Atom prop, Atom target,
static BOOL export_utf8_string( Display *display, Window win, Atom prop, Atom target,
void *data, size_t size )
{
- char *text = string_from_unicode_text( CP_UTF8, data, size, &size );
+ DWORD len;
+ char *text;
+
+ if (!(text = malloc( size / sizeof(WCHAR) * 3 ))) return FALSE;
+ RtlUnicodeToUTF8N( text, size / sizeof(WCHAR) * 3, &len, data, size );
+ string_from_unicode_text( text, len, &len );
- if (!text) return FALSE;
- put_property( display, win, prop, target, 8, text, size );
+ put_property( display, win, prop, target, 8, text, len );
free( text );
return TRUE;
}
@@ -1280,9 +1278,15 @@ static BOOL export_compound_text( Display *display, Window win, Atom prop, Atom
{
XTextProperty textprop;
XICCEncodingStyle style;
- char *text = string_from_unicode_text( CP_UNIXCP, data, size, &size );
+ DWORD len;
+ char *text;
+
+
+ if (!(text = malloc( size / sizeof(WCHAR) * 3 ))) return FALSE;
+ len = WideCharToMultiByte( CP_UNIXCP, 0, data, size / sizeof(WCHAR),
+ text, size / sizeof(WCHAR) * 3, NULL, NULL );
+ string_from_unicode_text( text, len, &len );
- if (!text) return FALSE;
if (target == x11drv_atom(COMPOUND_TEXT))
style = XCompoundTextStyle;
else
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/11
April 28, 2022
[PATCH 6/8] winex11: Move Unicode conversion out of unicode_text_from_string.
by Jacek Caban
From: Jacek Caban <jacek(a)codeweavers.com>
---
dlls/winex11.drv/clipboard.c | 45 +++++++++++++++++++++---------------
1 file changed, 27 insertions(+), 18 deletions(-)
diff --git a/dlls/winex11.drv/clipboard.c b/dlls/winex11.drv/clipboard.c
index 6baa87c5ce1..ce936f9012e 100644
--- a/dlls/winex11.drv/clipboard.c
+++ b/dlls/winex11.drv/clipboard.c
@@ -687,25 +687,19 @@ static WCHAR* uri_to_dos(char *encodedURI)
*
* Convert a string in the specified encoding to CF_UNICODETEXT format.
*/
-static WCHAR *unicode_text_from_string( UINT codepage, const void *data, size_t size, size_t *ret_size )
+static void *unicode_text_from_string( WCHAR *ret, const WCHAR *string, DWORD count, size_t *size )
{
- DWORD i, j, count;
- WCHAR *strW;
+ DWORD i, j;
- count = MultiByteToWideChar( codepage, 0, data, size, NULL, 0);
-
- if (!(strW = malloc( (count * 2 + 1) * sizeof(WCHAR) ))) return 0;
-
- MultiByteToWideChar( codepage, 0, data, size, strW + count, count );
for (i = j = 0; i < count; i++)
{
- if (strW[i + count] == '\n' && (!i || strW[i + count - 1] != '\r')) strW[j++] = '\r';
- strW[j++] = strW[i + count];
+ if (string[i] == '\n' && (!i || string[i - 1] != '\r')) ret[j++] = '\r';
+ ret[j++] = string[i];
}
- strW[j++] = 0;
- *ret_size = j * sizeof(WCHAR);
- TRACE( "returning %s\n", debugstr_wn( strW, j - 1 ));
- return strW;
+ ret[j++] = 0;
+ *size = j * sizeof(WCHAR);
+ TRACE( "returning %s\n", debugstr_wn( ret, j - 1 ));
+ return ret;
}
@@ -716,7 +710,12 @@ static WCHAR *unicode_text_from_string( UINT codepage, const void *data, size_t
*/
static void *import_string( Atom type, const void *data, size_t size, size_t *ret_size )
{
- return unicode_text_from_string( 28591, data, size, ret_size );
+ DWORD str_size;
+ WCHAR *ret;
+
+ if (!(ret = malloc( (size * 2 + 1) * sizeof(WCHAR) ))) return NULL;
+ str_size = MultiByteToWideChar( 28591, 0, data, size, ret + size, size );
+ return unicode_text_from_string( ret, ret + size, str_size, ret_size );
}
@@ -727,7 +726,12 @@ static void *import_string( Atom type, const void *data, size_t size, size_t *re
*/
static void *import_utf8_string( Atom type, const void *data, size_t size, size_t *ret_size )
{
- return unicode_text_from_string( CP_UTF8, data, size, ret_size );
+ DWORD str_size;
+ WCHAR *ret;
+
+ if (!(ret = malloc( (size * 2 + 1) * sizeof(WCHAR) ))) return NULL;
+ RtlUTF8ToUnicodeN( ret + size, size * sizeof(WCHAR), &str_size, data, size );
+ return unicode_text_from_string( ret, ret + size, str_size / sizeof(WCHAR), ret_size );
}
@@ -741,7 +745,8 @@ static void *import_compound_text( Atom type, const void *data, size_t size, siz
char** srcstr;
int count;
XTextProperty txtprop;
- void *ret;
+ DWORD len;
+ WCHAR *ret;
txtprop.value = (BYTE *)data;
txtprop.nitems = size;
@@ -750,7 +755,11 @@ static void *import_compound_text( Atom type, const void *data, size_t size, siz
if (XmbTextPropertyToTextList( thread_display(), &txtprop, &srcstr, &count ) != Success) return 0;
if (!count) return 0;
- ret = unicode_text_from_string( CP_UNIXCP, srcstr[0], strlen(srcstr[0]) + 1, ret_size );
+ len = strlen(srcstr[0]) + 1;
+ if (!(ret = malloc( (len * 2 + 1) * sizeof(WCHAR) ))) return NULL;
+ count = MultiByteToWideChar( CP_UNIXCP, 0, srcstr[0], len, ret + len, len );
+ ret = unicode_text_from_string( ret, ret + len, count, ret_size );
+
XFreeStringList(srcstr);
return ret;
}
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/11
April 28, 2022
[PATCH 5/8] winex11: Use inline intersect_rect helper instead of IntersectRect.
by Jacek Caban
From: Jacek Caban <jacek(a)codeweavers.com>
---
dlls/winex11.drv/bitblt.c | 2 +-
dlls/winex11.drv/display.c | 4 ++--
dlls/winex11.drv/init.c | 2 +-
dlls/winex11.drv/settings.c | 2 +-
dlls/winex11.drv/window.c | 4 ++--
dlls/winex11.drv/x11drv.h | 9 +++++++++
6 files changed, 16 insertions(+), 7 deletions(-)
diff --git a/dlls/winex11.drv/bitblt.c b/dlls/winex11.drv/bitblt.c
index 550c5f06f37..e21f975ef4a 100644
--- a/dlls/winex11.drv/bitblt.c
+++ b/dlls/winex11.drv/bitblt.c
@@ -1912,7 +1912,7 @@ static void x11drv_surface_flush( struct window_surface *window_surface )
coords.width = surface->header.rect.right - surface->header.rect.left;
coords.height = surface->header.rect.bottom - surface->header.rect.top;
SetRect( &coords.visrect, 0, 0, coords.width, coords.height );
- if (IntersectRect( &coords.visrect, &coords.visrect, &surface->bounds ))
+ if (intersect_rect( &coords.visrect, &coords.visrect, &surface->bounds ))
{
TRACE( "flushing %p %dx%d bounds %s bits %p\n",
surface, coords.width, coords.height,
diff --git a/dlls/winex11.drv/display.c b/dlls/winex11.drv/display.c
index f90cc455c36..aef4ed0b5b0 100644
--- a/dlls/winex11.drv/display.c
+++ b/dlls/winex11.drv/display.c
@@ -124,7 +124,7 @@ RECT get_work_area(const RECT *monitor_rect)
work_rect.right = work_rect.left + work_area[i * 4 + 2];
work_rect.bottom = work_rect.top + work_area[i * 4 + 3];
- if (IntersectRect(&work_rect, &work_rect, monitor_rect))
+ if (intersect_rect( &work_rect, &work_rect, monitor_rect ))
{
TRACE("work_rect:%s.\n", wine_dbgstr_rect(&work_rect));
XFree(work_area);
@@ -146,7 +146,7 @@ RECT get_work_area(const RECT *monitor_rect)
SetRect(&work_rect, work_area[0], work_area[1], work_area[0] + work_area[2],
work_area[1] + work_area[3]);
- if (IntersectRect(&work_rect, &work_rect, monitor_rect))
+ if (intersect_rect( &work_rect, &work_rect, monitor_rect ))
{
TRACE("work_rect:%s.\n", wine_dbgstr_rect(&work_rect));
XFree(work_area);
diff --git a/dlls/winex11.drv/init.c b/dlls/winex11.drv/init.c
index 9c5c394581b..27e070ef9b4 100644
--- a/dlls/winex11.drv/init.c
+++ b/dlls/winex11.drv/init.c
@@ -147,7 +147,7 @@ void add_device_bounds( X11DRV_PDEVICE *dev, const RECT *rect )
if (!dev->bounds) return;
if (dev->region && NtGdiGetRgnBox( dev->region, &rc ))
{
- if (IntersectRect( &rc, &rc, rect )) add_bounds_rect( dev->bounds, &rc );
+ if (intersect_rect( &rc, &rc, rect )) add_bounds_rect( dev->bounds, &rc );
}
else add_bounds_rect( dev->bounds, rect );
}
diff --git a/dlls/winex11.drv/settings.c b/dlls/winex11.drv/settings.c
index 652dedce601..44c61776b4b 100644
--- a/dlls/winex11.drv/settings.c
+++ b/dlls/winex11.drv/settings.c
@@ -709,7 +709,7 @@ static BOOL overlap_placed_displays(const RECT *rect, const struct x11drv_displa
for (display_idx = 0; display_idx < display_count; ++display_idx)
{
if (displays[display_idx].placed &&
- IntersectRect(&intersect, &displays[display_idx].new_rect, rect))
+ intersect_rect(&intersect, &displays[display_idx].new_rect, rect))
return TRUE;
}
return FALSE;
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c
index 6771368671a..d7027032465 100644
--- a/dlls/winex11.drv/window.c
+++ b/dlls/winex11.drv/window.c
@@ -2309,7 +2309,7 @@ static inline BOOL get_surface_rect( const RECT *visible_rect, RECT *surface_rec
{
*surface_rect = NtUserGetVirtualScreenRect();
- if (!IntersectRect( surface_rect, surface_rect, visible_rect )) return FALSE;
+ if (!intersect_rect( surface_rect, surface_rect, visible_rect )) return FALSE;
OffsetRect( surface_rect, -visible_rect->left, -visible_rect->top );
surface_rect->left &= ~31;
surface_rect->top &= ~31;
@@ -2761,7 +2761,7 @@ BOOL X11DRV_UpdateLayeredWindow( HWND hwnd, const UPDATELAYEREDWINDOWINFO *info,
if (info->prcDirty)
{
- IntersectRect( &rect, &rect, info->prcDirty );
+ intersect_rect( &rect, &rect, info->prcDirty );
memcpy( src_bits, dst_bits, bmi->bmiHeader.biSizeImage );
NtGdiPatBlt( hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, BLACKNESS );
}
diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h
index 69aa69404f4..4576564fda3 100644
--- a/dlls/winex11.drv/x11drv.h
+++ b/dlls/winex11.drv/x11drv.h
@@ -885,6 +885,15 @@ static inline HWND get_active_window(void)
return NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ) ? info.hwndActive : 0;
}
+static inline BOOL intersect_rect( RECT *dst, const RECT *src1, const RECT *src2 )
+{
+ dst->left = max( src1->left, src2->left );
+ dst->top = max( src1->top, src2->top );
+ dst->right = min( src1->right, src2->right );
+ dst->bottom = min( src1->bottom, src2->bottom );
+ return !IsRectEmpty( dst );
+}
+
/* registry helpers */
extern HKEY open_hkcu_key( const char *name ) DECLSPEC_HIDDEN;
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/11
April 28, 2022
[PATCH 4/8] winex11: Move XdndLeave event handler to event.c.
by Jacek Caban
From: Jacek Caban <jacek(a)codeweavers.com>
---
dlls/winex11.drv/event.c | 9 ++++++++-
dlls/winex11.drv/unixlib.h | 1 +
dlls/winex11.drv/x11drv.h | 1 -
dlls/winex11.drv/xdnd.c | 6 +++++-
4 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c
index a02f0340010..729a0a7eab6 100644
--- a/dlls/winex11.drv/event.c
+++ b/dlls/winex11.drv/event.c
@@ -1920,6 +1920,13 @@ static void handle_xdnd_drop_event( HWND hwnd, XClientMessageEvent *event )
}
+static void handle_xdnd_leave_event( HWND hwnd, XClientMessageEvent *event )
+{
+ UINT type = DND_LEAVE_EVENT;
+ handle_dnd_event( &type );
+}
+
+
struct client_message_handler
{
int atom; /* protocol atom */
@@ -1935,7 +1942,7 @@ static const struct client_message_handler client_messages[] =
{ XATOM_XdndEnter, handle_xdnd_enter_event },
{ XATOM_XdndPosition, handle_xdnd_position_event },
{ XATOM_XdndDrop, handle_xdnd_drop_event },
- { XATOM_XdndLeave, X11DRV_XDND_LeaveEvent }
+ { XATOM_XdndLeave, handle_xdnd_leave_event }
};
diff --git a/dlls/winex11.drv/unixlib.h b/dlls/winex11.drv/unixlib.h
index 41120385e60..537a8e0326a 100644
--- a/dlls/winex11.drv/unixlib.h
+++ b/dlls/winex11.drv/unixlib.h
@@ -31,6 +31,7 @@ struct format_entry
enum dnd_event_type
{
DND_DROP_EVENT,
+ DND_LEAVE_EVENT,
DND_POSITION_EVENT,
};
diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h
index c17dc45594c..69aa69404f4 100644
--- a/dlls/winex11.drv/x11drv.h
+++ b/dlls/winex11.drv/x11drv.h
@@ -294,7 +294,6 @@ extern BOOL IME_SetCompositionString(DWORD dwIndex, LPCVOID lpComp,
DWORD dwReadLen) DECLSPEC_HIDDEN;
extern void IME_SetResultString(LPWSTR lpResult, DWORD dwResultlen) DECLSPEC_HIDDEN;
-extern void X11DRV_XDND_LeaveEvent( HWND hWnd, XClientMessageEvent *event ) DECLSPEC_HIDDEN;
extern void handle_dnd_enter_event( struct format_entry *formats, ULONG size ) DECLSPEC_HIDDEN;
extern UINT handle_dnd_event( void *params ) DECLSPEC_HIDDEN;
diff --git a/dlls/winex11.drv/xdnd.c b/dlls/winex11.drv/xdnd.c
index 9de0b60d0cd..8240bbbf5d6 100644
--- a/dlls/winex11.drv/xdnd.c
+++ b/dlls/winex11.drv/xdnd.c
@@ -342,7 +342,7 @@ static DWORD handle_drop_event( struct dnd_drop_event_params *params )
*
* Handle an XdndLeave event.
*/
-void X11DRV_XDND_LeaveEvent( HWND hWnd, XClientMessageEvent *event )
+static NTSTATUS handle_leave_event(void)
{
IDropTarget *dropTarget;
@@ -362,6 +362,7 @@ void X11DRV_XDND_LeaveEvent( HWND hWnd, XClientMessageEvent *event )
}
X11DRV_XDND_FreeDragDropOp();
+ return 0;
}
@@ -736,6 +737,9 @@ UINT handle_dnd_event( void *params )
case DND_DROP_EVENT:
return handle_drop_event( params );
+ case DND_LEAVE_EVENT:
+ return handle_leave_event();
+
case DND_POSITION_EVENT:
return handle_position_event( params );
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/11
April 28, 2022
[PATCH 3/8] winex11: Move XdndDrop event handler to event.c.
by Jacek Caban
From: Jacek Caban <jacek(a)codeweavers.com>
---
dlls/winex11.drv/event.c | 26 ++++++++++++++++++++++-
dlls/winex11.drv/unixlib.h | 8 ++++++++
dlls/winex11.drv/x11drv.h | 1 -
dlls/winex11.drv/xdnd.c | 42 ++++++--------------------------------
4 files changed, 39 insertions(+), 38 deletions(-)
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c
index 2bfcd3211ae..a02f0340010 100644
--- a/dlls/winex11.drv/event.c
+++ b/dlls/winex11.drv/event.c
@@ -1896,6 +1896,30 @@ static void handle_xdnd_position_event( HWND hwnd, XClientMessageEvent *event )
}
+static void handle_xdnd_drop_event( HWND hwnd, XClientMessageEvent *event )
+{
+ struct dnd_drop_event_params params;
+ XClientMessageEvent e;
+ DWORD effect;
+
+ params.type = DND_DROP_EVENT;
+ params.hwnd = hwnd;
+ effect = handle_dnd_event( ¶ms );
+
+ /* Tell the target we are finished. */
+ memset( &e, 0, sizeof(e) );
+ e.type = ClientMessage;
+ e.display = event->display;
+ e.window = event->data.l[0];
+ e.message_type = x11drv_atom(XdndFinished);
+ e.format = 32;
+ e.data.l[0] = event->window;
+ e.data.l[1] = !!effect;
+ e.data.l[2] = drop_effect_to_xdnd_action( effect );
+ XSendEvent( event->display, event->data.l[0], False, NoEventMask, (XEvent *)&e );
+}
+
+
struct client_message_handler
{
int atom; /* protocol atom */
@@ -1910,7 +1934,7 @@ static const struct client_message_handler client_messages[] =
{ XATOM_DndProtocol, handle_dnd_protocol },
{ XATOM_XdndEnter, handle_xdnd_enter_event },
{ XATOM_XdndPosition, handle_xdnd_position_event },
- { XATOM_XdndDrop, X11DRV_XDND_DropEvent },
+ { XATOM_XdndDrop, handle_xdnd_drop_event },
{ XATOM_XdndLeave, X11DRV_XDND_LeaveEvent }
};
diff --git a/dlls/winex11.drv/unixlib.h b/dlls/winex11.drv/unixlib.h
index 7f8ec28b7cf..41120385e60 100644
--- a/dlls/winex11.drv/unixlib.h
+++ b/dlls/winex11.drv/unixlib.h
@@ -30,9 +30,17 @@ struct format_entry
enum dnd_event_type
{
+ DND_DROP_EVENT,
DND_POSITION_EVENT,
};
+/* DND_DROP_EVENT params */
+struct dnd_drop_event_params
+{
+ UINT type;
+ HWND hwnd;
+};
+
/* DND_POSITION_EVENT params */
struct dnd_position_event_params
{
diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h
index d49643a890b..c17dc45594c 100644
--- a/dlls/winex11.drv/x11drv.h
+++ b/dlls/winex11.drv/x11drv.h
@@ -294,7 +294,6 @@ extern BOOL IME_SetCompositionString(DWORD dwIndex, LPCVOID lpComp,
DWORD dwReadLen) DECLSPEC_HIDDEN;
extern void IME_SetResultString(LPWSTR lpResult, DWORD dwResultlen) DECLSPEC_HIDDEN;
-extern void X11DRV_XDND_DropEvent( HWND hWnd, XClientMessageEvent *event ) DECLSPEC_HIDDEN;
extern void X11DRV_XDND_LeaveEvent( HWND hWnd, XClientMessageEvent *event ) DECLSPEC_HIDDEN;
extern void handle_dnd_enter_event( struct format_entry *formats, ULONG size ) DECLSPEC_HIDDEN;
diff --git a/dlls/winex11.drv/xdnd.c b/dlls/winex11.drv/xdnd.c
index 7633738917f..9de0b60d0cd 100644
--- a/dlls/winex11.drv/xdnd.c
+++ b/dlls/winex11.drv/xdnd.c
@@ -142,20 +142,6 @@ static IDropTarget* get_droptarget_pointer(HWND hwnd)
return droptarget;
}
-/**************************************************************************
- * X11DRV_XDND_DROPEFFECTToXdndAction
- */
-static long X11DRV_XDND_DROPEFFECTToXdndAction(DWORD effect)
-{
- if (effect == DROPEFFECT_COPY)
- return x11drv_atom(XdndActionCopy);
- else if (effect == DROPEFFECT_MOVE)
- return x11drv_atom(XdndActionMove);
- else if (effect == DROPEFFECT_LINK)
- return x11drv_atom(XdndActionLink);
- FIXME("unknown drop effect %u, assuming XdndActionCopy\n", effect);
- return x11drv_atom(XdndActionCopy);
-}
/* Recursively searches for a window on given coordinates in a drag&drop specific manner.
*
@@ -278,14 +264,8 @@ static BOOL handle_position_event( struct dnd_position_event_params *params )
return accept ? effect : 0;
}
-/**************************************************************************
- * X11DRV_XDND_DropEvent
- *
- * Handle an XdndDrop event.
- */
-void X11DRV_XDND_DropEvent( HWND hWnd, XClientMessageEvent *event )
+static DWORD handle_drop_event( struct dnd_drop_event_params *params )
{
- XClientMessageEvent e;
IDropTarget *dropTarget;
DWORD effect = XDNDDropEffect;
int accept = 0; /* Assume we're not accepting */
@@ -338,7 +318,7 @@ void X11DRV_XDND_DropEvent( HWND hWnd, XClientMessageEvent *event )
/* Only send WM_DROPFILES if Drop didn't succeed or DROPEFFECT_NONE was set.
* Doing both causes winamp to duplicate the dropped files (#29081) */
- HWND hwnd_drop = window_accepting_files(window_from_point_dnd(hWnd, XDNDxy));
+ HWND hwnd_drop = window_accepting_files(window_from_point_dnd( params->hwnd, XDNDxy ));
if (hwnd_drop && X11DRV_XDND_HasHDROP())
{
@@ -354,20 +334,7 @@ void X11DRV_XDND_DropEvent( HWND hWnd, XClientMessageEvent *event )
TRACE("effectRequested(0x%x) accept(%d) performed(0x%x) at x(%d),y(%d)\n",
XDNDDropEffect, accept, effect, XDNDxy.x, XDNDxy.y);
- /* Tell the target we are finished. */
- memset(&e, 0, sizeof(e));
- e.type = ClientMessage;
- e.display = event->display;
- e.window = event->data.l[0];
- e.message_type = x11drv_atom(XdndFinished);
- e.format = 32;
- e.data.l[0] = event->window;
- e.data.l[1] = accept;
- if (accept)
- e.data.l[2] = X11DRV_XDND_DROPEFFECTToXdndAction(effect);
- else
- e.data.l[2] = None;
- XSendEvent(event->display, event->data.l[0], False, NoEventMask, (XEvent*)&e);
+ return accept ? effect : 0;
}
/**************************************************************************
@@ -766,6 +733,9 @@ UINT handle_dnd_event( void *params )
switch (*(UINT *)params)
{
+ case DND_DROP_EVENT:
+ return handle_drop_event( params );
+
case DND_POSITION_EVENT:
return handle_position_event( params );
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/11
April 28, 2022
[PATCH 2/8] winex11: Move XdndPosition event handler to event.c.
by Jacek Caban
From: Jacek Caban <jacek(a)codeweavers.com>
---
dlls/winex11.drv/event.c | 72 +++++++++++++++++++++++++++++++++++++-
dlls/winex11.drv/unixlib.h | 43 +++++++++++++++++++++++
dlls/winex11.drv/x11drv.h | 10 ++----
dlls/winex11.drv/xdnd.c | 70 ++++++++++--------------------------
4 files changed, 135 insertions(+), 60 deletions(-)
create mode 100644 dlls/winex11.drv/unixlib.h
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c
index c8cd3fd5ef1..2bfcd3211ae 100644
--- a/dlls/winex11.drv/event.c
+++ b/dlls/winex11.drv/event.c
@@ -1826,6 +1826,76 @@ static void handle_xdnd_enter_event( HWND hWnd, XClientMessageEvent *event )
}
+static DWORD xdnd_action_to_drop_effect( long action )
+{
+ /* In Windows, nothing but the given effects is allowed.
+ * In X the given action is just a hint, and you can always
+ * XdndActionCopy and XdndActionPrivate, so be more permissive. */
+ if (action == x11drv_atom(XdndActionCopy))
+ return DROPEFFECT_COPY;
+ else if (action == x11drv_atom(XdndActionMove))
+ return DROPEFFECT_MOVE | DROPEFFECT_COPY;
+ else if (action == x11drv_atom(XdndActionLink))
+ return DROPEFFECT_LINK | DROPEFFECT_COPY;
+ else if (action == x11drv_atom(XdndActionAsk))
+ /* FIXME: should we somehow ask the user what to do here? */
+ return DROPEFFECT_COPY | DROPEFFECT_MOVE | DROPEFFECT_LINK;
+
+ FIXME( "unknown action %ld, assuming DROPEFFECT_COPY\n", action );
+ return DROPEFFECT_COPY;
+}
+
+
+static long drop_effect_to_xdnd_action( DWORD effect )
+{
+ if (effect == DROPEFFECT_COPY)
+ return x11drv_atom(XdndActionCopy);
+ else if (effect == DROPEFFECT_MOVE)
+ return x11drv_atom(XdndActionMove);
+ else if (effect == DROPEFFECT_LINK)
+ return x11drv_atom(XdndActionLink);
+ else if (effect == DROPEFFECT_NONE)
+ return None;
+
+ FIXME( "unknown drop effect %u, assuming XdndActionCopy\n", effect );
+ return x11drv_atom(XdndActionCopy);
+}
+
+
+static void handle_xdnd_position_event( HWND hwnd, XClientMessageEvent *event )
+{
+ struct dnd_position_event_params params;
+ XClientMessageEvent e;
+ DWORD effect;
+
+ params.type = DND_POSITION_EVENT;
+ params.hwnd = hwnd;
+ params.point = root_to_virtual_screen( event->data.l[2] >> 16, event->data.l[2] & 0xFFFF );
+ params.effect = effect = xdnd_action_to_drop_effect( event->data.l[4] );
+
+ effect = handle_dnd_event( ¶ms );
+
+ TRACE( "actionRequested(%ld) chosen(0x%x) at x(%d),y(%d)\n",
+ event->data.l[4], effect, params.point.x, params.point.y );
+
+ /*
+ * Let source know if we're accepting the drop by
+ * sending a status message.
+ */
+ e.type = ClientMessage;
+ e.display = event->display;
+ e.window = event->data.l[0];
+ e.message_type = x11drv_atom(XdndStatus);
+ e.format = 32;
+ e.data.l[0] = event->window;
+ e.data.l[1] = !!effect;
+ e.data.l[2] = 0; /* Empty Rect */
+ e.data.l[3] = 0; /* Empty Rect */
+ e.data.l[4] = drop_effect_to_xdnd_action( effect );
+ XSendEvent( event->display, event->data.l[0], False, NoEventMask, (XEvent *)&e );
+}
+
+
struct client_message_handler
{
int atom; /* protocol atom */
@@ -1839,7 +1909,7 @@ static const struct client_message_handler client_messages[] =
{ XATOM__XEMBED, handle_xembed_protocol },
{ XATOM_DndProtocol, handle_dnd_protocol },
{ XATOM_XdndEnter, handle_xdnd_enter_event },
- { XATOM_XdndPosition, X11DRV_XDND_PositionEvent },
+ { XATOM_XdndPosition, handle_xdnd_position_event },
{ XATOM_XdndDrop, X11DRV_XDND_DropEvent },
{ XATOM_XdndLeave, X11DRV_XDND_LeaveEvent }
};
diff --git a/dlls/winex11.drv/unixlib.h b/dlls/winex11.drv/unixlib.h
new file mode 100644
index 00000000000..7f8ec28b7cf
--- /dev/null
+++ b/dlls/winex11.drv/unixlib.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2022 Jacek Caban 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 "ntuser.h"
+#include "wine/unixlib.h"
+
+/* DnD support */
+
+struct format_entry
+{
+ UINT format;
+ UINT size;
+ char data[1];
+};
+
+enum dnd_event_type
+{
+ DND_POSITION_EVENT,
+};
+
+/* DND_POSITION_EVENT params */
+struct dnd_position_event_params
+{
+ UINT type;
+ HWND hwnd;
+ POINT point;
+ DWORD effect;
+};
diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h
index 9339c1c08e6..d49643a890b 100644
--- a/dlls/winex11.drv/x11drv.h
+++ b/dlls/winex11.drv/x11drv.h
@@ -61,6 +61,7 @@ typedef int Status;
#include "winbase.h"
#include "ntgdi.h"
#include "wine/gdi_driver.h"
+#include "unixlib.h"
#include "wine/list.h"
#define MAX_DASHLEN 16
@@ -293,18 +294,11 @@ extern BOOL IME_SetCompositionString(DWORD dwIndex, LPCVOID lpComp,
DWORD dwReadLen) DECLSPEC_HIDDEN;
extern void IME_SetResultString(LPWSTR lpResult, DWORD dwResultlen) DECLSPEC_HIDDEN;
-extern void X11DRV_XDND_PositionEvent( HWND hWnd, XClientMessageEvent *event ) DECLSPEC_HIDDEN;
extern void X11DRV_XDND_DropEvent( HWND hWnd, XClientMessageEvent *event ) DECLSPEC_HIDDEN;
extern void X11DRV_XDND_LeaveEvent( HWND hWnd, XClientMessageEvent *event ) DECLSPEC_HIDDEN;
-struct format_entry
-{
- UINT format;
- UINT size;
- char data[1];
-};
-
extern void handle_dnd_enter_event( struct format_entry *formats, ULONG size ) DECLSPEC_HIDDEN;
+extern UINT handle_dnd_event( void *params ) DECLSPEC_HIDDEN;
extern struct format_entry *import_xdnd_selection( Display *display, Window win, Atom selection,
Atom *targets, UINT count,
diff --git a/dlls/winex11.drv/xdnd.c b/dlls/winex11.drv/xdnd.c
index e56fc964ada..7633738917f 100644
--- a/dlls/winex11.drv/xdnd.c
+++ b/dlls/winex11.drv/xdnd.c
@@ -142,27 +142,6 @@ static IDropTarget* get_droptarget_pointer(HWND hwnd)
return droptarget;
}
-/**************************************************************************
- * X11DRV_XDND_XdndActionToDROPEFFECT
- */
-static DWORD X11DRV_XDND_XdndActionToDROPEFFECT(long action)
-{
- /* In Windows, nothing but the given effects is allowed.
- * In X the given action is just a hint, and you can always
- * XdndActionCopy and XdndActionPrivate, so be more permissive. */
- if (action == x11drv_atom(XdndActionCopy))
- return DROPEFFECT_COPY;
- else if (action == x11drv_atom(XdndActionMove))
- return DROPEFFECT_MOVE | DROPEFFECT_COPY;
- else if (action == x11drv_atom(XdndActionLink))
- return DROPEFFECT_LINK | DROPEFFECT_COPY;
- else if (action == x11drv_atom(XdndActionAsk))
- /* FIXME: should we somehow ask the user what to do here? */
- return DROPEFFECT_COPY | DROPEFFECT_MOVE | DROPEFFECT_LINK;
- FIXME("unknown action %ld, assuming DROPEFFECT_COPY\n", action);
- return DROPEFFECT_COPY;
-}
-
/**************************************************************************
* X11DRV_XDND_DROPEFFECTToXdndAction
*/
@@ -215,22 +194,17 @@ static HWND window_accepting_files(HWND hwnd)
*
* Handle an XdndPosition event.
*/
-void X11DRV_XDND_PositionEvent( HWND hWnd, XClientMessageEvent *event )
+static BOOL handle_position_event( struct dnd_position_event_params *params )
{
- XClientMessageEvent e;
int accept = 0; /* Assume we're not accepting */
IDropTarget *dropTarget = NULL;
- DWORD effect;
+ DWORD effect = params->effect;
POINTL pointl;
HWND targetWindow;
HRESULT hr;
- XDNDxy = root_to_virtual_screen( event->data.l[2] >> 16, event->data.l[2] & 0xFFFF );
- targetWindow = window_from_point_dnd(hWnd, XDNDxy);
-
- pointl.x = XDNDxy.x;
- pointl.y = XDNDxy.y;
- effect = X11DRV_XDND_XdndActionToDROPEFFECT(event->data.l[4]);
+ XDNDxy = params->point;
+ targetWindow = window_from_point_dnd( params->hwnd, XDNDxy );
if (!XDNDAccepted || XDNDLastTargetWnd != targetWindow)
{
@@ -301,27 +275,7 @@ void X11DRV_XDND_PositionEvent( HWND hWnd, XClientMessageEvent *event )
}
}
- TRACE("actionRequested(%ld) accept(%d) chosen(0x%x) at x(%d),y(%d)\n",
- event->data.l[4], accept, effect, XDNDxy.x, XDNDxy.y);
-
- /*
- * Let source know if we're accepting the drop by
- * sending a status message.
- */
- e.type = ClientMessage;
- e.display = event->display;
- e.window = event->data.l[0];
- e.message_type = x11drv_atom(XdndStatus);
- e.format = 32;
- e.data.l[0] = event->window;
- e.data.l[1] = accept;
- e.data.l[2] = 0; /* Empty Rect */
- e.data.l[3] = 0; /* Empty Rect */
- if (accept)
- e.data.l[4] = X11DRV_XDND_DROPEFFECTToXdndAction(effect);
- else
- e.data.l[4] = None;
- XSendEvent(event->display, event->data.l[0], False, NoEventMask, (XEvent*)&e);
+ return accept ? effect : 0;
}
/**************************************************************************
@@ -806,3 +760,17 @@ static IDataObjectVtbl xdndDataObjectVtbl =
};
static IDataObject XDNDDataObject = { &xdndDataObjectVtbl };
+
+UINT handle_dnd_event( void *params )
+{
+
+ switch (*(UINT *)params)
+ {
+ case DND_POSITION_EVENT:
+ return handle_position_event( params );
+
+ default:
+ ERR( "invalid event\n" );
+ return 0;
+ }
+}
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/11
April 28, 2022
[PATCH 1/8] winex11: Move XdndEnter event handler to event.c.
by Jacek Caban
From: Jacek Caban <jacek(a)codeweavers.com>
In preparation for xdnd.c being in PE file.
---
dlls/winex11.drv/event.c | 76 +++++++++++++++++++++++++++++++++-
dlls/winex11.drv/x11drv.h | 3 +-
dlls/winex11.drv/xdnd.c | 86 ++-------------------------------------
3 files changed, 80 insertions(+), 85 deletions(-)
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c
index e1bcd70d35a..c8cd3fd5ef1 100644
--- a/dlls/winex11.drv/event.c
+++ b/dlls/winex11.drv/event.c
@@ -49,6 +49,7 @@
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(event);
+WINE_DECLARE_DEBUG_CHANNEL(xdnd);
extern BOOL ximInComposeMode;
@@ -1752,6 +1753,79 @@ static void handle_dnd_protocol( HWND hwnd, XClientMessageEvent *event )
}
+/**************************************************************************
+ * handle_xdnd_enter_event
+ *
+ * Handle an XdndEnter event.
+ */
+static void handle_xdnd_enter_event( HWND hWnd, XClientMessageEvent *event )
+{
+ struct format_entry *data;
+ unsigned long count = 0;
+ Atom *xdndtypes;
+ size_t size;
+ int version;
+
+ version = (event->data.l[1] & 0xFF000000) >> 24;
+
+ TRACE( "ver(%d) check-XdndTypeList(%ld) data=%ld,%ld,%ld,%ld,%ld\n",
+ version, (event->data.l[1] & 1),
+ event->data.l[0], event->data.l[1], event->data.l[2],
+ event->data.l[3], event->data.l[4] );
+
+ if (version > WINE_XDND_VERSION)
+ {
+ ERR("ignoring unsupported XDND version %d\n", version);
+ return;
+ }
+
+ /* If the source supports more than 3 data types we retrieve
+ * the entire list. */
+ if (event->data.l[1] & 1)
+ {
+ Atom acttype;
+ int actfmt;
+ unsigned long bytesret;
+
+ /* Request supported formats from source window */
+ XGetWindowProperty( event->display, event->data.l[0], x11drv_atom(XdndTypeList),
+ 0, 65535, FALSE, AnyPropertyType, &acttype, &actfmt, &count,
+ &bytesret, (unsigned char **)&xdndtypes );
+ }
+ else
+ {
+ count = 3;
+ xdndtypes = (Atom *)&event->data.l[2];
+ }
+
+ if (TRACE_ON(xdnd))
+ {
+ unsigned int i;
+
+ for (i = 0; i < count; i++)
+ {
+ if (xdndtypes[i] != 0)
+ {
+ char * pn = XGetAtomName( event->display, xdndtypes[i] );
+ TRACE( "XDNDEnterAtom %ld: %s\n", xdndtypes[i], pn );
+ XFree( pn );
+ }
+ }
+ }
+
+ data = import_xdnd_selection( event->display, event->window, x11drv_atom(XdndSelection),
+ xdndtypes, count, &size );
+ if (data)
+ {
+ handle_dnd_enter_event( data, size );
+ free( data );
+ }
+
+ if (event->data.l[1] & 1)
+ XFree(xdndtypes);
+}
+
+
struct client_message_handler
{
int atom; /* protocol atom */
@@ -1764,7 +1838,7 @@ static const struct client_message_handler client_messages[] =
{ XATOM_WM_PROTOCOLS, handle_wm_protocols },
{ XATOM__XEMBED, handle_xembed_protocol },
{ XATOM_DndProtocol, handle_dnd_protocol },
- { XATOM_XdndEnter, X11DRV_XDND_EnterEvent },
+ { XATOM_XdndEnter, handle_xdnd_enter_event },
{ XATOM_XdndPosition, X11DRV_XDND_PositionEvent },
{ XATOM_XdndDrop, X11DRV_XDND_DropEvent },
{ XATOM_XdndLeave, X11DRV_XDND_LeaveEvent }
diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h
index f6d28122fc3..9339c1c08e6 100644
--- a/dlls/winex11.drv/x11drv.h
+++ b/dlls/winex11.drv/x11drv.h
@@ -293,7 +293,6 @@ extern BOOL IME_SetCompositionString(DWORD dwIndex, LPCVOID lpComp,
DWORD dwReadLen) DECLSPEC_HIDDEN;
extern void IME_SetResultString(LPWSTR lpResult, DWORD dwResultlen) DECLSPEC_HIDDEN;
-extern void X11DRV_XDND_EnterEvent( HWND hWnd, XClientMessageEvent *event ) DECLSPEC_HIDDEN;
extern void X11DRV_XDND_PositionEvent( HWND hWnd, XClientMessageEvent *event ) DECLSPEC_HIDDEN;
extern void X11DRV_XDND_DropEvent( HWND hWnd, XClientMessageEvent *event ) DECLSPEC_HIDDEN;
extern void X11DRV_XDND_LeaveEvent( HWND hWnd, XClientMessageEvent *event ) DECLSPEC_HIDDEN;
@@ -305,6 +304,8 @@ struct format_entry
char data[1];
};
+extern void handle_dnd_enter_event( struct format_entry *formats, ULONG size ) DECLSPEC_HIDDEN;
+
extern struct format_entry *import_xdnd_selection( Display *display, Window win, Atom selection,
Atom *targets, UINT count,
size_t *size ) DECLSPEC_HIDDEN;
diff --git a/dlls/winex11.drv/xdnd.c b/dlls/winex11.drv/xdnd.c
index 34cb2a47081..e56fc964ada 100644
--- a/dlls/winex11.drv/xdnd.c
+++ b/dlls/winex11.drv/xdnd.c
@@ -55,8 +55,6 @@ static HWND XDNDLastTargetWnd;
/* might be an ancestor of XDNDLastTargetWnd */
static HWND XDNDLastDropTargetWnd;
-static void X11DRV_XDND_ResolveProperty(Display *display, Window xwin, Time tm,
- Atom *types, unsigned long count);
static BOOL X11DRV_XDND_HasHDROP(void);
static HRESULT X11DRV_XDND_SendDropFiles(HWND hwnd);
static void X11DRV_XDND_FreeDragDropOp(void);
@@ -180,73 +178,6 @@ static long X11DRV_XDND_DROPEFFECTToXdndAction(DWORD effect)
return x11drv_atom(XdndActionCopy);
}
-/**************************************************************************
- * X11DRV_XDND_EnterEvent
- *
- * Handle an XdndEnter event.
- */
-void X11DRV_XDND_EnterEvent( HWND hWnd, XClientMessageEvent *event )
-{
- int version;
- Atom *xdndtypes;
- unsigned long count = 0;
-
- version = (event->data.l[1] & 0xFF000000) >> 24;
- TRACE("ver(%d) check-XdndTypeList(%ld) data=%ld,%ld,%ld,%ld,%ld\n",
- version, (event->data.l[1] & 1),
- event->data.l[0], event->data.l[1], event->data.l[2],
- event->data.l[3], event->data.l[4]);
-
- if (version > WINE_XDND_VERSION)
- {
- ERR("ignoring unsupported XDND version %d\n", version);
- return;
- }
-
- XDNDAccepted = FALSE;
-
- /* If the source supports more than 3 data types we retrieve
- * the entire list. */
- if (event->data.l[1] & 1)
- {
- Atom acttype;
- int actfmt;
- unsigned long bytesret;
-
- /* Request supported formats from source window */
- XGetWindowProperty(event->display, event->data.l[0], x11drv_atom(XdndTypeList),
- 0, 65535, FALSE, AnyPropertyType, &acttype, &actfmt, &count,
- &bytesret, (unsigned char**)&xdndtypes);
- }
- else
- {
- count = 3;
- xdndtypes = (Atom*) &event->data.l[2];
- }
-
- if (TRACE_ON(xdnd))
- {
- unsigned int i;
-
- for (i = 0; i < count; i++)
- {
- if (xdndtypes[i] != 0)
- {
- char * pn = XGetAtomName(event->display, xdndtypes[i]);
- TRACE("XDNDEnterAtom %ld: %s\n", xdndtypes[i], pn);
- XFree(pn);
- }
- }
- }
-
- /* Do a one-time data read and cache results */
- X11DRV_XDND_ResolveProperty(event->display, event->window,
- event->data.l[1], xdndtypes, count);
-
- if (event->data.l[1] & 1)
- XFree(xdndtypes);
-}
-
/* Recursively searches for a window on given coordinates in a drag&drop specific manner.
*
* Don't use WindowFromPoint instead, because it omits the STATIC and transparent
@@ -514,29 +445,18 @@ void X11DRV_XDND_LeaveEvent( HWND hWnd, XClientMessageEvent *event )
/**************************************************************************
- * X11DRV_XDND_ResolveProperty
- *
- * Resolve all MIME types to windows clipboard formats. All data is cached.
+ * handle_dnd_enter_event
*/
-static void X11DRV_XDND_ResolveProperty(Display *display, Window xwin, Time tm,
- Atom *types, unsigned long count)
+void handle_dnd_enter_event( struct format_entry *formats, ULONG size )
{
- struct format_entry *formats;
- size_t size;
-
- TRACE("count(%ld)\n", count);
-
+ XDNDAccepted = FALSE;
X11DRV_XDND_FreeDragDropOp(); /* Clear previously cached data */
- formats = import_xdnd_selection( display, xwin, x11drv_atom(XdndSelection), types, count, &size );
- if (!formats) return;
-
if ((xdnd_formats = HeapAlloc( GetProcessHeap(), 0, size )))
{
memcpy( xdnd_formats, formats, size );
xdnd_formats_end = (struct format_entry *)((char *)xdnd_formats + size);
}
- free( formats );
}
--
GitLab
https://gitlab.winehq.org/wine/wine/-/merge_requests/11
April 28, 2022
[PATCH 0/8] MR11: winex11: PE conversion preparation
by Jacek Caban (@jacek)
Prepare xdnd.c to be in PE file and use ntdll interface for Unicode conversion.
--
https://gitlab.winehq.org/wine/wine/-/merge_requests/11
April 28, 2022
Re: [PATCH v3] wintypes: Implement IApiInformationStatics stubs.
by Rémi Bernon
> +
> +START_TEST(wintypes)
> +{
> + HMODULE combase;
> +
> + if (!(combase = LoadLibraryW(L"combase.dll")))
> + {
> + win_skip("Failed to load combase.dll, skipping tests.\n");
> + return;
> + }
> +
> +#define LOAD_FUNCPTR(x) \
> + if (!(p##x = (void *)GetProcAddress(combase, #x))) \
> + { \
> + win_skip("Failed to find %s in combase.dll, skipping tests.\n", #x); \
> + return; \
> + }
> +
> + LOAD_FUNCPTR(RoActivateInstance)
> + LOAD_FUNCPTR(RoGetActivationFactory)
> + LOAD_FUNCPTR(RoInitialize)
> + LOAD_FUNCPTR(RoUninitialize)
> + LOAD_FUNCPTR(WindowsCreateString)
> + LOAD_FUNCPTR(WindowsDeleteString)
> + LOAD_FUNCPTR(WindowsGetStringRawBuffer)
> +#undef LOAD_FUNCPTR
> +
> + test_IApiInformationStatics();
> +}
It looks good but I don't think you really need to load combase.dll
dynamically here. It was useful for dinput tests because it's testing
non-WinRT stuff which work on Win7 too.
April 28, 2022
[PATCH v2 7/7] wbemprox: Add FreeVirtualMemory property to Win32_Operatingsystem.
by Hans Leidekker
From: Louis Lenders <xerox.xerox2000x(a)gmail.com>
Wine-bug: https://bugs.winehq.org/show_bug.cgi?id=52887
Signed-off-by: Louis Lenders <xerox.xerox2000x(a)gmail.com>
Signed-off-by: Hans Leidekker <hans(a)codeweavers.com>
---
dlls/wbemprox/builtin.c | 12 ++++++++++++
dlls/wbemprox/tests/query.c | 1 +
2 files changed, 13 insertions(+)
diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c
index 98a32313461..d27130b00c9 100644
--- a/dlls/wbemprox/builtin.c
+++ b/dlls/wbemprox/builtin.c
@@ -239,6 +239,7 @@ static const struct column col_operatingsystem[] =
{ L"CSName", CIM_STRING|COL_FLAG_DYNAMIC },
{ L"CurrentTimeZone", CIM_SINT16 },
{ L"FreePhysicalMemory", CIM_UINT64 },
+ { L"FreeVirtualMemory", CIM_UINT64 },
{ L"InstallDate", CIM_DATETIME },
{ L"LastBootUpTime", CIM_DATETIME|COL_FLAG_DYNAMIC },
{ L"LocalDateTime", CIM_DATETIME|COL_FLAG_DYNAMIC },
@@ -676,6 +677,7 @@ struct record_operatingsystem
const WCHAR *csname;
INT16 currenttimezone;
UINT64 freephysicalmemory;
+ UINT64 freevirtualmemory;
const WCHAR *installdate;
const WCHAR *lastbootuptime;
const WCHAR *localdatetime;
@@ -1572,6 +1574,15 @@ static UINT64 get_available_physical_memory(void)
return status.ullAvailPhys;
}
+static UINT64 get_available_virtual_memory(void)
+{
+ MEMORYSTATUSEX status;
+
+ status.dwLength = sizeof(status);
+ if (!GlobalMemoryStatusEx( &status )) return 1024 * 1024 * 1024;
+ return status.ullAvailVirtual;
+}
+
static WCHAR *get_computername(void)
{
WCHAR *ret;
@@ -3683,6 +3694,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct
rec->csname = get_computername();
rec->currenttimezone = get_currenttimezone();
rec->freephysicalmemory = get_available_physical_memory() / 1024;
+ rec->freevirtualmemory = get_available_virtual_memory() / 1024;
rec->installdate = L"20140101000000.000000+000";
rec->lastbootuptime = get_lastbootuptime();
rec->localdatetime = get_localdatetime();
diff --git a/dlls/wbemprox/tests/query.c b/dlls/wbemprox/tests/query.c
index f8508cd1405..f6faaea0d99 100644
--- a/dlls/wbemprox/tests/query.c
+++ b/dlls/wbemprox/tests/query.c
@@ -1404,6 +1404,7 @@ static void test_Win32_OperatingSystem( IWbemServices *services )
VariantClear( &val );
check_property( obj, L"FreePhysicalMemory", VT_BSTR, CIM_UINT64 );
+ check_property( obj, L"FreeVirtualMemory", VT_BSTR, CIM_UINT64 );
check_property( obj, L"Name", VT_BSTR, CIM_STRING );
type = 0xdeadbeef;
--
2.30.2
April 28, 2022
[PATCH v2 6/7] wbemprox: Add SystemType property to Win32_Computersystem.
by Hans Leidekker
From: Louis Lenders <xerox.xerox2000x(a)gmail.com>
v2: Use a helper that maps directly from processor architecture.
Wine-bug: https://bugs.winehq.org/show_bug.cgi?id=52887
Signed-off-by: Louis Lenders <xerox.xerox2000x(a)gmail.com>
Signed-off-by: Hans Leidekker <hans(a)codeweavers.com>
---
dlls/wbemprox/builtin.c | 11 +++++++++++
dlls/wbemprox/tests/query.c | 1 +
2 files changed, 12 insertions(+)
diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c
index 27e1084b21e..98a32313461 100644
--- a/dlls/wbemprox/builtin.c
+++ b/dlls/wbemprox/builtin.c
@@ -105,6 +105,7 @@ static const struct column col_compsys[] =
{ L"Name", CIM_STRING|COL_FLAG_DYNAMIC },
{ L"NumberOfLogicalProcessors", CIM_UINT32 },
{ L"NumberOfProcessors", CIM_UINT32 },
+ { L"SystemType", CIM_STRING },
{ L"TotalPhysicalMemory", CIM_UINT64 },
{ L"UserName", CIM_STRING|COL_FLAG_DYNAMIC },
};
@@ -541,6 +542,7 @@ struct record_computersystem
const WCHAR *name;
UINT32 num_logical_processors;
UINT32 num_processors;
+ const WCHAR *systemtype;
UINT64 total_physical_memory;
const WCHAR *username;
};
@@ -1580,6 +1582,14 @@ static WCHAR *get_computername(void)
return ret;
}
+static const WCHAR *get_systemtype(void)
+{
+ SYSTEM_INFO info;
+ GetNativeSystemInfo( &info );
+ if (info.u.s.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) return L"x64 based PC";
+ return L"x86 based PC";
+}
+
static WCHAR *get_username(void)
{
WCHAR *ret;
@@ -1614,6 +1624,7 @@ static enum fill_status fill_compsys( struct table *table, const struct expr *co
rec->model = L"Wine";
rec->name = get_computername();
rec->num_logical_processors = get_logical_processor_count( NULL, &rec->num_processors );
+ rec->systemtype = get_systemtype();
rec->total_physical_memory = get_total_physical_memory();
rec->username = get_username();
if (!match_row( table, row, cond, &status )) free_row_values( table, row );
diff --git a/dlls/wbemprox/tests/query.c b/dlls/wbemprox/tests/query.c
index 54d80b9e196..f8508cd1405 100644
--- a/dlls/wbemprox/tests/query.c
+++ b/dlls/wbemprox/tests/query.c
@@ -752,6 +752,7 @@ static void test_Win32_ComputerSystem( IWbemServices *services )
}
check_property( obj, L"NumberOfProcessors", VT_I4, CIM_UINT32 );
+ check_property( obj, L"SystemType", VT_BSTR, CIM_STRING );
type = 0xdeadbeef;
VariantInit( &value );
--
2.30.2
April 28, 2022
[PATCH v2 5/7] wbemprox: Add WindowsDirectory property to Win32_Operatingsystem.
by Hans Leidekker
From: Louis Lenders <xerox.xerox2000x(a)gmail.com>
v2: Use GetWindowsDirectoryW() and mark the property as dynamic.
Wine-bug: https://bugs.winehq.org/show_bug.cgi?id=52887
Signed-off-by: Louis Lenders <xerox.xerox2000x(a)gmail.com>
Signed-off-by: Hans Leidekker <hans(a)codeweavers.com>
---
dlls/wbemprox/builtin.c | 22 ++++++++++++++++------
dlls/wbemprox/tests/query.c | 1 +
2 files changed, 17 insertions(+), 6 deletions(-)
diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c
index 8c5a158fea3..27e1084b21e 100644
--- a/dlls/wbemprox/builtin.c
+++ b/dlls/wbemprox/builtin.c
@@ -263,6 +263,7 @@ static const struct column col_operatingsystem[] =
{ L"TotalVirtualMemorySize", CIM_UINT64 },
{ L"TotalVisibleMemorySize", CIM_UINT64 },
{ L"Version", CIM_STRING|COL_FLAG_DYNAMIC },
+ { L"WindowsDirectory", CIM_STRING|COL_FLAG_DYNAMIC },
};
static const struct column col_pagefileusage[] =
{
@@ -698,6 +699,7 @@ struct record_operatingsystem
UINT64 totalvirtualmemorysize;
UINT64 totalvisiblememorysize;
const WCHAR *version;
+ const WCHAR *windowsdirectory;
};
struct record_pagefileusage
{
@@ -3446,6 +3448,15 @@ static enum fill_status fill_processor( struct table *table, const struct expr *
return status;
}
+static INT16 get_currenttimezone(void)
+{
+ TIME_ZONE_INFORMATION info;
+ DWORD status = GetTimeZoneInformation( &info );
+ if (status == TIME_ZONE_ID_INVALID) return 0;
+ if (status == TIME_ZONE_ID_DAYLIGHT) return -(info.Bias + info.DaylightBias);
+ return -(info.Bias + info.StandardBias);
+}
+
static WCHAR *get_lastbootuptime(void)
{
SYSTEM_TIMEOFDAY_INFORMATION ti;
@@ -3631,13 +3642,11 @@ static WCHAR *get_registereduser(void)
return get_reg_str( HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion", L"RegisteredOwner" );
}
-static INT16 get_currenttimezone(void)
+static WCHAR *get_windowsdirectory(void)
{
- TIME_ZONE_INFORMATION info;
- DWORD status = GetTimeZoneInformation( &info );
- if (status == TIME_ZONE_ID_INVALID) return 0;
- if (status == TIME_ZONE_ID_DAYLIGHT) return -(info.Bias + info.DaylightBias);
- return -(info.Bias + info.StandardBias);
+ WCHAR dir[MAX_PATH];
+ if (!GetWindowsDirectoryW( dir, ARRAY_SIZE(dir) )) return NULL;
+ return wcsdup( dir );
}
static enum fill_status fill_operatingsystem( struct table *table, const struct expr *cond )
@@ -3688,6 +3697,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct
rec->totalvirtualmemorysize = get_total_physical_memory() / 1024;
rec->totalvisiblememorysize = rec->totalvirtualmemorysize;
rec->version = get_osversion( &ver );
+ rec->windowsdirectory = get_windowsdirectory();
if (!match_row( table, row, cond, &status )) free_row_values( table, row );
else row++;
diff --git a/dlls/wbemprox/tests/query.c b/dlls/wbemprox/tests/query.c
index 37c149fab26..54d80b9e196 100644
--- a/dlls/wbemprox/tests/query.c
+++ b/dlls/wbemprox/tests/query.c
@@ -1441,6 +1441,7 @@ static void test_Win32_OperatingSystem( IWbemServices *services )
check_property( obj, L"TotalVirtualMemorySize", VT_BSTR, CIM_UINT64 );
check_property( obj, L"Status", VT_BSTR, CIM_STRING );
check_property( obj, L"SystemDrive", VT_BSTR, CIM_STRING );
+ check_property( obj, L"WindowsDirectory", VT_BSTR, CIM_STRING );
IWbemClassObject_Release( obj );
IEnumWbemClassObject_Release( result );
--
2.30.2
April 28, 2022
[PATCH v2 4/7] wbemprox: Add BootDevice property to Win32_Operatingsystem.
by Hans Leidekker
From: Louis Lenders <xerox.xerox2000x(a)gmail.com>
Wine-bug: https://bugs.winehq.org/show_bug.cgi?id=52887
Signed-off-by: Louis Lenders <xerox.xerox2000x(a)gmail.com>
Signed-off-by: Hans Leidekker <hans(a)codeweavers.com>
---
dlls/wbemprox/builtin.c | 3 +++
dlls/wbemprox/tests/query.c | 1 +
2 files changed, 4 insertions(+)
diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c
index ba3e728c8c5..8c5a158fea3 100644
--- a/dlls/wbemprox/builtin.c
+++ b/dlls/wbemprox/builtin.c
@@ -228,6 +228,7 @@ static const struct column col_networkadapterconfig[] =
};
static const struct column col_operatingsystem[] =
{
+ { L"BootDevice", CIM_STRING },
{ L"BuildNumber", CIM_STRING|COL_FLAG_DYNAMIC },
{ L"BuildType", CIM_STRING },
{ L"Caption", CIM_STRING|COL_FLAG_DYNAMIC },
@@ -662,6 +663,7 @@ struct record_networkadapterconfig
};
struct record_operatingsystem
{
+ const WCHAR *bootdevice;
const WCHAR *buildnumber;
const WCHAR *buildtype;
const WCHAR *caption;
@@ -3651,6 +3653,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct
RtlGetVersion( &ver );
rec = (struct record_operatingsystem *)table->data;
+ rec->bootdevice = L"\\Device\\HarddiskVolume1";
rec->buildnumber = get_osbuildnumber( &ver );
rec->buildtype = L"Wine build";
rec->caption = get_oscaption( &ver );
diff --git a/dlls/wbemprox/tests/query.c b/dlls/wbemprox/tests/query.c
index b31506a12d6..37c149fab26 100644
--- a/dlls/wbemprox/tests/query.c
+++ b/dlls/wbemprox/tests/query.c
@@ -1388,6 +1388,7 @@ static void test_Win32_OperatingSystem( IWbemServices *services )
hr = IWbemClassObject_EndEnumeration( obj );
ok( hr == S_OK, "got %#lx\n", hr );
+ check_property( obj, L"BootDevice", VT_BSTR, CIM_STRING );
check_property( obj, L"BuildNumber", VT_BSTR, CIM_STRING );
check_property( obj, L"BuildType", VT_BSTR, CIM_STRING );
check_property( obj, L"Caption", VT_BSTR, CIM_STRING );
--
2.30.2
April 28, 2022
[PATCH v2 3/7] wbemprox: Add Organization property to Win32_Operatingsystem.
by Hans Leidekker
From: Louis Lenders <xerox.xerox2000x(a)gmail.com>
v2: Use the helper.
Wine-bug: https://bugs.winehq.org/show_bug.cgi?id=52887
Signed-off-by: Louis Lenders <xerox.xerox2000x(a)gmail.com>
Signed-off-by: Hans Leidekker <hans(a)codeweavers.com>
---
dlls/wbemprox/builtin.c | 52 ++++++++++++++++++++++---------------
dlls/wbemprox/tests/query.c | 1 +
2 files changed, 32 insertions(+), 21 deletions(-)
diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c
index 5c53aae8d3b..ba3e728c8c5 100644
--- a/dlls/wbemprox/builtin.c
+++ b/dlls/wbemprox/builtin.c
@@ -244,6 +244,7 @@ static const struct column col_operatingsystem[] =
{ L"Manufacturer", CIM_STRING },
{ L"Name", CIM_STRING|COL_FLAG_DYNAMIC },
{ L"OperatingSystemSKU", CIM_UINT32 },
+ { L"Organization", CIM_STRING|COL_FLAG_DYNAMIC },
{ L"OSArchitecture", CIM_STRING },
{ L"OSLanguage", CIM_UINT32 },
{ L"OSProductSuite", CIM_UINT32 },
@@ -677,6 +678,7 @@ struct record_operatingsystem
const WCHAR *manufacturer;
const WCHAR *name;
UINT32 operatingsystemsku;
+ const WCHAR *organization;
const WCHAR *osarchitecture;
UINT32 oslanguage;
UINT32 osproductsuite;
@@ -3515,6 +3517,34 @@ static WCHAR *get_locale(void)
if (ret) GetLocaleInfoW( LOCALE_SYSTEM_DEFAULT, LOCALE_ILANGUAGE, ret, 5 );
return ret;
}
+
+static WCHAR *get_reg_str( HKEY root, const WCHAR *path, const WCHAR *value )
+{
+ HKEY hkey = 0;
+ DWORD size, type;
+ WCHAR *ret = NULL;
+
+ if (!RegOpenKeyExW( root, path, 0, KEY_READ, &hkey ) &&
+ !RegQueryValueExW( hkey, value, NULL, &type, NULL, &size ) && type == REG_SZ &&
+ (ret = malloc( size + sizeof(WCHAR) )))
+ {
+ size += sizeof(WCHAR);
+ if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)ret, &size ))
+ {
+ free( ret );
+ ret = NULL;
+ }
+ }
+ if (hkey) RegCloseKey( hkey );
+ return ret;
+}
+
+static WCHAR *get_organization(void)
+{
+ return get_reg_str( HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion",
+ L"RegisteredOrganization" );
+}
+
static WCHAR *get_osbuildnumber( OSVERSIONINFOEXW *ver )
{
WCHAR *ret = malloc( 11 * sizeof(WCHAR) );
@@ -3574,27 +3604,6 @@ static WCHAR *get_osname( const WCHAR *caption )
return ret;
}
-static WCHAR *get_reg_str( HKEY root, const WCHAR *path, const WCHAR *value )
-{
- HKEY hkey = 0;
- DWORD size, type;
- WCHAR *ret = NULL;
-
- if (!RegOpenKeyExW( root, path, 0, KEY_READ, &hkey ) &&
- !RegQueryValueExW( hkey, value, NULL, &type, NULL, &size ) && type == REG_SZ &&
- (ret = malloc( size + sizeof(WCHAR) )))
- {
- size += sizeof(WCHAR);
- if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)ret, &size ))
- {
- free( ret );
- ret = NULL;
- }
- }
- if (hkey) RegCloseKey( hkey );
- return ret;
-}
-
static WCHAR *get_osserialnumber(void)
{
WCHAR *ret = get_reg_str( HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion", L"ProductId" );
@@ -3658,6 +3667,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct
rec->manufacturer = L"The Wine Project";
rec->name = get_osname( rec->caption );
rec->operatingsystemsku = get_operatingsystemsku();
+ rec->organization = get_organization();
rec->osarchitecture = get_osarchitecture();
rec->oslanguage = GetSystemDefaultLangID();
rec->osproductsuite = 2461140; /* Windows XP Professional */
diff --git a/dlls/wbemprox/tests/query.c b/dlls/wbemprox/tests/query.c
index 7cca770ffc7..b31506a12d6 100644
--- a/dlls/wbemprox/tests/query.c
+++ b/dlls/wbemprox/tests/query.c
@@ -1428,6 +1428,7 @@ static void test_Win32_OperatingSystem( IWbemServices *services )
check_property( obj, L"CSName", VT_BSTR, CIM_STRING );
check_property( obj, L"CurrentTimeZone", VT_I2, CIM_SINT16 );
check_property( obj, L"Manufacturer", VT_BSTR, CIM_STRING );
+ check_property( obj, L"Organization", VT_BSTR, CIM_STRING );
check_property( obj, L"OSType", VT_I4, CIM_UINT16 );
check_property( obj, L"ProductType", VT_I4, CIM_UINT32 );
check_property( obj, L"RegisteredUser", VT_BSTR, CIM_STRING );
--
2.30.2
April 28, 2022
[PATCH v2 2/7] wbemprox: Add RegisterdUser property to Win32_Operatingsystem.
by Hans Leidekker
From: Louis Lenders <xerox.xerox2000x(a)gmail.com>
v2: Use a shared helper to get the registry value.
Wine-bug: https://bugs.winehq.org/show_bug.cgi?id=52887
Signed-off-by: Louis Lenders <xerox.xerox2000x(a)gmail.com>
Signed-off-by: Hans Leidekker <hans(a)codeweavers.com>
---
dlls/wbemprox/builtin.c | 27 ++++++++++++++++++++++-----
dlls/wbemprox/tests/query.c | 1 +
2 files changed, 23 insertions(+), 5 deletions(-)
diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c
index 93b26c23dd3..5c53aae8d3b 100644
--- a/dlls/wbemprox/builtin.c
+++ b/dlls/wbemprox/builtin.c
@@ -250,6 +250,7 @@ static const struct column col_operatingsystem[] =
{ L"OSType", CIM_UINT16 },
{ L"Primary", CIM_BOOLEAN },
{ L"ProductType", CIM_UINT32 },
+ { L"RegisteredUser", CIM_STRING|COL_FLAG_DYNAMIC },
{ L"SerialNumber", CIM_STRING|COL_FLAG_DYNAMIC },
{ L"ServicePackMajorVersion", CIM_UINT16 },
{ L"ServicePackMinorVersion", CIM_UINT16 },
@@ -682,6 +683,7 @@ struct record_operatingsystem
UINT16 ostype;
int primary;
UINT32 producttype;
+ const WCHAR *registereduser;
const WCHAR *serialnumber;
UINT16 servicepackmajor;
UINT16 servicepackminor;
@@ -3571,27 +3573,35 @@ static WCHAR *get_osname( const WCHAR *caption )
memcpy( ret + len, partitionW, sizeof(partitionW) );
return ret;
}
-static WCHAR *get_osserialnumber(void)
+
+static WCHAR *get_reg_str( HKEY root, const WCHAR *path, const WCHAR *value )
{
HKEY hkey = 0;
DWORD size, type;
WCHAR *ret = NULL;
- if (!RegOpenKeyExW( HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion", 0, KEY_READ, &hkey ) &&
- !RegQueryValueExW( hkey, L"ProductId", NULL, &type, NULL, &size ) && type == REG_SZ &&
+ if (!RegOpenKeyExW( root, path, 0, KEY_READ, &hkey ) &&
+ !RegQueryValueExW( hkey, value, NULL, &type, NULL, &size ) && type == REG_SZ &&
(ret = malloc( size + sizeof(WCHAR) )))
{
size += sizeof(WCHAR);
- if (RegQueryValueExW( hkey, L"ProductId", NULL, NULL, (BYTE *)ret, &size ))
+ if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)ret, &size ))
{
free( ret );
ret = NULL;
}
}
if (hkey) RegCloseKey( hkey );
- if (!ret) return wcsdup( L"12345-OEM-1234567-12345" );
return ret;
}
+
+static WCHAR *get_osserialnumber(void)
+{
+ WCHAR *ret = get_reg_str( HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion", L"ProductId" );
+ if (!ret) ret = wcsdup( L"12345-OEM-1234567-12345" );
+ return ret;
+}
+
static WCHAR *get_osversion( OSVERSIONINFOEXW *ver )
{
WCHAR *ret = malloc( 33 * sizeof(WCHAR) );
@@ -3604,6 +3614,12 @@ static DWORD get_operatingsystemsku(void)
GetProductInfo( 6, 0, 0, 0, &ret );
return ret;
}
+
+static WCHAR *get_registereduser(void)
+{
+ return get_reg_str( HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion", L"RegisteredOwner" );
+}
+
static INT16 get_currenttimezone(void)
{
TIME_ZONE_INFORMATION info;
@@ -3648,6 +3664,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct
rec->ostype = 18; /* WINNT */
rec->primary = -1;
rec->producttype = 1;
+ rec->registereduser = get_registereduser();
rec->serialnumber = get_osserialnumber();
rec->servicepackmajor = ver.wServicePackMajor;
rec->servicepackminor = ver.wServicePackMinor;
diff --git a/dlls/wbemprox/tests/query.c b/dlls/wbemprox/tests/query.c
index 865d1b6136a..7cca770ffc7 100644
--- a/dlls/wbemprox/tests/query.c
+++ b/dlls/wbemprox/tests/query.c
@@ -1430,6 +1430,7 @@ static void test_Win32_OperatingSystem( IWbemServices *services )
check_property( obj, L"Manufacturer", VT_BSTR, CIM_STRING );
check_property( obj, L"OSType", VT_I4, CIM_UINT16 );
check_property( obj, L"ProductType", VT_I4, CIM_UINT32 );
+ check_property( obj, L"RegisteredUser", VT_BSTR, CIM_STRING );
check_property( obj, L"ServicePackMajorVersion", VT_I4, CIM_UINT16 );
check_property( obj, L"ServicePackMinorVersion", VT_I4, CIM_UINT16 );
check_property( obj, L"SuiteMask", VT_I4, CIM_UINT32 );
--
2.30.2
April 28, 2022
[PATCH v2 1/7] wbemprox: Add BuildType property to Win32_OperatingSystem.
by Hans Leidekker
From: Louis Lenders <xerox.xerox2000x(a)gmail.com>
Wine-bug: https://bugs.winehq.org/show_bug.cgi?id=52887
Signed-off-by: Louis Lenders <xerox.xerox2000x(a)gmail.com>
Signed-off-by: Hans Leidekker <hans(a)codeweavers.com>
---
dlls/wbemprox/builtin.c | 3 +++
dlls/wbemprox/tests/query.c | 1 +
2 files changed, 4 insertions(+)
diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c
index 69d74d756f8..93b26c23dd3 100644
--- a/dlls/wbemprox/builtin.c
+++ b/dlls/wbemprox/builtin.c
@@ -229,6 +229,7 @@ static const struct column col_networkadapterconfig[] =
static const struct column col_operatingsystem[] =
{
{ L"BuildNumber", CIM_STRING|COL_FLAG_DYNAMIC },
+ { L"BuildType", CIM_STRING },
{ L"Caption", CIM_STRING|COL_FLAG_DYNAMIC },
{ L"CodeSet", CIM_STRING|COL_FLAG_DYNAMIC },
{ L"CountryCode", CIM_STRING|COL_FLAG_DYNAMIC },
@@ -660,6 +661,7 @@ struct record_networkadapterconfig
struct record_operatingsystem
{
const WCHAR *buildnumber;
+ const WCHAR *buildtype;
const WCHAR *caption;
const WCHAR *codeset;
const WCHAR *countrycode;
@@ -3625,6 +3627,7 @@ static enum fill_status fill_operatingsystem( struct table *table, const struct
rec = (struct record_operatingsystem *)table->data;
rec->buildnumber = get_osbuildnumber( &ver );
+ rec->buildtype = L"Wine build";
rec->caption = get_oscaption( &ver );
rec->codeset = get_codeset();
rec->countrycode = get_countrycode();
diff --git a/dlls/wbemprox/tests/query.c b/dlls/wbemprox/tests/query.c
index e0fe78b7fd1..865d1b6136a 100644
--- a/dlls/wbemprox/tests/query.c
+++ b/dlls/wbemprox/tests/query.c
@@ -1389,6 +1389,7 @@ static void test_Win32_OperatingSystem( IWbemServices *services )
ok( hr == S_OK, "got %#lx\n", hr );
check_property( obj, L"BuildNumber", VT_BSTR, CIM_STRING );
+ check_property( obj, L"BuildType", VT_BSTR, CIM_STRING );
check_property( obj, L"Caption", VT_BSTR, CIM_STRING );
type = 0xdeadbeef;
--
2.30.2
April 28, 2022
[PATCH v2 8/8] d2d1: Implement LoadVertexShader().
by Ziqing Hui
Signed-off-by: Ziqing Hui <zhui(a)codeweavers.com>
---
dlls/d2d1/d2d1_private.h | 10 ++++++++++
dlls/d2d1/effect.c | 41 ++++++++++++++++++++++++++++++++++++++--
2 files changed, 49 insertions(+), 2 deletions(-)
diff --git a/dlls/d2d1/d2d1_private.h b/dlls/d2d1/d2d1_private.h
index 8b7b0b7a3d5..cc275de47ec 100644
--- a/dlls/d2d1/d2d1_private.h
+++ b/dlls/d2d1/d2d1_private.h
@@ -568,6 +568,12 @@ struct d2d_device
void d2d_device_init(struct d2d_device *device, ID2D1Factory1 *factory, IDXGIDevice *dxgi_device) DECLSPEC_HIDDEN;
+struct d2d_shader
+{
+ const GUID *id;
+ void *shader;
+};
+
struct d2d_effect_context
{
ID2D1EffectContext ID2D1EffectContext_iface;
@@ -576,6 +582,10 @@ struct d2d_effect_context
ID2D1Factory *factory;
ID2D1DeviceContext *device_context;
ID3D11Device1 *d3d_device;
+
+ 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 60597919c91..fda1a64ea70 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((IUnknown *)effect_context->shaders[i].shader);
+ }
+ heap_free(effect_context->shaders);
+
ID3D11Device1_Release(effect_context->d3d_device);
ID2D1DeviceContext_Release(effect_context->device_context);
ID2D1Factory_Release(effect_context->factory);
@@ -180,10 +189,38 @@ 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->d3d_device,
+ buffer, buffer_size, NULL, &vertex_shader)))
+ {
+ WARN("Failed to create vertex shader, hr %#lx.\n", hr);
+ return hr;
+ }
+
+ 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;
+ }
+ }
+
+ shader = &effect_context->shaders[effect_context->shader_count - 1];
+ shader->id = shader_id;
+ shader->shader = vertex_shader;
+
+ return S_OK;
}
static HRESULT STDMETHODCALLTYPE d2d_effect_context_LoadComputeShader(ID2D1EffectContext *iface,
--
2.25.1
April 28, 2022
[PATCH v2 7/8] d2d1: Add a d3d device reference to effect context.
by Ziqing Hui
Signed-off-by: Ziqing Hui <zhui(a)codeweavers.com>
---
dlls/d2d1/d2d1_private.h | 3 ++-
dlls/d2d1/device.c | 2 +-
dlls/d2d1/effect.c | 8 +++++---
3 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/dlls/d2d1/d2d1_private.h b/dlls/d2d1/d2d1_private.h
index 8f8ffc5fd0e..8b7b0b7a3d5 100644
--- a/dlls/d2d1/d2d1_private.h
+++ b/dlls/d2d1/d2d1_private.h
@@ -575,10 +575,11 @@ struct d2d_effect_context
ID2D1Factory *factory;
ID2D1DeviceContext *device_context;
+ ID3D11Device1 *d3d_device;
};
void d2d_effect_context_init(struct d2d_effect_context *effect_context,
- ID2D1DeviceContext *device_context) DECLSPEC_HIDDEN;
+ struct d2d_device_context *device_context) DECLSPEC_HIDDEN;
struct d2d_effect_info
{
diff --git a/dlls/d2d1/device.c b/dlls/d2d1/device.c
index 03195c8c557..80badcddaac 100644
--- a/dlls/d2d1/device.c
+++ b/dlls/d2d1/device.c
@@ -1897,7 +1897,7 @@ static HRESULT STDMETHODCALLTYPE d2d_device_context_CreateEffect(ID2D1DeviceCont
if (!(effect_context = heap_alloc_zero(sizeof(*effect_context))))
return E_OUTOFMEMORY;
- d2d_effect_context_init(effect_context, iface);
+ d2d_effect_context_init(effect_context, context);
hr = ID2D1EffectContext_CreateEffect(&effect_context->ID2D1EffectContext_iface, effect_id, effect);
diff --git a/dlls/d2d1/effect.c b/dlls/d2d1/effect.c
index 566a4cf1523..60597919c91 100644
--- a/dlls/d2d1/effect.c
+++ b/dlls/d2d1/effect.c
@@ -37,6 +37,7 @@ static inline struct d2d_effect_context *impl_from_ID2D1EffectContext(ID2D1Effec
static void d2d_effect_context_cleanup(struct d2d_effect_context *effect_context)
{
+ ID3D11Device1_Release(effect_context->d3d_device);
ID2D1DeviceContext_Release(effect_context->device_context);
ID2D1Factory_Release(effect_context->factory);
}
@@ -307,12 +308,13 @@ static const ID2D1EffectContextVtbl d2d_effect_context_vtbl =
d2d_effect_context_IsBufferPrecisionSupported,
};
-void d2d_effect_context_init(struct d2d_effect_context *effect_context, ID2D1DeviceContext *device_context)
+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;
- ID2D1DeviceContext_GetFactory(device_context, &effect_context->factory);
- ID2D1DeviceContext_AddRef(effect_context->device_context = device_context);
+ ID2D1Factory_AddRef(effect_context->factory = device_context->factory);
+ ID2D1DeviceContext_AddRef(effect_context->device_context = &device_context->ID2D1DeviceContext_iface);
+ ID3D11Device1_AddRef(effect_context->d3d_device = device_context->d3d_device);
}
static inline struct d2d_effect *impl_from_ID2D1Effect(ID2D1Effect *iface)
--
2.25.1
April 28, 2022
[PATCH v2 6/8] d2d1: Introduce d2d_effect_context_cleanup().
by Ziqing Hui
Signed-off-by: Ziqing Hui <zhui(a)codeweavers.com>
---
dlls/d2d1/effect.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/dlls/d2d1/effect.c b/dlls/d2d1/effect.c
index b14c2fae373..566a4cf1523 100644
--- a/dlls/d2d1/effect.c
+++ b/dlls/d2d1/effect.c
@@ -35,6 +35,12 @@ static inline struct d2d_effect_context *impl_from_ID2D1EffectContext(ID2D1Effec
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);
+ ID2D1Factory_Release(effect_context->factory);
+}
+
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);
@@ -72,8 +78,7 @@ static ULONG STDMETHODCALLTYPE d2d_effect_context_Release(ID2D1EffectContext *if
if (!refcount)
{
- ID2D1DeviceContext_Release(effect_context->device_context);
- ID2D1Factory_Release(effect_context->factory);
+ d2d_effect_context_cleanup(effect_context);
heap_free(effect_context);
}
--
2.25.1
April 28, 2022
[PATCH v2 5/8] d2d1: Move CreateEffect() implementation to effect context.
by Ziqing Hui
Signed-off-by: Ziqing Hui <zhui(a)codeweavers.com>
---
We hold an effect context reference in effect.
See https://docs.microsoft.com/en-us/windows/win32/api/d2d1effectauthor/nn-d2d1…
"Each call to ID2D1EffectImpl::Initialize will be provided a different ID2D1EffectContext interface."
This means that device context and effect context are not one-on-one. And each effect has an effect context.
"This interface tracks resource allocations for the effect. When the effect is released,
the corresponding allocations will also be released."
dlls/d2d1/d2d1_private.h | 4 +++-
dlls/d2d1/device.c | 18 ++++++------------
dlls/d2d1/effect.c | 27 +++++++++++++++++++++++----
3 files changed, 32 insertions(+), 17 deletions(-)
diff --git a/dlls/d2d1/d2d1_private.h b/dlls/d2d1/d2d1_private.h
index eb2f71f7c03..8f8ffc5fd0e 100644
--- a/dlls/d2d1/d2d1_private.h
+++ b/dlls/d2d1/d2d1_private.h
@@ -597,12 +597,14 @@ struct d2d_effect
const struct d2d_effect_info *info;
ID2D1Factory *factory;
+ ID2D1EffectContext *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)
{
diff --git a/dlls/d2d1/device.c b/dlls/d2d1/device.c
index 13c99458cfa..03195c8c557 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, iface);
- 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 bfb2e9d16d9..b14c2fae373 100644
--- a/dlls/d2d1/effect.c
+++ b/dlls/d2d1/effect.c
@@ -92,9 +92,26 @@ static void STDMETHODCALLTYPE d2d_effect_context_GetDpi(ID2D1EffectContext *ifac
static HRESULT STDMETHODCALLTYPE d2d_effect_context_CreateEffect(ID2D1EffectContext *iface,
REFCLSID clsid, ID2D1Effect **effect)
{
- FIXME("iface %p, clsid %s, effect %p stub!\n", iface, debugstr_guid(clsid), effect);
+ struct d2d_effect_context *effect_context = impl_from_ID2D1EffectContext(iface);
+ struct d2d_effect *object;
+ HRESULT hr;
- return E_NOTIMPL;
+ 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,
@@ -308,6 +325,7 @@ static void d2d_effect_cleanup(struct d2d_effect *effect)
ID2D1Image_Release(effect->inputs[i]);
}
heap_free(effect->inputs);
+ ID2D1EffectContext_Release(effect->effect_context);
ID2D1Factory_Release(effect->factory);
}
@@ -635,7 +653,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;
@@ -649,7 +667,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);
+ ID2D1Factory_AddRef(effect->factory = effect_context->factory);
+ ID2D1EffectContext_AddRef(effect->effect_context = &effect_context->ID2D1EffectContext_iface);
return S_OK;
}
}
--
2.25.1
April 28, 2022