Issue #13709: wait we get our first surface focus before listing…

… input devices.

Per Carlos' advice on gtk#7534, I wait for us to get a focus, since the
pad devices are only created at that point.
Note that this is a Wayland-only issue, but since it doesn't matter too
much that input devices are not initialized before we have a focused GUI
anyway, let's make this simpler.

At the earliest, the splash focus can announce a focus, but since it is
possible to start GIMP without the splash, display shells will also
possibly announce the first focus (there will always be a display shell
focusing at some point for any GUI GIMP!).
This commit is contained in:
Jehan 2025-12-15 23:42:59 +01:00
parent 10cf3f05ee
commit d92c237a17
6 changed files with 217 additions and 108 deletions

View file

@ -97,6 +97,7 @@ enum
CLIPBOARD_CHANGED,
FILTER_HISTORY_CHANGED,
IMAGE_OPENED,
FOCUSED_ONCE,
LAST_SIGNAL
};
@ -205,6 +206,13 @@ gimp_class_init (GimpClass *klass)
NULL, NULL, NULL,
G_TYPE_NONE, 1, G_TYPE_FILE);
gimp_signals[FOCUSED_ONCE] =
g_signal_new ("focused-once",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, 0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
object_class->constructed = gimp_constructed;
object_class->set_property = gimp_set_property;
object_class->get_property = gimp_get_property;
@ -293,6 +301,8 @@ gimp_init (Gimp *gimp)
gimp->templates = gimp_list_new (GIMP_TYPE_TEMPLATE, TRUE);
gimp_object_set_static_name (GIMP_OBJECT (gimp->templates), "templates");
gimp->focused_once = FALSE;
}
static void
@ -917,6 +927,26 @@ gimp_exit (Gimp *gimp,
gimp, NULL);
}
void
gimp_set_focused_once (Gimp *gimp)
{
g_return_if_fail (GIMP_IS_GIMP (gimp));
if (! gimp->focused_once)
{
gimp->focused_once = TRUE;
g_signal_emit (gimp, gimp_signals[FOCUSED_ONCE], 0);
}
}
gboolean
gimp_has_focused_once (Gimp *gimp)
{
g_return_val_if_fail (GIMP_IS_GIMP (gimp), FALSE);
return gimp->focused_once;
}
GList *
gimp_get_image_iter (Gimp *gimp)
{

View file

@ -133,6 +133,9 @@ struct _Gimp
/* the context used by the interface */
GimpContext *user_context;
/* GUI focus occured. See gtk#7534. */
gboolean focused_once;
};
struct _GimpClass
@ -189,6 +192,9 @@ gboolean gimp_is_restored (Gimp *gimp);
void gimp_exit (Gimp *gimp,
gboolean force);
void gimp_set_focused_once (Gimp *gimp);
gboolean gimp_has_focused_once (Gimp *gimp);
GList * gimp_get_image_iter (Gimp *gimp);
GList * gimp_get_display_iter (Gimp *gimp);
GList * gimp_get_image_windows (Gimp *gimp);

View file

@ -258,8 +258,13 @@ gimp_display_shell_events (GtkWidget *widget,
{
GdkEventFocus *fevent = (GdkEventFocus *) event;
if (fevent->in && shell->display->config->activate_on_focus)
set_display = TRUE;
if (fevent->in)
{
gimp_set_focused_once (gimp);
if (shell->display->config->activate_on_focus)
set_display = TRUE;
}
}
break;

View file

@ -101,6 +101,10 @@ static gboolean splash_key_event (GtkWidget *window,
GimpSplash *splash);
static gboolean splash_unset_alt (GimpSplash *splash);
static gboolean splash_window_focus (GtkWidget *window,
GtkDirectionType direction,
Gimp *gimp);
static void splash_rectangle_union (GdkRectangle *dest,
PangoRectangle *pango_rect,
gint offset_x,
@ -293,6 +297,10 @@ splash_create (Gimp *gimp,
gtk_box_pack_end (GTK_BOX (vbox), splash->progress, FALSE, FALSE, 0);
gtk_widget_show (splash->progress);
g_signal_connect (splash->window, "focus",
G_CALLBACK (splash_window_focus),
gimp);
gtk_widget_show (splash->window);
#ifdef G_OS_WIN32
@ -485,6 +493,19 @@ splash_unset_alt (GimpSplash *splash)
return G_SOURCE_REMOVE;
}
static gboolean
splash_window_focus (GtkWidget *window,
GtkDirectionType direction,
Gimp *gimp)
{
g_signal_handlers_disconnect_by_func (splash->window,
G_CALLBACK (splash_window_focus),
gimp);
gimp_set_focused_once (gimp);
return FALSE;
}
/* area returns the union of the previous and new ink rectangles */
static void
splash_position_layouts (GimpSplash *splash,

View file

@ -77,6 +77,8 @@ static void gimp_device_manager_get_property (GObject *object,
GValue *value,
GParamSpec *pspec);
static void gimp_device_manager_focused_once (GimpDeviceManager *manager);
static void gimp_device_manager_display_opened (GdkDisplayManager *disp_manager,
GdkDisplay *display,
GimpDeviceManager *manager);
@ -168,54 +170,17 @@ gimp_device_manager_constructed (GObject *object)
{
GimpDeviceManager *manager = GIMP_DEVICE_MANAGER (object);
GimpDeviceManagerPrivate *private = GET_PRIVATE (object);
GdkDisplayManager *disp_manager;
GSList *displays;
GSList *list;
GdkDisplay *display;
GdkSeat *seat;
GdkDevice *pointer;
GimpDeviceInfo *device_info;
GimpContext *user_context;
G_OBJECT_CLASS (parent_class)->constructed (object);
gimp_assert (GIMP_IS_GIMP (private->gimp));
disp_manager = gdk_display_manager_get ();
displays = gdk_display_manager_list_displays (disp_manager);
/* present displays in the order in which they were opened */
displays = g_slist_reverse (displays);
for (list = displays; list; list = g_slist_next (list))
{
gimp_device_manager_display_opened (disp_manager, list->data, manager);
}
g_slist_free (displays);
g_signal_connect (disp_manager, "display-opened",
G_CALLBACK (gimp_device_manager_display_opened),
manager);
display = gdk_display_get_default ();
seat = gdk_display_get_default_seat (display);
pointer = gdk_seat_get_pointer (seat);
device_info = gimp_device_info_get_by_device (pointer);
gimp_device_manager_set_current_device (manager, device_info);
g_signal_connect_object (private->gimp->config, "notify::devices-share-tool",
G_CALLBACK (gimp_device_manager_config_notify),
manager, 0);
user_context = gimp_get_user_context (private->gimp);
g_signal_connect_object (user_context, "tool-changed",
G_CALLBACK (gimp_device_manager_tool_changed),
manager, 0);
if (gimp_has_focused_once (private->gimp))
gimp_device_manager_focused_once (manager);
else
g_signal_connect_object (private->gimp, "focused-once",
G_CALLBACK (gimp_device_manager_focused_once),
manager, G_CONNECT_SWAPPED);
}
static void
@ -397,6 +362,59 @@ gimp_device_manager_reconfigure_pads (GimpDeviceManager *manager)
/* private functions */
static void
gimp_device_manager_focused_once (GimpDeviceManager *manager)
{
GimpDeviceManagerPrivate *private = GET_PRIVATE (manager);
GdkDisplayManager *disp_manager;
GSList *displays;
GSList *list;
GdkDisplay *display;
GdkSeat *seat;
GdkDevice *pointer;
GimpDeviceInfo *device_info;
GimpContext *user_context;
g_signal_handlers_disconnect_by_func (private->gimp,
G_CALLBACK (gimp_device_manager_focused_once),
manager);
disp_manager = gdk_display_manager_get ();
displays = gdk_display_manager_list_displays (disp_manager);
/* present displays in the order in which they were opened */
displays = g_slist_reverse (displays);
for (list = displays; list; list = g_slist_next (list))
{
gimp_device_manager_display_opened (disp_manager, list->data, manager);
}
g_slist_free (displays);
g_signal_connect (disp_manager, "display-opened",
G_CALLBACK (gimp_device_manager_display_opened),
manager);
display = gdk_display_get_default ();
seat = gdk_display_get_default_seat (display);
pointer = gdk_seat_get_pointer (seat);
device_info = gimp_device_info_get_by_device (pointer);
gimp_device_manager_set_current_device (manager, device_info);
g_signal_connect_object (private->gimp->config, "notify::devices-share-tool",
G_CALLBACK (gimp_device_manager_config_notify),
manager, 0);
user_context = gimp_get_user_context (private->gimp);
g_signal_connect_object (user_context, "tool-changed",
G_CALLBACK (gimp_device_manager_tool_changed),
manager, 0);
}
static void
gimp_device_manager_display_opened (GdkDisplayManager *disp_manager,
GdkDisplay *display,
@ -427,6 +445,14 @@ gimp_device_manager_display_opened (GdkDisplayManager *disp_manager,
device = gdk_seat_get_pointer (seat);
gimp_device_manager_device_added (seat, device, manager);
/* XXX The whole reason why we created the "focused-once" signal on
* Gimp object is because on Wayland, GDK returns a NULL GdkDevice for
* tablet pads until a surface got in focus at least once (see
* gtk#7534). So we wait for this one-time focus before querying
* devices. It makes things a bit more bothersome, but is acceptable
* since anyway we can't do anything with input devices before we have
* a GUI.
*/
devices = gdk_seat_get_slaves (seat, GDK_SEAT_CAPABILITY_ALL);
/* create device info structures for present devices */

View file

@ -50,6 +50,9 @@
static gboolean devicerc_deleted = FALSE;
static void gimp_devices_restore_on_focused_once (Gimp *gimp);
/* public functions */
void
@ -87,69 +90,12 @@ gimp_devices_exit (Gimp *gimp)
void
gimp_devices_restore (Gimp *gimp)
{
GimpDeviceManager *manager;
GList *list;
GFile *file;
GError *error = NULL;
g_return_if_fail (GIMP_IS_GIMP (gimp));
manager = gimp_devices_get_manager (gimp);
g_return_if_fail (GIMP_IS_DEVICE_MANAGER (manager));
for (list = GIMP_LIST (manager)->queue->head;
list;
list = g_list_next (list))
{
GimpDeviceInfo *device_info = list->data;
gimp_device_info_save_tool (device_info);
gimp_device_info_set_default_tool (device_info);
}
file = gimp_directory_file ("devicerc", NULL);
if (gimp->be_verbose)
g_print ("Parsing '%s'\n", gimp_file_get_utf8_name (file));
if (! gimp_config_deserialize_file (GIMP_CONFIG (manager),
file,
gimp,
&error))
{
if (error->code != GIMP_CONFIG_ERROR_OPEN_ENOENT)
gimp_message_literal (gimp, NULL, GIMP_MESSAGE_ERROR, error->message);
g_error_free (error);
/* don't bail out here */
}
g_object_unref (file);
for (list = GIMP_LIST (manager)->queue->head;
list;
list = g_list_next (list))
{
GimpDeviceInfo *device_info = list->data;
if (! GIMP_TOOL_PRESET (device_info)->tool_options)
{
gimp_device_info_save_tool (device_info);
g_printerr ("%s: set default tool on loaded GimpDeviceInfo without tool options: %s\n",
G_STRFUNC, gimp_object_get_name (device_info));
}
}
if (! GIMP_GUI_CONFIG (gimp->config)->devices_share_tool)
{
GimpDeviceInfo *current_device;
current_device = gimp_device_manager_get_current_device (manager);
gimp_device_info_restore_tool (current_device);
}
if (gimp_has_focused_once (gimp))
gimp_devices_restore_on_focused_once (gimp);
else
g_signal_connect (gimp, "focused-once",
G_CALLBACK (gimp_devices_restore_on_focused_once),
NULL);
}
void
@ -395,3 +341,78 @@ gimp_devices_check_change (Gimp *gimp,
return FALSE;
}
/* Private functions */
static void
gimp_devices_restore_on_focused_once (Gimp *gimp)
{
GimpDeviceManager *manager;
GList *list;
GFile *file;
GError *error = NULL;
g_return_if_fail (GIMP_IS_GIMP (gimp));
g_signal_handlers_disconnect_by_func (gimp,
G_CALLBACK (gimp_devices_restore_on_focused_once),
NULL);
manager = gimp_devices_get_manager (gimp);
g_return_if_fail (GIMP_IS_DEVICE_MANAGER (manager));
for (list = GIMP_LIST (manager)->queue->head;
list;
list = g_list_next (list))
{
GimpDeviceInfo *device_info = list->data;
gimp_device_info_save_tool (device_info);
gimp_device_info_set_default_tool (device_info);
}
file = gimp_directory_file ("devicerc", NULL);
if (gimp->be_verbose)
g_print ("Parsing '%s'\n", gimp_file_get_utf8_name (file));
if (! gimp_config_deserialize_file (GIMP_CONFIG (manager),
file,
gimp,
&error))
{
if (error->code != GIMP_CONFIG_ERROR_OPEN_ENOENT)
gimp_message_literal (gimp, NULL, GIMP_MESSAGE_ERROR, error->message);
g_error_free (error);
/* don't bail out here */
}
g_object_unref (file);
for (list = GIMP_LIST (manager)->queue->head;
list;
list = g_list_next (list))
{
GimpDeviceInfo *device_info = list->data;
if (! GIMP_TOOL_PRESET (device_info)->tool_options)
{
gimp_device_info_save_tool (device_info);
g_printerr ("%s: set default tool on loaded GimpDeviceInfo without tool options: %s\n",
G_STRFUNC, gimp_object_get_name (device_info));
}
}
if (! GIMP_GUI_CONFIG (gimp->config)->devices_share_tool)
{
GimpDeviceInfo *current_device;
current_device = gimp_device_manager_get_current_device (manager);
gimp_device_info_restore_tool (current_device);
}
}