winehq.org
Sign In
Sign Up
Sign In
Sign Up
Manage this list
×
Keyboard Shortcuts
Thread View
j
: Next unread message
k
: Previous unread message
j a
: Jump to all threads
j l
: Jump to MailingList overview
2025
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
List overview
wine-commits
June 2024
----- 2025 -----
February 2025
January 2025
----- 2024 -----
December 2024
November 2024
October 2024
September 2024
August 2024
July 2024
June 2024
May 2024
April 2024
March 2024
February 2024
January 2024
----- 2023 -----
December 2023
November 2023
October 2023
September 2023
August 2023
July 2023
June 2023
May 2023
April 2023
March 2023
February 2023
January 2023
----- 2022 -----
December 2022
November 2022
October 2022
September 2022
August 2022
July 2022
June 2022
May 2022
April 2022
March 2022
February 2022
January 2022
----- 2021 -----
December 2021
November 2021
October 2021
September 2021
August 2021
July 2021
June 2021
May 2021
April 2021
March 2021
February 2021
January 2021
----- 2020 -----
December 2020
November 2020
October 2020
September 2020
August 2020
July 2020
June 2020
May 2020
April 2020
March 2020
February 2020
January 2020
----- 2019 -----
December 2019
November 2019
October 2019
September 2019
August 2019
July 2019
June 2019
May 2019
April 2019
March 2019
February 2019
January 2019
----- 2018 -----
December 2018
November 2018
October 2018
September 2018
August 2018
July 2018
June 2018
May 2018
April 2018
March 2018
February 2018
January 2018
----- 2017 -----
December 2017
November 2017
October 2017
September 2017
August 2017
July 2017
June 2017
May 2017
April 2017
March 2017
February 2017
January 2017
----- 2016 -----
December 2016
November 2016
October 2016
September 2016
August 2016
July 2016
June 2016
May 2016
April 2016
March 2016
February 2016
January 2016
----- 2015 -----
December 2015
November 2015
October 2015
September 2015
August 2015
July 2015
June 2015
May 2015
April 2015
March 2015
February 2015
January 2015
----- 2014 -----
December 2014
November 2014
October 2014
September 2014
August 2014
July 2014
June 2014
May 2014
April 2014
March 2014
February 2014
January 2014
----- 2013 -----
December 2013
November 2013
October 2013
September 2013
August 2013
July 2013
June 2013
May 2013
April 2013
March 2013
February 2013
January 2013
----- 2012 -----
December 2012
November 2012
October 2012
September 2012
August 2012
July 2012
June 2012
May 2012
April 2012
March 2012
February 2012
January 2012
----- 2011 -----
December 2011
November 2011
October 2011
September 2011
August 2011
July 2011
June 2011
May 2011
April 2011
March 2011
February 2011
January 2011
----- 2010 -----
December 2010
November 2010
October 2010
September 2010
August 2010
July 2010
June 2010
May 2010
April 2010
March 2010
February 2010
January 2010
----- 2009 -----
December 2009
November 2009
October 2009
September 2009
August 2009
July 2009
June 2009
May 2009
April 2009
March 2009
February 2009
January 2009
----- 2008 -----
December 2008
November 2008
October 2008
September 2008
August 2008
July 2008
June 2008
May 2008
April 2008
March 2008
February 2008
January 2008
----- 2007 -----
December 2007
November 2007
October 2007
September 2007
August 2007
July 2007
June 2007
May 2007
April 2007
March 2007
February 2007
January 2007
----- 2006 -----
December 2006
November 2006
October 2006
September 2006
August 2006
July 2006
June 2006
May 2006
April 2006
March 2006
February 2006
January 2006
----- 2005 -----
December 2005
November 2005
October 2005
September 2005
August 2005
July 2005
June 2005
May 2005
April 2005
March 2005
February 2005
January 2005
----- 2004 -----
December 2004
November 2004
October 2004
September 2004
August 2004
July 2004
June 2004
May 2004
April 2004
March 2004
February 2004
January 2004
----- 2003 -----
December 2003
November 2003
October 2003
September 2003
August 2003
July 2003
June 2003
May 2003
April 2003
March 2003
February 2003
January 2003
----- 2002 -----
December 2002
November 2002
October 2002
September 2002
August 2002
July 2002
June 2002
May 2002
April 2002
March 2002
February 2002
January 2002
----- 2001 -----
December 2001
November 2001
October 2001
September 2001
August 2001
July 2001
June 2001
May 2001
April 2001
March 2001
February 2001
wine-commits@winehq.org
1 participants
613 discussions
Start a n
N
ew thread
Eric Pouech : cmd: Introduce return code to indicate abort of current instruction.
by Alexandre Julliard
21 Jun '24
21 Jun '24
Module: wine Branch: master Commit: 411cce36b1fe7e12dcb5901562aa3782c541ef9a URL:
https://gitlab.winehq.org/wine/wine/-/commit/411cce36b1fe7e12dcb5901562aa37…
Author: Eric Pouech <epouech(a)codeweavers.com> Date: Wed May 1 09:51:04 2024 +0200 cmd: Introduce return code to indicate abort of current instruction. Use that return code for WCMD_exit() and WCMD_goto(). Signed-off-by: Eric Pouech <epouech(a)codeweavers.com> --- programs/cmd/batch.c | 2 +- programs/cmd/builtins.c | 28 +++++++++++++--------------- programs/cmd/wcmd.h | 6 ++++-- programs/cmd/wcmdmain.c | 7 +++++-- 4 files changed, 23 insertions(+), 20 deletions(-) diff --git a/programs/cmd/batch.c b/programs/cmd/batch.c index 15e8baa21c8..402a6f649d2 100644 --- a/programs/cmd/batch.c +++ b/programs/cmd/batch.c @@ -77,7 +77,7 @@ void WCMD_batch (WCHAR *file, WCHAR *command, BOOL called, WCHAR *startLabel, HA /* If processing a call :label, 'goto' the label in question */ if (startLabel) { lstrcpyW(param1, startLabel); - WCMD_goto(NULL); + WCMD_goto(); } /* diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c index 9033330e6d1..1c39c35ca78 100644 --- a/programs/cmd/builtins.c +++ b/programs/cmd/builtins.c @@ -1926,7 +1926,7 @@ void WCMD_give_help (const WCHAR *args) } /**************************************************************************** - * WCMD_go_to + * WCMD_goto * * Batch file jump instruction. Not the most efficient algorithm ;-) * Prints error message if the specified label cannot be found - the file pointer is @@ -1934,27 +1934,24 @@ void WCMD_give_help (const WCHAR *args) * FIXME: DOS is supposed to allow labels with spaces - we don't. */ -void WCMD_goto (CMD_NODE **cmdList) { - +RETURN_CODE WCMD_goto(void) +{ WCHAR string[MAX_PATH]; WCHAR *labelend = NULL; const WCHAR labelEndsW[] = L"><|& :\t"; - /* Do not process any more parts of a processed multipart or multilines command */ - if (cmdList) *cmdList = NULL; - if (context != NULL) { WCHAR *paramStart = param1, *str; if (param1[0] == 0x00) { WCMD_output_stderr(WCMD_LoadMessage(WCMD_NOARG)); - return; + return ERROR_INVALID_FUNCTION; } /* Handle special :EOF label */ if (lstrcmpiW(L":eof", param1) == 0) { context -> skip_rest = TRUE; - return; + return RETURN_CODE_ABORTED; } /* Support goto :label as well as goto label plus remove trailing chars */ @@ -2000,7 +1997,7 @@ void WCMD_goto (CMD_NODE **cmdList) { if (labelend) *labelend = 0x00; WINE_TRACE("comparing found label %s\n", wine_dbgstr_w(str)); - if (lstrcmpiW (str, paramStart) == 0) return; + if (lstrcmpiW (str, paramStart) == 0) return RETURN_CODE_ABORTED; } /* See if we have gone beyond the end point if second time through */ @@ -2021,7 +2018,7 @@ void WCMD_goto (CMD_NODE **cmdList) { WCMD_output_stderr(WCMD_LoadMessage(WCMD_NOTARGET)); context -> skip_rest = TRUE; } - return; + return ERROR_INVALID_FUNCTION; } /***************************************************************************** @@ -3966,16 +3963,17 @@ int WCMD_volume(BOOL set_label, const WCHAR *path) * */ -void WCMD_exit (CMD_NODE **cmdList) { +RETURN_CODE WCMD_exit(void) +{ int rc = wcstol(param1, NULL, 10); /* Note: wcstol of empty parameter is 0 */ - if (context && lstrcmpiW(quals, L"/B") == 0) { + if (context && lstrcmpiW(quals, L"/B") == 0) + { errorlevel = rc; context -> skip_rest = TRUE; - *cmdList = NULL; - } else { - ExitProcess(rc); + return RETURN_CODE_ABORTED; } + ExitProcess(rc); } diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h index d11fcd3768f..537e078c412 100644 --- a/programs/cmd/wcmd.h +++ b/programs/cmd/wcmd.h @@ -176,10 +176,12 @@ struct _DIRECTORY_STACK *WCMD_dir_stack_free(struct _DIRECTORY_STACK *dir); /* The return code: * - some of them are directly mapped to kernel32's errors * - some others are cmd.exe specific + * - ABORTED if used to break out of FOR/IF blocks (to handle GOTO, EXIT commands) */ typedef int RETURN_CODE; #define RETURN_CODE_SYNTAX_ERROR 255 #define RETURN_CODE_CANT_LAUNCH 9009 +#define RETURN_CODE_ABORTED (-999999) void WCMD_assoc (const WCHAR *, BOOL); void WCMD_batch (WCHAR *, WCHAR *, BOOL, WCHAR *, HANDLE); @@ -195,11 +197,11 @@ void WCMD_directory (WCHAR *); void WCMD_echo (const WCHAR *); void WCMD_endlocal (void); void WCMD_enter_paged_mode(const WCHAR *); -void WCMD_exit (CMD_NODE **cmdList); +RETURN_CODE WCMD_exit(void); void WCMD_for (WCHAR *, CMD_NODE **cmdList); BOOL WCMD_get_fullpath(const WCHAR *, SIZE_T, WCHAR *, WCHAR **); void WCMD_give_help (const WCHAR *args); -void WCMD_goto (CMD_NODE **cmdList); +RETURN_CODE WCMD_goto(void); void WCMD_if (WCHAR *, CMD_NODE **cmdList); void WCMD_leave_paged_mode(void); void WCMD_more (WCHAR *); diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index b3202edd0d4..d2edb22c9c4 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -1695,6 +1695,7 @@ static BOOL set_std_redirections(CMD_REDIRECTION *redir, WCHAR *in_pipe) */ static void execute_single_command(const WCHAR *command, CMD_NODE **cmdList, BOOL retrycall) { + RETURN_CODE return_code = NO_ERROR; WCHAR *cmd, *parms_start; int status, cmd_index, count; WCHAR *whichcmd; @@ -1797,7 +1798,7 @@ static void execute_single_command(const WCHAR *command, CMD_NODE **cmdList, BOO WCMD_echo(&whichcmd[count]); break; case WCMD_GOTO: - WCMD_goto (cmdList); + return_code = WCMD_goto(); break; case WCMD_HELP: WCMD_give_help (parms_start); @@ -1891,7 +1892,7 @@ static void execute_single_command(const WCHAR *command, CMD_NODE **cmdList, BOO WCMD_mklink(parms_start); break; case WCMD_EXIT: - WCMD_exit (cmdList); + return_code = WCMD_exit(); break; case WCMD_FOR: case WCMD_IF: @@ -1911,6 +1912,8 @@ static void execute_single_command(const WCHAR *command, CMD_NODE **cmdList, BOO } cleanup: free(cmd); + if (return_code == RETURN_CODE_ABORTED && cmdList) + *cmdList = NULL; } /*****************************************************************************
1
0
0
0
Eric Pouech : cmd: Use kernel32's error codes instead of literals.
by Alexandre Julliard
21 Jun '24
21 Jun '24
Module: wine Branch: master Commit: 844d6b553a72ed2ad658afd4b315f084dca525c5 URL:
https://gitlab.winehq.org/wine/wine/-/commit/844d6b553a72ed2ad658afd4b315f0…
Author: Eric Pouech <epouech(a)codeweavers.com> Date: Tue Jun 18 10:19:21 2024 +0200 cmd: Use kernel32's error codes instead of literals. Signed-off-by: Eric Pouech <epouech(a)codeweavers.com> --- programs/cmd/batch.c | 2 +- programs/cmd/builtins.c | 70 ++++++++++++++++++++++++------------------------ programs/cmd/directory.c | 18 ++++++------- programs/cmd/wcmd.h | 8 ++++++ programs/cmd/wcmdmain.c | 12 ++++----- 5 files changed, 59 insertions(+), 51 deletions(-)
1
0
0
0
Eric Pouech : cmd: Introduce token-based syntax parser for building command nodes.
by Alexandre Julliard
21 Jun '24
21 Jun '24
Module: wine Branch: master Commit: 2ec70835fc9ece48a34b3ccf04b87445b46d654e URL:
https://gitlab.winehq.org/wine/wine/-/commit/2ec70835fc9ece48a34b3ccf04b874…
Author: Eric Pouech <epouech(a)codeweavers.com> Date: Mon Apr 22 09:04:51 2024 +0200 cmd: Introduce token-based syntax parser for building command nodes. Signed-off-by: Eric Pouech <epouech(a)codeweavers.com> --- po/ar.po | 6 + po/ast.po | 6 + po/bg.po | 4 + po/ca.po | 6 + po/cs.po | 6 + po/da.po | 6 + po/de.po | 6 + po/el.po | 4 + po/en.po | 4 + po/en_US.po | 4 + po/eo.po | 4 + po/es.po | 6 + po/fa.po | 4 + po/fi.po | 6 + po/fr.po | 6 + po/he.po | 6 + po/hi.po | 4 + po/hr.po | 6 + po/hu.po | 6 + po/it.po | 6 + po/ja.po | 6 + po/ka.po | 6 + po/ko.po | 6 + po/lt.po | 6 + po/ml.po | 4 + po/nb_NO.po | 6 + po/nl.po | 6 + po/or.po | 4 + po/pa.po | 4 + po/pl.po | 6 + po/pt_BR.po | 6 + po/pt_PT.po | 6 + po/rm.po | 4 + po/ro.po | 6 + po/ru.po | 6 + po/si.po | 6 + po/sk.po | 4 + po/sl.po | 6 + po/sr_RS(a)cyrillic.po | 6 + po/sr_RS(a)latin.po | 6 + po/sv.po | 6 + po/ta.po | 4 + po/te.po | 4 + po/th.po | 4 + po/tr.po | 6 + po/uk.po | 6 + po/wa.po | 4 + po/wine.pot | 4 + po/zh_CN.po | 6 + po/zh_TW.po | 6 + programs/cmd/cmd.rc | 1 + programs/cmd/wcmd.h | 1 + programs/cmd/wcmdmain.c | 295 ++++++++++++++++++++++++++++++++++++++---------- 53 files changed, 505 insertions(+), 58 deletions(-)
1
0
0
0
Rémi Bernon : win32u: Use the shared data if possible for NtUserGetAsyncKeyState.
by Alexandre Julliard
21 Jun '24
21 Jun '24
Module: wine Branch: master Commit: 9a7408e7719d6a16ea06c3e1371a306e8e200611 URL:
https://gitlab.winehq.org/wine/wine/-/commit/9a7408e7719d6a16ea06c3e1371a30…
Author: Rémi Bernon <rbernon(a)codeweavers.com> Date: Sat Jun 15 15:49:55 2024 +0200 win32u: Use the shared data if possible for NtUserGetAsyncKeyState. Based on a patch by Huw Davies. --- dlls/win32u/hook.c | 2 -- dlls/win32u/input.c | 44 +++++++++++--------------------------------- dlls/win32u/message.c | 16 +++------------- dlls/win32u/ntuser_private.h | 8 -------- dlls/win32u/sysparams.c | 2 -- dlls/win32u/win32u_private.h | 1 - dlls/win32u/winstation.c | 2 -- 7 files changed, 14 insertions(+), 61 deletions(-) diff --git a/dlls/win32u/hook.c b/dlls/win32u/hook.c index fab2b960fa5..eca9e7d6ce9 100644 --- a/dlls/win32u/hook.c +++ b/dlls/win32u/hook.c @@ -345,8 +345,6 @@ static LRESULT call_hook( struct win_hook_params *info, const WCHAR *module, siz if (params != info) free( params ); } - if (info->id == WH_KEYBOARD_LL || info->id == WH_MOUSE_LL) - InterlockedIncrement( &global_key_state_counter ); /* force refreshing the key state cache */ return ret; } diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 4d0c2589c1b..a582826d18e 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -406,7 +406,6 @@ static const KBDTABLES kbdus_tables = static LONG clipping_cursor; /* clipping thread counter */ -LONG global_key_state_counter = 0; BOOL grab_pointer = TRUE; BOOL grab_fullscreen = FALSE; @@ -810,52 +809,31 @@ static void check_for_events( UINT flags ) */ SHORT WINAPI NtUserGetAsyncKeyState( INT key ) { - struct user_key_state_info *key_state_info = get_user_thread_info()->key_state; - INT counter = global_key_state_counter; - BYTE prev_key_state; - SHORT ret; + const desktop_shm_t *desktop_shm; + struct object_lock lock = OBJECT_LOCK_INIT; + NTSTATUS status; + BYTE state = 0; + SHORT ret = 0; if (key < 0 || key >= 256) return 0; check_for_events( QS_INPUT ); - if (key_state_info && !(key_state_info->state[key] & 0xc0) && - key_state_info->counter == counter && NtGetTickCount() - key_state_info->time < 50) - { - /* use cached value */ - return 0; - } - else if (!key_state_info) - { - key_state_info = calloc( 1, sizeof(*key_state_info) ); - get_user_thread_info()->key_state = key_state_info; - } + while ((status = get_shared_desktop( &lock, &desktop_shm )) == STATUS_PENDING) + state = desktop_shm->keystate[key]; - ret = 0; + if (status) return 0; + if (!(state & 0x40)) return (state & 0x80) << 8; + + /* Need to make a server call to reset the last pressed bit */ SERVER_START_REQ( get_key_state ) { req->async = 1; req->key = key; - if (key_state_info) - { - prev_key_state = key_state_info->state[key]; - wine_server_set_reply( req, key_state_info->state, sizeof(key_state_info->state) ); - } if (!wine_server_call( req )) { if (reply->state & 0x40) ret |= 0x0001; if (reply->state & 0x80) ret |= 0x8000; - if (key_state_info) - { - /* force refreshing the key state cache - some multithreaded programs - * (like Adobe Photoshop CS5) expect that changes to the async key state - * are also immediately available in other threads. */ - if (prev_key_state != key_state_info->state[key]) - counter = InterlockedIncrement( &global_key_state_counter ); - - key_state_info->time = NtGetTickCount(); - key_state_info->counter = counter; - } } } SERVER_END_REQ; diff --git a/dlls/win32u/message.c b/dlls/win32u/message.c index d1f49ca3ed9..b7062414658 100644 --- a/dlls/win32u/message.c +++ b/dlls/win32u/message.c @@ -3469,7 +3469,7 @@ NTSTATUS send_hardware_message( HWND hwnd, UINT flags, const INPUT *input, LPARA struct send_message_info info; int prev_x, prev_y, new_x, new_y; NTSTATUS ret; - BOOL wait, affects_key_state = FALSE; + BOOL wait; info.type = MSG_HARDWARE; info.dest_tid = 0; @@ -3495,10 +3495,6 @@ NTSTATUS send_hardware_message( HWND hwnd, UINT flags, const INPUT *input, LPARA req->input.mouse.flags = input->mi.dwFlags; req->input.mouse.time = input->mi.time; req->input.mouse.info = input->mi.dwExtraInfo; - affects_key_state = !!(input->mi.dwFlags & (MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP | - MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP | - MOUSEEVENTF_MIDDLEDOWN | MOUSEEVENTF_MIDDLEUP | - MOUSEEVENTF_XDOWN | MOUSEEVENTF_XUP)); break; case INPUT_KEYBOARD: if (input->ki.dwFlags & KEYEVENTF_SCANCODE) @@ -3523,7 +3519,6 @@ NTSTATUS send_hardware_message( HWND hwnd, UINT flags, const INPUT *input, LPARA req->input.kbd.flags = input->ki.dwFlags & ~KEYEVENTF_SCANCODE; req->input.kbd.time = input->ki.time; req->input.kbd.info = input->ki.dwExtraInfo; - affects_key_state = TRUE; break; case INPUT_HARDWARE: req->input.hw.msg = input->hi.uMsg; @@ -3553,13 +3548,8 @@ NTSTATUS send_hardware_message( HWND hwnd, UINT flags, const INPUT *input, LPARA } SERVER_END_REQ; - if (!ret) - { - if (affects_key_state) - InterlockedIncrement( &global_key_state_counter ); /* force refreshing the key state cache */ - if ((flags & SEND_HWMSG_INJECTED) && (prev_x != new_x || prev_y != new_y)) - user_driver->pSetCursorPos( new_x, new_y ); - } + if (!ret && (flags & SEND_HWMSG_INJECTED) && (prev_x != new_x || prev_y != new_y)) + user_driver->pSetCursorPos( new_x, new_y ); if (wait) { diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h index 39943e2261b..d95ea03d45f 100644 --- a/dlls/win32u/ntuser_private.h +++ b/dlls/win32u/ntuser_private.h @@ -116,7 +116,6 @@ struct user_thread_info HHOOK hook; /* Current hook */ UINT active_hooks; /* Bitmap of active hooks */ struct received_message_info *receive_info; /* Message being currently received */ - struct user_key_state_info *key_state; /* Cache of global key state */ struct imm_thread_data *imm_thread_data; /* IMM thread data */ HKL kbd_layout; /* Current keyboard layout */ UINT kbd_layout_id; /* Current keyboard layout ID */ @@ -134,13 +133,6 @@ static inline struct user_thread_info *get_user_thread_info(void) return CONTAINING_RECORD( NtUserGetThreadInfo(), struct user_thread_info, client_info ); } -struct user_key_state_info -{ - UINT time; /* Time of last key state refresh */ - INT counter; /* Counter to invalidate the key state */ - BYTE state[256]; /* State for each key */ -}; - struct hook_extra_info { HHOOK handle; diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index 34de10168e2..9a06db2c7f6 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -6334,8 +6334,6 @@ static void thread_detach(void) destroy_thread_windows(); user_driver->pThreadDetach(); - free( thread_info->key_state ); - thread_info->key_state = 0; free( thread_info->rawinput ); cleanup_imm_thread(); diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h index f7ba2c1a423..e2e35d6c40d 100644 --- a/dlls/win32u/win32u_private.h +++ b/dlls/win32u/win32u_private.h @@ -90,7 +90,6 @@ extern void unregister_imm_window( HWND hwnd ); extern BOOL grab_pointer; extern BOOL grab_fullscreen; extern BOOL destroy_caret(void); -extern LONG global_key_state_counter; extern HWND get_active_window(void); extern HWND get_capture(void); extern BOOL get_cursor_pos( POINT *pt ); diff --git a/dlls/win32u/winstation.c b/dlls/win32u/winstation.c index 7168a614735..1676539c69c 100644 --- a/dlls/win32u/winstation.c +++ b/dlls/win32u/winstation.c @@ -441,11 +441,9 @@ BOOL WINAPI NtUserSetThreadDesktop( HDESK handle ) if (ret) /* reset the desktop windows */ { struct user_thread_info *thread_info = get_user_thread_info(); - struct user_key_state_info *key_state_info = thread_info->key_state; get_session_thread_data()->shared_desktop = find_shared_session_object( locator ); thread_info->client_info.top_window = 0; thread_info->client_info.msg_window = 0; - if (key_state_info) key_state_info->time = 0; if (was_virtual_desktop != is_virtual_desktop()) update_display_cache( FALSE ); } return ret;
1
0
0
0
Rémi Bernon : server: Move the desktop keystate to shared memory.
by Alexandre Julliard
21 Jun '24
21 Jun '24
Module: wine Branch: master Commit: 2eeb4d5192e808c18e5949cc1c14b51b22fee342 URL:
https://gitlab.winehq.org/wine/wine/-/commit/2eeb4d5192e808c18e5949cc1c14b5…
Author: Rémi Bernon <rbernon(a)codeweavers.com> Date: Fri Jun 21 00:11:11 2024 +0200 server: Move the desktop keystate to shared memory. Based on a patch by Huw Davies. --- include/wine/server_protocol.h | 3 +- server/protocol.def | 1 + server/queue.c | 81 ++++++++++++++++++++++++++---------------- server/winstation.c | 2 +- 4 files changed, 55 insertions(+), 32 deletions(-)
1
0
0
0
Rémi Bernon : server: Use separate functions to update the desktop and input keystates.
by Alexandre Julliard
21 Jun '24
21 Jun '24
Module: wine Branch: master Commit: d5b4458c8d34ef5319f31caefcf11fab3eaf9cec URL:
https://gitlab.winehq.org/wine/wine/-/commit/d5b4458c8d34ef5319f31caefcf11f…
Author: Rémi Bernon <rbernon(a)codeweavers.com> Date: Fri Jun 16 09:58:53 2023 +0200 server: Use separate functions to update the desktop and input keystates. Based on a patch by Huw Davies. --- server/queue.c | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/server/queue.c b/server/queue.c index c17446a1dd9..699a8fdf248 100644 --- a/server/queue.c +++ b/server/queue.c @@ -1470,7 +1470,7 @@ static struct timer *set_timer( struct msg_queue *queue, unsigned int rate ) } /* change the input key state for a given key */ -static void set_input_key_state( unsigned char *keystate, unsigned char key, int down ) +static void set_input_key_state( unsigned char *keystate, unsigned char key, unsigned char down ) { if (down) { @@ -1481,34 +1481,33 @@ static void set_input_key_state( unsigned char *keystate, unsigned char key, int } /* update the input key state for a keyboard message */ -static void update_input_key_state( struct desktop *desktop, unsigned char *keystate, - unsigned int msg, lparam_t wparam ) +static void update_key_state( unsigned char *keystate, unsigned int msg, + lparam_t wparam, int desktop ) { - unsigned char key; - int down = 0; + unsigned char key, down = 0, down_val = desktop ? 0xc0 : 0x80; switch (msg) { case WM_LBUTTONDOWN: - down = (keystate == desktop->keystate) ? 0xc0 : 0x80; + down = down_val; /* fall through */ case WM_LBUTTONUP: set_input_key_state( keystate, VK_LBUTTON, down ); break; case WM_MBUTTONDOWN: - down = (keystate == desktop->keystate) ? 0xc0 : 0x80; + down = down_val; /* fall through */ case WM_MBUTTONUP: set_input_key_state( keystate, VK_MBUTTON, down ); break; case WM_RBUTTONDOWN: - down = (keystate == desktop->keystate) ? 0xc0 : 0x80; + down = down_val; /* fall through */ case WM_RBUTTONUP: set_input_key_state( keystate, VK_RBUTTON, down ); break; case WM_XBUTTONDOWN: - down = (keystate == desktop->keystate) ? 0xc0 : 0x80; + down = down_val; /* fall through */ case WM_XBUTTONUP: if (wparam >> 16 == XBUTTON1) set_input_key_state( keystate, VK_XBUTTON1, down ); @@ -1516,7 +1515,7 @@ static void update_input_key_state( struct desktop *desktop, unsigned char *keys break; case WM_KEYDOWN: case WM_SYSKEYDOWN: - down = (keystate == desktop->keystate) ? 0xc0 : 0x80; + down = down_val; /* fall through */ case WM_KEYUP: case WM_SYSKEYUP: @@ -1544,6 +1543,16 @@ static void update_input_key_state( struct desktop *desktop, unsigned char *keys } } +static void update_thread_input_key_state( struct thread_input *input, unsigned int msg, lparam_t wparam ) +{ + update_key_state( input->keystate, msg, wparam, 0 ); +} + +static void update_desktop_key_state( struct desktop *desktop, unsigned int msg, lparam_t wparam ) +{ + update_key_state( desktop->keystate, msg, wparam, 1 ); +} + /* release the hardware message currently being processed by the given thread */ static void release_hardware_message( struct msg_queue *queue, unsigned int hw_id ) { @@ -1569,7 +1578,7 @@ static void release_hardware_message( struct msg_queue *queue, unsigned int hw_i } if (clr_bit) clear_queue_bits( queue, clr_bit ); - update_input_key_state( input->desktop, input->keystate, msg->msg, msg->wparam ); + update_thread_input_key_state( input, msg->msg, msg->wparam ); list_remove( &msg->entry ); free_message( msg ); } @@ -1696,7 +1705,7 @@ static void queue_hardware_message( struct desktop *desktop, struct message *msg unsigned int msg_code; int flags; - update_input_key_state( desktop, desktop->keystate, msg->msg, msg->wparam ); + update_desktop_key_state( desktop, msg->msg, msg->wparam ); last_input_time = get_tick_count(); if (msg->msg != WM_MOUSEMOVE) always_queue = 1; @@ -1736,7 +1745,7 @@ static void queue_hardware_message( struct desktop *desktop, struct message *msg flags = thread ? get_rawinput_device_flags( thread->process, msg ) : 0; if (!win || !thread || (flags & RIDEV_NOLEGACY)) { - if (input && !(flags & RIDEV_NOLEGACY)) update_input_key_state( input->desktop, input->keystate, msg->msg, msg->wparam ); + if (input && !(flags & RIDEV_NOLEGACY)) update_thread_input_key_state( input, msg->msg, msg->wparam ); free_message( msg ); return; } @@ -2528,7 +2537,7 @@ static int get_hardware_message( struct thread *thread, unsigned int hw_id, user if (!win || !win_thread) { /* no window at all, remove it */ - update_input_key_state( input->desktop, input->keystate, msg->msg, msg->wparam ); + update_thread_input_key_state( input, msg->msg, msg->wparam ); list_remove( &msg->entry ); free_message( msg ); continue; @@ -2544,7 +2553,7 @@ static int get_hardware_message( struct thread *thread, unsigned int hw_id, user else { /* for another thread input, drop it */ - update_input_key_state( input->desktop, input->keystate, msg->msg, msg->wparam ); + update_thread_input_key_state( input, msg->msg, msg->wparam ); list_remove( &msg->entry ); free_message( msg ); }
1
0
0
0
Rémi Bernon : server: Use a separate variable to determine the message on Alt release.
by Alexandre Julliard
21 Jun '24
21 Jun '24
Module: wine Branch: master Commit: 534aff4a6307cd289746c993aed2c2a70be8f57b URL:
https://gitlab.winehq.org/wine/wine/-/commit/534aff4a6307cd289746c993aed2c2…
Author: Rémi Bernon <rbernon(a)codeweavers.com> Date: Thu Jun 20 23:59:52 2024 +0200 server: Use a separate variable to determine the message on Alt release. Based on a patch by Huw Davies. --- server/queue.c | 11 +++++------ server/user.h | 1 + server/winstation.c | 1 + 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/server/queue.c b/server/queue.c index 1a98714004c..c17446a1dd9 100644 --- a/server/queue.c +++ b/server/queue.c @@ -2171,17 +2171,16 @@ static int queue_keyboard_message( struct desktop *desktop, user_handle_t win, c if (input->kbd.flags & KEYEVENTF_KEYUP) { /* send WM_SYSKEYUP if Alt still pressed and no other key in between */ - /* we use 0x02 as a flag to track if some other SYSKEYUP was sent already */ - if ((desktop->keystate[VK_MENU] & 0x82) != 0x82) break; + if (!(desktop->keystate[VK_MENU] & 0x80) || !desktop->alt_pressed) break; message_code = WM_SYSKEYUP; - desktop->keystate[VK_MENU] &= ~0x02; + desktop->alt_pressed = 0; } else { /* send WM_SYSKEYDOWN for Alt except with Ctrl */ if (desktop->keystate[VK_CONTROL] & 0x80) break; message_code = WM_SYSKEYDOWN; - desktop->keystate[VK_MENU] |= 0x02; + desktop->alt_pressed = 1; } break; @@ -2191,7 +2190,7 @@ static int queue_keyboard_message( struct desktop *desktop, user_handle_t win, c if (!(input->kbd.flags & KEYEVENTF_KEYUP)) break; if (!(desktop->keystate[VK_MENU] & 0x80)) break; message_code = WM_SYSKEYUP; - desktop->keystate[VK_MENU] &= ~0x02; + desktop->alt_pressed = 0; break; default: @@ -2201,7 +2200,7 @@ static int queue_keyboard_message( struct desktop *desktop, user_handle_t win, c /* fall through */ case VK_F10: message_code = (input->kbd.flags & KEYEVENTF_KEYUP) ? WM_SYSKEYUP : WM_SYSKEYDOWN; - desktop->keystate[VK_MENU] &= ~0x02; + desktop->alt_pressed = 0; break; } diff --git a/server/user.h b/server/user.h index 7355177e13d..99491293a7c 100644 --- a/server/user.h +++ b/server/user.h @@ -81,6 +81,7 @@ struct desktop struct thread_input *foreground_input; /* thread input of foreground thread */ unsigned int users; /* processes and threads using this desktop */ unsigned char keystate[256]; /* asynchronous key state */ + unsigned char alt_pressed; /* last key press was Alt (used to determine msg on release) */ struct key_repeat key_repeat; /* key auto-repeat */ unsigned int clip_flags; /* last cursor clip flags */ user_handle_t cursor_win; /* window that contains the cursor */ diff --git a/server/winstation.c b/server/winstation.c index 300d899638b..63bdc3389de 100644 --- a/server/winstation.c +++ b/server/winstation.c @@ -294,6 +294,7 @@ static struct desktop *create_desktop( const struct unicode_str *name, unsigned list_init( &desktop->threads ); desktop->clip_flags = 0; desktop->cursor_win = 0; + desktop->alt_pressed = 0; memset( desktop->keystate, 0, sizeof(desktop->keystate) ); memset( &desktop->key_repeat, 0, sizeof(desktop->key_repeat) ); list_add_tail( &winstation->desktops, &desktop->entry );
1
0
0
0
Rémi Bernon : win32u: Use the shared memory for get_clip_cursor.
by Alexandre Julliard
21 Jun '24
21 Jun '24
Module: wine Branch: master Commit: 5b013260d149b256b4da36c4caef155ff42a5cb7 URL:
https://gitlab.winehq.org/wine/wine/-/commit/5b013260d149b256b4da36c4caef15…
Author: Rémi Bernon <rbernon(a)codeweavers.com> Date: Fri Jun 21 00:31:15 2024 +0200 win32u: Use the shared memory for get_clip_cursor. --- dlls/win32u/input.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 2fbe21e56af..4d0c2589c1b 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -2555,24 +2555,21 @@ BOOL WINAPI NtUserGetPointerInfoList( UINT32 id, POINTER_INPUT_TYPE type, UINT_P BOOL get_clip_cursor( RECT *rect, UINT dpi ) { - BOOL ret; + struct object_lock lock = OBJECT_LOCK_INIT; + const desktop_shm_t *desktop_shm; + NTSTATUS status; if (!rect) return FALSE; - SERVER_START_REQ( set_cursor ) - { - req->flags = 0; - if ((ret = !wine_server_call( req ))) - *rect = wine_server_get_rect( reply->new_clip ); - } - SERVER_END_REQ; + while ((status = get_shared_desktop( &lock, &desktop_shm )) == STATUS_PENDING) + *rect = wine_server_get_rect( desktop_shm->cursor.clip ); - if (ret) + if (!status) { HMONITOR monitor = monitor_from_rect( rect, MONITOR_DEFAULTTOPRIMARY, 0 ); *rect = map_dpi_rect( *rect, get_monitor_dpi( monitor ), dpi ); } - return ret; + return !status; } BOOL process_wine_clipcursor( HWND hwnd, UINT flags, BOOL reset )
1
0
0
0
Rémi Bernon : server: Get rid of the global cursor structure.
by Alexandre Julliard
21 Jun '24
21 Jun '24
Module: wine Branch: master Commit: 472ce7fd1dd048899d2342576f302c4eb3759e6e URL:
https://gitlab.winehq.org/wine/wine/-/commit/472ce7fd1dd048899d2342576f302c…
Author: Rémi Bernon <rbernon(a)codeweavers.com> Date: Mon Feb 19 22:27:55 2024 +0100 server: Get rid of the global cursor structure. Based on a patch by Huw Davies. --- server/queue.c | 12 ++++++------ server/user.h | 9 ++------- server/winstation.c | 3 ++- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/server/queue.c b/server/queue.c index e06f6afe0f7..1a98714004c 100644 --- a/server/queue.c +++ b/server/queue.c @@ -440,7 +440,7 @@ static struct thread_input *get_desktop_cursor_thread_input( struct desktop *des struct thread_input *input = NULL; struct thread *thread; - if ((thread = get_window_thread( desktop->cursor.win ))) + if ((thread = get_window_thread( desktop->cursor_win ))) { if (thread->queue) input = thread->queue->input; release_object( thread ); @@ -451,9 +451,9 @@ static struct thread_input *get_desktop_cursor_thread_input( struct desktop *des static int update_desktop_cursor_window( struct desktop *desktop, user_handle_t win ) { - int updated = win != desktop->cursor.win; + int updated = win != desktop->cursor_win; struct thread_input *input; - desktop->cursor.win = win; + desktop->cursor_win = win; if (updated && (input = get_desktop_cursor_thread_input( desktop ))) { @@ -495,7 +495,7 @@ static void update_desktop_cursor_handle( struct desktop *desktop, struct thread { if (input == get_desktop_cursor_thread_input( desktop )) { - user_handle_t win = desktop->cursor.win; + user_handle_t win = desktop->cursor_win; /* when clipping send the message to the foreground window as well, as some driver have an artificial overlay window */ if (is_cursor_clipped( desktop )) queue_cursor_message( desktop, 0, WM_WINE_SETCURSOR, win, handle ); queue_cursor_message( desktop, win, WM_WINE_SETCURSOR, win, handle ); @@ -560,8 +560,8 @@ void set_clip_rectangle( struct desktop *desktop, const rectangle_t *rect, unsig } SHARED_WRITE_END; - old_flags = desktop->cursor.clip_flags; - desktop->cursor.clip_flags = flags; + old_flags = desktop->clip_flags; + desktop->clip_flags = flags; /* warp the mouse to be inside the clip rect */ x = max( min( desktop_shm->cursor.x, new_rect.right - 1 ), new_rect.left ); diff --git a/server/user.h b/server/user.h index 7ad1e82ae54..7355177e13d 100644 --- a/server/user.h +++ b/server/user.h @@ -54,12 +54,6 @@ struct winstation struct namespace *desktop_names; /* namespace for desktops of this winstation */ }; -struct global_cursor -{ - unsigned int clip_flags; /* last cursor clip flags */ - user_handle_t win; /* window that contains the cursor */ -}; - struct key_repeat { int enable; /* enable auto-repeat */ @@ -86,9 +80,10 @@ struct desktop struct timeout_user *close_timeout; /* timeout before closing the desktop */ struct thread_input *foreground_input; /* thread input of foreground thread */ unsigned int users; /* processes and threads using this desktop */ - struct global_cursor cursor; /* global cursor information */ unsigned char keystate[256]; /* asynchronous key state */ struct key_repeat key_repeat; /* key auto-repeat */ + unsigned int clip_flags; /* last cursor clip flags */ + user_handle_t cursor_win; /* window that contains the cursor */ const desktop_shm_t *shared; /* desktop session shared memory */ }; diff --git a/server/winstation.c b/server/winstation.c index 3e08f4b3337..300d899638b 100644 --- a/server/winstation.c +++ b/server/winstation.c @@ -292,7 +292,8 @@ static struct desktop *create_desktop( const struct unicode_str *name, unsigned desktop->foreground_input = NULL; desktop->users = 0; list_init( &desktop->threads ); - memset( &desktop->cursor, 0, sizeof(desktop->cursor) ); + desktop->clip_flags = 0; + desktop->cursor_win = 0; memset( desktop->keystate, 0, sizeof(desktop->keystate) ); memset( &desktop->key_repeat, 0, sizeof(desktop->key_repeat) ); list_add_tail( &winstation->desktops, &desktop->entry );
1
0
0
0
Rémi Bernon : server: Store the cursor clip rect in the shared data.
by Alexandre Julliard
21 Jun '24
21 Jun '24
Module: wine Branch: master Commit: 496f6631574aea0bc372276a9547afa7f53af5ca URL:
https://gitlab.winehq.org/wine/wine/-/commit/496f6631574aea0bc372276a9547af…
Author: Rémi Bernon <rbernon(a)codeweavers.com> Date: Mon Feb 19 22:25:01 2024 +0100 server: Store the cursor clip rect in the shared data. Based on a patch by Huw Davies. --- include/wine/server_protocol.h | 3 ++- server/protocol.def | 1 + server/queue.c | 26 ++++++++++++++++---------- server/user.h | 1 - server/winstation.c | 4 ++++ 5 files changed, 23 insertions(+), 12 deletions(-) diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index df31a957486..162d00bd65f 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -888,6 +888,7 @@ struct shared_cursor int x; int y; unsigned int last_change; + rectangle_t clip; }; typedef volatile struct @@ -6567,7 +6568,7 @@ union generic_reply /* ### protocol_version begin ### */ -#define SERVER_PROTOCOL_VERSION 813 +#define SERVER_PROTOCOL_VERSION 814 /* ### protocol_version end ### */ diff --git a/server/protocol.def b/server/protocol.def index 20ed82982dd..0592dddaa97 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -904,6 +904,7 @@ struct shared_cursor int x; /* cursor position */ int y; unsigned int last_change; /* time of last position change */ + rectangle_t clip; /* cursor clip rectangle */ }; typedef volatile struct diff --git a/server/queue.c b/server/queue.c index c2e957cfd21..e06f6afe0f7 100644 --- a/server/queue.c +++ b/server/queue.c @@ -410,7 +410,8 @@ static struct message *alloc_hardware_message( lparam_t info, struct hw_msg_sour static int is_cursor_clipped( struct desktop *desktop ) { - rectangle_t top_rect, clip_rect = desktop->cursor.clip; + const desktop_shm_t *desktop_shm = desktop->shared; + rectangle_t top_rect, clip_rect = desktop_shm->cursor.clip; get_top_window_rectangle( desktop, &top_rect ); return !is_rect_equal( &clip_rect, &top_rect ); } @@ -471,8 +472,8 @@ static int update_desktop_cursor_pos( struct desktop *desktop, user_handle_t win int updated; unsigned int time = get_tick_count(); - x = max( min( x, desktop->cursor.clip.right - 1 ), desktop->cursor.clip.left ); - y = max( min( y, desktop->cursor.clip.bottom - 1 ), desktop->cursor.clip.top ); + x = max( min( x, desktop_shm->cursor.clip.right - 1 ), desktop_shm->cursor.clip.left ); + y = max( min( y, desktop_shm->cursor.clip.bottom - 1 ), desktop_shm->cursor.clip.top ); SHARED_WRITE_BEGIN( desktop_shm, desktop_shm_t ) { @@ -537,29 +538,34 @@ static void get_message_defaults( struct msg_queue *queue, int *x, int *y, unsig void set_clip_rectangle( struct desktop *desktop, const rectangle_t *rect, unsigned int flags, int reset ) { const desktop_shm_t *desktop_shm = desktop->shared; - rectangle_t top_rect; + rectangle_t top_rect, new_rect; unsigned int old_flags; int x, y; get_top_window_rectangle( desktop, &top_rect ); if (rect) { - rectangle_t new_rect = *rect; + new_rect = *rect; if (new_rect.left < top_rect.left) new_rect.left = top_rect.left; if (new_rect.right > top_rect.right) new_rect.right = top_rect.right; if (new_rect.top < top_rect.top) new_rect.top = top_rect.top; if (new_rect.bottom > top_rect.bottom) new_rect.bottom = top_rect.bottom; if (new_rect.left > new_rect.right || new_rect.top > new_rect.bottom) new_rect = top_rect; - desktop->cursor.clip = new_rect; } - else desktop->cursor.clip = top_rect; + else new_rect = top_rect; + + SHARED_WRITE_BEGIN( desktop_shm, desktop_shm_t ) + { + shared->cursor.clip = new_rect; + } + SHARED_WRITE_END; old_flags = desktop->cursor.clip_flags; desktop->cursor.clip_flags = flags; /* warp the mouse to be inside the clip rect */ - x = max( min( desktop_shm->cursor.x, desktop->cursor.clip.right - 1 ), desktop->cursor.clip.left ); - y = max( min( desktop_shm->cursor.y, desktop->cursor.clip.bottom - 1 ), desktop->cursor.clip.top ); + x = max( min( desktop_shm->cursor.x, new_rect.right - 1 ), new_rect.left ); + y = max( min( desktop_shm->cursor.y, new_rect.bottom - 1 ), new_rect.top ); if (x != desktop_shm->cursor.x || y != desktop_shm->cursor.y) set_cursor_pos( desktop, x, y ); /* request clip cursor rectangle reset to the desktop thread */ @@ -3745,7 +3751,7 @@ DECL_HANDLER(set_cursor) reply->new_x = desktop_shm->cursor.x; reply->new_y = desktop_shm->cursor.y; - reply->new_clip = desktop->cursor.clip; + reply->new_clip = desktop_shm->cursor.clip; reply->last_change = desktop_shm->cursor.last_change; } diff --git a/server/user.h b/server/user.h index a20aff99284..7ad1e82ae54 100644 --- a/server/user.h +++ b/server/user.h @@ -56,7 +56,6 @@ struct winstation struct global_cursor { - rectangle_t clip; /* cursor clip rectangle */ unsigned int clip_flags; /* last cursor clip flags */ user_handle_t win; /* window that contains the cursor */ }; diff --git a/server/winstation.c b/server/winstation.c index 87981f64167..3e08f4b3337 100644 --- a/server/winstation.c +++ b/server/winstation.c @@ -310,6 +310,10 @@ static struct desktop *create_desktop( const struct unicode_str *name, unsigned shared->cursor.x = 0; shared->cursor.y = 0; shared->cursor.last_change = 0; + shared->cursor.clip.left = 0; + shared->cursor.clip.top = 0; + shared->cursor.clip.right = 0; + shared->cursor.clip.bottom = 0; } SHARED_WRITE_END; }
1
0
0
0
← Newer
1
...
16
17
18
19
20
21
22
...
62
Older →
Jump to page:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
Results per page:
10
25
50
100
200