gtk/gdk/gdktoplevel.c

640 lines
21 KiB
C
Raw Normal View History

/*
* 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 <http://www.gnu.org/licenses/>.
*
* Authors: Matthias Clasen <mclasen@redhat.com>
*/
#include "config.h"
#include "gdkintl.h"
#include "gdk-private.h"
#include "gdktoplevelprivate.h"
#include <graphene-gobject.h>
#include <math.h>
/**
* SECTION:gdktoplevel
* @Short_description: Interface for toplevel surfaces
* @Title: Toplevels
2020-10-29 18:00:24 +00:00
* @See_also: #GdkSurface, #GdkPopup
*
* A #GdkToplevel is a freestanding toplevel surface.
2020-10-29 18:00:24 +00:00
*
* 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.
*/
2020-03-10 11:24:04 +00:00
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;
}
2020-03-09 18:38:08 +00:00
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;
2020-03-09 18:38:08 +00:00
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));
2020-03-09 18:49:59 +00:00
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");
2020-03-09 18:49:59 +00:00
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 youre 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);
}
2020-03-14 05:23:09 +00:00
/**
* 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);
}
2020-03-09 18:38:08 +00:00
/**
* 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(),
2020-05-28 08:00:03 +00:00
* 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);
}