mirror of
https://github.com/brl/mutter.git
synced 2024-11-23 16:40:41 -05:00
c575696acc
To be used for clipboard testing. Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2364>
1039 lines
27 KiB
C
1039 lines
27 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;
|
|
gboolean sync_after_lines = -1;
|
|
|
|
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
|
|
text_get_func (GtkClipboard *clipboard,
|
|
GtkSelectionData *selection_data,
|
|
unsigned int info,
|
|
gpointer data)
|
|
{
|
|
gtk_selection_data_set_text (selection_data, data, -1);
|
|
}
|
|
|
|
static void
|
|
text_clear_func (GtkClipboard *clipboard,
|
|
gpointer data)
|
|
{
|
|
g_free (data);
|
|
}
|
|
|
|
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], "freeze") == 0)
|
|
{
|
|
if (argc != 2)
|
|
{
|
|
g_print ("usage: freeze <id>\n");
|
|
goto out;
|
|
}
|
|
|
|
GtkWidget *window = lookup_window (argv[1]);
|
|
if (!window)
|
|
goto out;
|
|
|
|
gdk_window_freeze_updates (gtk_widget_get_window (window));
|
|
}
|
|
else if (strcmp (argv[0], "thaw") == 0)
|
|
{
|
|
if (argc != 2)
|
|
{
|
|
g_print ("usage: thaw <id>\n");
|
|
goto out;
|
|
}
|
|
|
|
GtkWidget *window = lookup_window (argv[1]);
|
|
if (!window)
|
|
goto out;
|
|
|
|
gdk_window_thaw_updates (gtk_widget_get_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 if (strcmp (argv[0], "stop_after_next") == 0)
|
|
{
|
|
if (sync_after_lines != -1)
|
|
{
|
|
g_print ("Can't invoke 'stop_after_next' while already stopped");
|
|
goto out;
|
|
}
|
|
|
|
sync_after_lines = 1;
|
|
}
|
|
else if (strcmp (argv[0], "continue") == 0)
|
|
{
|
|
if (sync_after_lines != 0)
|
|
{
|
|
g_print ("Can only invoke 'continue' while stopped");
|
|
goto out;
|
|
}
|
|
|
|
sync_after_lines = -1;
|
|
}
|
|
else if (strcmp (argv[0], "clipboard-set") == 0)
|
|
{
|
|
GdkDisplay *display = gdk_display_get_default ();
|
|
GtkClipboard *clipboard;
|
|
GdkAtom atom;
|
|
GtkTargetList *target_list;
|
|
GtkTargetEntry *targets;
|
|
int n_targets;
|
|
|
|
if (argc != 3)
|
|
{
|
|
g_print ("usage: clipboard-set <mimetype> <text>\n");
|
|
goto out;
|
|
}
|
|
|
|
clipboard = gtk_clipboard_get_for_display (display,
|
|
GDK_SELECTION_CLIPBOARD);
|
|
|
|
atom = gdk_atom_intern (argv[1], FALSE);
|
|
target_list = gtk_target_list_new (NULL, 0);
|
|
gtk_target_list_add (target_list, atom, 0, 0);
|
|
|
|
targets = gtk_target_table_new_from_list (target_list, &n_targets);
|
|
gtk_target_list_unref (target_list);
|
|
|
|
gtk_clipboard_set_with_data (clipboard,
|
|
targets, n_targets,
|
|
text_get_func, text_clear_func,
|
|
g_strdup (argv[2]));
|
|
gtk_target_table_free (targets, n_targets);
|
|
}
|
|
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)
|
|
{
|
|
while (sync_after_lines == 0)
|
|
{
|
|
GdkDisplay *display = gdk_display_get_default ();
|
|
g_autoptr (GError) error = NULL;
|
|
g_autofree char *line = NULL;
|
|
size_t length;
|
|
|
|
gdk_display_flush (display);
|
|
|
|
line = g_data_input_stream_read_line (in, &length, NULL, &error);
|
|
if (!line)
|
|
{
|
|
if (error)
|
|
g_printerr ("Error reading from stdin: %s\n", error->message);
|
|
gtk_main_quit ();
|
|
return;
|
|
}
|
|
|
|
process_line (line);
|
|
}
|
|
|
|
if (sync_after_lines >= 0)
|
|
sync_after_lines--;
|
|
|
|
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_log_writer_default_set_use_stderr (TRUE);
|
|
|
|
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;"
|
|
" padding: 0 0 0 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;
|
|
}
|