gdk/broadway: Implements modal hint for Broadway surface

Ensures modal window is raised above parent; does return focus to
parent window when transient child window is closed.

That solves problems with overlapped modal window and focus loss in
complex multi window UI.
This commit is contained in:
Maxim Zakharov 2022-01-31 11:30:32 +11:00
parent 41270ba8c9
commit 2290c2bb23
9 changed files with 113 additions and 0 deletions

View File

@ -213,6 +213,7 @@ typedef enum {
BROADWAY_REQUEST_RELEASE_TEXTURE, BROADWAY_REQUEST_RELEASE_TEXTURE,
BROADWAY_REQUEST_SET_NODES, BROADWAY_REQUEST_SET_NODES,
BROADWAY_REQUEST_ROUNDTRIP, BROADWAY_REQUEST_ROUNDTRIP,
BROADWAY_REQUEST_SET_MODAL_HINT,
} BroadwayRequestType; } BroadwayRequestType;
typedef struct { typedef struct {
@ -293,6 +294,12 @@ typedef struct {
guint32 show_keyboard; guint32 show_keyboard;
} BroadwayRequestSetShowKeyboard; } BroadwayRequestSetShowKeyboard;
typedef struct {
BroadwayRequestBase base;
guint32 id;
gboolean modal_hint;
} BroadwayRequestSetModalHint;
typedef union { typedef union {
BroadwayRequestBase base; BroadwayRequestBase base;
BroadwayRequestNewSurface new_surface; BroadwayRequestNewSurface new_surface;
@ -312,6 +319,7 @@ typedef union {
BroadwayRequestUploadTexture upload_texture; BroadwayRequestUploadTexture upload_texture;
BroadwayRequestReleaseTexture release_texture; BroadwayRequestReleaseTexture release_texture;
BroadwayRequestSetNodes set_nodes; BroadwayRequestSetNodes set_nodes;
BroadwayRequestSetModalHint set_modal_hint;
} BroadwayRequest; } BroadwayRequest;
typedef enum { typedef enum {

View File

@ -128,6 +128,7 @@ struct BroadwaySurface {
gboolean visible; gboolean visible;
gint32 transient_for; gint32 transient_for;
guint32 texture; guint32 texture;
gboolean modal_hint;
BroadwayNode *nodes; BroadwayNode *nodes;
GHashTable *node_lookup; GHashTable *node_lookup;
}; };
@ -425,6 +426,14 @@ update_event_state (BroadwayServer *server,
{ {
surface->x = message->configure_notify.x; surface->x = message->configure_notify.x;
surface->y = message->configure_notify.y; surface->y = message->configure_notify.y;
if (server->focused_surface_id != message->configure_notify.id &&
server->pointer_grab_surface_id == -1 && surface->modal_hint)
{
broadway_server_surface_raise (server, message->configure_notify.id);
broadway_server_focus_surface (server, message->configure_notify.id);
broadway_server_flush (server);
}
} }
break; break;
case BROADWAY_EVENT_ROUNDTRIP_NOTIFY: case BROADWAY_EVENT_ROUNDTRIP_NOTIFY:
@ -1572,6 +1581,7 @@ broadway_server_destroy_surface (BroadwayServer *server,
int id) int id)
{ {
BroadwaySurface *surface; BroadwaySurface *surface;
gint32 transient_for = -1;
if (server->mouse_in_surface_id == id) if (server->mouse_in_surface_id == id)
{ {
@ -1589,11 +1599,24 @@ broadway_server_destroy_surface (BroadwayServer *server,
surface = broadway_server_lookup_surface (server, id); surface = broadway_server_lookup_surface (server, id);
if (surface != NULL) if (surface != NULL)
{ {
if (server->focused_surface_id == id)
transient_for = surface->transient_for;
server->surfaces = g_list_remove (server->surfaces, surface); server->surfaces = g_list_remove (server->surfaces, surface);
g_hash_table_remove (server->surface_id_hash, g_hash_table_remove (server->surface_id_hash,
GINT_TO_POINTER (id)); GINT_TO_POINTER (id));
broadway_surface_free (server, surface); broadway_surface_free (server, surface);
} }
if (transient_for != -1)
{
surface = broadway_server_lookup_surface (server, transient_for);
if (surface != NULL)
{
broadway_server_focus_surface (server, transient_for);
broadway_server_flush (server);
}
}
} }
gboolean gboolean
@ -1714,6 +1737,19 @@ broadway_server_surface_set_transient_for (BroadwayServer *server,
} }
} }
void
broadway_server_surface_set_modal_hint (BroadwayServer *server,
int id, gboolean modal_hint)
{
BroadwaySurface *surface;
surface = broadway_server_lookup_surface (server, id);
if (surface == NULL)
return;
surface->modal_hint = modal_hint;
}
gboolean gboolean
broadway_server_has_client (BroadwayServer *server) broadway_server_has_client (BroadwayServer *server)
{ {

View File

@ -130,6 +130,9 @@ gboolean broadway_server_surface_move_resize (BroadwayServer *
int height); int height);
void broadway_server_focus_surface (BroadwayServer *server, void broadway_server_focus_surface (BroadwayServer *server,
int new_focused_surface); int new_focused_surface);
void broadway_server_surface_set_modal_hint (BroadwayServer *server,
int id,
gboolean modal_hint);
#endif /* __BROADWAY_SERVER__ */ #endif /* __BROADWAY_SERVER__ */

View File

@ -384,6 +384,11 @@ client_handle_request (BroadwayClient *client,
case BROADWAY_REQUEST_SET_SHOW_KEYBOARD: case BROADWAY_REQUEST_SET_SHOW_KEYBOARD:
broadway_server_set_show_keyboard (server, request->set_show_keyboard.show_keyboard); broadway_server_set_show_keyboard (server, request->set_show_keyboard.show_keyboard);
break; break;
case BROADWAY_REQUEST_SET_MODAL_HINT:
broadway_server_surface_set_modal_hint (server,
request->set_modal_hint.id,
request->set_modal_hint.modal_hint);
break;
default: default:
g_warning ("Unknown request of type %d", request->base.type); g_warning ("Unknown request of type %d", request->base.type);
} }

View File

@ -561,6 +561,18 @@ _gdk_broadway_server_surface_set_transient_for (GdkBroadwayServer *server,
BROADWAY_REQUEST_SET_TRANSIENT_FOR); BROADWAY_REQUEST_SET_TRANSIENT_FOR);
} }
void
_gdk_broadway_server_surface_set_modal_hint (GdkBroadwayServer *server,
int id, gboolean modal_hint)
{
BroadwayRequestSetModalHint msg;
msg.id = id;
msg.modal_hint = modal_hint;
gdk_broadway_server_send_message (server, msg,
BROADWAY_REQUEST_SET_MODAL_HINT);
}
static int static int
open_shared_memory (void) open_shared_memory (void)
{ {

View File

@ -78,5 +78,8 @@ gboolean _gdk_broadway_server_surface_move_resize (GdkBroadwaySe
int y, int y,
int width, int width,
int height); int height);
void _gdk_broadway_server_surface_set_modal_hint (GdkBroadwayServer *server,
int id,
gboolean modal_hint);
#endif /* __GDK_BROADWAY_SERVER__ */ #endif /* __GDK_BROADWAY_SERVER__ */

View File

@ -82,6 +82,26 @@ gdk_event_source_check (GSource *source)
return retval; return retval;
} }
static void
handle_focus_change (GdkEvent *event)
{
GdkSurface *surface = gdk_event_get_surface(event);
GdkEvent *focus_event;
gboolean focus_in = (gdk_event_get_event_type (event) == GDK_ENTER_NOTIFY);
if (gdk_crossing_event_get_detail (event) == GDK_NOTIFY_INFERIOR)
return;
if (!gdk_crossing_event_get_focus (event) )
return;
focus_event = gdk_focus_event_new (gdk_event_get_surface (event),
gdk_event_get_device (event),
focus_in);
gdk_display_put_event (gdk_event_get_display (event), focus_event);
gdk_event_unref (focus_event);
}
void void
_gdk_broadway_events_got_input (GdkDisplay *display, _gdk_broadway_events_got_input (GdkDisplay *display,
BroadwayInputMsg *message) BroadwayInputMsg *message)
@ -110,6 +130,8 @@ _gdk_broadway_events_got_input (GdkDisplay *display,
node = _gdk_event_queue_append (display, event); node = _gdk_event_queue_append (display, event);
_gdk_windowing_got_event (display, node, event, message->base.serial); _gdk_windowing_got_event (display, node, event, message->base.serial);
handle_focus_change (event);
} }
break; break;
case BROADWAY_EVENT_LEAVE: case BROADWAY_EVENT_LEAVE:
@ -126,6 +148,8 @@ _gdk_broadway_events_got_input (GdkDisplay *display,
message->crossing.mode, message->crossing.mode,
GDK_NOTIFY_ANCESTOR); GDK_NOTIFY_ANCESTOR);
handle_focus_change (event);
node = _gdk_event_queue_append (display, event); node = _gdk_event_queue_append (display, event);
_gdk_windowing_got_event (display, node, event, message->base.serial); _gdk_windowing_got_event (display, node, event, message->base.serial);
} }

View File

@ -709,6 +709,21 @@ gdk_broadway_surface_set_transient_for (GdkSurface *surface,
_gdk_broadway_server_surface_set_transient_for (display->server, impl->id, impl->transient_for); _gdk_broadway_server_surface_set_transient_for (display->server, impl->id, impl->transient_for);
} }
static void
gdk_broadway_surface_set_modal_hint (GdkSurface *surface,
gboolean modal)
{
GdkBroadwayDisplay *display;
GdkBroadwaySurface *impl;
impl = GDK_BROADWAY_SURFACE (surface);
impl->modal_hint = modal;
display = GDK_BROADWAY_DISPLAY (gdk_surface_get_display (surface));
_gdk_broadway_server_surface_set_modal_hint (display->server, impl->id, impl->modal_hint);
}
static void static void
gdk_broadway_surface_get_geometry (GdkSurface *surface, gdk_broadway_surface_get_geometry (GdkSurface *surface,
int *x, int *x,
@ -1433,6 +1448,8 @@ gdk_broadway_toplevel_set_property (GObject *object,
break; break;
case LAST_PROP + GDK_TOPLEVEL_PROP_MODAL: case LAST_PROP + GDK_TOPLEVEL_PROP_MODAL:
gdk_broadway_surface_set_modal_hint (surface, g_value_get_boolean (value));
g_object_notify_by_pspec (G_OBJECT (surface), pspec);
break; break;
case LAST_PROP + GDK_TOPLEVEL_PROP_ICON_LIST: case LAST_PROP + GDK_TOPLEVEL_PROP_ICON_LIST:
@ -1479,6 +1496,10 @@ gdk_broadway_toplevel_get_property (GObject *object,
g_value_set_object (value, surface->transient_for); g_value_set_object (value, surface->transient_for);
break; break;
case LAST_PROP + GDK_TOPLEVEL_PROP_MODAL:
g_value_set_boolean (value, surface->modal_hint);
break;
case LAST_PROP + GDK_TOPLEVEL_PROP_ICON_LIST: case LAST_PROP + GDK_TOPLEVEL_PROP_ICON_LIST:
g_value_set_pointer (value, NULL); g_value_set_pointer (value, NULL);
break; break;

View File

@ -54,6 +54,7 @@ struct _GdkBroadwaySurface
gboolean dirty; gboolean dirty;
gboolean last_synced; gboolean last_synced;
gboolean modal_hint;
GdkGeometry geometry_hints; GdkGeometry geometry_hints;
GdkSurfaceHints geometry_hints_mask; GdkSurfaceHints geometry_hints_mask;