Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
W
wine-winehq
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
wine
wine-winehq
Commits
37de2cb4
Commit
37de2cb4
authored
Jan 07, 2023
by
Rémi Bernon
Committed by
Alexandre Julliard
Feb 07, 2023
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
win32u: Use KBDTABLES for NtUserMapVirtualKeyEx VSC / VK mapping.
parent
48c71b31
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
111 additions
and
70 deletions
+111
-70
input.c
dlls/win32u/input.c
+111
-70
No files found.
dlls/win32u/input.c
View file @
37de2cb4
...
...
@@ -207,15 +207,115 @@ static const VSC_LPWSTR key_names_ext[] =
{
0
},
};
static
const
USHORT
vsc_to_vk
[]
=
{
T00
,
T01
,
T02
,
T03
,
T04
,
T05
,
T06
,
T07
,
T08
,
T09
,
T0A
,
T0B
,
T0C
,
T0D
,
T0E
,
T0F
,
T10
,
T11
,
T12
,
T13
,
T14
,
T15
,
T16
,
T17
,
T18
,
T19
,
T1A
,
T1B
,
T1C
,
T1D
,
T1E
,
T1F
,
T20
,
T21
,
T22
,
T23
,
T24
,
T25
,
T26
,
T27
,
T28
,
T29
,
T2A
,
T2B
,
T2C
,
T2D
,
T2E
,
T2F
,
T30
,
T31
,
T32
,
T33
,
T34
,
T35
,
T36
|
KBDEXT
,
T37
|
KBDMULTIVK
,
T38
,
T39
,
T3A
,
T3B
,
T3C
,
T3D
,
T3E
,
T3F
,
T40
,
T41
,
T42
,
T43
,
T44
,
T45
|
KBDEXT
|
KBDMULTIVK
,
T46
|
KBDMULTIVK
,
T47
|
KBDNUMPAD
|
KBDSPECIAL
,
T48
|
KBDNUMPAD
|
KBDSPECIAL
,
T49
|
KBDNUMPAD
|
KBDSPECIAL
,
T4A
,
T4B
|
KBDNUMPAD
|
KBDSPECIAL
,
T4C
|
KBDNUMPAD
|
KBDSPECIAL
,
T4D
|
KBDNUMPAD
|
KBDSPECIAL
,
T4E
,
T4F
|
KBDNUMPAD
|
KBDSPECIAL
,
T50
|
KBDNUMPAD
|
KBDSPECIAL
,
T51
|
KBDNUMPAD
|
KBDSPECIAL
,
T52
|
KBDNUMPAD
|
KBDSPECIAL
,
T53
|
KBDNUMPAD
|
KBDSPECIAL
,
T54
,
T55
,
T56
,
T57
,
T58
,
T59
,
T5A
,
T5B
,
T5C
,
T5D
,
T5E
,
T5F
,
T60
,
T61
,
T62
,
T63
,
T64
,
T65
,
T66
,
T67
,
T68
,
T69
,
T6A
,
T6B
,
T6C
,
T6D
,
T6E
,
T6F
,
T70
,
T71
,
T72
,
T73
,
T74
,
T75
,
T76
,
T77
,
T78
,
T79
,
T7A
,
T7B
,
T7C
,
T7D
,
T7E
};
static
const
VSC_VK
vsc_to_vk_e0
[]
=
{
{
0x10
,
X10
|
KBDEXT
},
{
0x19
,
X19
|
KBDEXT
},
{
0x1d
,
X1D
|
KBDEXT
},
{
0x20
,
X20
|
KBDEXT
},
{
0x21
,
X21
|
KBDEXT
},
{
0x22
,
X22
|
KBDEXT
},
{
0x24
,
X24
|
KBDEXT
},
{
0x2e
,
X2E
|
KBDEXT
},
{
0x30
,
X30
|
KBDEXT
},
{
0x32
,
X32
|
KBDEXT
},
{
0x35
,
X35
|
KBDEXT
},
{
0x37
,
X37
|
KBDEXT
},
{
0x38
,
X38
|
KBDEXT
},
{
0x47
,
X47
|
KBDEXT
},
{
0x48
,
X48
|
KBDEXT
},
{
0x49
,
X49
|
KBDEXT
},
{
0x4b
,
X4B
|
KBDEXT
},
{
0x4d
,
X4D
|
KBDEXT
},
{
0x4f
,
X4F
|
KBDEXT
},
{
0x50
,
X50
|
KBDEXT
},
{
0x51
,
X51
|
KBDEXT
},
{
0x52
,
X52
|
KBDEXT
},
{
0x53
,
X53
|
KBDEXT
},
{
0x5b
,
X5B
|
KBDEXT
},
{
0x5c
,
X5C
|
KBDEXT
},
{
0x5d
,
X5D
|
KBDEXT
},
{
0x5f
,
X5F
|
KBDEXT
},
{
0x65
,
X65
|
KBDEXT
},
{
0x66
,
X66
|
KBDEXT
},
{
0x67
,
X67
|
KBDEXT
},
{
0x68
,
X68
|
KBDEXT
},
{
0x69
,
X69
|
KBDEXT
},
{
0x6a
,
X6A
|
KBDEXT
},
{
0x6b
,
X6B
|
KBDEXT
},
{
0x6c
,
X6C
|
KBDEXT
},
{
0x6d
,
X6D
|
KBDEXT
},
{
0x1c
,
X1C
|
KBDEXT
},
{
0x46
,
X46
|
KBDEXT
},
{
0
},
};
static
const
VSC_VK
vsc_to_vk_e1
[]
=
{
{
0x1d
,
Y1D
},
{
0
},
};
static
const
KBDTABLES
kbdus_tables
=
{
.
pKeyNames
=
(
VSC_LPWSTR
*
)
key_names
,
.
pKeyNamesExt
=
(
VSC_LPWSTR
*
)
key_names_ext
,
.
pusVSCtoVK
=
(
USHORT
*
)
vsc_to_vk
,
.
bMaxVSCtoVK
=
sizeof
(
vsc_to_vk
)
/
sizeof
(
vsc_to_vk
[
0
]),
.
pVSCtoVK_E0
=
(
VSC_VK
*
)
vsc_to_vk_e0
,
.
pVSCtoVK_E1
=
(
VSC_VK
*
)
vsc_to_vk_e1
,
};
LONG
global_key_state_counter
=
0
;
static
void
kbd_tables_init_vsc2vk
(
const
KBDTABLES
*
tables
,
BYTE
vsc2vk
[
0x300
]
)
{
const
VSC_VK
*
entry
;
WORD
vsc
;
memset
(
vsc2vk
,
0
,
0x300
);
for
(
vsc
=
0
;
tables
->
pusVSCtoVK
&&
vsc
<=
tables
->
bMaxVSCtoVK
;
++
vsc
)
{
if
(
tables
->
pusVSCtoVK
[
vsc
]
==
VK__none_
)
continue
;
vsc2vk
[
vsc
]
=
(
BYTE
)
tables
->
pusVSCtoVK
[
vsc
];
}
for
(
entry
=
tables
->
pVSCtoVK_E0
;
entry
&&
entry
->
Vsc
;
entry
++
)
{
if
(
entry
->
Vk
==
VK__none_
)
continue
;
vsc2vk
[
entry
->
Vsc
+
0x100
]
=
(
BYTE
)
entry
->
Vk
;
}
for
(
entry
=
tables
->
pVSCtoVK_E1
;
entry
&&
entry
->
Vsc
;
entry
++
)
{
if
(
entry
->
Vk
==
VK__none_
)
continue
;
vsc2vk
[
entry
->
Vsc
+
0x200
]
=
(
BYTE
)
entry
->
Vk
;
}
}
/**********************************************************************
* NtUserAttachThreadInput (win32u.@)
*/
...
...
@@ -716,61 +816,6 @@ WORD WINAPI NtUserVkKeyScanEx( WCHAR chr, HKL layout )
return
ret
;
}
/* English keyboard layout (0x0409) */
static
const
UINT
kbd_en_vsc2vk
[]
=
{
0x00
,
0x1b
,
0x31
,
0x32
,
0x33
,
0x34
,
0x35
,
0x36
,
0x37
,
0x38
,
0x39
,
0x30
,
0xbd
,
0xbb
,
0x08
,
0x09
,
0x51
,
0x57
,
0x45
,
0x52
,
0x54
,
0x59
,
0x55
,
0x49
,
0x4f
,
0x50
,
0xdb
,
0xdd
,
0x0d
,
0xa2
,
0x41
,
0x53
,
0x44
,
0x46
,
0x47
,
0x48
,
0x4a
,
0x4b
,
0x4c
,
0xba
,
0xde
,
0xc0
,
0xa0
,
0xdc
,
0x5a
,
0x58
,
0x43
,
0x56
,
0x42
,
0x4e
,
0x4d
,
0xbc
,
0xbe
,
0xbf
,
0xa1
,
0x6a
,
0xa4
,
0x20
,
0x14
,
0x70
,
0x71
,
0x72
,
0x73
,
0x74
,
0x75
,
0x76
,
0x77
,
0x78
,
0x79
,
0x90
,
0x91
,
0x24
,
0x26
,
0x21
,
0x6d
,
0x25
,
0x0c
,
0x27
,
0x6b
,
0x23
,
0x28
,
0x22
,
0x2d
,
0x2e
,
0x2c
,
0x00
,
0xe2
,
0x7a
,
0x7b
,
0x0c
,
0xee
,
0xf1
,
0xea
,
0xf9
,
0xf5
,
0xf3
,
0x00
,
0x00
,
0xfb
,
0x2f
,
0x7c
,
0x7d
,
0x7e
,
0x7f
,
0x80
,
0x81
,
0x82
,
0x83
,
0x84
,
0x85
,
0x86
,
0xed
,
0x00
,
0xe9
,
0x00
,
0xc1
,
0x00
,
0x00
,
0x87
,
0x00
,
0x00
,
0x00
,
0x00
,
0xeb
,
0x09
,
0x00
,
0xc2
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
/* 0xe000 */
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0xb1
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0xb0
,
0x00
,
0x00
,
0x0d
,
0xa3
,
0x00
,
0x00
,
0xad
,
0xb7
,
0xb3
,
0x00
,
0xb2
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0xae
,
0x00
,
0xaf
,
0x00
,
0xac
,
0x00
,
0x00
,
0x6f
,
0x00
,
0x2c
,
0xa5
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x03
,
0x24
,
0x26
,
0x21
,
0x00
,
0x25
,
0x00
,
0x27
,
0x00
,
0x23
,
0x28
,
0x22
,
0x2d
,
0x2e
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x5b
,
0x5c
,
0x5d
,
0x00
,
0x5f
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0xaa
,
0xab
,
0xa8
,
0xa9
,
0xa7
,
0xa6
,
0xb6
,
0xb4
,
0xb5
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
/* 0xe100 */
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x13
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
};
static
const
UINT
kbd_en_vk2char
[]
=
{
0x00
,
0x00
,
0x00
,
0x03
,
0x00
,
0x00
,
0x00
,
0x00
,
0x08
,
0x09
,
0x00
,
0x00
,
0x00
,
0x0d
,
0x00
,
0x00
,
...
...
@@ -796,18 +841,18 @@ static const UINT kbd_en_vk2char[] =
*/
UINT
WINAPI
NtUserMapVirtualKeyEx
(
UINT
code
,
UINT
type
,
HKL
layout
)
{
const
UINT
*
vsc2vk
,
*
vk2char
;
UINT
vsc2vk_size
,
vk2char_size
;
const
KBDTABLES
*
kbd_tables
=
&
kbdus_tables
;
const
UINT
*
vk2char
;
BYTE
vsc2vk
[
0x300
];
UINT
vk2char_size
;
UINT
ret
;
TRACE_
(
keyboard
)(
"code %u, type %u, layout %p.
\n
"
,
code
,
type
,
layout
);
if
((
ret
=
user_driver
->
pMapVirtualKeyEx
(
code
,
type
,
layout
))
!=
-
1
)
return
ret
;
/* FIXME: English keyboard layout specific */
kbd_tables_init_vsc2vk
(
kbd_tables
,
vsc2vk
);
vsc2vk
=
kbd_en_vsc2vk
;
vsc2vk_size
=
ARRAYSIZE
(
kbd_en_vsc2vk
);
vk2char
=
kbd_en_vk2char
;
vk2char_size
=
ARRAYSIZE
(
kbd_en_vk2char
);
...
...
@@ -833,8 +878,8 @@ UINT WINAPI NtUserMapVirtualKeyEx( UINT code, UINT type, HKL layout )
case
VK_DECIMAL
:
code
=
VK_DELETE
;
break
;
}
for
(
ret
=
0
;
ret
<
vsc2vk_size
;
++
ret
)
if
(
vsc2vk
[
ret
]
==
code
)
break
;
if
(
ret
>=
vsc2vk_size
)
ret
=
0
;
for
(
ret
=
0
;
ret
<
ARRAY_SIZE
(
vsc2vk
)
;
++
ret
)
if
(
vsc2vk
[
ret
]
==
code
)
break
;
if
(
ret
>=
ARRAY_SIZE
(
vsc2vk
)
)
ret
=
0
;
if
(
type
==
MAPVK_VK_TO_VSC
)
{
...
...
@@ -846,7 +891,7 @@ UINT WINAPI NtUserMapVirtualKeyEx( UINT code, UINT type, HKL layout )
case
MAPVK_VSC_TO_VK
:
case
MAPVK_VSC_TO_VK_EX
:
if
(
code
&
0xe000
)
code
-=
0xdf00
;
if
(
code
>=
vsc2vk_size
)
ret
=
0
;
if
(
code
>=
ARRAY_SIZE
(
vsc2vk
)
)
ret
=
0
;
else
ret
=
vsc2vk
[
code
];
if
(
type
==
MAPVK_VSC_TO_VK
)
...
...
@@ -880,18 +925,14 @@ INT WINAPI NtUserGetKeyNameText( LONG lparam, WCHAR *buffer, INT size )
INT
code
=
((
lparam
>>
16
)
&
0x1ff
),
vkey
,
len
;
const
KBDTABLES
*
kbd_tables
=
&
kbdus_tables
;
VSC_LPWSTR
*
key_name
;
const
UINT
*
vsc2vk
;
UINT
vsc2vk_size
;
BYTE
vsc2vk
[
0x300
];
TRACE_
(
keyboard
)(
"lparam %#x, buffer %p, size %d.
\n
"
,
(
int
)
lparam
,
buffer
,
size
);
if
(
!
buffer
||
!
size
)
return
0
;
if
((
len
=
user_driver
->
pGetKeyNameText
(
lparam
,
buffer
,
size
))
>=
0
)
return
len
;
/* FIXME: English keyboard layout specific */
vsc2vk
=
kbd_en_vsc2vk
;
vsc2vk_size
=
ARRAYSIZE
(
kbd_en_vsc2vk
);
kbd_tables_init_vsc2vk
(
kbd_tables
,
vsc2vk
);
if
(
lparam
&
0x2000000
)
{
...
...
@@ -900,7 +941,7 @@ INT WINAPI NtUserGetKeyNameText( LONG lparam, WCHAR *buffer, INT size )
case
VK_RSHIFT
:
case
VK_RCONTROL
:
case
VK_RMENU
:
for
(
code
=
0
;
code
<
vsc2vk_size
;
++
code
)
for
(
code
=
0
;
code
<
ARRAY_SIZE
(
vsc2vk
)
;
++
code
)
if
(
vsc2vk
[
code
]
==
(
vkey
-
1
))
break
;
break
;
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment