[PATCH v4 0/3] MR11699: win32u and explorer: Exclude taskbar and appbars from system work area rect
This is a package of changes that fix https://bugs.winehq.org/show_bug.cgi?id=59976 and possibly other issues. It is both a usability and a compatibility improvement. Please see the commit messages for further details. Feedback on if anything here is reasonable to write conformance tests for would be appreciated. I don't know if it makes sense for the IPC things. I did a bunch of manual testing of how Windows and WINE behave using two small test programs: [appbar.c](/uploads/49ed846bdbd32bfecdb0603304f5c332/testapp.c) and [workarea.c](/-/project/5/uploads/ab8f82c70d8df1e337e8b15be5f32cab/workarea.c). --- Before these changes: {width="729" height="600"} {width="729" height="600"} Note how: * The Office 97 Shortcut Bar is covering up part of the taskbar. This is worse if it's docked to the right (which is the default), because then it covers up system tray icons. * The maximized Word 97 window is covering up both the taskbar and the Shortcut Bar. After these changes: {width="729" height="600"} You'll notice that the Word window here is still overhanging both the taskbar and the Shortcut Bar. I believe that's correct behavior as maximized windows always overhang the screen, and it just looks weird because WINE doesn't support `WS_EX_TOPMOST` yet in desktop mode. -- v4: explorer: Update system work area rect to exclude taskbar and appbars explorer: Account for the actual size of the taskbar if it is visible win32u: Apply work area changes to monitor info (in all processes) https://gitlab.winehq.org/wine/wine/-/merge_requests/11699
From: Andrea Faulds <ajf@ajf.me> Before this commit: - SPI_SETWORKAREA was only affecting the result of SPI_GETWORKAREA, which meant that, for example, it didn't affect the code in dlls/win32u/window.c which determines the size of a maximized window. - SPI_SETWORKAREA was only affecting the current process, rather than all processes on the same desktop. The multiprocess aspect is impractical to test with WINE's automated tests, but manual tests with a simple program found that SPI_SETWORKAREA does in fact affect the maximized size for itself and other processes on Windows 2000 and XP. --- dlls/win32u/sysparams.c | 90 ++++++++++++++++++++++------------ include/wine/server_protocol.h | 19 ++++++- server/protocol.def | 7 +++ server/queue.c | 18 +++++++ server/request_handlers.h | 4 ++ server/request_trace.h | 9 ++++ server/winstation.c | 4 ++ 7 files changed, 118 insertions(+), 33 deletions(-) diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index fa22d6294c0..4301bcd3500 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -294,7 +294,6 @@ union sysparam_all_entry static const struct ratio no_dpi; UINT system_dpi; -static RECT work_area; static DWORD process_layout = ~0u; static HDC display_dc; @@ -4697,6 +4696,27 @@ static BOOL get_monitor_info( HMONITOR handle, MONITORINFO *info, struct ratio d monitor_get_info( monitor, info, dpi ); unlock_display_devices(); + /* the primary monitor's work area can be updated by another process */ + if (is_monitor_primary( monitor )) + { + struct object_lock lock = OBJECT_LOCK_INIT; + const desktop_shm_t *desktop_shm; + NTSTATUS status; + struct rectangle shared_rect = { 0 }; + while ((status = get_shared_desktop( &lock, &desktop_shm )) == STATUS_PENDING) + shared_rect = desktop_shm->work_area; + TRACE( "got shared work area rect: %d %d %d %d\n", + shared_rect.left, shared_rect.top, shared_rect.right, shared_rect.bottom); + if (!(shared_rect.left == 0 && shared_rect.right == 0 && + shared_rect.top == 0 && shared_rect.bottom == 0)) + { + info->rcWork = map_monitor_rect( + monitor, wine_server_get_rect( shared_rect ), + dpi, MDT_DEFAULT, no_dpi, MDT_RAW_DPI + ); + } + } + TRACE( "flags %04x, monitor %s, work %s\n", info->dwFlags, wine_dbgstr_rect(&info->rcMonitor), wine_dbgstr_rect(&info->rcWork)); return TRUE; @@ -5728,16 +5748,6 @@ static USERPREF_ENTRY( CLIENTAREAANIMATION, 4, 0x02 ); static USERPREF_ENTRY( CLEARTYPE, 4, 0x10 ); static USERPREF_ENTRY( SPEECHRECOGNITION, 4, 0x20 ); -/* System parameter indexes */ -enum spi_index -{ - SPI_SETWORKAREA_IDX, - SPI_INDEX_COUNT -}; - -/* indicators whether system parameter value is loaded */ -static char spi_loaded[SPI_INDEX_COUNT]; - static struct sysparam_rgb_entry system_colors[] = { #define RGB_ENTRY(name,val,reg) { { get_rgb_entry, set_rgb_entry, init_rgb_entry, COLORS_KEY, reg }, (val) } @@ -6122,7 +6132,6 @@ BOOL WINAPI NtUserSystemParametersInfo( UINT action, UINT val, void *ptr, UINT w break BOOL ret = user_driver->pSystemParametersInfo( action, val, ptr, winini ); - unsigned spi_idx = 0; if (!ret) switch (action) { @@ -6362,41 +6371,58 @@ BOOL WINAPI NtUserSystemParametersInfo( UINT action, UINT val, void *ptr, UINT w } case SPI_SETWORKAREA: { + struct monitor *monitor; + struct ratio dpi = get_thread_dpi(); + RECT nodpi_rcWork = { 0 }; + if (!ptr) return FALSE; - spi_idx = SPI_SETWORKAREA_IDX; - work_area = *(RECT*)ptr; - spi_loaded[spi_idx] = TRUE; + + if (!lock_display_devices( FALSE )) return FALSE; + + LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry ) + { + if (!is_monitor_primary( monitor )) continue; + nodpi_rcWork = map_monitor_rect( monitor, *(RECT *)ptr, dpi, MDT_DEFAULT, no_dpi, MDT_RAW_DPI ); + break; + } + + unlock_display_devices(); + + /* Update the work area for all WINE processes on this desktop. */ + SERVER_START_REQ( set_work_area ) + { + req->work_area = wine_server_rectangle( nodpi_rcWork ); + TRACE("SERVER_START_REQ( set_work_area ): %d %d %d %d\n", + nodpi_rcWork.left, nodpi_rcWork.top, nodpi_rcWork.right, nodpi_rcWork.bottom); + wine_server_call( req ); + } + SERVER_END_REQ; + ret = TRUE; break; } case SPI_GETWORKAREA: { + struct monitor *monitor; MONITORINFO info = {.cbSize = sizeof(info)}; struct ratio dpi = get_thread_dpi(); if (!ptr) return FALSE; - spi_idx = SPI_SETWORKAREA_IDX; - if (!spi_loaded[spi_idx]) - { - struct monitor *monitor; + if (!lock_display_devices( FALSE )) return FALSE; - if (!lock_display_devices( FALSE )) return FALSE; + LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry ) + { + if (!is_monitor_primary( monitor )) continue; + monitor_get_info( monitor, &info, dpi ); + *(RECT *)ptr = info.rcWork; + break; + } - LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry ) - { - if (!is_monitor_primary( monitor )) continue; - monitor_get_info( monitor, &info, dpi ); - work_area = info.rcWork; - break; - } + unlock_display_devices(); - unlock_display_devices(); - spi_loaded[spi_idx] = TRUE; - } - *(RECT *)ptr = work_area; ret = TRUE; - TRACE("work area %s\n", wine_dbgstr_rect( &work_area )); + TRACE("work area %s\n", wine_dbgstr_rect( (RECT *)ptr )); break; } diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index 2f04955a486..b995343d642 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -998,6 +998,7 @@ typedef volatile struct unsigned int flags; struct shared_cursor cursor; unsigned char keystate[256]; + struct rectangle work_area; unsigned __int64 monitor_serial; unsigned __int64 keystate_serial; } desktop_shm_t; @@ -5820,6 +5821,19 @@ struct get_cursor_history_reply +struct set_work_area_request +{ + struct request_header __header; + struct rectangle work_area; + char __pad_28[4]; +}; +struct set_work_area_reply +{ + struct reply_header __header; +}; + + + struct get_rawinput_buffer_request { struct request_header __header; @@ -6521,6 +6535,7 @@ enum request REQ_free_user_handle, REQ_set_cursor, REQ_get_cursor_history, + REQ_set_work_area, REQ_get_rawinput_buffer, REQ_update_rawinput_devices, REQ_create_job, @@ -6836,6 +6851,7 @@ union generic_request struct free_user_handle_request free_user_handle_request; struct set_cursor_request set_cursor_request; struct get_cursor_history_request get_cursor_history_request; + struct set_work_area_request set_work_area_request; struct get_rawinput_buffer_request get_rawinput_buffer_request; struct update_rawinput_devices_request update_rawinput_devices_request; struct create_job_request create_job_request; @@ -7149,6 +7165,7 @@ union generic_reply struct free_user_handle_reply free_user_handle_reply; struct set_cursor_reply set_cursor_reply; struct get_cursor_history_reply get_cursor_history_reply; + struct set_work_area_reply set_work_area_reply; struct get_rawinput_buffer_reply get_rawinput_buffer_reply; struct update_rawinput_devices_reply update_rawinput_devices_reply; struct create_job_reply create_job_reply; @@ -7177,6 +7194,6 @@ union generic_reply struct alpc_create_port_reply alpc_create_port_reply; }; -#define SERVER_PROTOCOL_VERSION 959 +#define SERVER_PROTOCOL_VERSION 960 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */ diff --git a/server/protocol.def b/server/protocol.def index 118bafd9c9b..24db39980f5 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1014,6 +1014,7 @@ typedef volatile struct unsigned int flags; /* desktop flags */ struct shared_cursor cursor; /* global cursor information */ unsigned char keystate[256]; /* asynchronous key state */ + struct rectangle work_area; /* work area rect for primary monitor (if set with SPI_SETWORKAREA) */ unsigned __int64 monitor_serial; /* winstation monitor update counter */ unsigned __int64 keystate_serial; /* keystate update counter */ } desktop_shm_t; @@ -4082,6 +4083,12 @@ struct handle_info @END +/* Set the work area rectangle for the primary monitor */ +@REQ(set_work_area) + struct rectangle work_area; /* work area rectangle to set */ +@END + + /* Batch read rawinput message data */ @REQ(get_rawinput_buffer) data_size_t header_size; /* size of RAWINPUTHEADER structure */ diff --git a/server/queue.c b/server/queue.c index 3d8ae3f96dd..7df45709fcf 100644 --- a/server/queue.c +++ b/server/queue.c @@ -4040,6 +4040,24 @@ DECL_HANDLER(get_cursor_history) pos[i] = cursor_history[(i + cursor_history_latest) % ARRAY_SIZE(cursor_history)]; } +/* Set the work area rectangle for the primary monitor */ +DECL_HANDLER(set_work_area) +{ + struct msg_queue *queue = get_current_queue(); + struct desktop *desktop; + desktop_shm_t *desktop_shm; + + if (!queue) return; + desktop = queue->input->desktop; + desktop_shm = desktop->shared; + + SHARED_WRITE_BEGIN( desktop_shm, desktop_shm_t ) + { + shared->work_area = req->work_area; + } + SHARED_WRITE_END; +} + DECL_HANDLER(get_rawinput_buffer) { const size_t align = is_machine_64bit( current->process->machine ) ? 7 : 3; diff --git a/server/request_handlers.h b/server/request_handlers.h index 3372c6c92db..b0b6c615826 100644 --- a/server/request_handlers.h +++ b/server/request_handlers.h @@ -289,6 +289,7 @@ DECL_HANDLER(alloc_user_handle); DECL_HANDLER(free_user_handle); DECL_HANDLER(set_cursor); DECL_HANDLER(get_cursor_history); +DECL_HANDLER(set_work_area); DECL_HANDLER(get_rawinput_buffer); DECL_HANDLER(update_rawinput_devices); DECL_HANDLER(create_job); @@ -601,6 +602,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = (req_handler)req_free_user_handle, (req_handler)req_set_cursor, (req_handler)req_get_cursor_history, + (req_handler)req_set_work_area, (req_handler)req_get_rawinput_buffer, (req_handler)req_update_rawinput_devices, (req_handler)req_create_job, @@ -2261,6 +2263,8 @@ C_ASSERT( offsetof(struct set_cursor_reply, last_change) == 48 ); C_ASSERT( sizeof(struct set_cursor_reply) == 56 ); C_ASSERT( sizeof(struct get_cursor_history_request) == 16 ); C_ASSERT( sizeof(struct get_cursor_history_reply) == 8 ); +C_ASSERT( offsetof(struct set_work_area_request, work_area) == 12 ); +C_ASSERT( sizeof(struct set_work_area_request) == 32 ); C_ASSERT( offsetof(struct get_rawinput_buffer_request, header_size) == 12 ); C_ASSERT( offsetof(struct get_rawinput_buffer_request, read_data) == 16 ); C_ASSERT( sizeof(struct get_rawinput_buffer_request) == 24 ); diff --git a/server/request_trace.h b/server/request_trace.h index eed94a179b8..c10129aaefe 100644 --- a/server/request_trace.h +++ b/server/request_trace.h @@ -3266,6 +3266,11 @@ static void dump_get_cursor_history_reply( const struct get_cursor_history_reply dump_varargs_cursor_positions( " history=", cur_size ); } +static void dump_set_work_area_request( const struct set_work_area_request *req ) +{ + dump_rectangle( " work_area=", &req->work_area ); +} + static void dump_get_rawinput_buffer_request( const struct get_rawinput_buffer_request *req ) { fprintf( stderr, " header_size=%u", req->header_size ); @@ -3821,6 +3826,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = (dump_func)dump_free_user_handle_request, (dump_func)dump_set_cursor_request, (dump_func)dump_get_cursor_history_request, + (dump_func)dump_set_work_area_request, (dump_func)dump_get_rawinput_buffer_request, (dump_func)dump_update_rawinput_devices_request, (dump_func)dump_create_job_request, @@ -4133,6 +4139,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = NULL, (dump_func)dump_set_cursor_reply, (dump_func)dump_get_cursor_history_reply, + NULL, (dump_func)dump_get_rawinput_buffer_reply, NULL, (dump_func)dump_create_job_reply, @@ -4445,6 +4452,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = "free_user_handle", "set_cursor", "get_cursor_history", + "set_work_area", "get_rawinput_buffer", "update_rawinput_devices", "create_job", @@ -4550,6 +4558,7 @@ static const struct { "INVALID_PIPE_STATE", STATUS_INVALID_PIPE_STATE }, { "INVALID_READ_MODE", STATUS_INVALID_READ_MODE }, { "INVALID_SECURITY_DESCR", STATUS_INVALID_SECURITY_DESCR }, + { "INVALID_STATE_TRANSITION", STATUS_INVALID_STATE_TRANSITION }, { "INVALID_USER_BUFFER", STATUS_INVALID_USER_BUFFER }, { "IO_REPARSE_DATA_INVALID", STATUS_IO_REPARSE_DATA_INVALID }, { "IO_REPARSE_TAG_INVALID", STATUS_IO_REPARSE_TAG_INVALID }, diff --git a/server/winstation.c b/server/winstation.c index d27adc3ecb4..9b3fad5f0fd 100644 --- a/server/winstation.c +++ b/server/winstation.c @@ -299,6 +299,10 @@ static bool desktop_init( struct object *obj, const void *init_data ) shared->cursor.clip.top = 0; shared->cursor.clip.right = 0; shared->cursor.clip.bottom = 0; + shared->work_area.left = 0; + shared->work_area.top = 0; + shared->work_area.right = 0; + shared->work_area.bottom = 0; memset( (void *)shared->keystate, 0, sizeof(shared->keystate) ); shared->keystate_serial = 1; shared->monitor_serial = winstation->monitor_serial; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11699
From: Andrea Faulds <ajf@ajf.me> This means that, in desktop mode, appbars like the Microsoft Office Shortcut Bar can avoid overlapping the taskbar. --- programs/explorer/appbar.c | 42 ++++++++++++++++++++++------ programs/explorer/desktop.c | 3 +- programs/explorer/explorer_private.h | 1 + programs/explorer/systray.c | 6 ++++ 4 files changed, 42 insertions(+), 10 deletions(-) diff --git a/programs/explorer/appbar.c b/programs/explorer/appbar.c index 18b125257ee..46a988ebbed 100644 --- a/programs/explorer/appbar.c +++ b/programs/explorer/appbar.c @@ -73,13 +73,19 @@ struct appbar_data static struct list appbars = LIST_INIT(appbars); +static struct appbar_data* get_taskbar(void) +{ + return (struct appbar_data*)list_head( &appbars ); +} + static struct appbar_data* get_appbar(HWND hwnd) { struct appbar_data* data; LIST_FOR_EACH_ENTRY(data, &appbars, struct appbar_data, entry) { - if (data->hwnd == hwnd) + /* ignore taskbar to prevent accidental modification */ + if (data->hwnd == hwnd && data != get_taskbar()) return data; } @@ -92,7 +98,7 @@ static void send_poschanged(HWND hwnd) struct appbar_data* data; LIST_FOR_EACH_ENTRY(data, &appbars, struct appbar_data, entry) { - if (data->hwnd != hwnd) + if (data->hwnd != hwnd && data != get_taskbar()) { PostMessageW(data->hwnd, data->callback_msg, ABN_POSCHANGED, 0); } @@ -202,12 +208,9 @@ static UINT_PTR handle_appbarmessage(DWORD msg, struct appbar_data_msg *abd) return ABS_ALWAYSONTOP | ABS_AUTOHIDE; case ABM_GETTASKBARPOS: FIXME( "SHAppBarMessage(ABM_GETTASKBARPOS, hwnd=%p): stub\n", hwnd ); - /* Report the taskbar is at the bottom of the screen. */ - abd->rc.left = 0; - abd->rc.right = GetSystemMetrics(SM_CXSCREEN); - abd->rc.bottom = GetSystemMetrics(SM_CYSCREEN); - abd->rc.top = abd->rc.bottom-1; - abd->uEdge = ABE_BOTTOM; + data = get_taskbar(); + abd->rc = data->rc; + abd->uEdge = data->edge; return TRUE; case ABM_ACTIVATE: return TRUE; @@ -288,6 +291,8 @@ static LRESULT CALLBACK appbar_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARA void initialize_appbar(void) { WNDCLASSEXW class; + struct appbar_data* data; + int taskbar_height; /* register the appbar window class */ ZeroMemory(&class, sizeof(class)); @@ -308,4 +313,25 @@ void initialize_appbar(void) ERR( "Could not create appbar message window\n" ); return; } + + /* create appbar record for the taskbar (it has a privileged status and + * isn't registered like a normal appbar, this just simplifies accounting) + */ + data = calloc( 1, sizeof(struct appbar_data) ); + if (!data) + { + ERR( "out of memory\n" ); + return; + } + data->rc.left = 0; + data->rc.right = GetSystemMetrics(SM_CXSCREEN); + data->rc.bottom = GetSystemMetrics(SM_CYSCREEN); + taskbar_height = get_taskbar_height(); + if (taskbar_height == 0) + taskbar_height = 1; /* ensure taskbar has non-zero height */ + data->rc.top = data->rc.bottom - taskbar_height; + data->edge = ABE_BOTTOM; + data->space_reserved = TRUE; + + list_add_tail(&appbars, &data->entry); } diff --git a/programs/explorer/desktop.c b/programs/explorer/desktop.c index ce70c5dd79d..4f52b93afc7 100644 --- a/programs/explorer/desktop.c +++ b/programs/explorer/desktop.c @@ -1288,9 +1288,8 @@ void manage_desktop( WCHAR *arg ) SystemParametersInfoW( SPI_SETDESKWALLPAPER, 0, NULL, FALSE ); ClipCursor( NULL ); initialize_display_settings( width, height ); - initialize_appbar(); - initialize_systray( using_root, enable_shell, show_systray, no_tray_items ); + initialize_appbar(); if (!using_root && enable_launchers) initialize_launchers( hwnd ); if ((shell32 = LoadLibraryW( L"shell32.dll" )) && diff --git a/programs/explorer/explorer_private.h b/programs/explorer/explorer_private.h index 10e8aa0653f..443b8fcaee9 100644 --- a/programs/explorer/explorer_private.h +++ b/programs/explorer/explorer_private.h @@ -27,5 +27,6 @@ extern void initialize_appbar(void); extern void handle_parent_notify( HWND hwnd, WPARAM wp ); extern void do_startmenu( HWND owner ); extern LRESULT menu_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); +extern int get_taskbar_height(void); #endif /* __WINE_EXPLORER_PRIVATE_H */ diff --git a/programs/explorer/systray.c b/programs/explorer/systray.c index 69d3ec61b6a..175bc4d13a0 100644 --- a/programs/explorer/systray.c +++ b/programs/explorer/systray.c @@ -1097,6 +1097,12 @@ static void do_show_systray(void) sync_taskbar_buttons(); } +/* for use by appbar.c's accounting */ +int get_taskbar_height(void) +{ + return enable_taskbar ? tray_height : 0; +} + static LRESULT WINAPI shell_traywnd_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) { switch (msg) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11699
From: Andrea Faulds <ajf@ajf.me> Without this, while appbars are positioned correctly, maximized windows will still cover them. Manual testing on Windows 2000 and XP found that the work area rect seems to be managed by explorer (it gets reset when explorer is started, registering appbars appears to change the work area, and killing explorer makes the work area stop changing). Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=59976 --- programs/explorer/appbar.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/programs/explorer/appbar.c b/programs/explorer/appbar.c index 46a988ebbed..09d8ad1290c 100644 --- a/programs/explorer/appbar.c +++ b/programs/explorer/appbar.c @@ -111,7 +111,7 @@ static void appbar_cliprect( HWND hwnd, RECT *rect ) struct appbar_data* data; LIST_FOR_EACH_ENTRY(data, &appbars, struct appbar_data, entry) { - if (data->hwnd == hwnd) + if (hwnd != NULL && data->hwnd == hwnd) { /* we only care about appbars that were added before this one */ return; @@ -138,6 +138,26 @@ static void appbar_cliprect( HWND hwnd, RECT *rect ) } } +/* update the system work area rectangle to exclude the taskbar and appbars + * (affects what part of the screen maximized windows cover for example) + */ +static void update_work_area(void) +{ + RECT rc = { 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN) }; + + /* When not running in desktop mode, don't set the work area in order to + * avoid interfering with the non-WINE desktop environment's window + * management. (See for example freedesktop's _NET_WORKAREA.) + */ + if (get_taskbar_height() == 0) + return; + + appbar_cliprect( NULL, &rc ); + SystemParametersInfoW( SPI_SETWORKAREA, 0, &rc, 0 ); + + /* TODO: move and resize existing windows to fit the new work area */ +} + static UINT_PTR handle_appbarmessage(DWORD msg, struct appbar_data_msg *abd) { struct appbar_data* data; @@ -172,6 +192,8 @@ static UINT_PTR handle_appbarmessage(DWORD msg, struct appbar_data_msg *abd) send_poschanged(hwnd); free( data ); + + update_work_area(); } else WARN( "removing hwnd %p not on the list\n", hwnd ); return TRUE; @@ -197,6 +219,8 @@ static UINT_PTR handle_appbarmessage(DWORD msg, struct appbar_data_msg *abd) data->edge = abd->uEdge; data->rc = abd->rc; data->space_reserved = TRUE; + + update_work_area(); } else { @@ -334,4 +358,6 @@ void initialize_appbar(void) data->space_reserved = TRUE; list_add_tail(&appbars, &data->entry); + + update_work_area(); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11699
This probably should be done somewhere inside or alongside the `add_virtual_source` function in `win32u/sysparams.c`. That function creates the virtual monitor used for the virtual desktop mode, and the monitor should already support a non-fullscreen work area, and that work area should then be already shared with every other process like is the case for non-virtual desktop mode. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11699#note_149228
On Wed Aug 19 11:00:24 2026 +0000, Rémi Bernon wrote:
This probably should be done somewhere inside or alongside the `add_virtual_source` function in `win32u/sysparams.c`. That function creates the virtual monitor used for the virtual desktop mode, and the monitor should already support a non-fullscreen work area, and that work area should then be already shared with every other process like is the case for non-virtual desktop mode. Hmm, do you mean that the virtual monitor should have the work area rect adjusted at creation time? I don't think that works because the work area is meant to be a dynamic property. But I might be misunderstanding what you mean.
If you mean that this change should be made specific to the virtual monitor only: that would be reasonable, though one subtle aspect of the current patch is that it also attempts to preserve the ability to override the reported work area when _not_ in virtual desktop mode. I assume some app relies on that, otherwise WINE wouldn't have implemented it, though I must admit I don't know what it's useful for. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11699#note_149239
On Wed Aug 19 11:00:24 2026 +0000, Andrea Faulds wrote:
Hmm, do you mean that the virtual monitor should have the work area rect adjusted at creation time? I don't think that works because the work area is meant to be a dynamic property. But I might be misunderstanding what you mean. If you mean that this change should be made specific to the virtual monitor only: that would be reasonable, though one subtle aspect of the current patch is that it also attempts to preserve the ability to override the reported work area when _not_ in virtual desktop mode. I assume some app relies on that, otherwise WINE wouldn't have implemented it, though I must admit I don't know what it's useful for. It can be set at creation time but it can also be set later, we should support display device changes and that should already be synchronized across processes. One process can request a change, then calling `NtUserCallNoParam( NtUserCallNoParam_DisplayModeChanged )` should trigger a display devices refresh (which should take the change into account when recreating the virtual desktop monitor).
That could also work for non virtual desktop mode, by overriding each monitor rcWork when they are enumerated, although I'm not completely sure this is a good idea. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11699#note_149241
On Wed Aug 19 11:10:48 2026 +0000, Rémi Bernon wrote:
It can be set at creation time but it can also be set later, we should support display device changes and that should already be synchronized across processes. One process can request a change, then calling `NtUserCallNoParam( NtUserCallNoParam_DisplayModeChanged )` should trigger a display devices refresh (which should take the change into account when recreating the virtual desktop monitor). That could also work for non virtual desktop mode, by overriding each monitor rcWork when they are enumerated, although I'm not completely sure this is a good idea. Ah, so there _is_ a mechanism for syncing changes across processes? I had wanted to set this in the monitor info directly, but couldn't seem to find anywhere the cache was invalidated or the monitor info was read back from the server, despite the existence of the “monitor serial” implying this should exist.
Maybe that function is what I was missing. I'll dig into that, thanks a lot for the pointer. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11699#note_149242
participants (3)
-
Andrea Faulds -
Andrea Faulds (@hikari_no_yume) -
Rémi Bernon (@rbernon)