Files
xfce4-taskmanager/src/process-window.c
Brian Tarricone ecd1e1a645 Add minimal support for Wayland
This just gets it running properly, but disables features that are
difficult or impossible to do on Wayland (the window selector, and the
stuff that uses libwnck).  At least it doesn't segfault on startup
anymore.

Fixes: #73
2023-03-24 10:51:15 +00:00

520 lines
17 KiB
C

/*
* Copyright (c) 2010 Mike Massonnet, <mmassonnet@xfce.org>
*
* 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.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/Xos.h>
#include <X11/Xmu/WinUtil.h>
#include <X11/cursorfont.h>
#include <X11/Xproto.h>
#include <glib-object.h>
#include <gdk/gdkx.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <xfconf/xfconf.h>
#include <libxfce4ui/libxfce4ui.h>
#include "settings.h"
#include "task-manager.h"
#include "process-window.h"
#include "process-window_ui.h"
#include "process-monitor.h"
#include "process-tree-view.h"
#include "process-statusbar.h"
#include "settings-dialog.h"
typedef struct _XtmProcessWindowClass XtmProcessWindowClass;
struct _XtmProcessWindowClass
{
GtkWidgetClass parent_class;
};
struct _XtmProcessWindow
{
GtkWidget parent;
/*<private>*/
GtkBuilder * builder;
GtkWidget * window;
GtkWidget * filter_entry;
GtkWidget * filter_searchbar;
GtkWidget * cpu_monitor;
GtkWidget * mem_monitor;
GtkWidget * vpaned;
GtkWidget * treeview;
GtkWidget * statusbar;
GtkWidget * settings_button;
XtmSettings * settings;
XfconfChannel * channel;
gint width;
gint height;
gulong handler;
gboolean view_stuck;
};
G_DEFINE_TYPE (XtmProcessWindow, xtm_process_window, GTK_TYPE_WIDGET)
static void xtm_process_window_finalize (GObject *object);
static void xtm_process_window_hide (GtkWidget *widget);
static void emit_destroy_signal (XtmProcessWindow *window);
static gboolean xtm_process_window_key_pressed (XtmProcessWindow *window, GdkEventKey *event);
static void monitor_update_step_size (XtmProcessWindow *window);
static void
filter_entry_icon_pressed_cb (GtkEntry *entry,
gint position,
GdkEventButton *event __unused,
gpointer data __unused)
{
if (position == GTK_ENTRY_ICON_SECONDARY) {
gtk_entry_set_text (entry, "");
gtk_widget_grab_focus (GTK_WIDGET(entry));
}
}
static Window
Select_Window (Display *dpy, int screen)
{
int status;
Cursor cursor;
XEvent event;
Window target_win = None, root = RootWindow(dpy,screen);
int buttons = 0;
/* Make the target cursor */
cursor = XCreateFontCursor(dpy, XC_crosshair);
/* Grab the pointer using target cursor, letting it roam all over */
status = XGrabPointer(dpy, root, False,
ButtonPressMask|ButtonReleaseMask, GrabModeSync,
GrabModeAsync, root, cursor, CurrentTime);
if (status != GrabSuccess) {
fprintf (stderr, "Can't grab the mouse.\n");
return None;
}
/* Let the user select a window... */
while ((target_win == None) || (buttons != 0)) {
/* allow one more event */
XAllowEvents(dpy, SyncPointer, CurrentTime);
XWindowEvent(dpy, root, ButtonPressMask|ButtonReleaseMask, &event);
switch (event.type) {
case ButtonPress:
if (target_win == None) {
target_win = event.xbutton.subwindow; /* window selected */
if (target_win == None) target_win = root;
}
buttons++;
break;
case ButtonRelease:
if (buttons > 0) /* there may have been some down before we started */
buttons--;
break;
}
}
XUngrabPointer(dpy, CurrentTime); /* Done with pointer */
return target_win;
}
static void
xwininfo_clicked_cb (GtkButton *button __unused, gpointer user_data) {
XtmProcessWindow *window = (XtmProcessWindow *) user_data;
Window selected_window;
Display *dpy;
Atom atom_NET_WM_PID;
unsigned long _nitems;
Atom actual_type;
int actual_format;
unsigned char *prop;
int status;
unsigned long bytes_after;
GPid pid = 0;
dpy = XOpenDisplay (NULL);
selected_window = Select_Window (dpy, 0);
if (selected_window) {
selected_window = XmuClientWindow (dpy, selected_window);
}
atom_NET_WM_PID = XInternAtom(dpy, "_NET_WM_PID", False);
status = XGetWindowProperty(dpy, selected_window, atom_NET_WM_PID, 0, (~0L),
False, AnyPropertyType, &actual_type,
&actual_format, &_nitems, &bytes_after,
&prop);
if (status == BadWindow) {
XTM_SHOW_MESSAGE(GTK_MESSAGE_INFO,
_("Bad Window"), _("Window id 0x%lx does not exist!"), selected_window);
} if (status != Success) {
XTM_SHOW_MESSAGE(GTK_MESSAGE_ERROR,
_("XGetWindowProperty failed"), _("XGetWindowProperty failed!"));
} else {
if (_nitems > 0) {
memcpy(&pid, prop, sizeof(pid));
xtm_process_tree_view_highlight_pid(XTM_PROCESS_TREE_VIEW (window->treeview), pid);
} else {
XTM_SHOW_MESSAGE(GTK_MESSAGE_INFO,
_("No PID found"), _("No PID found for window 0x%lx."), selected_window);
}
g_free(prop);
}
}
static void
filter_entry_keyrelease_handler(GtkEntry *entry,
XtmProcessTreeView *treeview)
{
const gchar *text;
gboolean has_text;
text = gtk_editable_get_chars (GTK_EDITABLE(entry), 0, -1);
xtm_process_tree_view_set_filter(treeview, text);
has_text = gtk_entry_get_text_length (GTK_ENTRY(entry)) > 0;
gtk_entry_set_icon_sensitive (GTK_ENTRY(entry),
GTK_ENTRY_ICON_SECONDARY,
has_text);
}
static void
xtm_process_window_class_init (XtmProcessWindowClass *klass)
{
GObjectClass *class;
GtkWidgetClass *widget_class;
xtm_process_window_parent_class = g_type_class_peek_parent (klass);
class = G_OBJECT_CLASS (klass);
class->finalize = xtm_process_window_finalize;
widget_class = GTK_WIDGET_CLASS (klass);
widget_class->show = xtm_process_window_show;
widget_class->hide = xtm_process_window_hide;
}
static void
xtm_process_window_size_allocate (GtkWidget *widget, GtkAllocation *allocation, gpointer user_data)
{
XtmProcessWindow *window = (XtmProcessWindow *) user_data;
g_return_if_fail (gtk_widget_is_toplevel (widget));
gtk_window_get_size (GTK_WINDOW (widget), &window->width, &window->height);
}
static void
show_settings_dialog (GtkButton *button, gpointer user_data)
{
XtmProcessWindow *window = (XtmProcessWindow *) user_data;
g_signal_handler_block (G_OBJECT (window->window), window->handler);
xtm_settings_dialog_run (window->window);
g_signal_handler_unblock (G_OBJECT (window->window), window->handler);
}
static void
xtm_process_window_stick_view (GtkAdjustment *adjustment, XtmProcessWindow *window)
{
if (window->view_stuck)
gtk_adjustment_set_value (adjustment, 0);
else if (gtk_adjustment_get_value (adjustment) == 0)
window->view_stuck = TRUE;
}
static gboolean
xtm_process_window_unstick_view_event (GtkWidget *widget, GdkEvent *event, XtmProcessWindow *window)
{
GdkScrollDirection dir;
gdouble y;
if (! window->view_stuck)
return FALSE;
if (event->type == GDK_SCROLL && (
(gdk_event_get_scroll_direction (event, &dir) && dir == GDK_SCROLL_UP)
|| (gdk_event_get_scroll_deltas (event, NULL, &y) && y <= 0)
))
return FALSE;
window->view_stuck = FALSE;
return FALSE;
}
static void
xtm_process_window_unstick_view_cursor (GtkTreeView *tree_view, XtmProcessWindow *window)
{
GtkTreePath *cursor, *end;
if (! window->view_stuck)
return;
if (gtk_tree_view_get_visible_range (tree_view, NULL, &end)) {
gtk_tree_view_get_cursor (tree_view, &cursor, NULL);
if (cursor != NULL && gtk_tree_path_compare (cursor, end) >= 0)
window->view_stuck = FALSE;
gtk_tree_path_free (cursor);
gtk_tree_path_free (end);
}
}
static void
xtm_process_window_init (XtmProcessWindow *window)
{
GtkWidget *button;
window->settings = xtm_settings_get_default ();
window->channel = xfconf_channel_new (CHANNEL);
window->builder = gtk_builder_new ();
gtk_builder_add_from_string (window->builder, process_window_ui, process_window_ui_length, NULL);
window->window = GTK_WIDGET (gtk_builder_get_object (window->builder, "process-window"));
window->width = xfconf_channel_get_int (window->channel, SETTING_WINDOW_WIDTH, DEFAULT_WINDOW_WIDTH);
window->height = xfconf_channel_get_int (window->channel, SETTING_WINDOW_HEIGHT, DEFAULT_WINDOW_HEIGHT);
if (window->width >= 1 && window->height >= 1)
gtk_window_set_default_size (GTK_WINDOW (window->window), window->width, window->height);
g_signal_connect_swapped (window->window, "destroy", G_CALLBACK (emit_destroy_signal), window);
window->handler = g_signal_connect (window->window, "size-allocate", G_CALLBACK (xtm_process_window_size_allocate), window);
g_signal_connect_swapped (window->window, "key-press-event", G_CALLBACK(xtm_process_window_key_pressed), window);
button = GTK_WIDGET (gtk_builder_get_object (window->builder, "button-settings"));
g_signal_connect (G_OBJECT (button), "clicked",
G_CALLBACK (show_settings_dialog), window);
button = GTK_WIDGET (gtk_builder_get_object (window->builder, "button-identify"));
if (GDK_IS_X11_DISPLAY (gdk_display_get_default ())) {
g_signal_connect (G_OBJECT (button), "clicked",
G_CALLBACK (xwininfo_clicked_cb), window);
} else {
gtk_widget_hide (button);
}
window->filter_searchbar = GTK_WIDGET (gtk_builder_get_object (window->builder, "filter-searchbar"));
{
GtkWidget *toolitem;
guint refresh_rate;
g_object_get (window->settings,
"refresh-rate", &refresh_rate,
NULL);
window->vpaned = GTK_WIDGET (gtk_builder_get_object (window->builder, "mainview-vpaned"));
xfconf_g_property_bind (window->channel, SETTING_HANDLE_POSITION, G_TYPE_INT,
G_OBJECT (window->vpaned), "position");
toolitem = GTK_WIDGET (gtk_builder_get_object (window->builder, "graph-cpu"));
window->cpu_monitor = xtm_process_monitor_new ();
xtm_process_monitor_set_step_size (XTM_PROCESS_MONITOR (window->cpu_monitor), refresh_rate / 1000.0f);
xtm_process_monitor_set_type (XTM_PROCESS_MONITOR (window->cpu_monitor), 0);
gtk_widget_show (window->cpu_monitor);
gtk_container_add (GTK_CONTAINER (toolitem), window->cpu_monitor);
toolitem = GTK_WIDGET (gtk_builder_get_object (window->builder, "graph-mem"));
window->mem_monitor = xtm_process_monitor_new ();
xtm_process_monitor_set_step_size (XTM_PROCESS_MONITOR (window->mem_monitor), refresh_rate / 1000.0f);
xtm_process_monitor_set_type (XTM_PROCESS_MONITOR (window->mem_monitor), 1);
gtk_widget_show (window->mem_monitor);
gtk_container_add (GTK_CONTAINER (toolitem), window->mem_monitor);
g_signal_connect_swapped (window->settings, "notify::refresh-rate", G_CALLBACK (monitor_update_step_size), window);
}
window->statusbar = xtm_process_statusbar_new ();
gtk_widget_show (window->statusbar);
gtk_box_pack_start (GTK_BOX (gtk_builder_get_object (window->builder, "graph-vbox")), window->statusbar, FALSE, FALSE, 0);
gtk_widget_set_visible (GTK_WIDGET (gtk_builder_get_object (window->builder, "root-warning-box")), geteuid () == 0);
window->treeview = xtm_process_tree_view_new ();
gtk_widget_show (window->treeview);
{
GtkScrolledWindow *s_window;
GtkWidget *bar;
GtkAdjustment *adjust;
s_window = GTK_SCROLLED_WINDOW (gtk_builder_get_object (window->builder, "scrolledwindow"));
bar = gtk_scrolled_window_get_vscrollbar (s_window);
adjust = gtk_scrolled_window_get_vadjustment (s_window);
window->view_stuck = TRUE;
gtk_container_add (GTK_CONTAINER (s_window), window->treeview);
g_signal_connect (adjust, "value-changed", G_CALLBACK (xtm_process_window_stick_view), window);
g_signal_connect (bar, "button-press-event", G_CALLBACK (xtm_process_window_unstick_view_event), window);
g_signal_connect (bar, "scroll-event", G_CALLBACK (xtm_process_window_unstick_view_event), window);
g_signal_connect (window->treeview, "scroll-event", G_CALLBACK (xtm_process_window_unstick_view_event), window);
g_signal_connect (window->treeview, "cursor-changed", G_CALLBACK (xtm_process_window_unstick_view_cursor), window);
}
g_object_bind_property(window->settings, "show-legend",
gtk_builder_get_object(window->builder, "legend"), "visible",
G_BINDING_SYNC_CREATE);
window->filter_entry = GTK_WIDGET(gtk_builder_get_object (window->builder, "filter-entry"));
g_signal_connect (G_OBJECT(window->filter_entry), "icon-press", G_CALLBACK(filter_entry_icon_pressed_cb), NULL);
g_signal_connect (G_OBJECT(window->filter_entry), "changed", G_CALLBACK(filter_entry_keyrelease_handler), window->treeview);
gtk_widget_set_tooltip_text (window->filter_entry, _("Filter on process name"));
gtk_widget_grab_focus (window->filter_entry);
}
static void
xtm_process_window_finalize (GObject *object)
{
XtmProcessWindow *window = XTM_PROCESS_WINDOW (object);
if (GTK_IS_TREE_VIEW (window->treeview))
gtk_widget_destroy (window->treeview);
if (GTK_IS_BOX (window->statusbar))
gtk_widget_destroy (window->statusbar);
if (XTM_IS_SETTINGS (window->settings))
g_object_unref (window->settings);
g_object_unref (window->builder);
window->builder = NULL;
}
/**
* Helper functions
*/
static void
emit_destroy_signal (XtmProcessWindow *window)
{
/* Store window size */
xfconf_channel_set_int (window->channel, SETTING_WINDOW_WIDTH, window->width);
xfconf_channel_set_int (window->channel, SETTING_WINDOW_HEIGHT, window->height);
g_signal_emit_by_name (window, "destroy", G_TYPE_NONE);
}
static gboolean
xtm_process_window_key_pressed (XtmProcessWindow *window, GdkEventKey *event)
{
gboolean ret = FALSE;
if (event->keyval == GDK_KEY_Escape &&
gtk_widget_is_focus(GTK_WIDGET (window->filter_entry)))
{
if (xfconf_channel_get_bool (window->channel, SETTING_SHOW_FILTER, FALSE))
gtk_entry_set_text (GTK_ENTRY(window->filter_entry), "");
else
g_signal_emit_by_name (window, "delete-event", event, &ret, G_TYPE_BOOLEAN);
}
else if (event->keyval == GDK_KEY_Escape ||
(event->keyval == GDK_KEY_q && (event->state & GDK_CONTROL_MASK)))
{
g_signal_emit_by_name (window, "delete-event", event, &ret, G_TYPE_BOOLEAN);
ret = TRUE;
}
else if (event->keyval == GDK_KEY_f && (event->state & GDK_CONTROL_MASK))
{
gtk_widget_grab_focus (GTK_WIDGET(window->filter_entry));
xfconf_channel_set_bool (window->channel, SETTING_SHOW_FILTER, TRUE);
ret = TRUE;
}
return ret;
}
static void
monitor_update_step_size (XtmProcessWindow *window)
{
guint refresh_rate;
g_object_get (window->settings, "refresh-rate", &refresh_rate, NULL);
g_object_set (window->cpu_monitor, "step-size", refresh_rate / 1000.0, NULL);
g_object_set (window->mem_monitor, "step-size", refresh_rate / 1000.0, NULL);
}
/**
* Class functions
*/
GtkWidget *
xtm_process_window_new (void)
{
return g_object_new (XTM_TYPE_PROCESS_WINDOW, NULL);
}
void
xtm_process_window_show (GtkWidget *widget)
{
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_if_fail (GTK_IS_WIDGET (XTM_PROCESS_WINDOW (widget)->window));
gtk_widget_show (XTM_PROCESS_WINDOW (widget)->window);
gtk_window_present (GTK_WINDOW (XTM_PROCESS_WINDOW (widget)->window));
GTK_WIDGET_CLASS (xtm_process_window_parent_class)->show(widget);
}
static void
xtm_process_window_hide (GtkWidget *widget)
{
gint winx, winy;
g_return_if_fail (GTK_IS_WIDGET (widget));
if (!GTK_IS_WIDGET (XTM_PROCESS_WINDOW (widget)->window))
return;
gtk_window_get_position (GTK_WINDOW (XTM_PROCESS_WINDOW (widget)->window), &winx, &winy);
gtk_widget_hide (XTM_PROCESS_WINDOW (widget)->window);
gtk_window_move (GTK_WINDOW (XTM_PROCESS_WINDOW (widget)->window), winx, winy);
GTK_WIDGET_CLASS (xtm_process_window_parent_class)->hide(widget);
}
GtkTreeModel *
xtm_process_window_get_model (XtmProcessWindow *window)
{
g_return_val_if_fail (XTM_IS_PROCESS_WINDOW (window), NULL);
g_return_val_if_fail (XTM_IS_PROCESS_TREE_VIEW (window->treeview), NULL);
return xtm_process_tree_view_get_model (XTM_PROCESS_TREE_VIEW (window->treeview));
}
void
xtm_process_window_set_system_info (XtmProcessWindow *window, guint num_processes, gfloat cpu, gfloat memory, gchar* memory_str, gfloat swap, gchar* swap_str)
{
gchar text[100];
gchar value[4];
g_return_if_fail (XTM_IS_PROCESS_WINDOW (window));
g_return_if_fail (GTK_IS_BOX (window->statusbar));
g_object_set (window->statusbar, "num-processes", num_processes, "cpu", cpu, "memory", memory_str, "swap", swap_str, NULL);
xtm_process_monitor_add_peak (XTM_PROCESS_MONITOR (window->cpu_monitor), cpu / 100.0f, -1.0);
g_snprintf (value, sizeof(value), "%.0f", cpu);
g_snprintf (text, sizeof(text), _("CPU: %s%%"), value);
gtk_widget_set_tooltip_text (window->cpu_monitor, text);
xtm_process_monitor_add_peak (XTM_PROCESS_MONITOR (window->mem_monitor), memory / 100.0f, swap / 100.0f);
g_snprintf (text, sizeof(text), _("Memory: %s"), memory_str);
gtk_widget_set_tooltip_text (window->mem_monitor, text);
}
void
xtm_process_window_show_swap_usage (XtmProcessWindow *window, gboolean show_swap_usage)
{
g_return_if_fail (XTM_IS_PROCESS_WINDOW (window));
g_return_if_fail (GTK_IS_BOX (window->statusbar));
g_object_set (window->statusbar, "show-swap", show_swap_usage, NULL);
}