Blame src/shell-tray-icon.c

Packit Service ed5168
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
Packit Service ed5168
Packit Service ed5168
#include "config.h"
Packit Service ed5168
Packit Service ed5168
#include "shell-tray-icon.h"
Packit Service ed5168
#include "shell-gtk-embed.h"
Packit Service ed5168
#include "tray/na-tray-child.h"
Packit Service ed5168
#include <gdk/gdkx.h>
Packit Service ed5168
#include <X11/Xatom.h>
Packit Service ed5168
#include "st.h"
Packit Service ed5168
Packit Service ed5168
enum {
Packit Service ed5168
   PROP_0,
Packit Service ed5168
Packit Service ed5168
   PROP_PID,
Packit Service ed5168
   PROP_TITLE,
Packit Service ed5168
   PROP_WM_CLASS
Packit Service ed5168
};
Packit Service ed5168
Packit Service ed5168
typedef struct _ShellTrayIconPrivate ShellTrayIconPrivate;
Packit Service ed5168
Packit Service ed5168
struct _ShellTrayIcon
Packit Service ed5168
{
Packit Service ed5168
    ShellGtkEmbed parent;
Packit Service ed5168
Packit Service ed5168
    ShellTrayIconPrivate *priv;
Packit Service ed5168
};
Packit Service ed5168
Packit Service ed5168
struct _ShellTrayIconPrivate
Packit Service ed5168
{
Packit Service ed5168
  NaTrayChild *socket;
Packit Service ed5168
Packit Service ed5168
  pid_t pid;
Packit Service ed5168
  char *title, *wm_class;
Packit Service ed5168
};
Packit Service ed5168
Packit Service ed5168
G_DEFINE_TYPE_WITH_PRIVATE (ShellTrayIcon, shell_tray_icon, SHELL_TYPE_GTK_EMBED);
Packit Service ed5168
Packit Service ed5168
static void
Packit Service ed5168
shell_tray_icon_finalize (GObject *object)
Packit Service ed5168
{
Packit Service ed5168
  ShellTrayIcon *icon = SHELL_TRAY_ICON (object);
Packit Service ed5168
Packit Service ed5168
  g_free (icon->priv->title);
Packit Service ed5168
  g_free (icon->priv->wm_class);
Packit Service ed5168
Packit Service ed5168
  G_OBJECT_CLASS (shell_tray_icon_parent_class)->finalize (object);
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
static void
Packit Service ed5168
shell_tray_icon_constructed (GObject *object)
Packit Service ed5168
{
Packit Service ed5168
  GdkWindow *icon_app_window;
Packit Service ed5168
  ShellTrayIcon *icon = SHELL_TRAY_ICON (object);
Packit Service ed5168
  ShellEmbeddedWindow *window;
Packit Service ed5168
  GdkDisplay *display;
Packit Service ed5168
  Window plug_xid;
Packit Service ed5168
  Atom _NET_WM_PID, type;
Packit Service ed5168
  int result, format;
Packit Service ed5168
  gulong nitems, bytes_after, *val = NULL;
Packit Service ed5168
Packit Service ed5168
  /* We do all this now rather than computing it on the fly later,
Packit Service ed5168
   * because the shell may want to see their values from a
Packit Service ed5168
   * tray-icon-removed signal handler, at which point the plug has
Packit Service ed5168
   * already been removed from the socket.
Packit Service ed5168
   */
Packit Service ed5168
Packit Service ed5168
  g_object_get (object, "window", &window, NULL);
Packit Service ed5168
  g_return_if_fail (window != NULL);
Packit Service ed5168
  icon->priv->socket = NA_TRAY_CHILD (gtk_bin_get_child (GTK_BIN (window)));
Packit Service ed5168
  g_object_unref (window);
Packit Service ed5168
Packit Service ed5168
  icon->priv->title = na_tray_child_get_title (icon->priv->socket);
Packit Service ed5168
  na_tray_child_get_wm_class (icon->priv->socket, NULL, &icon->priv->wm_class);
Packit Service ed5168
Packit Service ed5168
  icon_app_window = gtk_socket_get_plug_window (GTK_SOCKET (icon->priv->socket));
Packit Service ed5168
  plug_xid = GDK_WINDOW_XID (icon_app_window);
Packit Service ed5168
Packit Service ed5168
  display = gtk_widget_get_display (GTK_WIDGET (icon->priv->socket));
Packit Service ed5168
  gdk_x11_display_error_trap_push (display);
Packit Service ed5168
  _NET_WM_PID = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_PID");
Packit Service ed5168
  result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), plug_xid,
Packit Service ed5168
                               _NET_WM_PID, 0, G_MAXLONG, False, XA_CARDINAL,
Packit Service ed5168
                               &type, &format, &nitems,
Packit Service ed5168
                               &bytes_after, (guchar **)&val;;
Packit Service ed5168
  if (!gdk_x11_display_error_trap_pop (display) &&
Packit Service ed5168
      result == Success &&
Packit Service ed5168
      type == XA_CARDINAL &&
Packit Service ed5168
      nitems == 1)
Packit Service ed5168
    icon->priv->pid = *val;
Packit Service ed5168
Packit Service ed5168
  if (val)
Packit Service ed5168
    XFree (val);
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
static void
Packit Service ed5168
shell_tray_icon_get_property (GObject         *object,
Packit Service ed5168
                              guint            prop_id,
Packit Service ed5168
                              GValue          *value,
Packit Service ed5168
                              GParamSpec      *pspec)
Packit Service ed5168
{
Packit Service ed5168
  ShellTrayIcon *icon = SHELL_TRAY_ICON (object);
Packit Service ed5168
Packit Service ed5168
  switch (prop_id)
Packit Service ed5168
    {
Packit Service ed5168
    case PROP_PID:
Packit Service ed5168
      g_value_set_uint (value, icon->priv->pid);
Packit Service ed5168
      break;
Packit Service ed5168
Packit Service ed5168
    case PROP_TITLE:
Packit Service ed5168
      g_value_set_string (value, icon->priv->title);
Packit Service ed5168
      break;
Packit Service ed5168
Packit Service ed5168
    case PROP_WM_CLASS:
Packit Service ed5168
      g_value_set_string (value, icon->priv->wm_class);
Packit Service ed5168
      break;
Packit Service ed5168
Packit Service ed5168
    default:
Packit Service ed5168
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Packit Service ed5168
      break;
Packit Service ed5168
    }
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
static void
Packit Service ed5168
shell_tray_icon_class_init (ShellTrayIconClass *klass)
Packit Service ed5168
{
Packit Service ed5168
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
Packit Service ed5168
Packit Service ed5168
  object_class->get_property = shell_tray_icon_get_property;
Packit Service ed5168
  object_class->constructed  = shell_tray_icon_constructed;
Packit Service ed5168
  object_class->finalize     = shell_tray_icon_finalize;
Packit Service ed5168
Packit Service ed5168
  g_object_class_install_property (object_class,
Packit Service ed5168
                                   PROP_PID,
Packit Service ed5168
                                   g_param_spec_uint ("pid",
Packit Service ed5168
                                                      "PID",
Packit Service ed5168
                                                      "The PID of the icon's application",
Packit Service ed5168
                                                      0, G_MAXUINT, 0,
Packit Service ed5168
                                                      G_PARAM_READABLE));
Packit Service ed5168
  g_object_class_install_property (object_class,
Packit Service ed5168
                                   PROP_TITLE,
Packit Service ed5168
                                   g_param_spec_string ("title",
Packit Service ed5168
                                                        "Title",
Packit Service ed5168
                                                        "The icon's window title",
Packit Service ed5168
                                                        NULL,
Packit Service ed5168
                                                        G_PARAM_READABLE));
Packit Service ed5168
  g_object_class_install_property (object_class,
Packit Service ed5168
                                   PROP_WM_CLASS,
Packit Service ed5168
                                   g_param_spec_string ("wm-class",
Packit Service ed5168
                                                        "WM Class",
Packit Service ed5168
                                                        "The icon's window WM_CLASS",
Packit Service ed5168
                                                        NULL,
Packit Service ed5168
                                                        G_PARAM_READABLE));
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
static void
Packit Service ed5168
shell_tray_icon_init (ShellTrayIcon *icon)
Packit Service ed5168
{
Packit Service ed5168
  icon->priv = shell_tray_icon_get_instance_private (icon);
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
/*
Packit Service ed5168
 * Public API
Packit Service ed5168
 */
Packit Service ed5168
ClutterActor *
Packit Service ed5168
shell_tray_icon_new (ShellEmbeddedWindow *window)
Packit Service ed5168
{
Packit Service ed5168
  g_return_val_if_fail (SHELL_IS_EMBEDDED_WINDOW (window), NULL);
Packit Service ed5168
Packit Service ed5168
  return g_object_new (SHELL_TYPE_TRAY_ICON,
Packit Service ed5168
                       "window", window,
Packit Service ed5168
                       NULL);
Packit Service ed5168
}
Packit Service ed5168
Packit Service ed5168
/**
Packit Service ed5168
 * shell_tray_icon_click:
Packit Service ed5168
 * @icon: a #ShellTrayIcon
Packit Service ed5168
 * @event: the #ClutterEvent triggering the fake click
Packit Service ed5168
 *
Packit Service ed5168
 * Fakes a press and release on @icon. @event must be a
Packit Service ed5168
 * %CLUTTER_BUTTON_RELEASE, %CLUTTER_KEY_PRESS or %CLUTTER_KEY_RELEASE event.
Packit Service ed5168
 * Its relevant details will be passed on to the icon, but its
Packit Service ed5168
 * coordinates will be ignored; the click is
Packit Service ed5168
 * always made on the center of @icon.
Packit Service ed5168
 */
Packit Service ed5168
void
Packit Service ed5168
shell_tray_icon_click (ShellTrayIcon *icon,
Packit Service ed5168
                       ClutterEvent  *event)
Packit Service ed5168
{
Packit Service ed5168
  XKeyEvent xkevent;
Packit Service ed5168
  XButtonEvent xbevent;
Packit Service ed5168
  XCrossingEvent xcevent;
Packit Service ed5168
  GdkDisplay *display;
Packit Service ed5168
  GdkWindow *remote_window;
Packit Service ed5168
  GdkScreen *screen;
Packit Service ed5168
  int x_root, y_root;
Packit Service ed5168
  Display *xdisplay;
Packit Service ed5168
  Window xwindow, xrootwindow;
Packit Service ed5168
  ClutterEventType event_type = clutter_event_type (event);
Packit Service ed5168
Packit Service ed5168
  g_return_if_fail (event_type == CLUTTER_BUTTON_RELEASE ||
Packit Service ed5168
                    event_type == CLUTTER_KEY_PRESS ||
Packit Service ed5168
                    event_type == CLUTTER_KEY_RELEASE);
Packit Service ed5168
Packit Service ed5168
  remote_window = gtk_socket_get_plug_window (GTK_SOCKET (icon->priv->socket));
Packit Service ed5168
  if (remote_window == NULL)
Packit Service ed5168
    {
Packit Service ed5168
      g_warning ("shell tray: plug window is gone");
Packit Service ed5168
      return;
Packit Service ed5168
    }
Packit Service ed5168
  xdisplay = GDK_WINDOW_XDISPLAY (remote_window);
Packit Service ed5168
Packit Service ed5168
  display = gdk_x11_lookup_xdisplay (xdisplay);
Packit Service ed5168
  gdk_x11_display_error_trap_push (display);
Packit Service ed5168
Packit Service ed5168
  xwindow = GDK_WINDOW_XID (remote_window);
Packit Service ed5168
  screen = gdk_window_get_screen (remote_window);
Packit Service ed5168
  xrootwindow = GDK_WINDOW_XID (gdk_screen_get_root_window (screen));
Packit Service ed5168
  gdk_window_get_origin (remote_window, &x_root, &y_root);
Packit Service ed5168
Packit Service ed5168
Packit Service ed5168
  /* First make the icon believe the pointer is inside it */
Packit Service ed5168
  xcevent.type = EnterNotify;
Packit Service ed5168
  xcevent.window = xwindow;
Packit Service ed5168
  xcevent.root = xrootwindow;
Packit Service ed5168
  xcevent.subwindow = None;
Packit Service ed5168
  xcevent.time = clutter_event_get_time (event);
Packit Service ed5168
  xcevent.x = gdk_window_get_width (remote_window) / 2;
Packit Service ed5168
  xcevent.y = gdk_window_get_height (remote_window) / 2;
Packit Service ed5168
  xcevent.x_root = x_root + xcevent.x;
Packit Service ed5168
  xcevent.y_root = y_root + xcevent.y;
Packit Service ed5168
  xcevent.mode = NotifyNormal;
Packit Service ed5168
  xcevent.detail = NotifyNonlinear;
Packit Service ed5168
  xcevent.same_screen = True;
Packit Service ed5168
  XSendEvent (xdisplay, xwindow, False, 0, (XEvent *)&xcevent);
Packit Service ed5168
Packit Service ed5168
  /* Now do the click */
Packit Service ed5168
  if (event_type == CLUTTER_BUTTON_RELEASE)
Packit Service ed5168
    {
Packit Service ed5168
      xbevent.window = xwindow;
Packit Service ed5168
      xbevent.root = xrootwindow;
Packit Service ed5168
      xbevent.subwindow = None;
Packit Service ed5168
      xbevent.time = xcevent.time;
Packit Service ed5168
      xbevent.x = xcevent.x;
Packit Service ed5168
      xbevent.y = xcevent.y;
Packit Service ed5168
      xbevent.x_root = xcevent.x_root;
Packit Service ed5168
      xbevent.y_root = xcevent.y_root;
Packit Service ed5168
      xbevent.state = clutter_event_get_state (event);
Packit Service ed5168
      xbevent.same_screen = True;
Packit Service ed5168
      xbevent.type = ButtonPress;
Packit Service ed5168
      xbevent.button = clutter_event_get_button (event);
Packit Service ed5168
      XSendEvent (xdisplay, xwindow, False, 0, (XEvent *)&xbevent);
Packit Service ed5168
Packit Service ed5168
      xbevent.type = ButtonRelease;
Packit Service ed5168
      XSendEvent (xdisplay, xwindow, False, 0, (XEvent *)&xbevent);
Packit Service ed5168
    }
Packit Service ed5168
  else
Packit Service ed5168
    {
Packit Service ed5168
      xkevent.window = xwindow;
Packit Service ed5168
      xkevent.root = xrootwindow;
Packit Service ed5168
      xkevent.subwindow = None;
Packit Service ed5168
      xkevent.time = xcevent.time;
Packit Service ed5168
      xkevent.x = xcevent.x;
Packit Service ed5168
      xkevent.y = xcevent.y;
Packit Service ed5168
      xkevent.x_root = xcevent.x_root;
Packit Service ed5168
      xkevent.y_root = xcevent.y_root;
Packit Service ed5168
      xkevent.state = clutter_event_get_state (event);
Packit Service ed5168
      xkevent.same_screen = True;
Packit Service ed5168
      xkevent.keycode = clutter_event_get_key_code (event);
Packit Service ed5168
Packit Service ed5168
      xkevent.type = KeyPress;
Packit Service ed5168
      XSendEvent (xdisplay, xwindow, False, 0, (XEvent *)&xkevent);
Packit Service ed5168
Packit Service ed5168
      if (event_type == CLUTTER_KEY_RELEASE)
Packit Service ed5168
        {
Packit Service ed5168
          /* If the application takes a grab on KeyPress, we don't
Packit Service ed5168
           * want to send it a KeyRelease. There's no good way of
Packit Service ed5168
           * knowing whether a tray icon will take a grab, so just
Packit Service ed5168
           * assume it does, and don't send the KeyRelease. That might
Packit Service ed5168
           * make the tracking for key events messed up if it doesn't take
Packit Service ed5168
           * a grab, but the tray icon won't get key focus in normal cases,
Packit Service ed5168
           * so let's hope this isn't too damaging...
Packit Service ed5168
           */
Packit Service ed5168
          xkevent.type = KeyRelease;
Packit Service ed5168
          XSendEvent (xdisplay, xwindow, False, 0, (XEvent *)&xkevent);
Packit Service ed5168
        }
Packit Service ed5168
    }
Packit Service ed5168
Packit Service ed5168
  /* And move the pointer back out */
Packit Service ed5168
  xcevent.type = LeaveNotify;
Packit Service ed5168
  XSendEvent (xdisplay, xwindow, False, 0, (XEvent *)&xcevent);
Packit Service ed5168
Packit Service ed5168
  gdk_x11_display_error_trap_pop_ignored (display);
Packit Service ed5168
}