Add a function to display the system menu for a window
Add SDL_ShowWindowSystemMenu() to display the system-level menu for windows. Typically, this is done by right-clicking on the system provided window decorations, however, if an application is rendering its own client-side decorations, there is currently no way to display it. This menu is provided by the system and can provide privileged desktop functionality such as moving or pinning a window to a specific workspace or display, setting the always-on-top property, or taking screenshots. In many cases, there are no APIs which allow applications to perform these actions manually. Implemented for Wayland via functionality provided by the xdg_toplevel protocol, Win32 via the undocumented message 0x313 (typically called WM_POPUPSYSTEMMENU), and X11 via the "_GTK_SHOW_WINDOW_MENU" atom (supported in GNOME and KDE).main
parent
be5f66c84e
commit
70323a8350
|
@ -118,6 +118,7 @@ typedef enum
|
|||
* \sa SDL_SetWindowResizable()
|
||||
* \sa SDL_SetWindowTitle()
|
||||
* \sa SDL_ShowWindow()
|
||||
* \sa SDL_ShowWindowSystemMenu()
|
||||
*/
|
||||
typedef struct SDL_Window SDL_Window;
|
||||
|
||||
|
@ -1675,6 +1676,28 @@ extern DECLSPEC int SDLCALL SDL_SetWindowModalFor(SDL_Window *modal_window, SDL_
|
|||
*/
|
||||
extern DECLSPEC int SDLCALL SDL_SetWindowInputFocus(SDL_Window *window);
|
||||
|
||||
/**
|
||||
* Display the system-level window menu.
|
||||
*
|
||||
* This default window menu is provided by the system and on some platforms
|
||||
* provides functionality for setting or changing privileged state on the
|
||||
* window, such as moving it between workspaces or displays, or toggling the
|
||||
* always-on-top property.
|
||||
*
|
||||
* On platforms or desktops where this is unsupported, this function
|
||||
* does nothing.
|
||||
*
|
||||
* \param window the window for which the menu will be displayed
|
||||
* \param x the x coordinate of the menu, relative to the origin (top-left) of the client area
|
||||
* \param y the y coordinate of the menu, relative to the origin (top-left) of the client area
|
||||
* \returns 0 on success or a negative error code on failure; call
|
||||
* SDL_GetError() for more information.
|
||||
*
|
||||
* \since This function is available since SDL 3.0.0.
|
||||
*
|
||||
*/
|
||||
extern DECLSPEC int SDLCALL SDL_ShowWindowSystemMenu(SDL_Window *window, int x, int y);
|
||||
|
||||
/**
|
||||
* Possible return values from the SDL_HitTest callback.
|
||||
*
|
||||
|
|
|
@ -887,6 +887,7 @@ SDL3_0.0.0 {
|
|||
SDL_UnpauseAudioDevice;
|
||||
SDL_IsAudioDevicePaused;
|
||||
SDL_GetAudioStreamBinding;
|
||||
SDL_ShowWindowSystemMenu;
|
||||
# extra symbols go here (don't modify this line)
|
||||
local: *;
|
||||
};
|
||||
|
|
|
@ -912,3 +912,4 @@
|
|||
#define SDL_UnpauseAudioDevice SDL_UnpauseAudioDevice_REAL
|
||||
#define SDL_IsAudioDevicePaused SDL_IsAudioDevicePaused_REAL
|
||||
#define SDL_GetAudioStreamBinding SDL_GetAudioStreamBinding_REAL
|
||||
#define SDL_ShowWindowSystemMenu SDL_ShowWindowSystemMenu_REAL
|
||||
|
|
|
@ -956,3 +956,4 @@ SDL_DYNAPI_PROC(int,SDL_PauseAudioDevice,(SDL_AudioDeviceID a),(a),return)
|
|||
SDL_DYNAPI_PROC(int,SDL_UnpauseAudioDevice,(SDL_AudioDeviceID a),(a),return)
|
||||
SDL_DYNAPI_PROC(SDL_bool,SDL_IsAudioDevicePaused,(SDL_AudioDeviceID a),(a),return)
|
||||
SDL_DYNAPI_PROC(SDL_AudioDeviceID,SDL_GetAudioStreamBinding,(SDL_AudioStream *a),(a),return)
|
||||
SDL_DYNAPI_PROC(int,SDL_ShowWindowSystemMenu,(SDL_Window *a, int b, int c),(a,b,c),return)
|
||||
|
|
|
@ -354,6 +354,9 @@ struct SDL_VideoDevice
|
|||
/* Tell window that app enabled drag'n'drop events */
|
||||
void (*AcceptDragAndDrop)(SDL_Window *window, SDL_bool accept);
|
||||
|
||||
/* Display the system-level window menu */
|
||||
void (*ShowWindowSystemMenu)(SDL_Window *window, int x, int y);
|
||||
|
||||
/* * * */
|
||||
/* Data common to all drivers */
|
||||
SDL_threadID thread;
|
||||
|
|
|
@ -5054,6 +5054,19 @@ SDL_bool SDL_ShouldAllowTopmost(void)
|
|||
return SDL_GetHintBoolean(SDL_HINT_ALLOW_TOPMOST, SDL_TRUE);
|
||||
}
|
||||
|
||||
int SDL_ShowWindowSystemMenu(SDL_Window *window, int x, int y)
|
||||
{
|
||||
CHECK_WINDOW_MAGIC(window, -1)
|
||||
CHECK_WINDOW_NOT_POPUP(window, -1)
|
||||
|
||||
if (_this->ShowWindowSystemMenu) {
|
||||
_this->ShowWindowSystemMenu(window, x, y);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
int SDL_SetWindowHitTest(SDL_Window *window, SDL_HitTest callback, void *callback_data)
|
||||
{
|
||||
CHECK_WINDOW_MAGIC(window, -1);
|
||||
|
|
|
@ -158,6 +158,7 @@ void SDL_WAYLAND_UnloadSymbols(void);
|
|||
#define libdecor_frame_is_visible (*WAYLAND_libdecor_frame_is_visible)
|
||||
#define libdecor_frame_is_floating (*WAYLAND_libdecor_frame_is_floating)
|
||||
#define libdecor_frame_set_parent (*WAYLAND_libdecor_frame_set_parent)
|
||||
#define libdecor_frame_show_window_menu (*WAYLAND_libdecor_frame_show_window_menu)
|
||||
#define libdecor_frame_get_xdg_surface (*WAYLAND_libdecor_frame_get_xdg_surface)
|
||||
#define libdecor_frame_get_xdg_toplevel (*WAYLAND_libdecor_frame_get_xdg_toplevel)
|
||||
#define libdecor_frame_translate_coordinate (*WAYLAND_libdecor_frame_translate_coordinate)
|
||||
|
|
|
@ -554,6 +554,19 @@ static void pointer_handle_leave(void *data, struct wl_pointer *pointer,
|
|||
}
|
||||
|
||||
if (input->pointer_focus) {
|
||||
SDL_WindowData *wind = (SDL_WindowData *)wl_surface_get_user_data(surface);
|
||||
|
||||
if (wind) {
|
||||
/* Clear the capture flag and raise all buttons */
|
||||
wind->sdlwindow->flags &= ~SDL_WINDOW_MOUSE_CAPTURE;
|
||||
|
||||
SDL_SendMouseButton(Wayland_GetPointerTimestamp(input, 0), wind->sdlwindow, 0, SDL_RELEASED, SDL_BUTTON_LEFT);
|
||||
SDL_SendMouseButton(Wayland_GetPointerTimestamp(input, 0), wind->sdlwindow, 0, SDL_RELEASED, SDL_BUTTON_RIGHT);
|
||||
SDL_SendMouseButton(Wayland_GetPointerTimestamp(input, 0), wind->sdlwindow, 0, SDL_RELEASED, SDL_BUTTON_MIDDLE);
|
||||
SDL_SendMouseButton(Wayland_GetPointerTimestamp(input, 0), wind->sdlwindow, 0, SDL_RELEASED, SDL_BUTTON_X1);
|
||||
SDL_SendMouseButton(Wayland_GetPointerTimestamp(input, 0), wind->sdlwindow, 0, SDL_RELEASED, SDL_BUTTON_X2);
|
||||
}
|
||||
|
||||
SDL_SetMouseFocus(NULL);
|
||||
input->pointer_focus = NULL;
|
||||
}
|
||||
|
|
|
@ -204,6 +204,7 @@ SDL_WAYLAND_SYM(bool, libdecor_frame_is_visible, (struct libdecor_frame *))
|
|||
SDL_WAYLAND_SYM(bool, libdecor_frame_is_floating, (struct libdecor_frame *))
|
||||
SDL_WAYLAND_SYM(void, libdecor_frame_set_parent, (struct libdecor_frame *,\
|
||||
struct libdecor_frame *))
|
||||
SDL_WAYLAND_SYM(void, libdecor_frame_show_window_menu, (struct libdecor_frame *, struct wl_seat *, uint32_t, int, int))
|
||||
SDL_WAYLAND_SYM(struct xdg_surface *, libdecor_frame_get_xdg_surface, (struct libdecor_frame *))
|
||||
SDL_WAYLAND_SYM(struct xdg_toplevel *, libdecor_frame_get_xdg_toplevel, (struct libdecor_frame *))
|
||||
SDL_WAYLAND_SYM(void, libdecor_frame_translate_coordinate, (struct libdecor_frame *, int, int, int *, int *))
|
||||
|
|
|
@ -209,6 +209,7 @@ static SDL_VideoDevice *Wayland_CreateDevice(void)
|
|||
device->SetWindowHitTest = Wayland_SetWindowHitTest;
|
||||
device->FlashWindow = Wayland_FlashWindow;
|
||||
device->HasScreenKeyboardSupport = Wayland_HasScreenKeyboardSupport;
|
||||
device->ShowWindowSystemMenu = Wayland_ShowWindowSystemMenu;
|
||||
|
||||
#ifdef SDL_USE_LIBDBUS
|
||||
if (SDL_SystemTheme_Init())
|
||||
|
|
|
@ -2357,6 +2357,23 @@ void Wayland_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window)
|
|||
WAYLAND_wl_display_flush(viddata->display);
|
||||
}
|
||||
|
||||
void Wayland_ShowWindowSystemMenu(SDL_Window *window, int x, int y)
|
||||
{
|
||||
SDL_WindowData *wind = window->driverdata;
|
||||
#ifdef HAVE_LIBDECOR_H
|
||||
if (wind->shell_surface_type == WAYLAND_SURFACE_LIBDECOR) {
|
||||
if (wind->shell_surface.libdecor.frame) {
|
||||
libdecor_frame_show_window_menu(wind->shell_surface.libdecor.frame, wind->waylandData->input->seat, wind->waylandData->input->last_implicit_grab_serial, x, y);
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
if (wind->shell_surface_type == WAYLAND_SURFACE_XDG_TOPLEVEL) {
|
||||
if (wind->shell_surface.xdg.roleobj.toplevel) {
|
||||
xdg_toplevel_show_window_menu(wind->shell_surface.xdg.roleobj.toplevel, wind->waylandData->input->seat, wind->waylandData->input->last_implicit_grab_serial, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Wayland_SuspendScreenSaver(SDL_VideoDevice *_this)
|
||||
{
|
||||
SDL_VideoData *data = _this->driverdata;
|
||||
|
|
|
@ -150,6 +150,7 @@ extern void Wayland_SetWindowMaximumSize(SDL_VideoDevice *_this, SDL_Window *win
|
|||
extern void Wayland_GetWindowSizeInPixels(SDL_VideoDevice *_this, SDL_Window *window, int *w, int *h);
|
||||
extern int Wayland_SetWindowModalFor(SDL_VideoDevice *_this, SDL_Window *modal_window, SDL_Window *parent_window);
|
||||
extern void Wayland_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern void Wayland_ShowWindowSystemMenu(SDL_Window *window, int x, int y);
|
||||
extern void Wayland_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern int Wayland_SuspendScreenSaver(SDL_VideoDevice *_this);
|
||||
|
||||
|
|
|
@ -207,6 +207,7 @@ static SDL_VideoDevice *WIN_CreateDevice(void)
|
|||
device->SetWindowHitTest = WIN_SetWindowHitTest;
|
||||
device->AcceptDragAndDrop = WIN_AcceptDragAndDrop;
|
||||
device->FlashWindow = WIN_FlashWindow;
|
||||
device->ShowWindowSystemMenu = WIN_ShowWindowSystemMenu;
|
||||
|
||||
device->shape_driver.CreateShaper = Win32_CreateShaper;
|
||||
device->shape_driver.SetWindowShape = Win32_SetWindowShape;
|
||||
|
|
|
@ -51,6 +51,14 @@ typedef HRESULT (WINAPI *DwmSetWindowAttribute_t)(HWND hwnd, DWORD dwAttribute,
|
|||
#define SWP_NOCOPYBITS 0
|
||||
#endif
|
||||
|
||||
/* An undocumented message to create a popup system menu
|
||||
* - wParam is always 0
|
||||
* - lParam = MAKELONG(x, y) where x and y are the screen coordinates where the menu should be displayed
|
||||
*/
|
||||
#ifndef WM_POPUPSYSTEMMENU
|
||||
#define WM_POPUPSYSTEMMENU 0x313
|
||||
#endif
|
||||
|
||||
/* #define HIGHDPI_DEBUG */
|
||||
|
||||
/* Fake window to help with DirectInput events. */
|
||||
|
@ -1485,6 +1493,17 @@ int WIN_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperati
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WIN_ShowWindowSystemMenu(SDL_Window *window, int x, int y)
|
||||
{
|
||||
const SDL_WindowData *data = window->driverdata;
|
||||
POINT pt;
|
||||
|
||||
pt.x = x;
|
||||
pt.y = y;
|
||||
ClientToScreen(data->hwnd, &pt);
|
||||
SendMessage(data->hwnd, WM_POPUPSYSTEMMENU, 0, MAKELPARAM(pt.x, pt.y));
|
||||
}
|
||||
#endif /*!defined(__XBOXONE__) && !defined(__XBOXSERIES__)*/
|
||||
|
||||
void WIN_UpdateDarkModeForHWND(HWND hwnd)
|
||||
|
|
|
@ -108,6 +108,7 @@ extern void WIN_AcceptDragAndDrop(SDL_Window *window, SDL_bool accept);
|
|||
extern int WIN_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation);
|
||||
extern void WIN_UpdateDarkModeForHWND(HWND hwnd);
|
||||
extern int WIN_SetWindowPositionInternal(SDL_Window *window, UINT flags);
|
||||
extern void WIN_ShowWindowSystemMenu(SDL_Window *window, int x, int y);
|
||||
|
||||
/* Ends C function definitions when using C++ */
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -213,6 +213,7 @@ static SDL_VideoDevice *X11_CreateDevice(void)
|
|||
device->SetWindowHitTest = X11_SetWindowHitTest;
|
||||
device->AcceptDragAndDrop = X11_AcceptDragAndDrop;
|
||||
device->FlashWindow = X11_FlashWindow;
|
||||
device->ShowWindowSystemMenu = X11_ShowWindowSystemMenu;
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_X11_XFIXES
|
||||
device->SetWindowMouseRect = X11_SetWindowMouseRect;
|
||||
|
|
|
@ -1957,4 +1957,29 @@ int SDL_X11_SetWindowTitle(Display *display, Window xwindow, char *title)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void X11_ShowWindowSystemMenu(SDL_Window *window, int x, int y)
|
||||
{
|
||||
SDL_WindowData *data = window->driverdata;
|
||||
SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window);
|
||||
Display *display = data->videodata->display;
|
||||
Window root = RootWindow(display, displaydata->screen);
|
||||
XClientMessageEvent e;
|
||||
Window childReturn;
|
||||
int wx, wy;
|
||||
|
||||
SDL_zero(e);
|
||||
X11_XTranslateCoordinates(display, data->xwindow, root, x, y, &wx, &wy, &childReturn);
|
||||
|
||||
e.type = ClientMessage;
|
||||
e.window = data->xwindow;
|
||||
e.message_type = X11_XInternAtom(display, "_GTK_SHOW_WINDOW_MENU", 0);
|
||||
e.data.l[0] = 0; /* GTK device ID (unused) */
|
||||
e.data.l[1] = wx; /* X coordinate relative to root */
|
||||
e.data.l[2] = wy; /* Y coordinate relative to root */
|
||||
e.format = 32;
|
||||
|
||||
X11_XSendEvent(display, root, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent *)&e);
|
||||
X11_XFlush(display);
|
||||
}
|
||||
|
||||
#endif /* SDL_VIDEO_DRIVER_X11 */
|
||||
|
|
|
@ -115,6 +115,7 @@ extern int X11_GetWindowWMInfo(SDL_VideoDevice *_this, SDL_Window *window, struc
|
|||
extern int X11_SetWindowHitTest(SDL_Window *window, SDL_bool enabled);
|
||||
extern void X11_AcceptDragAndDrop(SDL_Window *window, SDL_bool accept);
|
||||
extern int X11_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation);
|
||||
extern void X11_ShowWindowSystemMenu(SDL_Window *window, int x, int y);
|
||||
|
||||
int SDL_X11_SetWindowTitle(Display *display, Window xwindow, char *title);
|
||||
void X11_UpdateWindowPosition(SDL_Window *window);
|
||||
|
|
Loading…
Reference in New Issue