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
a21672eb
Commit
a21672eb
authored
Oct 02, 2001
by
Alexandre Julliard
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Merged mouse buttons states into the key state array.
Fixed confusion between queue state and async state.
parent
3f4f7fa7
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
143 additions
and
147 deletions
+143
-147
message.c
dlls/user/message.c
+0
-14
input.h
include/input.h
+0
-3
input.c
windows/input.c
+16
-116
message.c
windows/message.c
+127
-14
No files found.
dlls/user/message.c
View file @
a21672eb
...
...
@@ -1860,20 +1860,6 @@ BOOL WINAPI PeekMessageW( MSG *msg_out, HWND hwnd, UINT first, UINT last, UINT f
queue
->
GetMessagePosVal
=
MAKELONG
(
msg
.
pt
.
x
,
msg
.
pt
.
y
);
}
/* We got a message */
if
(
flags
&
PM_REMOVE
)
{
if
(
msg
.
message
==
WM_KEYDOWN
||
msg
.
message
==
WM_SYSKEYDOWN
)
{
BYTE
*
p
=
&
QueueKeyStateTable
[
msg
.
wParam
&
0xff
];
if
(
!
(
*
p
&
0x80
))
*
p
^=
0x01
;
*
p
|=
0x80
;
}
else
if
(
msg
.
message
==
WM_KEYUP
||
msg
.
message
==
WM_SYSKEYUP
)
QueueKeyStateTable
[
msg
.
wParam
&
0xff
]
&=
~
0x80
;
}
HOOK_CallHooksW
(
WH_GETMESSAGE
,
HC_ACTION
,
flags
&
PM_REMOVE
,
(
LPARAM
)
&
msg
);
/* copy back our internal safe copy of message data to msg_out.
...
...
include/input.h
View file @
a21672eb
...
...
@@ -9,10 +9,7 @@
#include "windef.h"
extern
BOOL
MouseButtonsStates
[
3
];
extern
BOOL
AsyncMouseButtonsStates
[
3
];
extern
BYTE
InputKeyStateTable
[
256
];
extern
BYTE
QueueKeyStateTable
[
256
];
extern
BYTE
AsyncKeyStateTable
[
256
];
#endif
/* __WINE_INPUT_H */
...
...
windows/input.c
View file @
a21672eb
...
...
@@ -41,19 +41,16 @@ DEFAULT_DEBUG_CHANNEL(event);
static
BOOL
InputEnabled
=
TRUE
;
static
BOOL
SwappedButtons
;
BOOL
MouseButtonsStates
[
3
];
BOOL
AsyncMouseButtonsStates
[
3
];
BYTE
InputKeyStateTable
[
256
];
BYTE
QueueKeyStateTable
[
256
];
BYTE
AsyncKeyStateTable
[
256
];
/* Storage for the USER-maintained mouse positions */
static
DWORD
PosX
,
PosY
;
#define GET_KEYSTATE() \
((
MouseButtonsStates[SwappedButtons ? 2 : 0]
? MK_LBUTTON : 0) | \
(
MouseButtonsStates[1]
? MK_RBUTTON : 0) | \
(
MouseButtonsStates[SwappedButtons ? 0 : 2]
? MK_MBUTTON : 0) | \
((
InputKeyStateTable[SwappedButtons ? VK_RBUTTON : VK_LBUTTON] & 0x80
? MK_LBUTTON : 0) | \
(
InputKeyStateTable[SwappedButtons ? VK_LBUTTON : VK_LBUTTON] & 0x80
? MK_RBUTTON : 0) | \
(
InputKeyStateTable[VK_MBUTTON] & 0x80
? MK_MBUTTON : 0) | \
(InputKeyStateTable[VK_SHIFT] & 0x80 ? MK_SHIFT : 0) | \
(InputKeyStateTable[VK_CONTROL] & 0x80 ? MK_CONTROL : 0))
...
...
@@ -190,37 +187,40 @@ static void queue_mouse_event( const MOUSEINPUT *mi, WORD keystate )
}
if
(
mi
->
dwFlags
&
(
!
SwappedButtons
?
MOUSEEVENTF_LEFTDOWN
:
MOUSEEVENTF_RIGHTDOWN
))
{
MouseButtonsStates
[
0
]
=
AsyncMouseButtonsStates
[
0
]
=
TRUE
;
InputKeyStateTable
[
VK_LBUTTON
]
|=
0x80
;
AsyncKeyStateTable
[
VK_LBUTTON
]
|=
0x80
;
queue_raw_hardware_message
(
WM_LBUTTONDOWN
,
keystate
,
0
,
PosX
,
PosY
,
mi
->
time
,
mi
->
dwExtraInfo
);
}
if
(
mi
->
dwFlags
&
(
!
SwappedButtons
?
MOUSEEVENTF_LEFTUP
:
MOUSEEVENTF_RIGHTUP
))
{
MouseButtonsStates
[
0
]
=
FALSE
;
AsyncKeyStateTable
[
VK_LBUTTON
]
&=
~
0x80
;
queue_raw_hardware_message
(
WM_LBUTTONUP
,
keystate
,
0
,
PosX
,
PosY
,
mi
->
time
,
mi
->
dwExtraInfo
);
}
if
(
mi
->
dwFlags
&
(
!
SwappedButtons
?
MOUSEEVENTF_RIGHTDOWN
:
MOUSEEVENTF_LEFTDOWN
))
{
MouseButtonsStates
[
2
]
=
AsyncMouseButtonsStates
[
2
]
=
TRUE
;
InputKeyStateTable
[
VK_RBUTTON
]
|=
0x80
;
AsyncKeyStateTable
[
VK_RBUTTON
]
|=
0x80
;
queue_raw_hardware_message
(
WM_RBUTTONDOWN
,
keystate
,
0
,
PosX
,
PosY
,
mi
->
time
,
mi
->
dwExtraInfo
);
}
if
(
mi
->
dwFlags
&
(
!
SwappedButtons
?
MOUSEEVENTF_RIGHTUP
:
MOUSEEVENTF_LEFTUP
))
{
MouseButtonsStates
[
2
]
=
FALSE
;
AsyncKeyStateTable
[
VK_RBUTTON
]
&=
~
0x80
;
queue_raw_hardware_message
(
WM_RBUTTONUP
,
keystate
,
0
,
PosX
,
PosY
,
mi
->
time
,
mi
->
dwExtraInfo
);
}
if
(
mi
->
dwFlags
&
MOUSEEVENTF_MIDDLEDOWN
)
{
MouseButtonsStates
[
1
]
=
AsyncMouseButtonsStates
[
1
]
=
TRUE
;
InputKeyStateTable
[
VK_MBUTTON
]
|=
0x80
;
AsyncKeyStateTable
[
VK_MBUTTON
]
|=
0x80
;
queue_raw_hardware_message
(
WM_MBUTTONDOWN
,
keystate
,
0
,
PosX
,
PosY
,
mi
->
time
,
mi
->
dwExtraInfo
);
}
if
(
mi
->
dwFlags
&
MOUSEEVENTF_MIDDLEUP
)
{
MouseButtonsStates
[
1
]
=
FALSE
;
AsyncKeyStateTable
[
VK_MBUTTON
]
&=
~
0x80
;
queue_raw_hardware_message
(
WM_MBUTTONUP
,
keystate
,
0
,
PosX
,
PosY
,
mi
->
time
,
mi
->
dwExtraInfo
);
}
...
...
@@ -340,9 +340,9 @@ void WINAPI mouse_event( DWORD dwFlags, DWORD dx, DWORD dy,
if
(
keyState
!=
GET_KEYSTATE
())
{
/* We need to update the keystate with what X provides us */
MouseButtonsStates
[
SwappedButtons
?
2
:
0
]
=
(
keyState
&
MK_LBUTTON
?
TRUE
:
FALSE
);
MouseButtonsStates
[
SwappedButtons
?
0
:
2
]
=
(
keyState
&
MK_RBUTTON
?
TRUE
:
FALSE
);
MouseButtonsStates
[
1
]
=
(
keyState
&
MK_MBUTTON
?
TRUE
:
FALSE
);
InputKeyStateTable
[
SwappedButtons
?
VK_RBUTTON
:
VK_LBUTTON
]
=
(
keyState
&
MK_LBUTTON
?
0x80
:
0
);
InputKeyStateTable
[
SwappedButtons
?
VK_LBUTTON
:
VK_RBUTTON
]
=
(
keyState
&
MK_RBUTTON
?
0x80
:
0
);
InputKeyStateTable
[
VK_MBUTTON
]
=
(
keyState
&
MK_MBUTTON
?
0x80
:
0
);
InputKeyStateTable
[
VK_SHIFT
]
=
(
keyState
&
MK_SHIFT
?
0x80
:
0
);
InputKeyStateTable
[
VK_CONTROL
]
=
(
keyState
&
MK_CONTROL
?
0x80
:
0
);
}
...
...
@@ -563,84 +563,6 @@ HWND WINAPI GetCapture(void)
}
/**********************************************************************
* GetKeyState (USER.106)
*/
INT16
WINAPI
GetKeyState16
(
INT16
vkey
)
{
return
GetKeyState
(
vkey
);
}
/**********************************************************************
* GetKeyState (USER32.@)
*
* An application calls the GetKeyState function in response to a
* keyboard-input message. This function retrieves the state of the key
* at the time the input message was generated. (SDK 3.1 Vol 2. p 390)
*/
SHORT
WINAPI
GetKeyState
(
INT
vkey
)
{
INT
retval
;
switch
(
vkey
)
{
case
VK_LBUTTON
:
/* VK_LBUTTON is 1 */
retval
=
MouseButtonsStates
[
0
]
?
0x8000
:
0
;
break
;
case
VK_MBUTTON
:
/* VK_MBUTTON is 4 */
retval
=
MouseButtonsStates
[
1
]
?
0x8000
:
0
;
break
;
case
VK_RBUTTON
:
/* VK_RBUTTON is 2 */
retval
=
MouseButtonsStates
[
2
]
?
0x8000
:
0
;
break
;
default
:
if
(
vkey
>=
'a'
&&
vkey
<=
'z'
)
vkey
+=
'A'
-
'a'
;
retval
=
(
(
WORD
)(
QueueKeyStateTable
[
vkey
]
&
0x80
)
<<
8
)
|
(
WORD
)(
QueueKeyStateTable
[
vkey
]
&
0x01
);
}
/* TRACE(key, "(0x%x) -> %x\n", vkey, retval); */
return
retval
;
}
/**********************************************************************
* GetKeyboardState (USER.222)
* GetKeyboardState (USER32.@)
*
* An application calls the GetKeyboardState function in response to a
* keyboard-input message. This function retrieves the state of the keyboard
* at the time the input message was generated. (SDK 3.1 Vol 2. p 387)
*/
BOOL
WINAPI
GetKeyboardState
(
LPBYTE
lpKeyState
)
{
TRACE_
(
key
)(
"(%p)
\n
"
,
lpKeyState
);
if
(
lpKeyState
!=
NULL
)
{
QueueKeyStateTable
[
VK_LBUTTON
]
=
MouseButtonsStates
[
0
]
?
0x80
:
0
;
QueueKeyStateTable
[
VK_MBUTTON
]
=
MouseButtonsStates
[
1
]
?
0x80
:
0
;
QueueKeyStateTable
[
VK_RBUTTON
]
=
MouseButtonsStates
[
2
]
?
0x80
:
0
;
memcpy
(
lpKeyState
,
QueueKeyStateTable
,
256
);
}
return
TRUE
;
}
/**********************************************************************
* SetKeyboardState (USER.223)
* SetKeyboardState (USER32.@)
*/
BOOL
WINAPI
SetKeyboardState
(
LPBYTE
lpKeyState
)
{
TRACE_
(
key
)(
"(%p)
\n
"
,
lpKeyState
);
if
(
lpKeyState
!=
NULL
)
{
memcpy
(
QueueKeyStateTable
,
lpKeyState
,
256
);
MouseButtonsStates
[
0
]
=
(
QueueKeyStateTable
[
VK_LBUTTON
]
!=
0
);
MouseButtonsStates
[
1
]
=
(
QueueKeyStateTable
[
VK_MBUTTON
]
!=
0
);
MouseButtonsStates
[
2
]
=
(
QueueKeyStateTable
[
VK_RBUTTON
]
!=
0
);
}
return
TRUE
;
}
/**********************************************************************
* GetAsyncKeyState (USER32.@)
*
* Determine if a key is or was pressed. retval has high-order
...
...
@@ -655,31 +577,9 @@ BOOL WINAPI SetKeyboardState(LPBYTE lpKeyState)
*/
WORD
WINAPI
GetAsyncKeyState
(
INT
nKey
)
{
WORD
retval
;
switch
(
nKey
)
{
case
VK_LBUTTON
:
retval
=
(
AsyncMouseButtonsStates
[
0
]
?
0x0001
:
0
)
|
(
MouseButtonsStates
[
0
]
?
0x8000
:
0
);
AsyncMouseButtonsStates
[
0
]
=
0
;
break
;
case
VK_MBUTTON
:
retval
=
(
AsyncMouseButtonsStates
[
1
]
?
0x0001
:
0
)
|
(
MouseButtonsStates
[
1
]
?
0x8000
:
0
);
AsyncMouseButtonsStates
[
1
]
=
0
;
break
;
case
VK_RBUTTON
:
retval
=
(
AsyncMouseButtonsStates
[
2
]
?
0x0001
:
0
)
|
(
MouseButtonsStates
[
2
]
?
0x8000
:
0
);
AsyncMouseButtonsStates
[
2
]
=
0
;
break
;
default:
retval
=
((
AsyncKeyStateTable
[
nKey
]
&
0x80
)
?
0x0001
:
0
)
|
WORD
retval
=
((
AsyncKeyStateTable
[
nKey
]
&
0x80
)
?
0x0001
:
0
)
|
((
InputKeyStateTable
[
nKey
]
&
0x80
)
?
0x8000
:
0
);
AsyncKeyStateTable
[
nKey
]
=
0
;
break
;
}
TRACE_
(
key
)(
"(%x) -> %x
\n
"
,
nKey
,
retval
);
return
retval
;
}
...
...
windows/message.c
View file @
a21672eb
...
...
@@ -37,6 +37,7 @@ DECLARE_DEBUG_CHANNEL(key);
#define WM_NCMOUSEFIRST WM_NCMOUSEMOVE
#define WM_NCMOUSELAST WM_NCMBUTTONDBLCLK
static
BYTE
QueueKeyStateTable
[
256
];
static
UINT
doubleClickSpeed
=
452
;
...
...
@@ -115,6 +116,52 @@ static void queue_hardware_message( MSG *msg, ULONG_PTR extra_info, enum message
/***********************************************************************
* update_queue_key_state
*/
static
void
update_queue_key_state
(
UINT
msg
,
WPARAM
wp
)
{
BOOL
down
=
FALSE
;
switch
(
msg
)
{
case
WM_LBUTTONDOWN
:
down
=
TRUE
;
/* fall through */
case
WM_LBUTTONUP
:
wp
=
VK_LBUTTON
;
break
;
case
WM_MBUTTONDOWN
:
down
=
TRUE
;
/* fall through */
case
WM_MBUTTONUP
:
wp
=
VK_MBUTTON
;
break
;
case
WM_RBUTTONDOWN
:
down
=
TRUE
;
/* fall through */
case
WM_RBUTTONUP
:
wp
=
VK_RBUTTON
;
break
;
case
WM_KEYDOWN
:
case
WM_SYSKEYDOWN
:
down
=
TRUE
;
/* fall through */
case
WM_KEYUP
:
case
WM_SYSKEYUP
:
wp
=
wp
&
0xff
;
break
;
}
if
(
down
)
{
BYTE
*
p
=
&
QueueKeyStateTable
[
wp
];
if
(
!
(
*
p
&
0x80
))
*
p
^=
0x01
;
*
p
|=
0x80
;
}
else
QueueKeyStateTable
[
wp
]
&=
~
0x80
;
}
/***********************************************************************
* MSG_SendParentNotify
*
* Send a WM_PARENTNOTIFY to all ancestors of the given window, unless
...
...
@@ -172,12 +219,13 @@ void MSG_JournalPlayBackMsg(void)
keyDown
++
;
if
(
!
keyDown
)
msg
.
lParam
|=
0x40000000
;
AsyncKeyStateTable
[
msg
.
wParam
]
=
InputKeyStateTable
[
msg
.
wParam
]
|=
0x80
;
InputKeyStateTable
[
msg
.
wParam
]
|=
0x80
;
AsyncKeyStateTable
[
msg
.
wParam
]
|=
0x80
;
}
else
/* WM_KEYUP, WM_SYSKEYUP */
{
msg
.
lParam
|=
0xC0000000
;
AsyncKeyStateTable
[
msg
.
wParam
]
=
InputKeyStateTable
[
msg
.
wParam
]
&=
~
0x80
;
InputKeyStateTable
[
msg
.
wParam
]
&=
~
0x80
;
}
if
(
InputKeyStateTable
[
VK_MENU
]
&
0x80
)
msg
.
lParam
|=
0x20000000
;
...
...
@@ -192,27 +240,33 @@ void MSG_JournalPlayBackMsg(void)
switch
(
tmpMsg
.
message
)
{
case
WM_LBUTTONDOWN
:
MouseButtonsStates
[
0
]
=
AsyncMouseButtonsStates
[
0
]
=
TRUE
;
break
;
InputKeyStateTable
[
VK_LBUTTON
]
|=
0x80
;
AsyncKeyStateTable
[
VK_LBUTTON
]
|=
0x80
;
break
;
case
WM_LBUTTONUP
:
MouseButtonsStates
[
0
]
=
AsyncMouseButtonsStates
[
0
]
=
FALSE
;
break
;
InputKeyStateTable
[
VK_LBUTTON
]
&=
~
0x80
;
break
;
case
WM_MBUTTONDOWN
:
MouseButtonsStates
[
1
]
=
AsyncMouseButtonsStates
[
1
]
=
TRUE
;
break
;
InputKeyStateTable
[
VK_MBUTTON
]
|=
0x80
;
AsyncKeyStateTable
[
VK_MBUTTON
]
|=
0x80
;
break
;
case
WM_MBUTTONUP
:
MouseButtonsStates
[
1
]
=
AsyncMouseButtonsStates
[
1
]
=
FALSE
;
break
;
InputKeyStateTable
[
VK_MBUTTON
]
&=
~
0x80
;
break
;
case
WM_RBUTTONDOWN
:
MouseButtonsStates
[
2
]
=
AsyncMouseButtonsStates
[
2
]
=
TRUE
;
break
;
InputKeyStateTable
[
VK_RBUTTON
]
|=
0x80
;
AsyncKeyStateTable
[
VK_RBUTTON
]
|=
0x80
;
break
;
case
WM_RBUTTONUP
:
MouseButtonsStates
[
2
]
=
AsyncMouseButtonsStates
[
2
]
=
FALSE
;
break
;
InputKeyStateTable
[
VK_RBUTTON
]
&=
~
0x80
;
break
;
}
AsyncKeyStateTable
[
VK_LBUTTON
]
=
InputKeyStateTable
[
VK_LBUTTON
]
=
MouseButtonsStates
[
0
]
?
0x80
:
0
;
AsyncKeyStateTable
[
VK_MBUTTON
]
=
InputKeyStateTable
[
VK_MBUTTON
]
=
MouseButtonsStates
[
1
]
?
0x80
:
0
;
AsyncKeyStateTable
[
VK_RBUTTON
]
=
InputKeyStateTable
[
VK_RBUTTON
]
=
MouseButtonsStates
[
2
]
?
0x80
:
0
;
SetCursorPos
(
tmpMsg
.
paramL
,
tmpMsg
.
paramH
);
msg
.
lParam
=
MAKELONG
(
tmpMsg
.
paramL
,
tmpMsg
.
paramH
);
msg
.
wParam
=
0
;
if
(
MouseButtonsStates
[
0
]
)
msg
.
wParam
|=
MK_LBUTTON
;
if
(
MouseButtonsStates
[
1
]
)
msg
.
wParam
|=
MK_MBUTTON
;
if
(
MouseButtonsStates
[
2
]
)
msg
.
wParam
|=
MK_RBUTTON
;
if
(
InputKeyStateTable
[
VK_LBUTTON
]
&
0x80
)
msg
.
wParam
|=
MK_LBUTTON
;
if
(
InputKeyStateTable
[
VK_MBUTTON
]
&
0x80
)
msg
.
wParam
|=
MK_MBUTTON
;
if
(
InputKeyStateTable
[
VK_RBUTTON
]
&
0x80
)
msg
.
wParam
|=
MK_RBUTTON
;
msg
.
pt
.
x
=
tmpMsg
.
paramL
;
msg
.
pt
.
y
=
tmpMsg
.
paramH
;
...
...
@@ -270,6 +324,8 @@ static BOOL process_cooked_keyboard_message( MSG *msg, BOOL remove )
{
if
(
remove
)
{
update_queue_key_state
(
msg
->
message
,
msg
->
wParam
);
/* Handle F1 key by sending out WM_HELP message */
if
((
msg
->
message
==
WM_KEYUP
)
&&
(
msg
->
wParam
==
VK_F1
)
&&
...
...
@@ -400,6 +456,8 @@ static BOOL process_cooked_mouse_message( MSG *msg, BOOL remove )
raw_message
+=
WM_LBUTTONDOWN
-
WM_LBUTTONDBLCLK
;
}
if
(
remove
)
update_queue_key_state
(
raw_message
,
0
);
if
(
HOOK_IsHooked
(
WH_MOUSE
))
{
MOUSEHOOKSTRUCT
hook
;
...
...
@@ -538,6 +596,61 @@ BOOL MSG_process_cooked_hardware_message( MSG *msg, BOOL remove )
/**********************************************************************
* GetKeyState (USER.106)
*/
INT16
WINAPI
GetKeyState16
(
INT16
vkey
)
{
return
GetKeyState
(
vkey
);
}
/**********************************************************************
* GetKeyState (USER32.@)
*
* An application calls the GetKeyState function in response to a
* keyboard-input message. This function retrieves the state of the key
* at the time the input message was generated. (SDK 3.1 Vol 2. p 390)
*/
SHORT
WINAPI
GetKeyState
(
INT
vkey
)
{
INT
retval
;
if
(
vkey
>=
'a'
&&
vkey
<=
'z'
)
vkey
+=
'A'
-
'a'
;
retval
=
((
WORD
)(
QueueKeyStateTable
[
vkey
]
&
0x80
)
<<
8
)
|
(
QueueKeyStateTable
[
vkey
]
&
0x01
);
/* TRACE(key, "(0x%x) -> %x\n", vkey, retval); */
return
retval
;
}
/**********************************************************************
* GetKeyboardState (USER.222)
* GetKeyboardState (USER32.@)
*
* An application calls the GetKeyboardState function in response to a
* keyboard-input message. This function retrieves the state of the keyboard
* at the time the input message was generated. (SDK 3.1 Vol 2. p 387)
*/
BOOL
WINAPI
GetKeyboardState
(
LPBYTE
lpKeyState
)
{
TRACE_
(
key
)(
"(%p)
\n
"
,
lpKeyState
);
if
(
lpKeyState
)
memcpy
(
lpKeyState
,
QueueKeyStateTable
,
256
);
return
TRUE
;
}
/**********************************************************************
* SetKeyboardState (USER.223)
* SetKeyboardState (USER32.@)
*/
BOOL
WINAPI
SetKeyboardState
(
LPBYTE
lpKeyState
)
{
TRACE_
(
key
)(
"(%p)
\n
"
,
lpKeyState
);
if
(
lpKeyState
)
memcpy
(
QueueKeyStateTable
,
lpKeyState
,
256
);
return
TRUE
;
}
/**********************************************************************
* SetDoubleClickTime (USER32.@)
*/
BOOL
WINAPI
SetDoubleClickTime
(
UINT
interval
)
...
...
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