mirror of
https://github.com/libsdl-org/SDL.git
synced 2024-11-23 02:43:30 +08:00
Add support for modal windows to more platforms
- Adds support for modal windows to Win32, Mac, and Haiku, and enhances functionality on Wayland and X11, which previous set only the parent window, but not the modal state. - Windows can be declared modal at creation time, and the modal state can be toggled at any time via SDL_SetWindowModalFor() (tested with UE5 through sdl2-compat). - Allows dynamic unparenting/reparenting of windows. - Includes a modal window test.
This commit is contained in:
parent
97f7b4620a
commit
c6a70d6898
@ -144,6 +144,7 @@ typedef Uint32 SDL_WindowFlags;
|
||||
#define SDL_WINDOW_INPUT_FOCUS 0x00000200U /**< window has input focus */
|
||||
#define SDL_WINDOW_MOUSE_FOCUS 0x00000400U /**< window has mouse focus */
|
||||
#define SDL_WINDOW_EXTERNAL 0x00000800U /**< window not created by SDL */
|
||||
#define SDL_WINDOW_MODAL 0x00001000U /**< window is modal */
|
||||
#define SDL_WINDOW_HIGH_PIXEL_DENSITY 0x00002000U /**< window uses high pixel density back buffer if possible */
|
||||
#define SDL_WINDOW_MOUSE_CAPTURE 0x00004000U /**< window has mouse captured (unrelated to MOUSE_GRABBED) */
|
||||
#define SDL_WINDOW_ALWAYS_ON_TOP 0x00008000U /**< window should always be above others */
|
||||
@ -907,13 +908,15 @@ extern DECLSPEC SDL_Window *SDLCALL SDL_CreatePopupWindow(SDL_Window *parent, in
|
||||
* with Metal rendering
|
||||
* - `SDL_PROP_WINDOW_CREATE_MINIMIZED_BOOLEAN`: true if the window should
|
||||
* start minimized
|
||||
* - `SDL_PROP_WINDOW_CREATE_MODAL_BOOLEAN`: true if the window is modal to its
|
||||
* parent
|
||||
* - `SDL_PROP_WINDOW_CREATE_MOUSE_GRABBED_BOOLEAN`: true if the window starts
|
||||
* with grabbed mouse focus
|
||||
* - `SDL_PROP_WINDOW_CREATE_OPENGL_BOOLEAN`: true if the window will be used
|
||||
* with OpenGL rendering
|
||||
* - `SDL_PROP_WINDOW_CREATE_PARENT_POINTER`: an SDL_Window that will be the
|
||||
* parent of this window, required for windows with the "toolip" and "menu"
|
||||
* properties
|
||||
* parent of this window, required for windows with the "toolip", "menu", and
|
||||
* "modal" properties
|
||||
* - `SDL_PROP_WINDOW_CREATE_RESIZABLE_BOOLEAN`: true if the window should be
|
||||
* resizable
|
||||
* - `SDL_PROP_WINDOW_CREATE_TITLE_STRING`: the title of the window, in UTF-8
|
||||
@ -1008,6 +1011,7 @@ extern DECLSPEC SDL_Window *SDLCALL SDL_CreateWindowWithProperties(SDL_Propertie
|
||||
#define SDL_PROP_WINDOW_CREATE_MENU_BOOLEAN "menu"
|
||||
#define SDL_PROP_WINDOW_CREATE_METAL_BOOLEAN "metal"
|
||||
#define SDL_PROP_WINDOW_CREATE_MINIMIZED_BOOLEAN "minimized"
|
||||
#define SDL_PROP_WINDOW_CREATE_MODAL_BOOLEAN "modal"
|
||||
#define SDL_PROP_WINDOW_CREATE_MOUSE_GRABBED_BOOLEAN "mouse_grabbed"
|
||||
#define SDL_PROP_WINDOW_CREATE_OPENGL_BOOLEAN "opengl"
|
||||
#define SDL_PROP_WINDOW_CREATE_PARENT_POINTER "parent"
|
||||
@ -2000,7 +2004,12 @@ extern DECLSPEC int SDLCALL SDL_SetWindowOpacity(SDL_Window *window, float opaci
|
||||
extern DECLSPEC int SDLCALL SDL_GetWindowOpacity(SDL_Window *window, float *out_opacity);
|
||||
|
||||
/**
|
||||
* Set the window as a modal for another window.
|
||||
* Set the window as a modal to a parent window.
|
||||
*
|
||||
* If the window is already modal to an existing window, it will be reparented to the new owner.
|
||||
* Setting the parent window to null unparents the modal window and removes modal status.
|
||||
*
|
||||
* Setting a window as modal to a parent that is a descendent of the modal window results in undefined behavior.
|
||||
*
|
||||
* \param modal_window the window that should be set modal
|
||||
* \param parent_window the parent window for the modal window
|
||||
@ -2181,6 +2190,8 @@ extern DECLSPEC int SDLCALL SDL_FlashWindow(SDL_Window *window, SDL_FlashOperati
|
||||
/**
|
||||
* Destroy a window.
|
||||
*
|
||||
* Any popups or modal windows owned by the window will be recursively destroyed as well.
|
||||
*
|
||||
* If `window` is NULL, this function will return immediately after setting
|
||||
* the SDL error message to "Invalid window". See SDL_GetError().
|
||||
*
|
||||
|
@ -200,6 +200,33 @@ static void SDL_SyncIfRequired(SDL_Window *window)
|
||||
}
|
||||
}
|
||||
|
||||
static void SDL_SetWindowParent(SDL_Window *window, SDL_Window *parent)
|
||||
{
|
||||
/* Unlink the window from the existing parent. */
|
||||
if (window->parent) {
|
||||
if (window->next_sibling) {
|
||||
window->next_sibling->prev_sibling = window->prev_sibling;
|
||||
}
|
||||
if (window->prev_sibling) {
|
||||
window->prev_sibling->next_sibling = window->next_sibling;
|
||||
} else {
|
||||
window->parent->first_child = window->next_sibling;
|
||||
}
|
||||
|
||||
window->parent = NULL;
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
window->parent = parent;
|
||||
|
||||
window->next_sibling = parent->first_child;
|
||||
if (parent->first_child) {
|
||||
parent->first_child->prev_sibling = window;
|
||||
}
|
||||
parent->first_child = window;
|
||||
}
|
||||
}
|
||||
|
||||
/* Support for framebuffer emulation using an accelerated renderer */
|
||||
|
||||
#define SDL_PROP_WINDOW_TEXTUREDATA_POINTER "SDL.internal.window.texturedata"
|
||||
@ -2002,6 +2029,7 @@ static struct {
|
||||
{ SDL_PROP_WINDOW_CREATE_MENU_BOOLEAN, SDL_WINDOW_POPUP_MENU, SDL_FALSE },
|
||||
{ SDL_PROP_WINDOW_CREATE_METAL_BOOLEAN, SDL_WINDOW_METAL, SDL_FALSE },
|
||||
{ SDL_PROP_WINDOW_CREATE_MINIMIZED_BOOLEAN, SDL_WINDOW_MINIMIZED, SDL_FALSE },
|
||||
{ SDL_PROP_WINDOW_CREATE_MODAL_BOOLEAN, SDL_WINDOW_MODAL, SDL_FALSE },
|
||||
{ SDL_PROP_WINDOW_CREATE_MOUSE_GRABBED_BOOLEAN, SDL_WINDOW_MOUSE_GRABBED, SDL_FALSE },
|
||||
{ SDL_PROP_WINDOW_CREATE_OPENGL_BOOLEAN, SDL_WINDOW_OPENGL, SDL_FALSE },
|
||||
{ SDL_PROP_WINDOW_CREATE_RESIZABLE_BOOLEAN, SDL_WINDOW_RESIZABLE, SDL_FALSE },
|
||||
@ -2057,6 +2085,11 @@ SDL_Window *SDL_CreateWindowWithProperties(SDL_PropertiesID props)
|
||||
}
|
||||
}
|
||||
|
||||
if ((flags & SDL_WINDOW_MODAL) && (!parent || parent->magic != &_this->window_magic)) {
|
||||
SDL_SetError("Modal windows must specify a parent window");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((flags & (SDL_WINDOW_TOOLTIP | SDL_WINDOW_POPUP_MENU)) != 0) {
|
||||
if (!(_this->device_caps & VIDEO_DEVICE_CAPS_HAS_POPUP_WINDOW_SUPPORT)) {
|
||||
SDL_Unsupported();
|
||||
@ -2074,7 +2107,7 @@ SDL_Window *SDL_CreateWindowWithProperties(SDL_PropertiesID props)
|
||||
}
|
||||
|
||||
/* Ensure no more than one of these flags is set */
|
||||
type_flags = flags & (SDL_WINDOW_UTILITY | SDL_WINDOW_TOOLTIP | SDL_WINDOW_POPUP_MENU);
|
||||
type_flags = flags & (SDL_WINDOW_UTILITY | SDL_WINDOW_TOOLTIP | SDL_WINDOW_POPUP_MENU | SDL_WINDOW_MODAL);
|
||||
if (type_flags & (type_flags - 1)) {
|
||||
SDL_SetError("Conflicting window type flags specified: 0x%.8x", (unsigned int)type_flags);
|
||||
return NULL;
|
||||
@ -2200,14 +2233,9 @@ SDL_Window *SDL_CreateWindowWithProperties(SDL_PropertiesID props)
|
||||
}
|
||||
_this->windows = window;
|
||||
|
||||
if (parent) {
|
||||
window->parent = parent;
|
||||
|
||||
window->next_sibling = parent->first_child;
|
||||
if (parent->first_child) {
|
||||
parent->first_child->prev_sibling = window;
|
||||
}
|
||||
parent->first_child = window;
|
||||
/* Set the parent before creation if this is non-modal, otherwise it will be set later. */
|
||||
if (!(flags & SDL_WINDOW_MODAL)) {
|
||||
SDL_SetWindowParent(window, parent);
|
||||
}
|
||||
|
||||
if (_this->CreateSDLWindow && _this->CreateSDLWindow(_this, window, props) < 0) {
|
||||
@ -2236,6 +2264,9 @@ SDL_Window *SDL_CreateWindowWithProperties(SDL_PropertiesID props)
|
||||
flags = window->flags;
|
||||
#endif
|
||||
|
||||
if (flags & SDL_WINDOW_MODAL) {
|
||||
SDL_SetWindowModalFor(window, parent);
|
||||
}
|
||||
if (title) {
|
||||
SDL_SetWindowTitle(window, title);
|
||||
}
|
||||
@ -2293,6 +2324,7 @@ int SDL_RecreateWindow(SDL_Window *window, SDL_WindowFlags flags)
|
||||
SDL_bool need_vulkan_unload = SDL_FALSE;
|
||||
SDL_bool need_vulkan_load = SDL_FALSE;
|
||||
SDL_WindowFlags graphics_flags;
|
||||
SDL_Window *parent = window->parent;
|
||||
|
||||
/* ensure no more than one of these flags is set */
|
||||
graphics_flags = flags & (SDL_WINDOW_OPENGL | SDL_WINDOW_METAL | SDL_WINDOW_VULKAN);
|
||||
@ -2317,6 +2349,11 @@ int SDL_RecreateWindow(SDL_Window *window, SDL_WindowFlags flags)
|
||||
flags &= ~SDL_WINDOW_EXTERNAL;
|
||||
}
|
||||
|
||||
/* If this is a modal dialog, clear the modal status. */
|
||||
if (window->flags & SDL_WINDOW_MODAL) {
|
||||
SDL_SetWindowModalFor(window, NULL);
|
||||
}
|
||||
|
||||
/* Restore video mode, etc. */
|
||||
if (!(window->flags & SDL_WINDOW_EXTERNAL)) {
|
||||
const SDL_bool restore_on_show = window->restore_on_show;
|
||||
@ -2410,6 +2447,10 @@ int SDL_RecreateWindow(SDL_Window *window, SDL_WindowFlags flags)
|
||||
window->flags |= SDL_WINDOW_EXTERNAL;
|
||||
}
|
||||
|
||||
if (flags & SDL_WINDOW_MODAL) {
|
||||
SDL_SetWindowModalFor(window, parent);
|
||||
}
|
||||
|
||||
if (_this->SetWindowTitle && window->title) {
|
||||
_this->SetWindowTitle(_this, window);
|
||||
}
|
||||
@ -3259,15 +3300,35 @@ int SDL_GetWindowOpacity(SDL_Window *window, float *out_opacity)
|
||||
int SDL_SetWindowModalFor(SDL_Window *modal_window, SDL_Window *parent_window)
|
||||
{
|
||||
CHECK_WINDOW_MAGIC(modal_window, -1);
|
||||
CHECK_WINDOW_MAGIC(parent_window, -1);
|
||||
CHECK_WINDOW_NOT_POPUP(modal_window, -1);
|
||||
CHECK_WINDOW_NOT_POPUP(parent_window, -1);
|
||||
|
||||
if (parent_window) {
|
||||
CHECK_WINDOW_MAGIC(parent_window, -1);
|
||||
CHECK_WINDOW_NOT_POPUP(parent_window, -1);
|
||||
}
|
||||
|
||||
if (!_this->SetWindowModalFor) {
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
return _this->SetWindowModalFor(_this, modal_window, parent_window);
|
||||
if (parent_window) {
|
||||
modal_window->flags |= SDL_WINDOW_MODAL;
|
||||
} else if (modal_window->flags & SDL_WINDOW_MODAL) {
|
||||
modal_window->flags &= ~SDL_WINDOW_MODAL;
|
||||
} else {
|
||||
return 0; /* Not modal; nothing to do. */
|
||||
}
|
||||
|
||||
const int ret = _this->SetWindowModalFor(_this, modal_window, parent_window);
|
||||
|
||||
/* The existing parent might be needed when changing the modal status,
|
||||
* so don't change the heirarchy until after setting the new modal state.
|
||||
*/
|
||||
if (!ret) {
|
||||
SDL_SetWindowParent(modal_window, !ret ? parent_window : NULL);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int SDL_SetWindowInputFocus(SDL_Window *window)
|
||||
@ -3686,16 +3747,12 @@ void SDL_DestroyWindow(SDL_Window *window)
|
||||
|
||||
SDL_DestroyProperties(window->props);
|
||||
|
||||
/* If this is a child window, unlink it from its siblings */
|
||||
if (window->parent) {
|
||||
if (window->next_sibling) {
|
||||
window->next_sibling->prev_sibling = window->prev_sibling;
|
||||
}
|
||||
if (window->prev_sibling) {
|
||||
window->prev_sibling->next_sibling = window->next_sibling;
|
||||
} else {
|
||||
window->parent->first_child = window->next_sibling;
|
||||
}
|
||||
/* Clear the modal status, but don't unset the parent, as it may be
|
||||
* needed later in the destruction process if a backend needs to
|
||||
* update the input focus.
|
||||
*/
|
||||
if (_this->SetWindowModalFor && (window->flags & SDL_WINDOW_MODAL)) {
|
||||
_this->SetWindowModalFor(_this, window, NULL);
|
||||
}
|
||||
|
||||
/* Restore video mode, etc. */
|
||||
@ -3765,6 +3822,9 @@ void SDL_DestroyWindow(SDL_Window *window)
|
||||
SDL_free(window->title);
|
||||
SDL_DestroySurface(window->icon);
|
||||
|
||||
/* Unlink the window from its siblings. */
|
||||
SDL_SetWindowParent(window, NULL);
|
||||
|
||||
/* Unlink the window from the list */
|
||||
if (window->next) {
|
||||
window->next->prev = window->prev;
|
||||
|
@ -563,6 +563,14 @@ Uint64 Cocoa_GetEventTimestamp(NSTimeInterval nsTimestamp)
|
||||
|
||||
int Cocoa_PumpEventsUntilDate(SDL_VideoDevice *_this, NSDate *expiration, bool accumulate)
|
||||
{
|
||||
/* Run any existing modal sessions. */
|
||||
for (SDL_Window *w = _this->windows; w; w = w->next) {
|
||||
SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)w->driverdata;
|
||||
if (data.modal_session) {
|
||||
[NSApp runModalSession:data.modal_session];
|
||||
}
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
NSEvent *event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:expiration inMode:NSDefaultRunLoopMode dequeue:YES];
|
||||
if (event == nil) {
|
||||
|
@ -122,6 +122,7 @@ static SDL_VideoDevice *Cocoa_CreateDevice(void)
|
||||
device->UpdateWindowShape = Cocoa_UpdateWindowShape;
|
||||
device->FlashWindow = Cocoa_FlashWindow;
|
||||
device->SetWindowFocusable = Cocoa_SetWindowFocusable;
|
||||
device->SetWindowModalFor = Cocoa_SetWindowModalFor;
|
||||
device->SyncWindow = Cocoa_SyncWindow;
|
||||
|
||||
#ifdef SDL_VIDEO_OPENGL_CGL
|
||||
|
@ -138,6 +138,7 @@ typedef enum
|
||||
@property(nonatomic) NSInteger flash_request;
|
||||
@property(nonatomic) SDL_Window *keyboard_focus;
|
||||
@property(nonatomic) Cocoa_WindowListener *listener;
|
||||
@property(nonatomic) NSModalSession modal_session;
|
||||
@property(nonatomic) SDL_CocoaVideoData *videodata;
|
||||
@property(nonatomic) SDL_bool send_floating_size;
|
||||
@property(nonatomic) SDL_bool send_floating_position;
|
||||
@ -178,6 +179,7 @@ extern int Cocoa_SetWindowHitTest(SDL_Window *window, SDL_bool enabled);
|
||||
extern void Cocoa_AcceptDragAndDrop(SDL_Window *window, SDL_bool accept);
|
||||
extern int Cocoa_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation);
|
||||
extern int Cocoa_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool focusable);
|
||||
extern int Cocoa_SetWindowModalFor(SDL_VideoDevice *_this, SDL_Window *modal_window, SDL_Window *parent_window);
|
||||
extern int Cocoa_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
|
||||
#endif /* SDL_cocoawindow_h_ */
|
||||
|
@ -2369,6 +2369,10 @@ void Cocoa_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
NSWindow *nsparent = ((__bridge SDL_CocoaWindowData *)window->parent->driverdata).nswindow;
|
||||
[nsparent addChildWindow:nswindow ordered:NSWindowAbove];
|
||||
} else {
|
||||
if ((window->flags & SDL_WINDOW_MODAL) && window->parent) {
|
||||
Cocoa_SetWindowModalFor(_this, window, window->parent);
|
||||
}
|
||||
|
||||
if (bActivate) {
|
||||
[nswindow makeKeyAndOrderFront:nil];
|
||||
} else {
|
||||
@ -2402,6 +2406,11 @@ void Cocoa_HideWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
[nswindow close];
|
||||
}
|
||||
|
||||
/* If this window is the source of a modal session, end it when
|
||||
* hidden, or other windows will be prevented from closing.
|
||||
*/
|
||||
Cocoa_SetWindowModalFor(_this, window, NULL);
|
||||
|
||||
/* Transfer keyboard focus back to the parent */
|
||||
if (window->flags & SDL_WINDOW_POPUP_MENU) {
|
||||
if (window == SDL_GetKeyboardFocus()) {
|
||||
@ -2928,6 +2937,24 @@ void Cocoa_AcceptDragAndDrop(SDL_Window *window, SDL_bool accept)
|
||||
}
|
||||
}
|
||||
|
||||
int Cocoa_SetWindowModalFor(SDL_VideoDevice *_this, SDL_Window *modal_window, SDL_Window *parent_window)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL_CocoaWindowData *modal_data = (__bridge SDL_CocoaWindowData *)modal_window->driverdata;
|
||||
|
||||
if (modal_data.modal_session) {
|
||||
[NSApp endModalSession:modal_data.modal_session];
|
||||
modal_data.modal_session = nil;
|
||||
}
|
||||
|
||||
if (parent_window) {
|
||||
modal_data.modal_session = [NSApp beginModalSessionForWindow:modal_data.nswindow];
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Cocoa_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation)
|
||||
{
|
||||
@autoreleasepool {
|
||||
|
@ -39,7 +39,7 @@ static SDL_INLINE SDL_BLooper *_GetBeLooper() {
|
||||
return SDL_Looper;
|
||||
}
|
||||
|
||||
static int _InitWindow(SDL_VideoDevice *_this, SDL_Window *window) {
|
||||
static int _InitWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props) {
|
||||
uint32 flags = 0;
|
||||
window_look look = B_TITLED_WINDOW_LOOK;
|
||||
|
||||
@ -77,7 +77,7 @@ static int _InitWindow(SDL_VideoDevice *_this, SDL_Window *window) {
|
||||
}
|
||||
|
||||
int HAIKU_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props) {
|
||||
if (_InitWindow(_this, window) < 0) {
|
||||
if (_InitWindow(_this, window, create_props) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -171,6 +171,25 @@ int HAIKU_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window * window, SDL_bo
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
int HAIKU_SetWindowModalFor(SDL_VideoDevice *_this, SDL_Window *modal_window, SDL_Window *parent_window) {
|
||||
if (modal_window->parent && modal_window->parent != parent_window) {
|
||||
/* Remove from the subset of a previous parent. */
|
||||
_ToBeWin(modal_window)->RemoveFromSubset(_ToBeWin(modal_window->parent));
|
||||
}
|
||||
|
||||
if (parent_window) {
|
||||
_ToBeWin(modal_window)->SetLook(B_MODAL_WINDOW_LOOK);
|
||||
_ToBeWin(modal_window)->SetFeel(B_MODAL_SUBSET_WINDOW_FEEL);
|
||||
_ToBeWin(modal_window)->AddToSubset(_ToBeWin(parent_window));
|
||||
} else {
|
||||
window_look look = (modal_window->flags & SDL_WINDOW_BORDERLESS) ? B_NO_BORDER_WINDOW_LOOK : B_TITLED_WINDOW_LOOK;
|
||||
_ToBeWin(modal_window)->SetLook(look);
|
||||
_ToBeWin(modal_window)->SetFeel(B_NORMAL_WINDOW_FEEL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void HAIKU_DestroyWindow(SDL_VideoDevice *_this, SDL_Window * window) {
|
||||
_ToBeWin(window)->LockLooper(); /* This MUST be locked */
|
||||
_GetBeLooper()->ClearID(_ToBeWin(window));
|
||||
|
@ -57,6 +57,7 @@
|
||||
#include "viewporter-client-protocol.h"
|
||||
#include "xdg-activation-v1-client-protocol.h"
|
||||
#include "xdg-decoration-unstable-v1-client-protocol.h"
|
||||
#include "xdg-dialog-v1-client-protocol.h"
|
||||
#include "xdg-foreign-unstable-v2-client-protocol.h"
|
||||
#include "xdg-output-unstable-v1-client-protocol.h"
|
||||
#include "xdg-shell-client-protocol.h"
|
||||
@ -1088,6 +1089,8 @@ static void display_handle_global(void *data, struct wl_registry *registry, uint
|
||||
}
|
||||
} else if (SDL_strcmp(interface, "zxdg_exporter_v2") == 0) {
|
||||
d->zxdg_exporter_v2 = wl_registry_bind(d->registry, id, &zxdg_exporter_v2_interface, 1);
|
||||
} else if (SDL_strcmp(interface, "xdg_wm_dialog_v1") == 0) {
|
||||
d->xdg_wm_dialog_v1 = wl_registry_bind(d->registry, id, &xdg_wm_dialog_v1_interface, 1);
|
||||
} else if (SDL_strcmp(interface, "kde_output_order_v1") == 0) {
|
||||
d->kde_output_order = wl_registry_bind(d->registry, id, &kde_output_order_v1_interface, 1);
|
||||
kde_output_order_v1_add_listener(d->kde_output_order, &kde_output_order_listener, d);
|
||||
@ -1346,6 +1349,11 @@ static void Wayland_VideoCleanup(SDL_VideoDevice *_this)
|
||||
data->zxdg_exporter_v2 = NULL;
|
||||
}
|
||||
|
||||
if (data->xdg_wm_dialog_v1) {
|
||||
xdg_wm_dialog_v1_destroy(data->xdg_wm_dialog_v1);
|
||||
data->xdg_wm_dialog_v1 = NULL;
|
||||
}
|
||||
|
||||
if (data->kde_output_order) {
|
||||
Wayland_FlushOutputOrder(data);
|
||||
kde_output_order_v1_destroy(data->kde_output_order);
|
||||
|
@ -80,6 +80,7 @@ struct SDL_VideoData
|
||||
struct wp_fractional_scale_manager_v1 *fractional_scale_manager;
|
||||
struct zwp_input_timestamps_manager_v1 *input_timestamps_manager;
|
||||
struct zxdg_exporter_v2 *zxdg_exporter_v2;
|
||||
struct xdg_wm_dialog_v1 *xdg_wm_dialog_v1;
|
||||
struct kde_output_order_v1 *kde_output_order;
|
||||
|
||||
struct xkb_context *xkb_context;
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include "viewporter-client-protocol.h"
|
||||
#include "fractional-scale-v1-client-protocol.h"
|
||||
#include "xdg-foreign-unstable-v2-client-protocol.h"
|
||||
#include "xdg-dialog-v1-client-protocol.h"
|
||||
|
||||
#ifdef HAVE_LIBDECOR_H
|
||||
#include <libdecor.h>
|
||||
@ -654,6 +655,8 @@ static void surface_frame_done(void *data, struct wl_callback *cb, uint32_t time
|
||||
for (SDL_Window *w = wind->sdlwindow->first_child; w; w = w->next_sibling) {
|
||||
if (w->driverdata->surface_status == WAYLAND_SURFACE_STATUS_SHOW_PENDING) {
|
||||
Wayland_ShowWindow(SDL_GetVideoDevice(), w);
|
||||
} else if ((w->flags & SDL_WINDOW_MODAL) && w->driverdata->modal_reparenting_required) {
|
||||
Wayland_SetWindowModalFor(SDL_GetVideoDevice(), w, w->parent);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1434,35 +1437,56 @@ int Wayland_SetWindowModalFor(SDL_VideoDevice *_this, SDL_Window *modal_window,
|
||||
{
|
||||
SDL_VideoData *viddata = _this->driverdata;
|
||||
SDL_WindowData *modal_data = modal_window->driverdata;
|
||||
SDL_WindowData *parent_data = parent_window->driverdata;
|
||||
SDL_WindowData *parent_data = parent_window ? parent_window->driverdata : NULL;
|
||||
struct xdg_toplevel *modal_toplevel = NULL;
|
||||
struct xdg_toplevel *parent_toplevel = NULL;
|
||||
|
||||
if (modal_data->shell_surface_type == WAYLAND_SURFACE_XDG_POPUP || parent_data->shell_surface_type == WAYLAND_SURFACE_XDG_POPUP) {
|
||||
return SDL_SetError("Modal/Parent was a popup, not a toplevel");
|
||||
modal_data->modal_reparenting_required = SDL_FALSE;
|
||||
|
||||
if (parent_data && parent_data->surface_status != WAYLAND_SURFACE_STATUS_SHOWN) {
|
||||
/* Need to wait for the parent to become mapped, or it's the same as setting a null parent. */
|
||||
modal_data->modal_reparenting_required = SDL_TRUE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Libdecor crashes on attempts to unset the parent by passing null, which is allowed by the
|
||||
* toplevel spec, so just use the raw xdg-toplevel instead (that's what libdecor does
|
||||
* internally anyways).
|
||||
*/
|
||||
#ifdef HAVE_LIBDECOR_H
|
||||
if (viddata->shell.libdecor) {
|
||||
if (!modal_data->shell_surface.libdecor.frame) {
|
||||
return SDL_SetError("Modal window was hidden");
|
||||
}
|
||||
if (!parent_data->shell_surface.libdecor.frame) {
|
||||
return SDL_SetError("Parent window was hidden");
|
||||
}
|
||||
libdecor_frame_set_parent(modal_data->shell_surface.libdecor.frame,
|
||||
parent_data->shell_surface.libdecor.frame);
|
||||
if (modal_data->shell_surface_type == WAYLAND_SURFACE_LIBDECOR && modal_data->shell_surface.libdecor.frame) {
|
||||
modal_toplevel = libdecor_frame_get_xdg_toplevel(modal_data->shell_surface.libdecor.frame);
|
||||
} else
|
||||
#endif
|
||||
if (viddata->shell.xdg) {
|
||||
if (modal_data->shell_surface.xdg.roleobj.toplevel == NULL) {
|
||||
return SDL_SetError("Modal window was hidden");
|
||||
if (modal_data->shell_surface_type == WAYLAND_SURFACE_XDG_TOPLEVEL && modal_data->shell_surface.xdg.roleobj.toplevel) {
|
||||
modal_toplevel = modal_data->shell_surface.xdg.roleobj.toplevel;
|
||||
}
|
||||
|
||||
if (parent_data) {
|
||||
#ifdef HAVE_LIBDECOR_H
|
||||
if (parent_data->shell_surface_type == WAYLAND_SURFACE_LIBDECOR && parent_data->shell_surface.libdecor.frame) {
|
||||
parent_toplevel = libdecor_frame_get_xdg_toplevel(parent_data->shell_surface.libdecor.frame);
|
||||
} else
|
||||
#endif
|
||||
if (parent_data->shell_surface_type == WAYLAND_SURFACE_XDG_TOPLEVEL && parent_data->shell_surface.xdg.roleobj.toplevel) {
|
||||
parent_toplevel = parent_data->shell_surface.xdg.roleobj.toplevel;
|
||||
}
|
||||
if (parent_data->shell_surface.xdg.roleobj.toplevel == NULL) {
|
||||
return SDL_SetError("Parent window was hidden");
|
||||
}
|
||||
|
||||
if (modal_toplevel) {
|
||||
xdg_toplevel_set_parent(modal_toplevel, parent_toplevel);
|
||||
|
||||
if (viddata->xdg_wm_dialog_v1) {
|
||||
if (parent_toplevel) {
|
||||
if (!modal_data->xdg_dialog_v1) {
|
||||
modal_data->xdg_dialog_v1 = xdg_wm_dialog_v1_get_xdg_dialog(viddata->xdg_wm_dialog_v1, modal_toplevel);
|
||||
}
|
||||
|
||||
xdg_dialog_v1_set_modal(modal_data->xdg_dialog_v1);
|
||||
} else if (modal_data->xdg_dialog_v1) {
|
||||
xdg_dialog_v1_unset_modal(modal_data->xdg_dialog_v1);
|
||||
}
|
||||
}
|
||||
xdg_toplevel_set_parent(modal_data->shell_surface.xdg.roleobj.toplevel,
|
||||
parent_data->shell_surface.xdg.roleobj.toplevel);
|
||||
} else {
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1653,6 +1677,10 @@ void Wayland_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
}
|
||||
|
||||
/* Restore state that was set prior to this call */
|
||||
if (window->flags & SDL_WINDOW_MODAL) {
|
||||
Wayland_SetWindowModalFor(_this, window, window->parent);
|
||||
}
|
||||
|
||||
Wayland_SetWindowTitle(_this, window);
|
||||
|
||||
/* We have to wait until the surface gets a "configure" event, or use of
|
||||
@ -2590,6 +2618,10 @@ void Wayland_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
wp_fractional_scale_v1_destroy(wind->fractional_scale);
|
||||
}
|
||||
|
||||
if (wind->xdg_dialog_v1) {
|
||||
xdg_dialog_v1_destroy(wind->xdg_dialog_v1);
|
||||
}
|
||||
|
||||
SDL_free(wind->outputs);
|
||||
SDL_free(wind->app_id);
|
||||
|
||||
|
@ -96,6 +96,7 @@ struct SDL_WindowData
|
||||
struct wp_viewport *viewport;
|
||||
struct wp_fractional_scale_v1 *fractional_scale;
|
||||
struct zxdg_exported_v2 *exported;
|
||||
struct xdg_dialog_v1 *xdg_dialog_v1;
|
||||
|
||||
SDL_AtomicInt swap_interval_ready;
|
||||
|
||||
@ -172,6 +173,7 @@ struct SDL_WindowData
|
||||
SDL_bool fullscreen_was_positioned;
|
||||
SDL_bool show_hide_sync_required;
|
||||
SDL_bool scale_to_display;
|
||||
SDL_bool modal_reparenting_required;
|
||||
|
||||
SDL_HitTestResult hit_test_result;
|
||||
|
||||
|
@ -202,6 +202,7 @@ static SDL_VideoDevice *WIN_CreateDevice(void)
|
||||
device->SetWindowResizable = WIN_SetWindowResizable;
|
||||
device->SetWindowAlwaysOnTop = WIN_SetWindowAlwaysOnTop;
|
||||
device->SetWindowFullscreen = WIN_SetWindowFullscreen;
|
||||
device->SetWindowModalFor = WIN_SetWindowModalFor;
|
||||
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
|
||||
device->GetWindowICCProfile = WIN_GetWindowICCProfile;
|
||||
device->SetWindowMouseRect = WIN_SetWindowMouseRect;
|
||||
|
@ -984,6 +984,10 @@ void WIN_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
WIN_SetWindowPosition(_this, window);
|
||||
}
|
||||
|
||||
if (window->flags & SDL_WINDOW_MODAL) {
|
||||
EnableWindow(window->parent->driverdata->hwnd, FALSE);
|
||||
}
|
||||
|
||||
hwnd = window->driverdata->hwnd;
|
||||
style = GetWindowLong(hwnd, GWL_EXSTYLE);
|
||||
if (style & WS_EX_NOACTIVATE) {
|
||||
@ -1006,6 +1010,11 @@ void WIN_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
void WIN_HideWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
{
|
||||
HWND hwnd = window->driverdata->hwnd;
|
||||
|
||||
if (window->flags & SDL_WINDOW_MODAL) {
|
||||
EnableWindow(window->parent->driverdata->hwnd, TRUE);
|
||||
}
|
||||
|
||||
ShowWindow(hwnd, SW_HIDE);
|
||||
|
||||
/* Transfer keyboard focus back to the parent */
|
||||
@ -1720,4 +1729,39 @@ void WIN_UpdateDarkModeForHWND(HWND hwnd)
|
||||
}
|
||||
}
|
||||
|
||||
int WIN_SetWindowModalFor(SDL_VideoDevice *_this, SDL_Window *modal_window, SDL_Window *parent_window)
|
||||
{
|
||||
SDL_WindowData *modal_data = modal_window->driverdata;
|
||||
const LONG_PTR parent_hwnd = (LONG_PTR)(parent_window ? parent_window->driverdata->hwnd : NULL);
|
||||
const LONG_PTR old_ptr = GetWindowLongPtr(modal_data->hwnd, GWLP_HWNDPARENT);
|
||||
const DWORD style = GetWindowLong(modal_data->hwnd, GWL_STYLE);
|
||||
|
||||
if (old_ptr == parent_hwnd) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Reenable the old parent window. */
|
||||
if (old_ptr) {
|
||||
EnableWindow((HWND)old_ptr, TRUE);
|
||||
}
|
||||
|
||||
if (!(style & WS_CHILD)) {
|
||||
/* Despite the name, this changes the *owner* of a toplevel window, not
|
||||
* the parent of a child window.
|
||||
*
|
||||
* https://devblogs.microsoft.com/oldnewthing/20100315-00/?p=14613
|
||||
*/
|
||||
SetWindowLongPtr(modal_data->hwnd, GWLP_HWNDPARENT, parent_hwnd);
|
||||
} else {
|
||||
SetParent(modal_data->hwnd, (HWND)parent_hwnd);
|
||||
}
|
||||
|
||||
/* Disable the new parent window if the modal window is visible. */
|
||||
if (!(modal_window->flags & SDL_WINDOW_HIDDEN) && parent_hwnd) {
|
||||
EnableWindow((HWND)parent_hwnd, FALSE);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* SDL_VIDEO_DRIVER_WINDOWS */
|
||||
|
@ -118,6 +118,7 @@ extern void WIN_ShowWindowSystemMenu(SDL_Window *window, int x, int y);
|
||||
extern int WIN_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool focusable);
|
||||
extern int WIN_AdjustWindowRect(SDL_Window *window, int *x, int *y, int *width, int *height, SDL_WindowRect rect_type);
|
||||
extern int WIN_AdjustWindowRectForHWND(HWND hwnd, LPRECT lpRect, UINT frame_dpi);
|
||||
extern int WIN_SetWindowModalFor(SDL_VideoDevice *_this, SDL_Window *modal_window, SDL_Window *parent_window);
|
||||
|
||||
/* Ends C function definitions when using C++ */
|
||||
#ifdef __cplusplus
|
||||
|
@ -389,6 +389,7 @@ int X11_VideoInit(SDL_VideoDevice *_this)
|
||||
GET_ATOM(WM_DELETE_WINDOW);
|
||||
GET_ATOM(WM_TAKE_FOCUS);
|
||||
GET_ATOM(WM_NAME);
|
||||
GET_ATOM(WM_TRANSIENT_FOR);
|
||||
GET_ATOM(_NET_WM_STATE);
|
||||
GET_ATOM(_NET_WM_STATE_HIDDEN);
|
||||
GET_ATOM(_NET_WM_STATE_FOCUSED);
|
||||
@ -398,6 +399,7 @@ int X11_VideoInit(SDL_VideoDevice *_this)
|
||||
GET_ATOM(_NET_WM_STATE_ABOVE);
|
||||
GET_ATOM(_NET_WM_STATE_SKIP_TASKBAR);
|
||||
GET_ATOM(_NET_WM_STATE_SKIP_PAGER);
|
||||
GET_ATOM(_NET_WM_STATE_MODAL);
|
||||
GET_ATOM(_NET_WM_ALLOWED_ACTIONS);
|
||||
GET_ATOM(_NET_WM_ACTION_FULLSCREEN);
|
||||
GET_ATOM(_NET_WM_NAME);
|
||||
|
@ -67,6 +67,7 @@ struct SDL_VideoData
|
||||
Atom WM_DELETE_WINDOW;
|
||||
Atom WM_TAKE_FOCUS;
|
||||
Atom WM_NAME;
|
||||
Atom WM_TRANSIENT_FOR;
|
||||
Atom _NET_WM_STATE;
|
||||
Atom _NET_WM_STATE_HIDDEN;
|
||||
Atom _NET_WM_STATE_FOCUSED;
|
||||
@ -76,6 +77,7 @@ struct SDL_VideoData
|
||||
Atom _NET_WM_STATE_ABOVE;
|
||||
Atom _NET_WM_STATE_SKIP_TASKBAR;
|
||||
Atom _NET_WM_STATE_SKIP_PAGER;
|
||||
Atom _NET_WM_STATE_MODAL;
|
||||
Atom _NET_WM_ALLOWED_ACTIONS;
|
||||
Atom _NET_WM_ACTION_FULLSCREEN;
|
||||
Atom _NET_WM_NAME;
|
||||
|
@ -138,6 +138,7 @@ void X11_SetNetWMState(SDL_VideoDevice *_this, Window xwindow, SDL_WindowFlags f
|
||||
Atom _NET_WM_STATE_ABOVE = videodata->_NET_WM_STATE_ABOVE;
|
||||
Atom _NET_WM_STATE_SKIP_TASKBAR = videodata->_NET_WM_STATE_SKIP_TASKBAR;
|
||||
Atom _NET_WM_STATE_SKIP_PAGER = videodata->_NET_WM_STATE_SKIP_PAGER;
|
||||
Atom _NET_WM_STATE_MODAL = videodata->_NET_WM_STATE_MODAL;
|
||||
Atom atoms[16];
|
||||
int count = 0;
|
||||
|
||||
@ -167,6 +168,9 @@ void X11_SetNetWMState(SDL_VideoDevice *_this, Window xwindow, SDL_WindowFlags f
|
||||
if (flags & SDL_WINDOW_FULLSCREEN) {
|
||||
atoms[count++] = _NET_WM_STATE_FULLSCREEN;
|
||||
}
|
||||
if (flags & SDL_WINDOW_MODAL) {
|
||||
atoms[count++] = _NET_WM_STATE_MODAL;
|
||||
}
|
||||
|
||||
SDL_assert(count <= SDL_arraysize(atoms));
|
||||
|
||||
@ -1204,10 +1208,43 @@ int X11_SetWindowOpacity(SDL_VideoDevice *_this, SDL_Window *window, float opaci
|
||||
int X11_SetWindowModalFor(SDL_VideoDevice *_this, SDL_Window *modal_window, SDL_Window *parent_window)
|
||||
{
|
||||
SDL_WindowData *data = modal_window->driverdata;
|
||||
SDL_WindowData *parent_data = parent_window->driverdata;
|
||||
Display *display = data->videodata->display;
|
||||
SDL_WindowData *parent_data = parent_window ? parent_window->driverdata : NULL;
|
||||
SDL_VideoData *video_data = _this->driverdata;
|
||||
SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(modal_window);
|
||||
Display *display = video_data->display;
|
||||
Uint32 flags = modal_window->flags;
|
||||
Atom _NET_WM_STATE = data->videodata->_NET_WM_STATE;
|
||||
Atom _NET_WM_STATE_MODAL = data->videodata->_NET_WM_STATE_MODAL;
|
||||
|
||||
if (parent_data) {
|
||||
flags |= SDL_WINDOW_MODAL;
|
||||
X11_XSetTransientForHint(display, data->xwindow, parent_data->xwindow);
|
||||
} else {
|
||||
flags &= ~SDL_WINDOW_MODAL;
|
||||
X11_XDeleteProperty(display, data->xwindow, video_data->WM_TRANSIENT_FOR);
|
||||
}
|
||||
|
||||
if (X11_IsWindowMapped(_this, modal_window)) {
|
||||
XEvent e;
|
||||
|
||||
SDL_zero(e);
|
||||
e.xany.type = ClientMessage;
|
||||
e.xclient.message_type = _NET_WM_STATE;
|
||||
e.xclient.format = 32;
|
||||
e.xclient.window = data->xwindow;
|
||||
e.xclient.data.l[0] =
|
||||
parent_data ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
|
||||
e.xclient.data.l[1] = _NET_WM_STATE_MODAL;
|
||||
e.xclient.data.l[3] = 0l;
|
||||
|
||||
X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0,
|
||||
SubstructureNotifyMask | SubstructureRedirectMask, &e);
|
||||
} else {
|
||||
X11_SetNetWMState(_this, data->xwindow, flags);
|
||||
}
|
||||
|
||||
X11_XFlush(display);
|
||||
|
||||
X11_XSetTransientForHint(display, data->xwindow, parent_data->xwindow);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -414,6 +414,7 @@ add_sdl_test_executable(testpopup SOURCES testpopup.c)
|
||||
add_sdl_test_executable(testdialog SOURCES testdialog.c)
|
||||
add_sdl_test_executable(testtime SOURCES testtime.c)
|
||||
add_sdl_test_executable(testmanymouse SOURCES testmanymouse.c)
|
||||
add_sdl_test_executable(testmodal SOURCES testmodal.c)
|
||||
|
||||
if (HAVE_WAYLAND)
|
||||
# Set the GENERATED property on the protocol file, since it is first created at build time
|
||||
|
172
test/testmodal.c
Normal file
172
test/testmodal.c
Normal file
@ -0,0 +1,172 @@
|
||||
/*
|
||||
Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely.
|
||||
*/
|
||||
/* Sample program: Create a parent window and a modal child window. */
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_main.h>
|
||||
#include <SDL3/SDL_test.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
SDL_Window *w1 = NULL, *w2 = NULL;
|
||||
SDL_Renderer *r1 = NULL, *r2 = NULL;
|
||||
SDLTest_CommonState *state = NULL;
|
||||
Uint64 show_deadline = 0;
|
||||
int i;
|
||||
int exit_code = 0;
|
||||
|
||||
/* Initialize test framework */
|
||||
state = SDLTest_CommonCreateState(argv, 0);
|
||||
if (state == NULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Enable standard application logging */
|
||||
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
|
||||
|
||||
/* Parse commandline */
|
||||
for (i = 1; i < argc;) {
|
||||
int consumed;
|
||||
|
||||
consumed = SDLTest_CommonArg(state, i);
|
||||
|
||||
if (consumed <= 0) {
|
||||
static const char *options[] = { NULL };
|
||||
SDLTest_CommonLogUsage(state, argv[0], options);
|
||||
return 1;
|
||||
}
|
||||
|
||||
i += consumed;
|
||||
}
|
||||
|
||||
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
|
||||
SDL_Log("SDL_Init failed (%s)", SDL_GetError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (SDL_CreateWindowAndRenderer("Parent Window", 640, 480, 0, &w1, &r1)) {
|
||||
SDL_Log("Failed to create parent window and/or renderer: %s\n", SDL_GetError());
|
||||
exit_code = 1;
|
||||
goto sdl_quit;
|
||||
}
|
||||
|
||||
SDL_CreateWindowAndRenderer("Non-Modal Window", 320, 200, 0, &w2, &r2);
|
||||
if (!w2) {
|
||||
SDL_Log("Failed to create parent window and/or renderer: %s\n", SDL_GetError());
|
||||
exit_code = 1;
|
||||
goto sdl_quit;
|
||||
}
|
||||
|
||||
if (!SDL_SetWindowModalFor(w2, w1)) {
|
||||
SDL_SetWindowTitle(w2, "Modal Window");
|
||||
}
|
||||
|
||||
while (1) {
|
||||
int quit = 0;
|
||||
SDL_Event e;
|
||||
while (SDL_PollEvent(&e)) {
|
||||
if (e.type == SDL_EVENT_QUIT) {
|
||||
quit = 1;
|
||||
break;
|
||||
} else if (e.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED) {
|
||||
if (e.window.windowID == SDL_GetWindowID(w2)) {
|
||||
SDL_DestroyRenderer(r2);
|
||||
SDL_DestroyWindow(w2);
|
||||
r2 = NULL;
|
||||
w2 = NULL;
|
||||
} else if (e.window.windowID == SDL_GetWindowID(w1)) {
|
||||
SDL_DestroyRenderer(r1);
|
||||
SDL_DestroyWindow(w1);
|
||||
r1 = NULL;
|
||||
w1 = NULL;
|
||||
}
|
||||
} else if (e.type == SDL_EVENT_KEY_DOWN) {
|
||||
if ((e.key.keysym.sym == SDLK_m || e.key.keysym.sym == SDLK_n) && !w2) {
|
||||
if (SDL_CreateWindowAndRenderer("Non-Modal Window", 320, 200, SDL_WINDOW_HIDDEN, &w2, &r2) < 0) {
|
||||
SDL_Log("Failed to create modal window and/or renderer: %s\n", SDL_GetError());
|
||||
exit_code = 1;
|
||||
goto sdl_quit;
|
||||
}
|
||||
|
||||
if (e.key.keysym.sym == SDLK_m) {
|
||||
if (!SDL_SetWindowModalFor(w2, w1)) {
|
||||
SDL_SetWindowTitle(w2, "Modal Window");
|
||||
}
|
||||
}
|
||||
SDL_ShowWindow(w2);
|
||||
} else if (e.key.keysym.sym == SDLK_ESCAPE && w2) {
|
||||
SDL_DestroyWindow(w2);
|
||||
r2 = NULL;
|
||||
w2 = NULL;
|
||||
} else if (e.key.keysym.sym == SDLK_h) {
|
||||
if (e.key.keysym.mod & SDL_KMOD_CTRL) {
|
||||
/* Hide the parent, which should hide the modal too. */
|
||||
show_deadline = SDL_GetTicksNS() + SDL_SECONDS_TO_NS(3);
|
||||
SDL_HideWindow(w1);
|
||||
} else if (w2) {
|
||||
/* Show/hide the modal window */
|
||||
if (SDL_GetWindowFlags(w2) & SDL_WINDOW_HIDDEN) {
|
||||
SDL_ShowWindow(w2);
|
||||
} else {
|
||||
SDL_HideWindow(w2);
|
||||
}
|
||||
}
|
||||
} else if (e.key.keysym.sym == SDLK_p && w2) {
|
||||
if (SDL_GetWindowFlags(w2) & SDL_WINDOW_MODAL) {
|
||||
/* Unparent the window */
|
||||
if (!SDL_SetWindowModalFor(w2, NULL)) {
|
||||
SDL_SetWindowTitle(w2, "Non-Modal Window");
|
||||
}
|
||||
} else {
|
||||
/* Reparent the window */
|
||||
if (!SDL_SetWindowModalFor(w2, w1)) {
|
||||
SDL_SetWindowTitle(w2, "Modal Window");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (quit) {
|
||||
break;
|
||||
}
|
||||
SDL_Delay(100);
|
||||
|
||||
if (show_deadline && show_deadline <= SDL_GetTicksNS()) {
|
||||
SDL_ShowWindow(w1);
|
||||
}
|
||||
|
||||
/* Parent window is red */
|
||||
if (r1) {
|
||||
SDL_SetRenderDrawColor(r1, 224, 48, 12, SDL_ALPHA_OPAQUE);
|
||||
SDL_RenderClear(r1);
|
||||
SDL_RenderPresent(r1);
|
||||
}
|
||||
|
||||
/* Child window is blue */
|
||||
if (r2) {
|
||||
SDL_SetRenderDrawColor(r2, 6, 76, 255, SDL_ALPHA_OPAQUE);
|
||||
SDL_RenderClear(r2);
|
||||
SDL_RenderPresent(r2);
|
||||
}
|
||||
}
|
||||
|
||||
sdl_quit:
|
||||
if (w1) {
|
||||
/* The child window and renderer will be cleaned up automatically. */
|
||||
SDL_DestroyWindow(w1);
|
||||
}
|
||||
|
||||
SDL_Quit();
|
||||
SDLTest_CommonDestroyState(state);
|
||||
return exit_code;
|
||||
}
|
110
wayland-protocols/xdg-dialog-v1.xml
Normal file
110
wayland-protocols/xdg-dialog-v1.xml
Normal file
@ -0,0 +1,110 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<protocol name="xdg_dialog_v1">
|
||||
<copyright>
|
||||
Copyright © 2023 Carlos Garnacho
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice (including the next
|
||||
paragraph) shall be included in all copies or substantial portions of the
|
||||
Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
</copyright>
|
||||
|
||||
<interface name="xdg_wm_dialog_v1" version="1">
|
||||
<description summary="create dialogs related to other toplevels">
|
||||
The xdg_wm_dialog_v1 interface is exposed as a global object allowing
|
||||
to register surfaces with a xdg_toplevel role as "dialogs" relative to
|
||||
another toplevel.
|
||||
|
||||
The compositor may let this relation influence how the surface is
|
||||
placed, displayed or interacted with.
|
||||
|
||||
Warning! The protocol described in this file is currently in the testing
|
||||
phase. Backward compatible changes may be added together with the
|
||||
corresponding interface version bump. Backward incompatible changes can
|
||||
only be done by creating a new major version of the extension.
|
||||
</description>
|
||||
|
||||
<enum name="error">
|
||||
<entry name="already_used" value="0"
|
||||
summary="the xdg_toplevel object has already been used to create a xdg_dialog_v1"/>
|
||||
</enum>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="destroy the dialog manager object">
|
||||
Destroys the xdg_wm_dialog_v1 object. This does not affect
|
||||
the xdg_dialog_v1 objects generated through it.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<request name="get_xdg_dialog">
|
||||
<description summary="create a dialog object">
|
||||
Creates a xdg_dialog_v1 object for the given toplevel. See the interface
|
||||
description for more details.
|
||||
|
||||
Compositors must raise an already_used error if clients attempt to
|
||||
create multiple xdg_dialog_v1 objects for the same xdg_toplevel.
|
||||
</description>
|
||||
<arg name="id" type="new_id" interface="xdg_dialog_v1"/>
|
||||
<arg name="toplevel" type="object" interface="xdg_toplevel"/>
|
||||
</request>
|
||||
</interface>
|
||||
|
||||
<interface name="xdg_dialog_v1" version="1">
|
||||
<description summary="dialog object">
|
||||
A xdg_dialog_v1 object is an ancillary object tied to a xdg_toplevel. Its
|
||||
purpose is hinting the compositor that the toplevel is a "dialog" (e.g. a
|
||||
temporary window) relative to another toplevel (see
|
||||
xdg_toplevel.set_parent). If the xdg_toplevel is destroyed, the xdg_dialog_v1
|
||||
becomes inert.
|
||||
|
||||
Through this object, the client may provide additional hints about
|
||||
the purpose of the secondary toplevel. This interface has no effect
|
||||
on toplevels that are not attached to a parent toplevel.
|
||||
</description>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="destroy the dialog object">
|
||||
Destroys the xdg_dialog_v1 object. If this object is destroyed
|
||||
before the related xdg_toplevel, the compositor should unapply its
|
||||
effects.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<request name="set_modal">
|
||||
<description summary="mark dialog as modal">
|
||||
Hints that the dialog has "modal" behavior. Modal dialogs typically
|
||||
require to be fully addressed by the user (i.e. closed) before resuming
|
||||
interaction with the parent toplevel, and may require a distinct
|
||||
presentation.
|
||||
|
||||
Clients must implement the logic to filter events in the parent
|
||||
toplevel on their own.
|
||||
|
||||
Compositors may choose any policy in event delivery to the parent
|
||||
toplevel, from delivering all events unfiltered to using them for
|
||||
internal consumption.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<request name="unset_modal">
|
||||
<description summary="mark dialog as not modal">
|
||||
Drops the hint that this dialog has "modal" behavior. See
|
||||
xdg_dialog_v1.set_modal for more details.
|
||||
</description>
|
||||
</request>
|
||||
</interface>
|
||||
</protocol>
|
Loading…
Reference in New Issue
Block a user