diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:49:37 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:49:37 +0000 |
commit | 35504d91654321ff2b378229ff13150f53d5aad2 (patch) | |
tree | cb85edefc751b37c8423d78c5e5888f42cc01e4b /tools | |
parent | Initial commit. (diff) | |
download | gnome-session-upstream/43.0.tar.xz gnome-session-upstream/43.0.zip |
Adding upstream version 43.0.upstream/43.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | tools/gnome-session-check-accelerated-common.h | 29 | ||||
-rw-r--r-- | tools/gnome-session-check-accelerated-gl-helper.c | 517 | ||||
-rw-r--r-- | tools/gnome-session-check-accelerated-gles-helper.c | 201 | ||||
-rw-r--r-- | tools/gnome-session-check-accelerated.c | 318 | ||||
-rw-r--r-- | tools/gnome-session-ctl.c | 304 | ||||
-rwxr-xr-x | tools/gnome-session-custom-session | 4 | ||||
-rw-r--r-- | tools/gnome-session-inhibit.c | 411 | ||||
-rw-r--r-- | tools/gnome-session-quit.c | 216 | ||||
-rw-r--r-- | tools/gnome-session-selector.c | 698 | ||||
-rw-r--r-- | tools/meson.build | 71 |
10 files changed, 2769 insertions, 0 deletions
diff --git a/tools/gnome-session-check-accelerated-common.h b/tools/gnome-session-check-accelerated-common.h new file mode 100644 index 0000000..59631d9 --- /dev/null +++ b/tools/gnome-session-check-accelerated-common.h @@ -0,0 +1,29 @@ +/* -*- mode:c; c-basic-offset: 8; indent-tabs-mode: nil; -*- */ +/* Tool to set the property _GNOME_SESSION_ACCELERATED on the root window */ +/* + * Copyright (C) 2013 SUSE LINUX Products GmbH, Nuernberg, Germany. + * + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: + * Frederic Crozat <fcrozat@suse.com> + */ + +/* Exit value for helper */ +#define HELPER_ACCEL 0 +#define HELPER_NO_ACCEL 1 +#define HELPER_SOFTWARE_RENDERING 2 + diff --git a/tools/gnome-session-check-accelerated-gl-helper.c b/tools/gnome-session-check-accelerated-gl-helper.c new file mode 100644 index 0000000..4be9638 --- /dev/null +++ b/tools/gnome-session-check-accelerated-gl-helper.c @@ -0,0 +1,517 @@ +/* gcc -o gnome-session-accelerated `pkg-config --cflags --libs xcomposite gl` -Wall gnome-session-is-accelerated.c */ + +/* + * Copyright (C) 2010 Novell, Inc. + * Copyright (C) 2006-2009 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: + * Vincent Untz <vuntz@gnome.org> + * + * Most of the code comes from desktop-effects [1], released under GPLv2+. + * desktop-effects was written by: + * Soren Sandmann <sandmann@redhat.com> + * + * [1] http://git.fedorahosted.org/git/?p=desktop-effects.git;a=blob_plain;f=desktop-effects.c;hb=HEAD + */ + +/* + * Here's the rationale behind this helper, quoting Owen, in his mail to the + * release team: + * (http://mail.gnome.org/archives/release-team/2010-June/msg00079.html) + * + * """ + * There are some limits to what we can do here automatically without + * knowing anything about the driver situation on the system. The basic + * problem is that there are all sorts of suck: + * + * * No GL at all. This typically only happens if a system is + * misconfigured. + * + * * Only software GL. This one is easy to detect. We have code in + * the Fedora desktop-effects tool, etc. + * + * * GL that isn't featureful enough. (Tiny texture size limits, no + * texture-from-pixmap, etc.) Possible to detect with more work, but + * largely a fringe case. + * + * * Buggy GL. This isn't possible to detect. Except for the case where + * all GL programs crash. For that reason, we probably don't want + * gnome-session to directly try and do any GL detection; better to + * use a helper binary. + * + * * Horribly slow hardware GL. We could theoretically develop some sort + * of benchmark, but it's a tricky area. And how slow is too slow? + * """ + * + * Some other tools are doing similar checks: + * - desktop-effects (Fedora Config Tool) [1] + * - drak3d (Mandriva Config Tool) [2] + * - compiz-manager (Compiz wrapper) [3] + * + * [1] http://git.fedorahosted.org/git/?p=desktop-effects.git;a=blob_plain;f=desktop-effects.c;hb=HEAD + * [2] http://svn.mandriva.com/cgi-bin/viewvc.cgi/soft/drak3d/trunk/lib/Xconfig/glx.pm?view=markup + * [3] http://git.compiz.org/fusion/misc/compiz-manager/tree/compiz-manager + */ + +/* for strcasestr */ +#define _GNU_SOURCE + +#include <ctype.h> +#include <locale.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <glib.h> + +#include <regex.h> + +#ifdef __FreeBSD__ +#include <kenv.h> +#endif + +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <X11/extensions/Xcomposite.h> +#include <GL/gl.h> +#include <GL/glx.h> + +#include "gnome-session-check-accelerated-common.h" + +#define SIZE_UNSET 0 +#define SIZE_ERROR -1 +static int max_texture_size = SIZE_UNSET; +static int max_renderbuffer_size = SIZE_UNSET; +static gboolean has_llvmpipe = FALSE; + +static inline void +_print_error (const char *str) +{ + fprintf (stderr, "gnome-session-is-accelerated: %s\n", str); +} + +#define CMDLINE_UNSET -1 +#define CMDLINE_NON_FALLBACK_FORCED 0 +#define CMDLINE_FALLBACK_FORCED 1 + +#if defined(__linux__) +static int +_parse_kcmdline (void) +{ + int ret = CMDLINE_UNSET; + GRegex *regex; + GMatchInfo *match; + char *contents; + char *word; + const char *arg; + + if (!g_file_get_contents ("/proc/cmdline", &contents, NULL, NULL)) + return ret; + + regex = g_regex_new ("gnome.fallback=(\\S+)", 0, G_REGEX_MATCH_NOTEMPTY, NULL); + if (!g_regex_match (regex, contents, G_REGEX_MATCH_NOTEMPTY, &match)) + goto out; + + word = g_match_info_fetch (match, 0); + g_debug ("Found command-line match '%s'", word); + arg = word + strlen ("gnome.fallback="); + if (*arg != '0' && *arg != '1') + fprintf (stderr, "gnome-session-is-accelerated: Invalid value '%s' for gnome.fallback passed in kernel command line.\n", arg); + else + ret = atoi (arg); + g_free (word); + +out: + g_match_info_free (match); + g_regex_unref (regex); + g_free (contents); + + g_debug ("Command-line parsed to %d", ret); + + return ret; +} +#elif defined(__FreeBSD__) +static int +_parse_kcmdline (void) +{ + int ret = CMDLINE_UNSET; + char value[KENV_MVALLEN]; + + /* a compile time check to avoid unexpected stack overflow */ + _Static_assert(KENV_MVALLEN < 1024 * 1024, "KENV_MVALLEN is too large"); + + if (kenv (KENV_GET, "gnome.fallback", value, KENV_MVALLEN) == -1) + return ret; + + if (*value != '0' && *value != '1') + fprintf (stderr, "gnome-session-is-accelerated: Invalid value '%s' for gnome.fallback passed in kernel environment.\n", value); + else + ret = atoi (value); + + g_debug ("Kernel environment parsed to %d", ret); + + return ret; +} +#else +static int +_parse_kcmdline (void) +{ + return CMDLINE_UNSET; +} +#endif + +static gboolean +_has_composite (Display *display) +{ + int dummy1, dummy2; + + return XCompositeQueryExtension (display, &dummy1, &dummy2); +} + +static gboolean +_is_comment (const char *line) +{ + while (*line && isspace(*line)) + line++; + + if (*line == '#' || *line == '\0') + return TRUE; + return FALSE; +} + +static gboolean +_is_gl_renderer_blacklisted (const char *renderer) +{ + FILE *blacklist; + char *line = NULL; + size_t line_len = 0; + gboolean ret = TRUE; + + blacklist = fopen(PKGDATADIR "/hardware-compatibility", "r"); + if (blacklist == NULL) + goto out; + + while (getline (&line, &line_len, blacklist) != -1) { + int whitelist = 0; + const char *re_str; + regex_t re; + int status; + + if (line == NULL) + break; + + /* Drop trailing \n */ + line[strlen(line) - 1] = '\0'; + + if (_is_comment (line)) { + free (line); + line = NULL; + continue; + } + + if (line[0] == '+') + whitelist = 1; + else if (line[0] == '-') + whitelist = 0; + else { + _print_error ("Invalid syntax in this line for hardware compatibility:"); + _print_error (line); + free (line); + line = NULL; + continue; + } + + re_str = line + 1; + + if (regcomp (&re, re_str, REG_EXTENDED|REG_ICASE|REG_NOSUB) != 0) { + _print_error ("Cannot use this regular expression for hardware compatibility:"); + _print_error (re_str); + } else { + status = regexec (&re, renderer, 0, NULL, 0); + regfree(&re); + + if (status == 0) { + if (whitelist) + ret = FALSE; + goto out; + } + } + + free (line); + line = NULL; + } + + ret = FALSE; + +out: + if (line != NULL) + free (line); + + if (blacklist != NULL) + fclose (blacklist); + + return ret; +} + +static char * +_get_hardware_gl (Display *display) +{ + int screen; + Window root; + XVisualInfo *visual = NULL; + GLXContext context = NULL; + XSetWindowAttributes cwa = { 0 }; + Window window = None; + char *renderer = NULL; + + int attrlist[] = { + GLX_RGBA, + GLX_RED_SIZE, 1, + GLX_GREEN_SIZE, 1, + GLX_BLUE_SIZE, 1, + GLX_DOUBLEBUFFER, + None + }; + + screen = DefaultScreen (display); + root = RootWindow (display, screen); + + visual = glXChooseVisual (display, screen, attrlist); + if (!visual) + goto out; + + context = glXCreateContext (display, visual, NULL, True); + if (!context) + goto out; + + cwa.colormap = XCreateColormap (display, root, + visual->visual, AllocNone); + cwa.background_pixel = 0; + cwa.border_pixel = 0; + window = XCreateWindow (display, root, + 0, 0, 1, 1, 0, + visual->depth, InputOutput, visual->visual, + CWColormap | CWBackPixel | CWBorderPixel, + &cwa); + + if (!glXMakeCurrent (display, window, context)) + goto out; + + renderer = g_strdup ((const char *) glGetString (GL_RENDERER)); + if (_is_gl_renderer_blacklisted (renderer)) { + g_clear_pointer (&renderer, g_free); + goto out; + } + if (renderer && strcasestr (renderer, "llvmpipe")) + has_llvmpipe = TRUE; + + /* we need to get the max texture and renderbuffer sizes while we have + * a context, but we'll check their values later */ + + glGetIntegerv (GL_MAX_TEXTURE_SIZE, &max_texture_size); + if (glGetError() != GL_NO_ERROR) + max_texture_size = SIZE_ERROR; + + glGetIntegerv (GL_MAX_RENDERBUFFER_SIZE_EXT, &max_renderbuffer_size); + if (glGetError() != GL_NO_ERROR) + max_renderbuffer_size = SIZE_ERROR; + +out: + glXMakeCurrent (display, None, None); + if (context) + glXDestroyContext (display, context); + if (window) + XDestroyWindow (display, window); + if (cwa.colormap) + XFreeColormap (display, cwa.colormap); + + return renderer; +} + +static gboolean +_has_extension (const char *extension_list, + const char *extension) +{ + char **extensions; + guint i; + gboolean ret; + + g_return_val_if_fail (extension != NULL, TRUE); + + /* Extension_list is one big string, containing extensions + * separated by spaces. */ + if (extension_list == NULL) + return FALSE; + + ret = FALSE; + + extensions = g_strsplit (extension_list, " ", -1); + if (extensions == NULL) + return FALSE; + + for (i = 0; extensions[i] != NULL; i++) { + if (g_str_equal (extensions[i], extension)) { + ret = TRUE; + break; + } + } + + g_strfreev (extensions); + + return ret; +} + +static gboolean +_has_texture_from_pixmap (Display *display) +{ + int screen; + const char *server_extensions; + const char *client_extensions; + gboolean ret = FALSE; + + screen = DefaultScreen (display); + + server_extensions = glXQueryServerString (display, screen, + GLX_EXTENSIONS); + if (!_has_extension (server_extensions, + "GLX_EXT_texture_from_pixmap")) + goto out; + + client_extensions = glXGetClientString (display, GLX_EXTENSIONS); + if (!_has_extension (client_extensions, + "GLX_EXT_texture_from_pixmap")) + goto out; + + ret = TRUE; + +out: + return ret; +} + +static void +_set_max_screen_size_property (Display *display, int screen, int size) +{ + Atom max_screen_size_atom; + + max_screen_size_atom = XInternAtom (display, "_GNOME_MAX_SCREEN_SIZE", + False); + + /* Will be read by gnome-settings-daemon and + * gnome-control-center to avoid display configurations where 3D + * is not available (and would break gnome-shell) */ + XChangeProperty (display, RootWindow(display, screen), + max_screen_size_atom, + XA_CARDINAL, 32, PropModeReplace, + (unsigned char *)&size, 1); + + XSync(display, False); +} + +static gboolean +_is_max_texture_size_big_enough (Display *display) +{ + int screen, size; + + screen = DefaultScreen (display); + size = MIN(max_renderbuffer_size, max_texture_size); + if (size < DisplayWidth (display, screen) || + size < DisplayHeight (display, screen)) + return FALSE; + + _set_max_screen_size_property (display, screen, size); + + return TRUE; +} + +static gboolean print_renderer = FALSE; + +static const GOptionEntry entries[] = { + { "print-renderer", 'p', 0, G_OPTION_ARG_NONE, &print_renderer, "Print GL renderer name", NULL }, + { NULL }, +}; + +int +main (int argc, char **argv) +{ + int kcmdline_parsed; + Display *display = NULL; + int ret = HELPER_NO_ACCEL; + GOptionContext *context; + GError *error = NULL; + char *renderer = NULL; + + setlocale (LC_ALL, ""); + + context = g_option_context_new (NULL); + g_option_context_add_main_entries (context, entries, NULL); + + if (!g_option_context_parse (context, &argc, &argv, &error)) { + g_error ("Can't parse command line: %s\n", error->message); + g_error_free (error); + goto out; + } + + kcmdline_parsed = _parse_kcmdline (); + if (kcmdline_parsed > CMDLINE_UNSET) { + if (kcmdline_parsed == CMDLINE_NON_FALLBACK_FORCED) { + _print_error ("Non-fallback mode forced by kernel command line."); + ret = HELPER_ACCEL; + goto out; + } else if (kcmdline_parsed == CMDLINE_FALLBACK_FORCED) { + _print_error ("Fallback mode forced by kernel command line."); + goto out; + } + } + + display = XOpenDisplay (NULL); + if (!display) { + _print_error ("No X display."); + goto out; + } + + if (!_has_composite (display)) { + _print_error ("No composite extension."); + goto out; + } + + renderer = _get_hardware_gl (display); + if (!renderer) { + _print_error ("No hardware 3D support."); + goto out; + } + + if (!_has_texture_from_pixmap (display)) { + _print_error ("No GLX_EXT_texture_from_pixmap support."); + goto out; + } + + if (!_is_max_texture_size_big_enough (display)) { + _print_error ("GL_MAX_{TEXTURE,RENDERBUFFER}_SIZE is too small."); + goto out; + } + + ret = has_llvmpipe ? HELPER_SOFTWARE_RENDERING : HELPER_ACCEL; + + if (print_renderer) + g_print ("%s", renderer); + +out: + if (display) + XCloseDisplay (display); + g_free (renderer); + + return ret; +} diff --git a/tools/gnome-session-check-accelerated-gles-helper.c b/tools/gnome-session-check-accelerated-gles-helper.c new file mode 100644 index 0000000..6a4463d --- /dev/null +++ b/tools/gnome-session-check-accelerated-gles-helper.c @@ -0,0 +1,201 @@ +/* -*- mode:c; c-basic-offset: 8; indent-tabs-mode: nil; -*- */ +/* + * + * Copyright (C) 2016 Endless Mobile, 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Author: + * Cosimo Cecchi <cosimo@endlessm.com> + */ + +/* for strcasestr */ +#define _GNU_SOURCE + +#include <config.h> + +#include <gtk/gtk.h> +#include <locale.h> +#include <stdlib.h> +#include <string.h> + +#ifdef GDK_WINDOWING_X11 +#define GL_GLEXT_PROTOTYPES + +#include <gdk/gdkx.h> +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <EGL/egl.h> +#endif + +#include "gnome-session-check-accelerated-common.h" + +#ifdef GDK_WINDOWING_X11 +static char * +get_gles_renderer (void) +{ + /* Select GLESv2 config */ + EGLint attribs[] = { + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_RED_SIZE, 1, + EGL_GREEN_SIZE, 1, + EGL_BLUE_SIZE, 1, + EGL_NONE + }; + + EGLint ctx_attribs[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + gboolean egl_inited = FALSE; + Display *display; + Window win = None; + EGLContext egl_ctx = NULL; + EGLDisplay egl_dpy = NULL; + EGLSurface egl_surf = NULL; + char *renderer = NULL; + + gdk_error_trap_push (); + + display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); + egl_dpy = eglGetDisplay (display); + if (!egl_dpy) { + g_warning ("eglGetDisplay() failed"); + goto out; + } + + EGLint egl_major, egl_minor; + if (!eglInitialize (egl_dpy, &egl_major, &egl_minor)) { + g_warning ("eglInitialize() failed"); + goto out; + } + + egl_inited = TRUE; + + EGLint num_configs; + EGLConfig config; + if (!eglChooseConfig (egl_dpy, attribs, &config, 1, &num_configs)) { + g_warning ("Failed to get EGL configuration"); + goto out; + } + + EGLint vid; + if (!eglGetConfigAttrib (egl_dpy, config, EGL_NATIVE_VISUAL_ID, &vid)) { + g_warning ("Failed to get EGL visual"); + goto out; + } + + /* The X window visual must match the EGL config */ + XVisualInfo *vis_info, vis_template; + int num_visuals; + vis_template.visualid = vid; + vis_info = XGetVisualInfo (display, VisualIDMask, &vis_template, &num_visuals); + if (!vis_info) { + g_warning ("Failed to get X visual"); + goto out; + } + + XSetWindowAttributes attr; + attr.colormap = XCreateColormap (display, DefaultRootWindow (display), + vis_info->visual, AllocNone); + win = XCreateWindow (display, DefaultRootWindow (display), + 0, 0, /* x, y */ + 1, 1, /* width, height */ + 0, /* border_width */ + vis_info->depth, InputOutput, + vis_info->visual, CWColormap, &attr); + XFree (vis_info); + + eglBindAPI (EGL_OPENGL_ES_API); + + egl_ctx = eglCreateContext (egl_dpy, config, EGL_NO_CONTEXT, ctx_attribs); + if (!egl_ctx) { + g_warning ("Failed to create EGL context"); + goto out; + } + + egl_surf = eglCreateWindowSurface (egl_dpy, config, win, NULL); + if (!egl_surf) { + g_warning ("Failed to create EGL surface"); + goto out; + } + + if (!eglMakeCurrent (egl_dpy, egl_surf, egl_surf, egl_ctx)) { + g_warning ("eglMakeCurrent() failed"); + goto out; + } + + renderer = g_strdup ((const char *) glGetString (GL_RENDERER)); + + out: + if (egl_ctx) + eglDestroyContext (egl_dpy, egl_ctx); + if (egl_surf) + eglDestroySurface (egl_dpy, egl_surf); + if (egl_inited) + eglTerminate (egl_dpy); + if (win != None) + XDestroyWindow (display, win); + + gdk_error_trap_pop_ignored (); + return renderer; +} +#endif + +static gboolean print_renderer = FALSE; + +static const GOptionEntry entries[] = { + { "print-renderer", 'p', 0, G_OPTION_ARG_NONE, &print_renderer, "Print EGL renderer name", NULL }, + { NULL }, +}; + +int +main (int argc, + char **argv) +{ + char *renderer = NULL; + GOptionContext *context; + int ret = HELPER_NO_ACCEL; + GError *error = NULL; + + setlocale (LC_ALL, ""); + + context = g_option_context_new (NULL); + g_option_context_add_group (context, gtk_get_option_group (TRUE)); + g_option_context_add_main_entries (context, entries, NULL); + + if (!g_option_context_parse (context, &argc, &argv, &error)) { + g_error ("Can't parse command line: %s\n", error->message); + g_error_free (error); + goto out; + } + +#ifdef GDK_WINDOWING_X11 + renderer = get_gles_renderer (); +#endif + + if (renderer != NULL) { + if (print_renderer) + g_print ("%s", renderer); + if (strcasestr (renderer, "llvmpipe")) + ret = HELPER_SOFTWARE_RENDERING; + else + ret = HELPER_ACCEL; + } + +out: + return ret; +} diff --git a/tools/gnome-session-check-accelerated.c b/tools/gnome-session-check-accelerated.c new file mode 100644 index 0000000..3765f52 --- /dev/null +++ b/tools/gnome-session-check-accelerated.c @@ -0,0 +1,318 @@ +/* -*- mode:c; c-basic-offset: 8; indent-tabs-mode: nil; -*- */ +/* Tool to set the property _GNOME_SESSION_ACCELERATED on the root window */ +/* + * Copyright (C) 2011 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: + * Colin Walters <walters@verbum.org> + */ + +#include <string.h> +#include <unistd.h> +#include <stdlib.h> + +#include <gtk/gtk.h> +#include <epoxy/gl.h> +#include <gdk/gdkx.h> +#include <X11/Xatom.h> +#include <sys/wait.h> + +#include "gnome-session-check-accelerated-common.h" + +/* Wait up to this long for a running check to finish */ +#define PROPERTY_CHANGE_TIMEOUT 5000 + +/* Values used for the _GNOME_SESSION_ACCELERATED root window property */ +#define NO_ACCEL 0 +#define HAVE_ACCEL 1 +#define ACCEL_CHECK_RUNNING 2 + +static Atom is_accelerated_atom; +static Atom is_software_rendering_atom; +static Atom renderer_atom; +static gboolean property_changed; + +static gboolean +on_property_notify_timeout (gpointer data) +{ + gtk_main_quit (); + return FALSE; +} + +static GdkFilterReturn +property_notify_filter (GdkXEvent *xevent, + GdkEvent *event, + gpointer data) +{ + XPropertyEvent *ev = xevent; + + if (ev->type == PropertyNotify && ev->atom == is_accelerated_atom) { + property_changed = TRUE; + gtk_main_quit (); + } + + return GDK_FILTER_CONTINUE; +} + +static gboolean +wait_for_property_notify (void) +{ + GdkDisplay *display; + GdkScreen *screen; + GdkWindow *root; + Window rootwin; + + property_changed = FALSE; + + display = gdk_display_get_default (); + screen = gdk_display_get_default_screen (display); + root = gdk_screen_get_root_window (screen); + rootwin = gdk_x11_window_get_xid (root); + + XSelectInput (GDK_DISPLAY_XDISPLAY (display), rootwin, PropertyChangeMask); + gdk_window_add_filter (root, property_notify_filter, NULL); + g_timeout_add (PROPERTY_CHANGE_TIMEOUT, on_property_notify_timeout, NULL); + + gtk_main (); + + return property_changed; +} + +static char * +get_gtk_gles_renderer (GError **error) +{ + GtkWidget *win; + GdkGLContext *context; + char *renderer = NULL; + + win = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_widget_realize (win); + context = gdk_window_create_gl_context (gtk_widget_get_window (win), error); + if (!context) + return NULL; + gdk_gl_context_make_current (context); + renderer = g_strdup ((char *) glGetString (GL_RENDERER)); + gdk_gl_context_clear_current (); + g_object_unref (context); + + return renderer; +} + +static gboolean +is_discrete_gpu_check (void) +{ + const char *dri_prime; + + dri_prime = g_getenv ("DRI_PRIME"); + if (!dri_prime) + return FALSE; + if (*dri_prime != '1') + return FALSE; + return TRUE; +} + +int +main (int argc, char **argv) +{ + GdkDisplay *display = NULL; + int estatus; + char *gl_helper_argv[] = { LIBEXECDIR "/gnome-session-check-accelerated-gl-helper", "--print-renderer", NULL }; + char *gles_helper_argv[] = { LIBEXECDIR "/gnome-session-check-accelerated-gles-helper", "--print-renderer", NULL }; + const char *renderer_string = NULL; + char *gl_renderer_string = NULL, *gles_renderer_string = NULL; + gboolean gl_software_rendering = FALSE, gles_software_rendering = FALSE; + Window rootwin; + glong is_accelerated, is_software_rendering; + GError *gl_error = NULL, *gles_error = NULL, *gtk_gles_error = NULL; + + gtk_init (NULL, NULL); + + /* gnome-session-check-accelerated gets run before X is started in the wayland + * case, and it currently requires X. Until we have that working, just always + * assume wayland will work. + * Also make sure that we don't read cached information about the first GPU + * when requesting information about the second. + */ + if (is_discrete_gpu_check () || g_strcmp0 (g_getenv ("XDG_SESSION_TYPE"), "x11") != 0) { + g_autofree char *discrete_renderer_string = get_gtk_gles_renderer (>k_gles_error); + if (discrete_renderer_string) { + g_print ("%s", discrete_renderer_string); + return 0; + } + if (gtk_gles_error != NULL) { + g_printerr ("gnome-session-check-accelerated: " + "Failed to get GTK GLES renderer: %s\n", + gtk_gles_error->message); + g_clear_error (>k_gles_error); + } + return 1; + } + + display = gdk_display_get_default (); + /* when running on X11 with a nested wayland GDK will default to wayland + * so looking for X11 atoms will not work (and crash). + */ + if (!GDK_IS_X11_DISPLAY (display)) { + g_printerr ("gnome-session-check-accelerated: no X11 display found\n"); + return 1; + } + + rootwin = gdk_x11_get_default_root_xwindow (); + + is_accelerated_atom = gdk_x11_get_xatom_by_name_for_display (display, "_GNOME_SESSION_ACCELERATED"); + is_software_rendering_atom = gdk_x11_get_xatom_by_name_for_display (display, "_GNOME_IS_SOFTWARE_RENDERING"); + renderer_atom = gdk_x11_get_xatom_by_name_for_display (display, "_GNOME_SESSION_RENDERER"); + + { + Atom type; + gint format; + gulong nitems; + gulong bytes_after; + guchar *data; + + read: + gdk_x11_display_error_trap_push (display); + XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), rootwin, + is_accelerated_atom, + 0, G_MAXLONG, False, XA_CARDINAL, &type, &format, &nitems, + &bytes_after, &data); + gdk_x11_display_error_trap_pop_ignored (display); + + if (type == XA_CARDINAL) { + glong *is_accelerated_ptr = (glong*) data; + + if (*is_accelerated_ptr == ACCEL_CHECK_RUNNING) { + /* Test in progress, wait */ + if (wait_for_property_notify ()) + goto read; + /* else fall through and do the check ourselves */ + + } else { + gdk_x11_display_error_trap_push (display); + XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), rootwin, + renderer_atom, + 0, G_MAXLONG, False, XA_STRING, &type, &format, &nitems, + &bytes_after, &data); + gdk_x11_display_error_trap_pop_ignored (display); + + if (type == XA_STRING) { + g_print ("%s", data); + } + + return (*is_accelerated_ptr == 0 ? 1 : 0); + } + } + } + + /* We don't have the property or it's the wrong type. + * Try to compute it now. + */ + + /* First indicate that a test is in progress */ + is_accelerated = ACCEL_CHECK_RUNNING; + is_software_rendering = FALSE; + estatus = 1; + + XChangeProperty (GDK_DISPLAY_XDISPLAY (display), + rootwin, + is_accelerated_atom, + XA_CARDINAL, 32, PropModeReplace, (guchar *) &is_accelerated, 1); + + gdk_display_sync (display); + + /* First, try the GL helper */ + if (g_spawn_sync (NULL, (char **) gl_helper_argv, NULL, 0, + NULL, NULL, &gl_renderer_string, NULL, &estatus, &gl_error)) { + is_accelerated = (WEXITSTATUS(estatus) == HELPER_ACCEL); + gl_software_rendering = (WEXITSTATUS(estatus) == HELPER_SOFTWARE_RENDERING); + if (is_accelerated) { + renderer_string = gl_renderer_string; + goto finish; + } + + g_printerr ("gnome-session-check-accelerated: GL Helper exited with code %d\n", estatus); + } + + /* Then, try the GLES helper */ + if (g_spawn_sync (NULL, (char **) gles_helper_argv, NULL, 0, + NULL, NULL, &gles_renderer_string, NULL, &estatus, &gles_error)) { + is_accelerated = (WEXITSTATUS(estatus) == HELPER_ACCEL); + gles_software_rendering = (WEXITSTATUS(estatus) == HELPER_SOFTWARE_RENDERING); + if (is_accelerated) { + renderer_string = gles_renderer_string; + goto finish; + } + + g_printerr ("gnome-session-check-accelerated: GLES Helper exited with code %d\n", estatus); + } + + /* If we got here, GL software rendering is our best bet */ + if (gl_software_rendering || gles_software_rendering) { + is_software_rendering = TRUE; + is_accelerated = TRUE; + + if (gl_software_rendering) + renderer_string = gl_renderer_string; + else if (gles_software_rendering) + renderer_string = gles_renderer_string; + + goto finish; + } + + /* Both helpers failed; print their error messages */ + if (gl_error != NULL) { + g_printerr ("gnome-session-check-accelerated: Failed to run GL helper: %s\n", gl_error->message); + g_clear_error (&gl_error); + } + + if (gles_error != NULL) { + g_printerr ("gnome-session-check-accelerated: Failed to run GLES helper: %s\n", gles_error->message); + g_clear_error (&gles_error); + } + + finish: + if (is_accelerated) { + XChangeProperty (GDK_DISPLAY_XDISPLAY (display), + rootwin, + is_accelerated_atom, + XA_CARDINAL, 32, PropModeReplace, (guchar *) &is_accelerated, 1); + } + + if (is_software_rendering) { + XChangeProperty (GDK_DISPLAY_XDISPLAY (display), + rootwin, + is_software_rendering_atom, + XA_CARDINAL, 32, PropModeReplace, (guchar *) &is_software_rendering, 1); + } + + if (renderer_string != NULL) { + XChangeProperty (GDK_DISPLAY_XDISPLAY (display), + rootwin, + renderer_atom, + XA_STRING, 8, PropModeReplace, (guchar *) renderer_string, strlen (renderer_string)); + + /* Print the renderer */ + g_print ("%s", renderer_string); + } + + gdk_display_sync (display); + + g_free (gl_renderer_string); + g_free (gles_renderer_string); + + return is_accelerated ? 0 : 1; +} diff --git a/tools/gnome-session-ctl.c b/tools/gnome-session-ctl.c new file mode 100644 index 0000000..eb9d6b5 --- /dev/null +++ b/tools/gnome-session-ctl.c @@ -0,0 +1,304 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * gnome-session-tl.c - Small utility program to manage gnome systemd session. + + Copyright (C) 1998 Tom Tromey + Copyright (C) 2008,2019 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, 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 <locale.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <systemd/sd-daemon.h> + +#include <glib.h> +#include <glib-unix.h> +#include <glib/gstdio.h> +#include <glib/gi18n.h> +#include <gio/gio.h> + +#define GSM_SERVICE_DBUS "org.gnome.SessionManager" +#define GSM_PATH_DBUS "/org/gnome/SessionManager" +#define GSM_INTERFACE_DBUS "org.gnome.SessionManager" + +#define SYSTEMD_DBUS "org.freedesktop.systemd1" +#define SYSTEMD_PATH_DBUS "/org/freedesktop/systemd1" +#define SYSTEMD_INTERFACE_DBUS "org.freedesktop.systemd1.Manager" + +static GDBusConnection * +get_session_bus (void) +{ + g_autoptr(GError) error = NULL; + GDBusConnection *bus; + + bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); + + if (bus == NULL) + g_warning ("Couldn't connect to session bus: %s", error->message); + + return bus; +} + +static void +do_signal_init (void) +{ + g_autoptr(GDBusConnection) connection = NULL; + g_autoptr(GVariant) reply = NULL; + g_autoptr(GError) error = NULL; + + connection = get_session_bus (); + if (connection == NULL) + return; + + reply = g_dbus_connection_call_sync (connection, + GSM_SERVICE_DBUS, + GSM_PATH_DBUS, + GSM_INTERFACE_DBUS, + "Initialized", + NULL, + NULL, + G_DBUS_CALL_FLAGS_NO_AUTO_START, + -1, NULL, &error); + + if (error != NULL) + g_warning ("Failed to call signal initialization: %s", + error->message); +} + +static void +do_start_unit (const gchar *unit, const char *mode) +{ + g_autoptr(GDBusConnection) connection = NULL; + g_autoptr(GVariant) reply = NULL; + g_autoptr(GError) error = NULL; + + connection = get_session_bus (); + if (connection == NULL) + return; + + reply = g_dbus_connection_call_sync (connection, + SYSTEMD_DBUS, + SYSTEMD_PATH_DBUS, + SYSTEMD_INTERFACE_DBUS, + "StartUnit", + g_variant_new ("(ss)", + unit, + mode), + NULL, + G_DBUS_CALL_FLAGS_NO_AUTO_START, + -1, NULL, &error); + + if (error != NULL) + g_warning ("Failed to start shutdown target: %s", + error->message); +} + +static void +do_restart_dbus (void) +{ + g_autoptr(GDBusConnection) connection = NULL; + g_autoptr(GVariant) reply = NULL; + g_autoptr(GError) error = NULL; + + connection = get_session_bus (); + if (connection == NULL) + return; + + reply = g_dbus_connection_call_sync (connection, + SYSTEMD_DBUS, + SYSTEMD_PATH_DBUS, + SYSTEMD_INTERFACE_DBUS, + "StopUnit", + g_variant_new ("(ss)", + "dbus.service", + "fail"), + NULL, + G_DBUS_CALL_FLAGS_NO_AUTO_START, + -1, NULL, &error); + + if (error != NULL) + g_warning ("Failed to restart DBus service: %s", + error->message); +} + +typedef struct { + GMainLoop *loop; + gint fifo_fd; +} MonitorLeader; + +static gboolean +leader_term_or_int_signal_cb (gpointer user_data) +{ + MonitorLeader *data = (MonitorLeader*) user_data; + + g_main_quit (data->loop); + + return G_SOURCE_REMOVE; +} + +static gboolean +leader_fifo_io_cb (gint fd, + GIOCondition condition, + gpointer user_data) +{ + MonitorLeader *data = (MonitorLeader*) user_data; + + sd_notify (0, "STOPPING=1"); + + if (condition | G_IO_IN) { + char buf[1]; + read (data->fifo_fd, buf, 1); + } + if (condition | G_IO_HUP) { + g_main_loop_quit (data->loop); + } + + return G_SOURCE_CONTINUE; +} + +/** + * do_monitor_leader: + * + * Function to monitor the leader to ensure clean session shutdown and + * propagation of this information to/from loginctl/GDM. + * See main.c systemd_leader_run() for more information. + */ +static void +do_monitor_leader (void) +{ + MonitorLeader data; + g_autofree char *fifo_name = NULL; + int res; + + data.loop = g_main_loop_new (NULL, TRUE); + + fifo_name = g_strdup_printf ("%s/gnome-session-leader-fifo", + g_get_user_runtime_dir ()); + res = mkfifo (fifo_name, 0666); + if (res < 0 && errno != EEXIST) + g_warning ("Error creating FIFO: %m"); + + data.fifo_fd = g_open (fifo_name, O_RDONLY | O_CLOEXEC, 0666); + if (data.fifo_fd >= 0) { + struct stat buf; + + res = fstat (data.fifo_fd, &buf); + if (res < 0) { + g_warning ("Unable to monitor session leader: stat failed with error %m"); + sd_notifyf (0, "STATUS=Unable to monitor session leader: FD is not a FIFO %m"); + close (data.fifo_fd); + data.fifo_fd = -1; + } else if (!(buf.st_mode & S_IFIFO)) { + g_warning ("Unable to monitor session leader: FD is not a FIFO"); + sd_notify (0, "STATUS=Unable to monitor session leader: FD is not a FIFO"); + close (data.fifo_fd); + data.fifo_fd = -1; + } else { + sd_notify (0, "STATUS=Watching session leader"); + g_unix_fd_add (data.fifo_fd, G_IO_HUP | G_IO_IN, leader_fifo_io_cb, &data); + } + } else { + g_warning ("Unable to monitor session leader: Opening FIFO failed with %m"); + sd_notifyf (0, "STATUS=Unable to monitor session leader: Opening FIFO failed with %m"); + } + + g_unix_signal_add (SIGTERM, leader_term_or_int_signal_cb, &data); + g_unix_signal_add (SIGINT, leader_term_or_int_signal_cb, &data); + + g_main_loop_run (data.loop); + + g_main_loop_unref (data.loop); + /* FD is closed with the application. */ +} + +int +main (int argc, char *argv[]) +{ + g_autoptr(GError) error = NULL; + static gboolean opt_shutdown; + static gboolean opt_monitor; + static gboolean opt_signal_init; + static gboolean opt_restart_dbus; + static gboolean opt_exec_stop_check; + int conflicting_options; + GOptionContext *ctx; + static const GOptionEntry options[] = { + { "shutdown", '\0', 0, G_OPTION_ARG_NONE, &opt_shutdown, N_("Start gnome-session-shutdown.target"), NULL }, + { "monitor", '\0', 0, G_OPTION_ARG_NONE, &opt_monitor, N_("Start gnome-session-shutdown.target when receiving EOF or a single byte on stdin"), NULL }, + { "signal-init", '\0', 0, G_OPTION_ARG_NONE, &opt_signal_init, N_("Signal initialization done to gnome-session"), NULL }, + { "restart-dbus", '\0', 0, G_OPTION_ARG_NONE, &opt_restart_dbus, N_("Restart dbus.service if it is running"), NULL }, + { "exec-stop-check", '\0', 0, G_OPTION_ARG_NONE, &opt_exec_stop_check, N_("Run from ExecStopPost to start gnome-session-failed.target on service failure"), NULL }, + { NULL }, + }; + + /* Initialize the i18n stuff */ + setlocale (LC_ALL, ""); + bindtextdomain (GETTEXT_PACKAGE, LOCALE_DIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + ctx = g_option_context_new (""); + g_option_context_add_main_entries (ctx, options, GETTEXT_PACKAGE); + if (! g_option_context_parse (ctx, &argc, &argv, &error)) { + g_warning ("Unable to start: %s", error->message); + exit (1); + } + g_option_context_free (ctx); + + conflicting_options = 0; + if (opt_shutdown) + conflicting_options++; + if (opt_monitor) + conflicting_options++; + if (opt_signal_init) + conflicting_options++; + if (opt_restart_dbus) + conflicting_options++; + if (opt_exec_stop_check) + conflicting_options++; + if (conflicting_options != 1) { + g_printerr (_("Program needs exactly one parameter")); + exit (1); + } + + sd_notify (0, "READY=1"); + + if (opt_signal_init) { + do_signal_init (); + } else if (opt_restart_dbus) { + do_restart_dbus (); + } else if (opt_shutdown) { + do_start_unit ("gnome-session-shutdown.target", "replace-irreversibly"); + } else if (opt_monitor) { + do_monitor_leader (); + do_start_unit ("gnome-session-shutdown.target", "replace-irreversibly"); + } else if (opt_exec_stop_check) { + /* Start failed target if the restart limit was hit */ + if (g_strcmp0 ("start-limit-hit", g_getenv ("SERVICE_RESULT")) == 0) { + do_start_unit ("gnome-session-failed.target", "fail"); + } + } else { + g_assert_not_reached (); + } + + return 0; +} diff --git a/tools/gnome-session-custom-session b/tools/gnome-session-custom-session new file mode 100755 index 0000000..07fdb0c --- /dev/null +++ b/tools/gnome-session-custom-session @@ -0,0 +1,4 @@ +#! /bin/sh + +gnome-session-selector +exec gnome-session diff --git a/tools/gnome-session-inhibit.c b/tools/gnome-session-inhibit.c new file mode 100644 index 0000000..738a9b4 --- /dev/null +++ b/tools/gnome-session-inhibit.c @@ -0,0 +1,411 @@ +/* + * Copyright (C) 2012 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, 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 <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <locale.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <gio/gio.h> + +#include "gnome-session/gsm-inhibitor-flag.h" + +static GsmInhibitorFlag parse_flags (const gchar *arg) +{ + GsmInhibitorFlag flags; + gchar **args; + gint i; + + flags = 0; + + args = g_strsplit (arg, ":", 0); + for (i = 0; args[i]; i++) + { + if (strcmp (args[i], "logout") == 0) + flags |= GSM_INHIBITOR_FLAG_LOGOUT; + else if (strcmp (args[i], "switch-user") == 0) + flags |= GSM_INHIBITOR_FLAG_SWITCH_USER; + else if (strcmp (args[i], "suspend") == 0) + flags |= GSM_INHIBITOR_FLAG_SUSPEND; + else if (strcmp (args[i], "idle") == 0) + flags |= GSM_INHIBITOR_FLAG_IDLE; + else if (strcmp (args[i], "automount") == 0) + flags |= GSM_INHIBITOR_FLAG_AUTOMOUNT; + else + g_print ("Ignoring inhibit argument: %s\n", args[i]); + } + + g_strfreev (args); + + return flags; +} + +static gboolean inhibit (const gchar *app_id, + const gchar *reason, + GsmInhibitorFlag flags) +{ + GDBusConnection *bus; + GVariant *ret; + GError *error = NULL; + + bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); + + if (bus == NULL) + { + g_warning ("Failed to connect to session bus: %s", error->message); + g_error_free (error); + return FALSE; + } + + ret = g_dbus_connection_call_sync (bus, + "org.gnome.SessionManager", + "/org/gnome/SessionManager", + "org.gnome.SessionManager", + "Inhibit", + g_variant_new ("(susu)", + app_id, 0, reason, flags), + G_VARIANT_TYPE ("(u)"), + 0, + G_MAXINT, + NULL, + &error); + + if (ret == NULL) + { + g_warning ("Failed to call Inhibit: %s\n", error->message); + g_error_free (error); + return FALSE; + } + + g_variant_unref (ret); + + return TRUE; +} + +static void usage (void) +{ + g_print (_("%s [OPTION…] COMMAND\n" + "\n" + "Execute COMMAND while inhibiting some session functionality.\n" + "\n" + " -h, --help Show this help\n" + " --version Show program version\n" + " --app-id ID The application id to use\n" + " when inhibiting (optional)\n" + " --reason REASON The reason for inhibiting (optional)\n" + " --inhibit ARG Things to inhibit, colon-separated list of:\n" + " logout, switch-user, suspend, idle, automount\n" + " --inhibit-only Do not launch COMMAND and wait forever instead\n" + " -l, --list List the existing inhibitions, and exit\n" + "\n" + "If no --inhibit option is specified, idle is assumed.\n"), + g_get_prgname ()); +} + +static void version (void) +{ + g_print ("%s %s\n", g_get_prgname (), PACKAGE_VERSION); +} + +static GVariant * +get_inhibitor_prop (GDBusProxy *inhibitor, + const char *method_name) +{ + g_autoptr(GVariant) variant = NULL; + g_autoptr(GError) error = NULL; + + variant = g_dbus_proxy_call_sync (inhibitor, + method_name, + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + + if (variant == NULL) { + g_debug ("Failed to get property via '%s': %s", + method_name, error->message); + return NULL; + } + + return g_variant_get_child_value (variant, 0); +} + +static void +string_append_comma (GString *s, + const char *value) +{ + if (s->len != 0) + g_string_append (s, ", "); + g_string_append (s, value); +} + +static char * +flags_to_str (guint32 flags) +{ + GString *s; + + s = g_string_new (NULL); + if (flags & GSM_INHIBITOR_FLAG_LOGOUT) + string_append_comma (s, "logout"); + if (flags & GSM_INHIBITOR_FLAG_SWITCH_USER) + string_append_comma (s, "switch-user"); + if (flags & GSM_INHIBITOR_FLAG_SUSPEND) + string_append_comma (s, "suspend"); + if (flags & GSM_INHIBITOR_FLAG_IDLE) + string_append_comma (s, "idle"); + if (flags & GSM_INHIBITOR_FLAG_AUTOMOUNT) + string_append_comma (s, "automount"); + + return g_string_free (s, FALSE); +} + +static void list (void) +{ + g_autoptr(GDBusConnection) bus = NULL; + g_autoptr(GVariant) ret = NULL; + g_autoptr(GVariant) array = NULL; + g_autoptr(GError) error = NULL; + guint num_children; + g_autoptr(GVariantIter) iter = NULL; + const char *inhibitor_path; + + bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); + + if (bus == NULL) + { + g_warning ("Failed to connect to session bus: %s", error->message); + g_error_free (error); + return; + } + + ret = g_dbus_connection_call_sync (bus, + "org.gnome.SessionManager", + "/org/gnome/SessionManager", + "org.gnome.SessionManager", + "GetInhibitors", + NULL, + NULL, + 0, + G_MAXINT, + NULL, + &error); + + if (ret == NULL) + { + g_warning ("Failed to call GetInhibitors: %s\n", error->message); + g_error_free (error); + return; + } + + array = g_variant_get_child_value (ret, 0); + + num_children = g_variant_n_children (array); + if (num_children == 0) + { + g_print ("No inhibitors\n"); + return; + } + + g_variant_get (array, "ao", &iter); + while (g_variant_iter_loop (iter, "&o", &inhibitor_path)) + { + g_autoptr(GDBusProxy) inhibitor = NULL; + g_autoptr(GVariant) app_id = NULL; + g_autoptr(GVariant) reason = NULL; + g_autoptr(GVariant) flags = NULL; + g_autofree char *flags_str = NULL; + + inhibitor = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + "org.gnome.SessionManager", + inhibitor_path, + "org.gnome.SessionManager.Inhibitor", + NULL, + &error); + + /* Skip inhibitors that might have disappeared */ + if (!inhibitor) + { + g_debug ("Failed to get proxy for %s: %s", + inhibitor_path, + error->message); + continue; + } + + app_id = get_inhibitor_prop (inhibitor, "GetAppId"); + reason = get_inhibitor_prop (inhibitor, "GetReason"); + flags = get_inhibitor_prop (inhibitor, "GetFlags"); + if (!app_id || !reason || !flags) + continue; + flags_str = flags_to_str (g_variant_get_uint32 (flags)); + + g_print ("%s: %s (%s)\n", + g_variant_get_string (app_id, NULL), + g_variant_get_string (reason, NULL), + flags_str); + } +} + +static void +wait_for_child_app (char **argv) +{ + pid_t pid; + int status; + + pid = fork (); + if (pid < 0) + { + g_print ("fork failed\n"); + exit (1); + } + + if (pid == 0) + { + execvp (*argv, argv); + g_print (_("Failed to execute %s\n"), *argv); + exit (1); + } + + do + { + if (waitpid (pid, &status, 0) == -1) + { + g_print ("waitpid failed\n"); + exit (1); + } + + if (WIFEXITED (status)) + exit (WEXITSTATUS (status)); + + } while (!WIFEXITED (status) && ! WIFSIGNALED(status)); +} + +static void +wait_forever (void) +{ + g_print ("Inhibiting until Ctrl+C is pressed...\n"); + while (1) + sleep (UINT32_MAX); +} + +int main (int argc, char *argv[]) +{ + gchar *prgname; + GsmInhibitorFlag inhibit_flags = 0; + gboolean show_help = FALSE; + gboolean show_version = FALSE; + gboolean show_list = FALSE; + gboolean no_launch = FALSE; + gint i; + const gchar *app_id = "unknown"; + const gchar *reason = "not specified"; + + prgname = g_path_get_basename (argv[0]); + g_set_prgname (prgname); + g_free (prgname); + + setlocale (LC_ALL, ""); + bindtextdomain (GETTEXT_PACKAGE, LOCALE_DIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + for (i = 1; i < argc; i++) + { + if (strcmp (argv[i], "--help") == 0 || + strcmp (argv[i], "-h") == 0) + show_help = TRUE; + if (strcmp (argv[i], "--list") == 0 || + strcmp (argv[i], "-l") == 0) + show_list = TRUE; + else if (strcmp (argv[i], "--version") == 0) + show_version = TRUE; + else if (strcmp (argv[i], "--inhibit-only") == 0) + no_launch = TRUE; + else if (strcmp (argv[i], "--app-id") == 0) + { + i++; + if (i == argc) + { + g_print (_("%s requires an argument\n"), argv[i]); + exit (1); + } + app_id = argv[i]; + } + else if (strcmp (argv[i], "--reason") == 0) + { + i++; + if (i == argc) + { + g_print (_("%s requires an argument\n"), argv[i]); + exit (1); + } + reason = argv[i]; + } + else if (strcmp (argv[i], "--inhibit") == 0) + { + i++; + if (i == argc) + { + g_print (_("%s requires an argument\n"), argv[i]); + exit (1); + } + inhibit_flags |= parse_flags (argv[i]); + } + else + break; + } + + if (show_version) + { + version (); + return 0; + } + + if (show_list) + { + list (); + return 0; + } + + if (show_help || (i == argc && !no_launch)) + { + usage (); + return 0; + } + + if (inhibit_flags == 0) + inhibit_flags = GSM_INHIBITOR_FLAG_IDLE; + + if (inhibit (app_id, reason, inhibit_flags) == FALSE) + return 1; + + if (!no_launch) + wait_for_child_app (argv + i); + else + wait_forever (); + + return 0; +} diff --git a/tools/gnome-session-quit.c b/tools/gnome-session-quit.c new file mode 100644 index 0000000..4dddbfe --- /dev/null +++ b/tools/gnome-session-quit.c @@ -0,0 +1,216 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * save-session.c - Small program to talk to session manager. + + Copyright (C) 1998 Tom Tromey + Copyright (C) 2008 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, 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 <locale.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <gio/gio.h> + +#define GSM_SERVICE_DBUS "org.gnome.SessionManager" +#define GSM_PATH_DBUS "/org/gnome/SessionManager" +#define GSM_INTERFACE_DBUS "org.gnome.SessionManager" + +enum { + GSM_LOGOUT_MODE_NORMAL = 0, + GSM_LOGOUT_MODE_NO_CONFIRMATION, + GSM_LOGOUT_MODE_FORCE +}; + +static gboolean opt_logout = FALSE; +static gboolean opt_power_off = FALSE; +static gboolean opt_reboot = FALSE; +static gboolean opt_no_prompt = FALSE; +static gboolean opt_force = FALSE; + +static GOptionEntry options[] = { + {"logout", '\0', 0, G_OPTION_ARG_NONE, &opt_logout, N_("Log out"), NULL}, + {"power-off", '\0', 0, G_OPTION_ARG_NONE, &opt_power_off, N_("Power off"), NULL}, + {"reboot", '\0', 0, G_OPTION_ARG_NONE, &opt_reboot, N_("Reboot"), NULL}, + {"force", '\0', 0, G_OPTION_ARG_NONE, &opt_force, N_("Ignoring any existing inhibitors"), NULL}, + {"no-prompt", '\0', 0, G_OPTION_ARG_NONE, &opt_no_prompt, N_("Don’t prompt for user confirmation"), NULL}, + {NULL} +}; + +static void +display_error (const char *message) +{ + g_printerr ("%s\n", message); +} + +static GDBusConnection * +get_session_bus (void) +{ + GDBusConnection *bus; + GError *error = NULL; + + bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); + + if (bus == NULL) { + g_warning ("Couldn't connect to session bus: %s", error->message); + g_error_free (error); + } + + return bus; +} + +static GDBusProxy * +get_sm_proxy (void) +{ + GDBusConnection *connection; + GDBusProxy *sm_proxy; + + connection = get_session_bus (); + if (connection == NULL) { + display_error (_("Could not connect to the session manager")); + return NULL; + } + + sm_proxy = g_dbus_proxy_new_sync (connection, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + GSM_SERVICE_DBUS, + GSM_PATH_DBUS, + GSM_INTERFACE_DBUS, + NULL, NULL); + g_object_unref (connection); + + if (sm_proxy == NULL) { + display_error (_("Could not connect to the session manager")); + return NULL; + } + + return sm_proxy; +} + +static void +do_logout (unsigned int mode) +{ + GDBusProxy *sm_proxy; + GVariant *reply; + GError *error; + + sm_proxy = get_sm_proxy (); + if (sm_proxy == NULL) { + return; + } + + error = NULL; + reply = g_dbus_proxy_call_sync (sm_proxy, + "Logout", + g_variant_new ("(u)", mode), + G_DBUS_CALL_FLAGS_NONE, + -1, NULL, &error); + + if (error != NULL) { + g_warning ("Failed to call logout: %s", + error->message); + g_error_free (error); + } else { + g_variant_unref (reply); + } + g_clear_object (&sm_proxy); +} + +static void +do_power_off (const char *action) +{ + GDBusProxy *sm_proxy; + GVariant *reply; + GError *error; + + sm_proxy = get_sm_proxy (); + if (sm_proxy == NULL) { + return; + } + + error = NULL; + reply = g_dbus_proxy_call_sync (sm_proxy, + action, + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, NULL, &error); + + if (error != NULL) { + g_warning ("Failed to call %s: %s", + action, error->message); + g_error_free (error); + } else { + g_variant_unref (reply); + } + g_clear_object (&sm_proxy); +} + +int +main (int argc, char *argv[]) +{ + GError *error; + int conflicting_options; + GOptionContext *ctx; + + /* Initialize the i18n stuff */ + setlocale (LC_ALL, ""); + bindtextdomain (GETTEXT_PACKAGE, LOCALE_DIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + error = NULL; + ctx = g_option_context_new (""); + g_option_context_add_main_entries (ctx, options, GETTEXT_PACKAGE); + if (! g_option_context_parse (ctx, &argc, &argv, &error)) { + g_warning ("Unable to start: %s", error->message); + g_error_free (error); + exit (1); + } + g_option_context_free (ctx); + + conflicting_options = 0; + if (opt_logout) + conflicting_options++; + if (opt_power_off) + conflicting_options++; + if (opt_reboot) + conflicting_options++; + if (conflicting_options > 1) + display_error (_("Program called with conflicting options")); + + if (opt_power_off) { + do_power_off ("Shutdown"); + } else if (opt_reboot) { + do_power_off ("Reboot"); + } else { + /* default to logout */ + + if (opt_force) + do_logout (GSM_LOGOUT_MODE_FORCE); + else if (opt_no_prompt) + do_logout (GSM_LOGOUT_MODE_NO_CONFIRMATION); + else + do_logout (GSM_LOGOUT_MODE_NORMAL); + } + + return 0; +} diff --git a/tools/gnome-session-selector.c b/tools/gnome-session-selector.c new file mode 100644 index 0000000..71892c4 --- /dev/null +++ b/tools/gnome-session-selector.c @@ -0,0 +1,698 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright 2010, 2013 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/>. + * + * Written by: Matthias Clasen <mclasen@redhat.com> + */ + +#include "config.h" + +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#include <glib.h> +#include <gtk/gtk.h> +#include <gio/gio.h> + +#include <glib/gi18n.h> +#include <glib/gstdio.h> + +#define GSM_MANAGER_SCHEMA "org.gnome.SessionManager" +#define KEY_AUTOSAVE_ONE_SHOT "auto-save-session-one-shot" + +static GtkBuilder *builder; +static GtkWidget *session_list; +static GtkListStore *store; +static GtkTreeModelSort *sort_model; + +static void select_session (const char *name); + +static char * +get_session_path (const char *name) +{ + return g_build_filename (g_get_user_config_dir (), "gnome-session", name, NULL); +} + +static char * +find_new_session_name (void) +{ + char *name; + char *path; + int i; + + for (i = 1; i < 20; i++) { + name = g_strdup_printf (_("Session %d"), i); + path = get_session_path (name); + if (!g_file_test (path, G_FILE_TEST_EXISTS)) { + g_free (path); + return name; + } + g_free (path); + g_free (name); + } + + return NULL; +} + +static gboolean +is_valid_session_name (const char *name) +{ + GtkTreeIter iter; + char *n; + const char *info_text; + char *warning_text; + gboolean user_tried_dot; + gboolean user_tried_slash; + GtkWidget *info_bar; + GtkWidget *label; + + if (name[0] == 0) { + return FALSE; + } + + if (name[0] == '.') { + user_tried_dot = TRUE; + } else { + user_tried_dot = FALSE; + } + + if (strchr (name, '/') != NULL) { + user_tried_slash = TRUE; + } else { + user_tried_slash = FALSE; + } + + info_text = _("Please select a custom session to run"); + warning_text = NULL; + if (user_tried_dot && user_tried_slash) { + warning_text = g_strdup_printf ("%s\n<small><b>Note:</b> <i>%s</i></small>", + info_text, + _("Session names are not allowed to start with “.” or contain “/” characters")); + } else if (user_tried_dot) { + warning_text = g_strdup_printf ("%s\n<small><b>Note:</b> <i>%s</i></small>", + info_text, + _("Session names are not allowed to start with “.”")); + } else if (user_tried_slash) { + warning_text = g_strdup_printf ("%s\n<small><b>Note:</b> <i>%s</i></small>", + info_text, + _("Session names are not allowed to contain “/” characters")); + } + + gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter); + do { + gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, 0, &n, -1); + if (strcmp (n, name) == 0) { + char *message; + message = g_strdup_printf (_("A session named “%s” already exists"), name); + warning_text = g_strdup_printf ("%s\n<small><b>Note:</b> <i>%s</i></small>", info_text, message); + g_free (message); + g_free (n); + break; + } + g_free (n); + } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter)); + + info_bar = (GtkWidget *) gtk_builder_get_object (builder, "info-bar"); + label = (GtkWidget*) gtk_builder_get_object (builder, "info-label"); + + if (warning_text != NULL) { + gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_WARNING); + gtk_label_set_markup (GTK_LABEL (label), warning_text); + g_free (warning_text); + return FALSE; + } + + gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_OTHER); + gtk_label_set_markup (GTK_LABEL (label), info_text); + + return TRUE; +} + +static void +populate_session_list (GtkWidget *session_list) +{ + GtkTreeIter iter; + char *path; + const char *name; + GDir *dir; + GError *error; + char *saved_session; + char *default_session; + char *default_name; + char last_session[PATH_MAX] = ""; + + saved_session = get_session_path ("saved-session"); + + if (!g_file_test (saved_session, G_FILE_TEST_IS_SYMLINK)) { + default_name = find_new_session_name (); + default_session = get_session_path (default_name); + rename (saved_session, default_session); + if (symlink (default_name, saved_session) < 0) + g_warning ("Failed to convert saved-session to symlink"); + g_free (default_name); + g_free (default_session); + } + + path = g_build_filename (g_get_user_config_dir (), "gnome-session", NULL); + error = NULL; + dir = g_dir_open (path, 0, &error); + if (dir == NULL) { + g_warning ("Failed to open %s: %s", path, error->message); + g_error_free (error); + goto out; + } + + default_name = NULL; + if (readlink (saved_session, last_session, PATH_MAX - 1) > 0) { + default_name = g_path_get_basename (last_session); + } + + while ((name = g_dir_read_name (dir)) != NULL) { + if (strcmp (name, "saved-session") == 0) + continue; + + gtk_list_store_insert_with_values (store, &iter, 100, 0, name, -1); + + if (g_strcmp0 (default_name, name) == 0) { + GtkTreeSelection *selection; + GtkTreeIter child_iter; + + gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &child_iter, &iter); + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (session_list)); + gtk_tree_selection_select_iter (selection, &child_iter); + } + } + + g_free (default_name); + g_dir_close (dir); + + out: + g_free (saved_session); + g_free (path); +} + +static char * +get_selected_session (void) +{ + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter; + gchar *name; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (session_list)); + if (gtk_tree_selection_get_selected (selection, &model, &iter)) { + gtk_tree_model_get (model, &iter, 0, &name, -1); + return name; + } + + return NULL; +} + +static void +remove_session (const char *name) +{ + char *path1, *path2; + char *n, *path; + const char *d; + GDir *dir; + GError *error; + + path1 = get_session_path ("saved-session"); + path2 = get_session_path (name); + + error = NULL; + n = g_file_read_link (path1, &error); + if (n == NULL) { + g_warning ("Failed to read link: %s", error->message); + g_error_free (error); + } + else if (strcmp (n, name) == 0) { + unlink (path1); + } + g_free (n); + + dir = g_dir_open (path2, 0, NULL); + while ((d = g_dir_read_name (dir)) != NULL) { + path = g_build_filename (path2, d, NULL); + unlink (path); + g_free (path); + } + g_dir_close (dir); + + remove (path2); + + g_free (path1); + g_free (path2); +} + +static void +on_remove_session_clicked (GtkButton *button, + gpointer data) +{ + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter; + char *name; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (session_list)); + if (gtk_tree_selection_get_selected (selection, &model, &iter)) { + GtkTreeIter child_iter; + gtk_tree_model_get (model, &iter, 0, &name, -1); + remove_session (name); + g_free (name); + + gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (model), &child_iter, &iter); + gtk_list_store_remove (GTK_LIST_STORE (store), &child_iter); + + if (!gtk_tree_selection_get_selected (selection, NULL, NULL)) { + gtk_tree_model_get_iter_first (model, &iter); + gtk_tree_model_get (model, &iter, 0, &name, -1); + select_session (name); + g_free (name); + } + } +} + +static void +begin_rename (void) +{ + GtkTreePath *path; + GtkTreeViewColumn *column; + GList *cells; + + gtk_widget_grab_focus (session_list); + + gtk_tree_view_get_cursor (GTK_TREE_VIEW (session_list), + &path, &column); + + cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column)); + + if (cells != NULL) { + GtkCellRenderer *cell; + + cell = (GtkCellRenderer *) cells->data; + g_list_free (cells); + + g_object_set (cell, "editable", TRUE, NULL); + gtk_tree_view_set_cursor_on_cell (GTK_TREE_VIEW (session_list), path, + column, cell, TRUE); + } + gtk_tree_path_free (path); +} + +static void +on_rename_session_clicked (GtkButton *button, + gpointer data) +{ + begin_rename (); +} + +static void +on_continue_clicked (GtkButton *button, + gpointer data) +{ + char *name; + + name = get_selected_session (); + g_free (name); + + gtk_main_quit (); +} + +static void +create_session (const char *name) +{ + char *path; + GtkTreeIter iter; + + path = get_session_path (name); + + if (mkdir (path, 0755) < 0) { + g_warning ("Failed to create directory %s", path); + } + else { + char *marker; + + gtk_list_store_insert_with_values (store, &iter, 100, 0, name, -1); + + marker = g_build_filename (path, ".new-session", NULL); + creat (marker, 0600); + g_free (marker); + } + + g_free (path); +} + +static gboolean +rename_session (const char *old_name, + const char *new_name) +{ + char *old_path, *new_path; + int result; + + if (g_strcmp0 (old_name, new_name) == 0) { + return TRUE; + } + + if (!is_valid_session_name (new_name)) { + return FALSE; + } + + old_path = get_session_path (old_name); + new_path = get_session_path (new_name); + + result = g_rename (old_path, new_path); + + if (result < 0) { + g_warning ("Failed to rename session from '%s' to '%s': %m", old_name, new_name); + } + + g_free (old_path); + g_free (new_path); + + return result == 0; +} + +static gboolean +make_session_current (const char *name) +{ + char *path1; + gboolean ret = TRUE; + + path1 = g_build_filename (g_get_user_config_dir (), "gnome-session", "saved-session", NULL); + + unlink (path1); + if (symlink (name, path1) < 0) { + g_warning ("Failed to make session '%s' current", name); + ret = FALSE; + } + + g_free (path1); + + return ret; +} + +static gboolean +create_and_select_session (const char *name) +{ + gchar *path; + + if (name[0] == 0 || name[0] == '.' || strchr (name, '/')) { + g_warning ("Invalid session name"); + return FALSE; + } + + path = g_build_filename (g_get_user_config_dir (), "gnome-session", name, NULL); + if (!g_file_test (path, G_FILE_TEST_IS_DIR)) { + if (mkdir (path, 0755) < 0) { + g_warning ("Failed to create directory %s", path); + g_free (path); + return FALSE; + } + } + + g_free (path); + + return make_session_current (name); +} + +static void +select_session (const char *name) +{ + GtkTreeIter iter; + char *n; + + make_session_current (name); + + /* now select it in the list */ + gtk_tree_model_get_iter_first (GTK_TREE_MODEL (sort_model), &iter); + do { + gtk_tree_model_get (GTK_TREE_MODEL (sort_model), &iter, 0, &n, -1); + if (strcmp (n, name) == 0) { + GtkTreePath *path; + + path = gtk_tree_model_get_path (GTK_TREE_MODEL (sort_model), &iter); + gtk_tree_view_set_cursor (GTK_TREE_VIEW (session_list), path, NULL, FALSE); + gtk_tree_path_free (path); + g_free (n); + break; + } + g_free (n); + } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (sort_model), &iter)); +} + +static void +on_new_session_clicked (GtkButton *button, + gpointer data) +{ + gchar *name; + + name = find_new_session_name (); + create_session (name); + select_session (name); + + begin_rename (); +} + +static void +on_selection_changed (GtkTreeSelection *selection, + gpointer data) +{ + char *name; + + name = get_selected_session (); + + if (name == NULL) { + return; + } + + make_session_current (name); + + g_free (name); +} + +static void +update_remove_button (void) +{ + GtkWidget *button; + + button = (GtkWidget *)gtk_builder_get_object (builder, "remove-session"); + if (gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), NULL) > 1) { + gtk_widget_set_sensitive (button, TRUE); + } else { + gtk_widget_set_sensitive (button, FALSE); + } +} + +static void +on_row_edited (GtkCellRendererText *cell, + const char *path_string, + const char *new_name, + gpointer data) +{ + GtkTreePath *path; + GtkTreeIter sort_iter, items_iter; + char *old_name; + gboolean was_renamed; + + path = gtk_tree_path_new_from_string (path_string); + gtk_tree_model_get_iter (GTK_TREE_MODEL (sort_model), &sort_iter, path); + + gtk_tree_model_get (GTK_TREE_MODEL (sort_model), &sort_iter, 0, &old_name, -1); + + was_renamed = rename_session (old_name, new_name); + + if (was_renamed) { + gtk_tree_model_sort_convert_iter_to_child_iter (sort_model, &items_iter, &sort_iter); + + gtk_list_store_set (store, &items_iter, 0, g_strdup (new_name), -1); + g_free (old_name); + make_session_current (new_name); + } else { + begin_rename (); + } + + gtk_tree_path_free (path); + + g_object_set (cell, "editable", FALSE, NULL); +} + +static void +on_row_deleted (GtkTreeModel *model, + GtkTreePath *path, + gpointer data) +{ + update_remove_button (); +} + +static void +on_row_inserted (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + update_remove_button (); +} + +static void +on_row_activated (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + gpointer data) +{ + char *name; + + name = get_selected_session (); + g_free (name); + + gtk_main_quit (); +} + +static void +auto_save_next_session (void) +{ + GSettings *settings; + + settings = g_settings_new (GSM_MANAGER_SCHEMA); + g_settings_set_boolean (settings, KEY_AUTOSAVE_ONE_SHOT, TRUE); + g_object_unref (settings); +} + +static void +auto_save_next_session_if_needed (void) +{ + char *marker; + + marker = g_build_filename (g_get_user_config_dir (), + "gnome-session", "saved-session", + ".new-session", NULL); + + if (g_file_test (marker, G_FILE_TEST_EXISTS)) { + auto_save_next_session (); + unlink (marker); + } + g_free (marker); +} + +static int +compare_sessions (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer data) +{ + char *name_a, *name_b; + int result; + + gtk_tree_model_get (model, a, 0, &name_a, -1); + gtk_tree_model_get (model, b, 0, &name_b, -1); + + result = g_utf8_collate (name_a, name_b); + + g_free (name_a); + g_free (name_b); + + return result; +} + +static void +on_map (GtkWidget *widget, + gpointer data) +{ + gdk_window_focus (gtk_widget_get_window (widget), GDK_CURRENT_TIME); +} + +int +main (int argc, char *argv[]) +{ + GtkWidget *window; + GtkWidget *widget; + GtkCellRenderer *cell; + GtkTreeViewColumn *column; + GtkTreeSelection *selection; + GError *error; + + if (getenv ("SESSION_MANAGER") != NULL) + return 1; + + gtk_init (&argc, &argv); + if (argc > 1) { + g_print ("create and select session\n"); + if (!create_and_select_session (argv[1])) + return 1; + else + return 0; + } + + builder = gtk_builder_new (); + gtk_builder_set_translation_domain (builder, GETTEXT_PACKAGE); + + error = NULL; + if (!gtk_builder_add_from_file (builder, GTKBUILDER_DIR "/" "session-selector.ui", &error)) { + g_warning ("Could not load file 'session-selector.ui': %s", error->message); + exit (1); + } + + window = (GtkWidget *) gtk_builder_get_object (builder, "main-window"); + + store = (GtkListStore *) gtk_builder_get_object (builder, "session-store"); + sort_model = (GtkTreeModelSort *) gtk_builder_get_object (builder, "sort-model"); + + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sort_model), + 0, compare_sessions, NULL, NULL); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model), + 0, GTK_SORT_ASCENDING); + g_signal_connect (store, "row-deleted", G_CALLBACK (on_row_deleted), NULL); + g_signal_connect (store, "row-inserted", G_CALLBACK (on_row_inserted), NULL); + session_list = (GtkWidget *) gtk_builder_get_object (builder, "session-list"); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (session_list)); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); + + populate_session_list (session_list); + + cell = gtk_cell_renderer_text_new (); + g_signal_connect (cell, "edited", G_CALLBACK (on_row_edited), NULL); + + column = gtk_tree_view_column_new_with_attributes ("", cell, "text", 0, NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (session_list), GTK_TREE_VIEW_COLUMN (column)); + + g_signal_connect (session_list, "row-activated", G_CALLBACK (on_row_activated), NULL); + + g_signal_connect (selection, "changed", + G_CALLBACK (on_selection_changed), NULL); + + widget = (GtkWidget *) gtk_builder_get_object (builder, "new-session"); + g_signal_connect (widget, "clicked", G_CALLBACK (on_new_session_clicked), NULL); + widget = (GtkWidget *) gtk_builder_get_object (builder, "remove-session"); + g_signal_connect (widget, "clicked", G_CALLBACK (on_remove_session_clicked), NULL); + widget = (GtkWidget *) gtk_builder_get_object (builder, "rename-session"); + g_signal_connect (widget, "clicked", G_CALLBACK (on_rename_session_clicked), NULL); + widget = (GtkWidget *) gtk_builder_get_object (builder, "continue-button"); + g_signal_connect (widget, "clicked", G_CALLBACK (on_continue_clicked), NULL); + + g_signal_connect (window, "map", G_CALLBACK (on_map), NULL); + gtk_widget_show (window); + + gtk_main (); + + auto_save_next_session_if_needed (); + + return 0; +} diff --git a/tools/meson.build b/tools/meson.build new file mode 100644 index 0000000..b21519d --- /dev/null +++ b/tools/meson.build @@ -0,0 +1,71 @@ +deps = session_deps + [ + sm_dep, + ice_dep +] + +cflags = ['-DLOCALE_DIR="@0@"'.format(session_localedir)] + +programs = [ + # name, deps, cflags, install_dir + ['gnome-session-quit', deps, cflags, session_bindir], + ['gnome-session-inhibit', session_deps, cflags, session_bindir] +] + +if enable_systemd_session + programs += [['gnome-session-ctl', session_bin_deps, cflags, session_libexecdir]] +endif + +if enable_session_selector + deps = [ + glib_dep, + gtk_dep + ] + + install_data( + 'gnome-session-custom-session', + install_dir: session_bindir + ) + + cflags += '-DGTKBUILDER_DIR="@0@"'.format(session_pkgdatadir) + + programs += [['gnome-session-selector', deps, cflags, session_bindir]] +endif + +deps = [ + gtk_dep, + x11_dep, + dependency('egl'), + dependency('glesv2') +] + +cflags = '-DPKGDATADIR="@0@"'.format(session_pkgdatadir) + +programs += [['gnome-session-check-accelerated-gles-helper', deps, cflags, session_libexecdir]] + +deps = [ + glib_dep, + x11_dep, + dependency('gl'), + dependency('epoxy'), + dependency('xcomposite') +] + +programs += [['gnome-session-check-accelerated-gl-helper', deps, cflags, session_libexecdir]] + +deps += [gtk_dep] + +cflags = '-DLIBEXECDIR="@0@"'.format(session_libexecdir) + +programs += [['gnome-session-check-accelerated', deps, cflags, session_libexecdir]] + +foreach program: programs + executable( + program[0], + program[0] + '.c', + include_directories: top_inc, + dependencies: program[1], + c_args: program[2], + install: true, + install_dir: program[3] + ) +endforeach |