mirror of
https://github.com/brl/mutter.git
synced 2024-11-27 18:40:40 -05:00
7b45de941b
The shadow was disabled for the X11 client as it was far to unreliable when comparing sizes. It seems that the Wayland backend has been somewhat unreliable as well, where some race condition causing incorrect sizes thus a flaky test. https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1288
916 lines
24 KiB
C
916 lines
24 KiB
C
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
|
|
/*
|
|
* Copyright (C) 2014 Red Hat, Inc.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program 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
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <gio/gunixinputstream.h>
|
|
#include <gtk/gtk.h>
|
|
#include <gdk/gdkx.h>
|
|
#include <gdk/gdkwayland.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <X11/extensions/sync.h>
|
|
|
|
const char *client_id = "0";
|
|
static gboolean wayland;
|
|
GHashTable *windows;
|
|
GQuark event_source_quark;
|
|
GQuark event_handlers_quark;
|
|
GQuark can_take_focus_quark;
|
|
|
|
typedef void (*XEventHandler) (GtkWidget *window, XEvent *event);
|
|
|
|
static void read_next_line (GDataInputStream *in);
|
|
|
|
static void
|
|
window_export_handle_cb (GdkWindow *window,
|
|
const char *handle_str,
|
|
gpointer user_data)
|
|
{
|
|
GdkWindow *gdk_window = gtk_widget_get_window (GTK_WIDGET (user_data));
|
|
|
|
if (!gdk_wayland_window_set_transient_for_exported (gdk_window,
|
|
(gchar *) handle_str))
|
|
g_print ("Fail to set transient_for exported window handle %s\n", handle_str);
|
|
gdk_window_set_modal_hint (gdk_window, TRUE);
|
|
}
|
|
|
|
static GtkWidget *
|
|
lookup_window (const char *window_id)
|
|
{
|
|
GtkWidget *window = g_hash_table_lookup (windows, window_id);
|
|
if (!window)
|
|
g_print ("Window %s doesn't exist\n", window_id);
|
|
|
|
return window;
|
|
}
|
|
|
|
typedef struct {
|
|
GSource base;
|
|
GSource **self_ref;
|
|
GPollFD event_poll_fd;
|
|
Display *xdisplay;
|
|
} XClientEventSource;
|
|
|
|
static gboolean
|
|
x_event_source_prepare (GSource *source,
|
|
int *timeout)
|
|
{
|
|
XClientEventSource *x_source = (XClientEventSource *) source;
|
|
|
|
*timeout = -1;
|
|
|
|
return XPending (x_source->xdisplay);
|
|
}
|
|
|
|
static gboolean
|
|
x_event_source_check (GSource *source)
|
|
{
|
|
XClientEventSource *x_source = (XClientEventSource *) source;
|
|
|
|
return XPending (x_source->xdisplay);
|
|
}
|
|
|
|
static gboolean
|
|
x_event_source_dispatch (GSource *source,
|
|
GSourceFunc callback,
|
|
gpointer user_data)
|
|
{
|
|
XClientEventSource *x_source = (XClientEventSource *) source;
|
|
|
|
while (XPending (x_source->xdisplay))
|
|
{
|
|
GHashTableIter iter;
|
|
XEvent event;
|
|
gpointer value;
|
|
|
|
XNextEvent (x_source->xdisplay, &event);
|
|
|
|
g_hash_table_iter_init (&iter, windows);
|
|
while (g_hash_table_iter_next (&iter, NULL, &value))
|
|
{
|
|
GList *l;
|
|
GtkWidget *window = value;
|
|
GList *handlers =
|
|
g_object_get_qdata (G_OBJECT (window), event_handlers_quark);
|
|
|
|
for (l = handlers; l; l = l->next)
|
|
{
|
|
XEventHandler handler = l->data;
|
|
handler (window, &event);
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
x_event_source_finalize (GSource *source)
|
|
{
|
|
XClientEventSource *x_source = (XClientEventSource *) source;
|
|
|
|
*x_source->self_ref = NULL;
|
|
}
|
|
|
|
static GSourceFuncs x_event_funcs = {
|
|
x_event_source_prepare,
|
|
x_event_source_check,
|
|
x_event_source_dispatch,
|
|
x_event_source_finalize,
|
|
};
|
|
|
|
static GSource*
|
|
ensure_xsource_handler (GdkDisplay *gdkdisplay)
|
|
{
|
|
static GSource *source = NULL;
|
|
Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdkdisplay);
|
|
XClientEventSource *x_source;
|
|
|
|
if (source)
|
|
return g_source_ref (source);
|
|
|
|
source = g_source_new (&x_event_funcs, sizeof (XClientEventSource));
|
|
x_source = (XClientEventSource *) source;
|
|
x_source->self_ref = &source;
|
|
x_source->xdisplay = xdisplay;
|
|
x_source->event_poll_fd.fd = ConnectionNumber (xdisplay);
|
|
x_source->event_poll_fd.events = G_IO_IN;
|
|
g_source_add_poll (source, &x_source->event_poll_fd);
|
|
|
|
g_source_set_priority (source, GDK_PRIORITY_EVENTS - 1);
|
|
g_source_set_can_recurse (source, TRUE);
|
|
g_source_attach (source, NULL);
|
|
|
|
return source;
|
|
}
|
|
|
|
static gboolean
|
|
window_has_x11_event_handler (GtkWidget *window,
|
|
XEventHandler handler)
|
|
{
|
|
GList *handlers =
|
|
g_object_get_qdata (G_OBJECT (window), event_handlers_quark);
|
|
|
|
g_return_val_if_fail (handler, FALSE);
|
|
g_return_val_if_fail (!wayland, FALSE);
|
|
|
|
return g_list_find (handlers, handler) != NULL;
|
|
}
|
|
|
|
static void
|
|
unref_and_maybe_destroy_gsource (GSource *source)
|
|
{
|
|
g_source_unref (source);
|
|
|
|
if (source->ref_count == 1)
|
|
g_source_destroy (source);
|
|
}
|
|
|
|
static void
|
|
window_add_x11_event_handler (GtkWidget *window,
|
|
XEventHandler handler)
|
|
{
|
|
GSource *source;
|
|
GList *handlers =
|
|
g_object_get_qdata (G_OBJECT (window), event_handlers_quark);
|
|
|
|
g_return_if_fail (!window_has_x11_event_handler (window, handler));
|
|
|
|
source = ensure_xsource_handler (gtk_widget_get_display (window));
|
|
g_object_set_qdata_full (G_OBJECT (window), event_source_quark, source,
|
|
(GDestroyNotify) unref_and_maybe_destroy_gsource);
|
|
|
|
handlers = g_list_append (handlers, handler);
|
|
g_object_set_qdata (G_OBJECT (window), event_handlers_quark, handlers);
|
|
}
|
|
|
|
static void
|
|
window_remove_x11_event_handler (GtkWidget *window,
|
|
XEventHandler handler)
|
|
{
|
|
GList *handlers =
|
|
g_object_get_qdata (G_OBJECT (window), event_handlers_quark);
|
|
|
|
g_return_if_fail (window_has_x11_event_handler (window, handler));
|
|
|
|
g_object_set_qdata (G_OBJECT (window), event_source_quark, NULL);
|
|
|
|
handlers = g_list_remove (handlers, handler);
|
|
g_object_set_qdata (G_OBJECT (window), event_handlers_quark, handlers);
|
|
}
|
|
|
|
static void
|
|
handle_take_focus (GtkWidget *window,
|
|
XEvent *xevent)
|
|
{
|
|
GdkWindow *gdkwindow = gtk_widget_get_window (window);
|
|
GdkDisplay *display = gtk_widget_get_display (window);
|
|
Atom wm_protocols =
|
|
gdk_x11_get_xatom_by_name_for_display (display, "WM_PROTOCOLS");
|
|
Atom wm_take_focus =
|
|
gdk_x11_get_xatom_by_name_for_display (display, "WM_TAKE_FOCUS");
|
|
|
|
if (xevent->xany.type != ClientMessage ||
|
|
xevent->xany.window != GDK_WINDOW_XID (gdkwindow))
|
|
return;
|
|
|
|
if (xevent->xclient.message_type == wm_protocols &&
|
|
xevent->xclient.data.l[0] == wm_take_focus)
|
|
{
|
|
XSetInputFocus (xevent->xany.display,
|
|
GDK_WINDOW_XID (gdkwindow),
|
|
RevertToParent,
|
|
xevent->xclient.data.l[1]);
|
|
}
|
|
}
|
|
|
|
static int
|
|
calculate_titlebar_height (GtkWindow *window)
|
|
{
|
|
GtkWidget *titlebar;
|
|
GdkWindow *gdk_window;
|
|
|
|
gdk_window = gtk_widget_get_window (GTK_WIDGET (window));
|
|
if (gdk_window_get_state (gdk_window) & GDK_WINDOW_STATE_FULLSCREEN)
|
|
return 0;
|
|
|
|
titlebar = gtk_window_get_titlebar (window);
|
|
if (!titlebar)
|
|
return 0;
|
|
|
|
return gtk_widget_get_allocated_height (titlebar);
|
|
}
|
|
|
|
static void
|
|
process_line (const char *line)
|
|
{
|
|
GError *error = NULL;
|
|
int argc;
|
|
char **argv;
|
|
|
|
if (!g_shell_parse_argv (line, &argc, &argv, &error))
|
|
{
|
|
g_print ("error parsing command: %s\n", error->message);
|
|
g_error_free (error);
|
|
return;
|
|
}
|
|
|
|
if (argc < 1)
|
|
{
|
|
g_print ("Empty command\n");
|
|
goto out;
|
|
}
|
|
|
|
if (strcmp (argv[0], "create") == 0)
|
|
{
|
|
int i;
|
|
|
|
if (argc < 2)
|
|
{
|
|
g_print ("usage: create <id> [override|csd]\n");
|
|
goto out;
|
|
}
|
|
|
|
if (g_hash_table_lookup (windows, argv[1]))
|
|
{
|
|
g_print ("window %s already exists\n", argv[1]);
|
|
goto out;
|
|
}
|
|
|
|
gboolean override = FALSE;
|
|
gboolean csd = FALSE;
|
|
for (i = 2; i < argc; i++)
|
|
{
|
|
if (strcmp (argv[i], "override") == 0)
|
|
override = TRUE;
|
|
if (strcmp (argv[i], "csd") == 0)
|
|
csd = TRUE;
|
|
}
|
|
|
|
if (override && csd)
|
|
{
|
|
g_print ("override and csd keywords are exclusive\n");
|
|
goto out;
|
|
}
|
|
|
|
GtkWidget *window = gtk_window_new (override ? GTK_WINDOW_POPUP : GTK_WINDOW_TOPLEVEL);
|
|
g_hash_table_insert (windows, g_strdup (argv[1]), window);
|
|
|
|
if (csd)
|
|
{
|
|
GtkWidget *headerbar = gtk_header_bar_new ();
|
|
gtk_window_set_titlebar (GTK_WINDOW (window), headerbar);
|
|
gtk_widget_show (headerbar);
|
|
}
|
|
|
|
gtk_window_set_default_size (GTK_WINDOW (window), 100, 100);
|
|
|
|
gchar *title = g_strdup_printf ("test/%s/%s", client_id, argv[1]);
|
|
gtk_window_set_title (GTK_WINDOW (window), title);
|
|
g_free (title);
|
|
|
|
g_object_set_qdata (G_OBJECT (window), can_take_focus_quark,
|
|
GUINT_TO_POINTER (TRUE));
|
|
|
|
gtk_widget_realize (window);
|
|
|
|
if (!wayland)
|
|
{
|
|
/* The cairo xlib backend creates a window when initialized, which
|
|
* confuses our testing if it happens asynchronously the first
|
|
* time a window is painted. By creating an Xlib surface and
|
|
* destroying it, we force initialization at a more predictable time.
|
|
*/
|
|
GdkWindow *window_gdk = gtk_widget_get_window (window);
|
|
cairo_surface_t *surface = gdk_window_create_similar_surface (window_gdk,
|
|
CAIRO_CONTENT_COLOR,
|
|
1, 1);
|
|
cairo_surface_destroy (surface);
|
|
}
|
|
|
|
}
|
|
else if (strcmp (argv[0], "set_parent") == 0)
|
|
{
|
|
if (argc != 3)
|
|
{
|
|
g_print ("usage: set_parent <window-id> <parent-id>\n");
|
|
goto out;
|
|
}
|
|
|
|
GtkWidget *window = lookup_window (argv[1]);
|
|
if (!window)
|
|
{
|
|
g_print ("unknown window %s\n", argv[1]);
|
|
goto out;
|
|
}
|
|
|
|
GtkWidget *parent_window = lookup_window (argv[2]);
|
|
if (!parent_window)
|
|
{
|
|
g_print ("unknown parent window %s\n", argv[2]);
|
|
goto out;
|
|
}
|
|
|
|
gtk_window_set_transient_for (GTK_WINDOW (window),
|
|
GTK_WINDOW (parent_window));
|
|
}
|
|
else if (strcmp (argv[0], "set_parent_exported") == 0)
|
|
{
|
|
if (argc != 3)
|
|
{
|
|
g_print ("usage: set_parent_exported <window-id> <parent-id>\n");
|
|
goto out;
|
|
}
|
|
|
|
GtkWidget *window = lookup_window (argv[1]);
|
|
if (!window)
|
|
{
|
|
g_print ("unknown window %s\n", argv[1]);
|
|
goto out;
|
|
}
|
|
|
|
GtkWidget *parent_window = lookup_window (argv[2]);
|
|
if (!parent_window)
|
|
{
|
|
g_print ("unknown parent window %s\n", argv[2]);
|
|
goto out;
|
|
}
|
|
|
|
GdkWindow *parent_gdk_window = gtk_widget_get_window (parent_window);
|
|
if (!gdk_wayland_window_export_handle (parent_gdk_window,
|
|
window_export_handle_cb,
|
|
window,
|
|
NULL))
|
|
g_print ("Fail to export handle for window id %s\n", argv[2]);
|
|
}
|
|
else if (strcmp (argv[0], "accept_focus") == 0)
|
|
{
|
|
if (argc != 3)
|
|
{
|
|
g_print ("usage: %s <window-id> [true|false]\n", argv[0]);
|
|
goto out;
|
|
}
|
|
|
|
GtkWidget *window = lookup_window (argv[1]);
|
|
if (!window)
|
|
{
|
|
g_print ("unknown window %s\n", argv[1]);
|
|
goto out;
|
|
}
|
|
|
|
if (!wayland &&
|
|
window_has_x11_event_handler (window, handle_take_focus))
|
|
{
|
|
g_print ("Impossible to use %s for windows accepting take focus\n",
|
|
argv[1]);
|
|
goto out;
|
|
}
|
|
|
|
gboolean enabled = g_ascii_strcasecmp (argv[2], "true") == 0;
|
|
gtk_window_set_accept_focus (GTK_WINDOW (window), enabled);
|
|
}
|
|
else if (strcmp (argv[0], "can_take_focus") == 0)
|
|
{
|
|
if (argc != 3)
|
|
{
|
|
g_print ("usage: %s <window-id> [true|false]\n", argv[0]);
|
|
goto out;
|
|
}
|
|
|
|
GtkWidget *window = lookup_window (argv[1]);
|
|
if (!window)
|
|
{
|
|
g_print ("unknown window %s\n", argv[1]);
|
|
goto out;
|
|
}
|
|
|
|
if (wayland)
|
|
{
|
|
g_print ("%s not supported under wayland\n", argv[0]);
|
|
goto out;
|
|
}
|
|
|
|
if (window_has_x11_event_handler (window, handle_take_focus))
|
|
{
|
|
g_print ("Impossible to change %s for windows accepting take focus\n",
|
|
argv[1]);
|
|
goto out;
|
|
}
|
|
|
|
GdkDisplay *display = gdk_display_get_default ();
|
|
GdkWindow *gdkwindow = gtk_widget_get_window (window);
|
|
Display *xdisplay = gdk_x11_display_get_xdisplay (display);
|
|
Window xwindow = GDK_WINDOW_XID (gdkwindow);
|
|
Atom wm_take_focus = gdk_x11_get_xatom_by_name_for_display (display, "WM_TAKE_FOCUS");
|
|
gboolean add = g_ascii_strcasecmp(argv[2], "true") == 0;
|
|
Atom *protocols = NULL;
|
|
Atom *new_protocols;
|
|
int n_protocols = 0;
|
|
int i, n = 0;
|
|
|
|
gdk_display_sync (display);
|
|
XGetWMProtocols (xdisplay, xwindow, &protocols, &n_protocols);
|
|
new_protocols = g_new0 (Atom, n_protocols + (add ? 1 : 0));
|
|
|
|
for (i = 0; i < n_protocols; ++i)
|
|
{
|
|
if (protocols[i] != wm_take_focus)
|
|
new_protocols[n++] = protocols[i];
|
|
}
|
|
|
|
if (add)
|
|
new_protocols[n++] = wm_take_focus;
|
|
|
|
XSetWMProtocols (xdisplay, xwindow, new_protocols, n);
|
|
g_object_set_qdata (G_OBJECT (window), can_take_focus_quark,
|
|
GUINT_TO_POINTER (add));
|
|
|
|
XFree (new_protocols);
|
|
XFree (protocols);
|
|
}
|
|
else if (strcmp (argv[0], "accept_take_focus") == 0)
|
|
{
|
|
if (argc != 3)
|
|
{
|
|
g_print ("usage: %s <window-id> [true|false]\n", argv[0]);
|
|
goto out;
|
|
}
|
|
|
|
GtkWidget *window = lookup_window (argv[1]);
|
|
if (!window)
|
|
{
|
|
g_print ("unknown window %s\n", argv[1]);
|
|
goto out;
|
|
}
|
|
|
|
if (wayland)
|
|
{
|
|
g_print ("%s not supported under wayland\n", argv[0]);
|
|
goto out;
|
|
}
|
|
|
|
if (gtk_window_get_accept_focus (GTK_WINDOW (window)))
|
|
{
|
|
g_print ("%s not supported for input windows\n", argv[0]);
|
|
goto out;
|
|
}
|
|
|
|
if (!g_object_get_qdata (G_OBJECT (window), can_take_focus_quark))
|
|
{
|
|
g_print ("%s not supported for windows with no WM_TAKE_FOCUS set\n",
|
|
argv[0]);
|
|
goto out;
|
|
}
|
|
|
|
if (g_ascii_strcasecmp (argv[2], "true") == 0)
|
|
window_add_x11_event_handler (window, handle_take_focus);
|
|
else
|
|
window_remove_x11_event_handler (window, handle_take_focus);
|
|
}
|
|
else if (strcmp (argv[0], "show") == 0)
|
|
{
|
|
if (argc != 2)
|
|
{
|
|
g_print ("usage: show <id>\n");
|
|
goto out;
|
|
}
|
|
|
|
GtkWidget *window = lookup_window (argv[1]);
|
|
if (!window)
|
|
goto out;
|
|
|
|
gtk_widget_show (window);
|
|
gdk_display_sync (gdk_display_get_default ());
|
|
}
|
|
else if (strcmp (argv[0], "hide") == 0)
|
|
{
|
|
if (argc != 2)
|
|
{
|
|
g_print ("usage: hide <id>\n");
|
|
goto out;
|
|
}
|
|
|
|
GtkWidget *window = lookup_window (argv[1]);
|
|
if (!window)
|
|
goto out;
|
|
|
|
gtk_widget_hide (window);
|
|
}
|
|
else if (strcmp (argv[0], "activate") == 0)
|
|
{
|
|
if (argc != 2)
|
|
{
|
|
g_print ("usage: activate <id>\n");
|
|
goto out;
|
|
}
|
|
|
|
GtkWidget *window = lookup_window (argv[1]);
|
|
if (!window)
|
|
goto out;
|
|
|
|
gtk_window_present (GTK_WINDOW (window));
|
|
}
|
|
else if (strcmp (argv[0], "resize") == 0)
|
|
{
|
|
if (argc != 4)
|
|
{
|
|
g_print ("usage: resize <id> <width> <height>\n");
|
|
goto out;
|
|
}
|
|
|
|
GtkWidget *window = lookup_window (argv[1]);
|
|
if (!window)
|
|
goto out;
|
|
|
|
int width = atoi (argv[2]);
|
|
int height = atoi (argv[3]);
|
|
int titlebar_height = calculate_titlebar_height (GTK_WINDOW (window));
|
|
gtk_window_resize (GTK_WINDOW (window),
|
|
width,
|
|
height - titlebar_height);
|
|
}
|
|
else if (strcmp (argv[0], "raise") == 0)
|
|
{
|
|
if (argc != 2)
|
|
{
|
|
g_print ("usage: raise <id>\n");
|
|
goto out;
|
|
}
|
|
|
|
GtkWidget *window = lookup_window (argv[1]);
|
|
if (!window)
|
|
goto out;
|
|
|
|
gdk_window_raise (gtk_widget_get_window (window));
|
|
}
|
|
else if (strcmp (argv[0], "lower") == 0)
|
|
{
|
|
if (argc != 2)
|
|
{
|
|
g_print ("usage: lower <id>\n");
|
|
goto out;
|
|
}
|
|
|
|
GtkWidget *window = lookup_window (argv[1]);
|
|
if (!window)
|
|
goto out;
|
|
|
|
gdk_window_lower (gtk_widget_get_window (window));
|
|
}
|
|
else if (strcmp (argv[0], "destroy") == 0)
|
|
{
|
|
if (argc != 2)
|
|
{
|
|
g_print ("usage: destroy <id>\n");
|
|
goto out;
|
|
}
|
|
|
|
GtkWidget *window = lookup_window (argv[1]);
|
|
if (!window)
|
|
goto out;
|
|
|
|
g_hash_table_remove (windows, argv[1]);
|
|
gtk_widget_destroy (window);
|
|
}
|
|
else if (strcmp (argv[0], "destroy_all") == 0)
|
|
{
|
|
if (argc != 1)
|
|
{
|
|
g_print ("usage: destroy_all\n");
|
|
goto out;
|
|
}
|
|
|
|
GHashTableIter iter;
|
|
gpointer key, value;
|
|
|
|
g_hash_table_iter_init (&iter, windows);
|
|
while (g_hash_table_iter_next (&iter, &key, &value))
|
|
gtk_widget_destroy (value);
|
|
|
|
g_hash_table_remove_all (windows);
|
|
}
|
|
else if (strcmp (argv[0], "sync") == 0)
|
|
{
|
|
if (argc != 1)
|
|
{
|
|
g_print ("usage: sync\n");
|
|
goto out;
|
|
}
|
|
|
|
gdk_display_sync (gdk_display_get_default ());
|
|
}
|
|
else if (strcmp (argv[0], "set_counter") == 0)
|
|
{
|
|
XSyncCounter counter;
|
|
int value;
|
|
|
|
if (argc != 3)
|
|
{
|
|
g_print ("usage: set_counter <counter> <value>\n");
|
|
goto out;
|
|
}
|
|
|
|
if (wayland)
|
|
{
|
|
g_print ("usage: set_counter can only be used for X11\n");
|
|
goto out;
|
|
}
|
|
|
|
counter = strtoul(argv[1], NULL, 10);
|
|
value = atoi(argv[2]);
|
|
XSyncValue sync_value;
|
|
XSyncIntToValue (&sync_value, value);
|
|
|
|
XSyncSetCounter (gdk_x11_display_get_xdisplay (gdk_display_get_default ()),
|
|
counter, sync_value);
|
|
}
|
|
else if (strcmp (argv[0], "minimize") == 0)
|
|
{
|
|
if (argc != 2)
|
|
{
|
|
g_print ("usage: minimize <id>\n");
|
|
goto out;
|
|
}
|
|
|
|
GtkWidget *window = lookup_window (argv[1]);
|
|
if (!window)
|
|
goto out;
|
|
|
|
gtk_window_iconify (GTK_WINDOW (window));
|
|
}
|
|
else if (strcmp (argv[0], "unminimize") == 0)
|
|
{
|
|
if (argc != 2)
|
|
{
|
|
g_print ("usage: unminimize <id>\n");
|
|
goto out;
|
|
}
|
|
|
|
GtkWidget *window = lookup_window (argv[1]);
|
|
if (!window)
|
|
goto out;
|
|
|
|
gtk_window_deiconify (GTK_WINDOW (window));
|
|
}
|
|
else if (strcmp (argv[0], "maximize") == 0)
|
|
{
|
|
if (argc != 2)
|
|
{
|
|
g_print ("usage: maximize <id>\n");
|
|
goto out;
|
|
}
|
|
|
|
GtkWidget *window = lookup_window (argv[1]);
|
|
if (!window)
|
|
goto out;
|
|
|
|
gtk_window_maximize (GTK_WINDOW (window));
|
|
}
|
|
else if (strcmp (argv[0], "unmaximize") == 0)
|
|
{
|
|
if (argc != 2)
|
|
{
|
|
g_print ("usage: unmaximize <id>\n");
|
|
goto out;
|
|
}
|
|
|
|
GtkWidget *window = lookup_window (argv[1]);
|
|
if (!window)
|
|
goto out;
|
|
|
|
gtk_window_unmaximize (GTK_WINDOW (window));
|
|
}
|
|
else if (strcmp (argv[0], "fullscreen") == 0)
|
|
{
|
|
if (argc != 2)
|
|
{
|
|
g_print ("usage: fullscreen <id>\n");
|
|
goto out;
|
|
}
|
|
|
|
GtkWidget *window = lookup_window (argv[1]);
|
|
if (!window)
|
|
goto out;
|
|
|
|
gtk_window_fullscreen (GTK_WINDOW (window));
|
|
}
|
|
else if (strcmp (argv[0], "unfullscreen") == 0)
|
|
{
|
|
if (argc != 2)
|
|
{
|
|
g_print ("usage: unfullscreen <id>\n");
|
|
goto out;
|
|
}
|
|
|
|
GtkWidget *window = lookup_window (argv[1]);
|
|
if (!window)
|
|
goto out;
|
|
|
|
gtk_window_unfullscreen (GTK_WINDOW (window));
|
|
}
|
|
else if (strcmp (argv[0], "assert_size") == 0)
|
|
{
|
|
int expected_width;
|
|
int expected_height;
|
|
int width;
|
|
int height;
|
|
|
|
if (argc != 4)
|
|
{
|
|
g_print ("usage: assert_size <id> <width> <height>\n");
|
|
goto out;
|
|
}
|
|
|
|
GtkWidget *window = lookup_window (argv[1]);
|
|
if (!window)
|
|
goto out;
|
|
|
|
gtk_window_get_size (GTK_WINDOW (window), &width, &height);
|
|
height += calculate_titlebar_height (GTK_WINDOW (window));
|
|
|
|
expected_width = atoi (argv[2]);
|
|
expected_height = atoi (argv[3]);
|
|
if (expected_width != width || expected_height != height)
|
|
{
|
|
g_print ("Expected size %dx%d didn't match actual size %dx%d\n",
|
|
expected_width, expected_height,
|
|
width, height);
|
|
goto out;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g_print ("Unknown command %s\n", argv[0]);
|
|
goto out;
|
|
}
|
|
|
|
g_print ("OK\n");
|
|
|
|
out:
|
|
g_strfreev (argv);
|
|
}
|
|
|
|
static void
|
|
on_line_received (GObject *source,
|
|
GAsyncResult *result,
|
|
gpointer user_data)
|
|
{
|
|
GDataInputStream *in = G_DATA_INPUT_STREAM (source);
|
|
GError *error = NULL;
|
|
gsize length;
|
|
char *line = g_data_input_stream_read_line_finish_utf8 (in, result, &length, &error);
|
|
|
|
if (line == NULL)
|
|
{
|
|
if (error != NULL)
|
|
g_printerr ("Error reading from stdin: %s\n", error->message);
|
|
gtk_main_quit ();
|
|
return;
|
|
}
|
|
|
|
process_line (line);
|
|
g_free (line);
|
|
read_next_line (in);
|
|
}
|
|
|
|
static void
|
|
read_next_line (GDataInputStream *in)
|
|
{
|
|
g_data_input_stream_read_line_async (in, G_PRIORITY_DEFAULT, NULL,
|
|
on_line_received, NULL);
|
|
}
|
|
|
|
const GOptionEntry options[] = {
|
|
{
|
|
"wayland", 0, 0, G_OPTION_ARG_NONE,
|
|
&wayland,
|
|
"Create a wayland client, not an X11 one",
|
|
NULL
|
|
},
|
|
{
|
|
"client-id", 0, 0, G_OPTION_ARG_STRING,
|
|
&client_id,
|
|
"Identifier used in Window titles for this client",
|
|
"CLIENT_ID",
|
|
},
|
|
{ NULL }
|
|
};
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
GOptionContext *context = g_option_context_new (NULL);
|
|
GdkScreen *screen;
|
|
GtkCssProvider *provider;
|
|
GError *error = NULL;
|
|
|
|
g_option_context_add_main_entries (context, options, NULL);
|
|
|
|
if (!g_option_context_parse (context,
|
|
&argc, &argv, &error))
|
|
{
|
|
g_printerr ("%s", error->message);
|
|
return 1;
|
|
}
|
|
|
|
if (wayland)
|
|
gdk_set_allowed_backends ("wayland");
|
|
else
|
|
gdk_set_allowed_backends ("x11");
|
|
|
|
gtk_init (NULL, NULL);
|
|
|
|
screen = gdk_screen_get_default ();
|
|
provider = gtk_css_provider_new ();
|
|
static const char *no_decoration_css =
|
|
"decoration {"
|
|
" border-radius: 0 0 0 0;"
|
|
" border-width: 0;"
|
|
" box-shadow: 0 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0 rgba(0, 0, 0, 0);"
|
|
" margin: 0px;"
|
|
"}";
|
|
if (!gtk_css_provider_load_from_data (provider,
|
|
no_decoration_css,
|
|
strlen (no_decoration_css),
|
|
&error))
|
|
{
|
|
g_printerr ("%s", error->message);
|
|
return 1;
|
|
}
|
|
gtk_style_context_add_provider_for_screen (screen, GTK_STYLE_PROVIDER (provider),
|
|
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
|
|
|
|
windows = g_hash_table_new_full (g_str_hash, g_str_equal,
|
|
g_free, NULL);
|
|
event_source_quark = g_quark_from_static_string ("event-source");
|
|
event_handlers_quark = g_quark_from_static_string ("event-handlers");
|
|
can_take_focus_quark = g_quark_from_static_string ("can-take-focus");
|
|
|
|
GInputStream *raw_in = g_unix_input_stream_new (0, FALSE);
|
|
GDataInputStream *in = g_data_input_stream_new (raw_in);
|
|
|
|
read_next_line (in);
|
|
|
|
gtk_main ();
|
|
|
|
return 0;
|
|
}
|