/* * Copyright © 2020 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Authors: Matthias Clasen */ #include "config.h" #include "gdkintl.h" #include "gdk-private.h" #include "gdktoplevelprivate.h" #include #include /** * SECTION:gdktoplevel * @Short_description: Interface for toplevel surfaces * @Title: Toplevels * @See_also: #GdkSurface, #GdkPopup * * A #GdkToplevel is a freestanding toplevel surface. * * The #GdkToplevel interface provides useful APIs for * interacting with the windowing system, such as controlling * maximization and size of the surface, setting icons and * transient parents for dialogs. */ G_DEFINE_INTERFACE (GdkToplevel, gdk_toplevel, GDK_TYPE_SURFACE) enum { COMPUTE_SIZE, N_SIGNALS }; static guint signals[N_SIGNALS] = { 0 }; static void gdk_toplevel_default_present (GdkToplevel *toplevel, GdkToplevelLayout *layout) { } static gboolean gdk_toplevel_default_minimize (GdkToplevel *toplevel) { return FALSE; } static gboolean gdk_toplevel_default_lower (GdkToplevel *toplevel) { return FALSE; } static void gdk_toplevel_default_focus (GdkToplevel *toplevel, guint32 timestamp) { } static gboolean gdk_toplevel_default_show_window_menu (GdkToplevel *toplevel, GdkEvent *event) { return FALSE; } static gboolean gdk_toplevel_default_supports_edge_constraints (GdkToplevel *toplevel) { return FALSE; } static void gdk_toplevel_default_inhibit_system_shortcuts (GdkToplevel *toplevel, GdkEvent *event) { } static void gdk_toplevel_default_restore_system_shortcuts (GdkToplevel *toplevel) { } void gdk_toplevel_notify_compute_size (GdkToplevel *toplevel, GdkToplevelSize *size) { g_signal_emit (toplevel, signals[COMPUTE_SIZE], 0, size); gdk_toplevel_size_validate (size); } static void gdk_toplevel_default_init (GdkToplevelInterface *iface) { iface->present = gdk_toplevel_default_present; iface->minimize = gdk_toplevel_default_minimize; iface->lower = gdk_toplevel_default_lower; iface->focus = gdk_toplevel_default_focus; iface->show_window_menu = gdk_toplevel_default_show_window_menu; iface->supports_edge_constraints = gdk_toplevel_default_supports_edge_constraints; iface->inhibit_system_shortcuts = gdk_toplevel_default_inhibit_system_shortcuts; iface->restore_system_shortcuts = gdk_toplevel_default_restore_system_shortcuts; g_object_interface_install_property (iface, g_param_spec_flags ("state", P_("State"), P_("State"), GDK_TYPE_TOPLEVEL_STATE, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_interface_install_property (iface, g_param_spec_string ("title", "Title", "The title of the surface", NULL, G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY)); g_object_interface_install_property (iface, g_param_spec_string ("startup-id", "Startup ID", "The startup ID of the surface", NULL, G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY)); g_object_interface_install_property (iface, g_param_spec_object ("transient-for", "Transient For", "The transient parent of the surface", GDK_TYPE_SURFACE, G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY)); g_object_interface_install_property (iface, g_param_spec_boolean ("modal", "Modal", "Whether the surface is modal", FALSE, G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY)); g_object_interface_install_property (iface, g_param_spec_pointer ("icon-list", "Icon List", "The list of icon textures", G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY)); g_object_interface_install_property (iface, g_param_spec_boolean ("decorated", "Decorated", "Decorated", FALSE, G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY)); g_object_interface_install_property (iface, g_param_spec_boolean ("deletable", "Deletable", "Deletable", FALSE, G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY)); g_object_interface_install_property (iface, g_param_spec_enum ("fullscreen-mode", "Fullscreen mode", "Fullscreen mode", GDK_TYPE_FULLSCREEN_MODE, GDK_FULLSCREEN_ON_CURRENT_MONITOR, G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY)); g_object_interface_install_property (iface, g_param_spec_boolean ("shortcuts-inhibited", "Shortcuts inhibited", "Whether keyboard shortcuts are inhibited", FALSE, G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY)); /** * GdkToplevel::compute-size: * @toplevel: a #GdkToplevel * @size: (type Gdk.ToplevelSize) (out caller-allocates): a #GdkToplevelSize * * Compute the desired size of the toplevel, given the information passed via * the #GdkToplevelSize object. * * It will normally be emitted during or after gdk_toplevel_present(), * depending on the configuration received by the windowing system. It may * also be emitted at any other point in time, in response to the windowing * system spontaneously changing the configuration. * * It is the responsibility of the GdkToplevel user to handle this signal; * failing to do so will result in an arbitrary size being used as a result. */ signals[COMPUTE_SIZE] = g_signal_new ("compute-size", GDK_TYPE_TOPLEVEL, G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, GDK_TYPE_TOPLEVEL_SIZE | G_SIGNAL_TYPE_STATIC_SCOPE); } guint gdk_toplevel_install_properties (GObjectClass *object_class, guint first_prop) { g_object_class_override_property (object_class, first_prop + GDK_TOPLEVEL_PROP_STATE, "state"); g_object_class_override_property (object_class, first_prop + GDK_TOPLEVEL_PROP_TITLE, "title"); g_object_class_override_property (object_class, first_prop + GDK_TOPLEVEL_PROP_STARTUP_ID, "startup-id"); g_object_class_override_property (object_class, first_prop + GDK_TOPLEVEL_PROP_TRANSIENT_FOR, "transient-for"); g_object_class_override_property (object_class, first_prop + GDK_TOPLEVEL_PROP_MODAL, "modal"); g_object_class_override_property (object_class, first_prop + GDK_TOPLEVEL_PROP_ICON_LIST, "icon-list"); g_object_class_override_property (object_class, first_prop + GDK_TOPLEVEL_PROP_DECORATED, "decorated"); g_object_class_override_property (object_class, first_prop + GDK_TOPLEVEL_PROP_DELETABLE, "deletable"); g_object_class_override_property (object_class, first_prop + GDK_TOPLEVEL_PROP_FULLSCREEN_MODE, "fullscreen-mode"); g_object_class_override_property (object_class, first_prop + GDK_TOPLEVEL_PROP_SHORTCUTS_INHIBITED, "shortcuts-inhibited"); return GDK_TOPLEVEL_NUM_PROPERTIES; } /** * gdk_toplevel_present: * @toplevel: the #GdkToplevel to show * @layout: the #GdkToplevelLayout object used to layout * * Present @toplevel after having processed the #GdkToplevelLayout rules. * If the toplevel was previously not showing, it will be showed, * otherwise it will change layout according to @layout. * * GDK may emit the 'compute-size' signal to let the user of this toplevel * compute the preferred size of the toplevel surface. See * #GdkToplevel::compute-size for details. * * Presenting is asynchronous and the specified layout parameters are not * guaranteed to be respected. */ void gdk_toplevel_present (GdkToplevel *toplevel, GdkToplevelLayout *layout) { g_return_if_fail (GDK_IS_TOPLEVEL (toplevel)); g_return_if_fail (layout != NULL); GDK_TOPLEVEL_GET_IFACE (toplevel)->present (toplevel, layout); } /** * gdk_toplevel_minimize: * @toplevel: a #GdkToplevel * * Asks to minimize the @toplevel. * * The windowing system may choose to ignore the request. * * Returns: %TRUE if the surface was minimized */ gboolean gdk_toplevel_minimize (GdkToplevel *toplevel) { g_return_val_if_fail (GDK_IS_TOPLEVEL (toplevel), FALSE); return GDK_TOPLEVEL_GET_IFACE (toplevel)->minimize (toplevel); } /** * gdk_toplevel_lower: * @toplevel: a #GdkToplevel * * Asks to lower the @toplevel below other windows. * * The windowing system may choose to ignore the request. * * Returns: %TRUE if the surface was lowered */ gboolean gdk_toplevel_lower (GdkToplevel *toplevel) { g_return_val_if_fail (GDK_IS_TOPLEVEL (toplevel), FALSE); return GDK_TOPLEVEL_GET_IFACE (toplevel)->lower (toplevel); } /** * gdk_toplevel_focus: * @toplevel: a #GdkToplevel * @timestamp: timestamp of the event triggering the surface focus * * Sets keyboard focus to @surface. * * In most cases, gtk_window_present_with_time() should be used * on a #GtkWindow, rather than calling this function. */ void gdk_toplevel_focus (GdkToplevel *toplevel, guint32 timestamp) { g_return_if_fail (GDK_IS_TOPLEVEL (toplevel)); return GDK_TOPLEVEL_GET_IFACE (toplevel)->focus (toplevel, timestamp); } /** * gdk_toplevel_get_state: * @toplevel: a #GdkToplevel * * Gets the bitwise OR of the currently active surface state flags, * from the #GdkToplevelState enumeration. * * Returns: surface state bitfield */ GdkToplevelState gdk_toplevel_get_state (GdkToplevel *toplevel) { GdkToplevelState state; g_return_val_if_fail (GDK_IS_TOPLEVEL (toplevel), 0); g_object_get (toplevel, "state", &state, NULL); return state; } /** * gdk_toplevel_set_title: * @toplevel: a #GdkToplevel * @title: title of @surface * * Sets the title of a toplevel surface, to be displayed in the titlebar, * in lists of windows, etc. */ void gdk_toplevel_set_title (GdkToplevel *toplevel, const char *title) { g_return_if_fail (GDK_IS_TOPLEVEL (toplevel)); g_object_set (toplevel, "title", title, NULL); } /** * gdk_toplevel_set_startup_id: * @toplevel: a #GdkToplevel * @startup_id: a string with startup-notification identifier * * When using GTK, typically you should use gtk_window_set_startup_id() * instead of this low-level function. */ void gdk_toplevel_set_startup_id (GdkToplevel *toplevel, const char *startup_id) { g_return_if_fail (GDK_IS_TOPLEVEL (toplevel)); g_object_set (toplevel, "startup-id", startup_id, NULL); } /** * gdk_toplevel_set_transient_for: * @toplevel: a #GdkToplevel * @parent: another toplevel #GdkSurface * * Indicates to the window manager that @surface is a transient dialog * associated with the application surface @parent. This allows the * window manager to do things like center @surface on @parent and * keep @surface above @parent. * * See gtk_window_set_transient_for() if you’re using #GtkWindow or * #GtkDialog. */ void gdk_toplevel_set_transient_for (GdkToplevel *toplevel, GdkSurface *parent) { g_return_if_fail (GDK_IS_TOPLEVEL (toplevel)); g_object_set (toplevel, "transient-for", parent, NULL); } /** * gdk_toplevel_set_modal: * @toplevel: A toplevel surface * @modal: %TRUE if the surface is modal, %FALSE otherwise. * * The application can use this hint to tell the * window manager that a certain surface has modal * behaviour. The window manager can use this information * to handle modal surfaces in a special way. * * You should only use this on surfaces for which you have * previously called gdk_toplevel_set_transient_for(). */ void gdk_toplevel_set_modal (GdkToplevel *toplevel, gboolean modal) { g_return_if_fail (GDK_IS_TOPLEVEL (toplevel)); g_object_set (toplevel, "modal", modal, NULL); } /** * gdk_toplevel_set_icon_list: * @toplevel: a #GdkToplevel * @surfaces: (transfer none) (element-type GdkTexture): * A list of textures to use as icon, of different sizes * * Sets a list of icons for the surface. * * One of these will be used to represent the surface in iconic form. * The icon may be shown in window lists or task bars. Which icon * size is shown depends on the window manager. The window manager * can scale the icon but setting several size icons can give better * image quality. * * Note that some platforms don't support surface icons. */ void gdk_toplevel_set_icon_list (GdkToplevel *toplevel, GList *surfaces) { g_return_if_fail (GDK_IS_TOPLEVEL (toplevel)); g_object_set (toplevel, "icon-list", surfaces, NULL); } /** * gdk_toplevel_show_window_menu: * @toplevel: a #GdkToplevel * @event: a #GdkEvent to show the menu for * * Asks the windowing system to show the window menu. * * The window menu is the menu shown when right-clicking the titlebar * on traditional windows managed by the window manager. This is useful * for windows using client-side decorations, activating it with a * right-click on the window decorations. * * Returns: %TRUE if the window menu was shown and %FALSE otherwise. */ gboolean gdk_toplevel_show_window_menu (GdkToplevel *toplevel, GdkEvent *event) { g_return_val_if_fail (GDK_IS_TOPLEVEL (toplevel), FALSE); return GDK_TOPLEVEL_GET_IFACE (toplevel)->show_window_menu (toplevel, event); } /** * gdk_toplevel_set_decorated: * @toplevel: a #GdkToplevel * @decorated: %TRUE to request decorations * * Setting @decorated to %FALSE hints the desktop environment * that the surface has its own, client-side decorations and * does not need to have window decorations added. */ void gdk_toplevel_set_decorated (GdkToplevel *toplevel, gboolean decorated) { g_return_if_fail (GDK_IS_TOPLEVEL (toplevel)); g_object_set (toplevel, "decorated", decorated, NULL); } /** * gdk_toplevel_set_deletable: * @toplevel: a #GdkToplevel * @deletable: %TRUE to request a delete button * * Setting @deletable to %TRUE hints the desktop environment * that it should offer the user a way to close the surface. */ void gdk_toplevel_set_deletable (GdkToplevel *toplevel, gboolean deletable) { g_return_if_fail (GDK_IS_TOPLEVEL (toplevel)); g_object_set (toplevel, "deletable", deletable, NULL); } /** * gdk_toplevel_supports_edge_constraints: * @toplevel: a #GdkToplevel * * Returns whether the desktop environment supports * tiled window states. * * Returns: %TRUE if the desktop environment supports * tiled window states */ gboolean gdk_toplevel_supports_edge_constraints (GdkToplevel *toplevel) { g_return_val_if_fail (GDK_IS_TOPLEVEL (toplevel), FALSE); return GDK_TOPLEVEL_GET_IFACE (toplevel)->supports_edge_constraints (toplevel); } /** * gdk_toplevel_inhibit_system_shortcuts: * @toplevel: the #GdkToplevel requesting system keyboard shortcuts * @event: (nullable): the #GdkEvent that is triggering the inhibit * request, or %NULL if none is available. * * Requests that the @toplevel inhibit the system shortcuts, asking the * desktop environment/windowing system to let all keyboard events reach * the surface, as long as it is focused, instead of triggering system * actions. * * If granted, the rerouting remains active until the default shortcuts * processing is restored with gdk_toplevel_restore_system_shortcuts(), * or the request is revoked by the desktop environment, windowing system * or the user. * * A typical use case for this API is remote desktop or virtual machine * viewers which need to inhibit the default system keyboard shortcuts * so that the remote session or virtual host gets those instead of the * local environment. * * The windowing system or desktop environment may ask the user to grant * or deny the request or even choose to ignore the request entirely. * * The caller can be notified whenever the request is granted or revoked * by listening to the GdkToplevel::shortcuts-inhibited property. * */ void gdk_toplevel_inhibit_system_shortcuts (GdkToplevel *toplevel, GdkEvent *event) { g_return_if_fail (GDK_IS_TOPLEVEL (toplevel)); GDK_TOPLEVEL_GET_IFACE (toplevel)->inhibit_system_shortcuts (toplevel, event); } /** * gdk_toplevel_restore_system_shortcuts: * @toplevel: a #GdkToplevel * * Restore default system keyboard shortcuts which were previously * requested to be inhibited by gdk_toplevel_inhibit_system_shortcuts(). */ void gdk_toplevel_restore_system_shortcuts (GdkToplevel *toplevel) { g_return_if_fail (GDK_IS_TOPLEVEL (toplevel)); GDK_TOPLEVEL_GET_IFACE (toplevel)->restore_system_shortcuts (toplevel); } /** * gdk_toplevel_begin_resize: * @toplevel: a #GdkToplevel * @edge: the edge or corner from which the drag is started * @device: (nullable): the device used for the operation * @button: the button being used to drag, or 0 for a keyboard-initiated drag * @x: surface X coordinate of mouse click that began the drag * @y: surface Y coordinate of mouse click that began the drag * @timestamp: timestamp of mouse click that began the drag (use gdk_event_get_time()) * * Begins an interactive resize operation (for a toplevel surface). * You might use this function to implement a “window resize grip.” */ void gdk_toplevel_begin_resize (GdkToplevel *toplevel, GdkSurfaceEdge edge, GdkDevice *device, int button, double x, double y, guint32 timestamp) { g_return_if_fail (GDK_IS_TOPLEVEL (toplevel)); if (device == NULL) { GdkSeat *seat = gdk_display_get_default_seat (GDK_SURFACE (toplevel)->display); if (button == 0) device = gdk_seat_get_keyboard (seat); else device = gdk_seat_get_pointer (seat); } GDK_TOPLEVEL_GET_IFACE (toplevel)->begin_resize (toplevel, edge, device, button, x, y, timestamp); } /** * gdk_toplevel_begin_move: * @toplevel: a #GdkToplevel * @device: the device used for the operation * @button: the button being used to drag, or 0 for a keyboard-initiated drag * @x: surface X coordinate of mouse click that began the drag * @y: surface Y coordinate of mouse click that began the drag * @timestamp: timestamp of mouse click that began the drag * * Begins an interactive move operation (for a toplevel surface). * You might use this function to implement draggable titlebars. */ void gdk_toplevel_begin_move (GdkToplevel *toplevel, GdkDevice *device, int button, double x, double y, guint32 timestamp) { g_return_if_fail (GDK_IS_TOPLEVEL (toplevel)); if (device == NULL) { GdkSeat *seat = gdk_display_get_default_seat (GDK_SURFACE (toplevel)->display); if (button == 0) device = gdk_seat_get_keyboard (seat); else device = gdk_seat_get_pointer (seat); } GDK_TOPLEVEL_GET_IFACE (toplevel)->begin_move (toplevel, device, button, x, y, timestamp); }