Permalink
Browse files

Apply libsm patch `sm1-2012-04-30.diff`

Only minor changes made to src/Makefile.am and wscript files

See: https://sourceforge.net/tracker/?func=detail&aid=3361963&group_id=153444&atid=787793
  • Loading branch information...
1 parent f0f3fc8 commit 0f07b31aa866f92dcbaf29659ead7beab60a1dde @codebrainz codebrainz committed Jul 11, 2012
Showing with 1,119 additions and 52 deletions.
  1. +255 −0 ChangeLog.sm
  2. +20 −0 configure.ac
  3. +1 −1 doc/Doxyfile.in
  4. +2 −0 src/Makefile.am
  5. +7 −17 src/callbacks.c
  6. +33 −5 src/document.c
  7. +4 −0 src/document.h
  8. +31 −7 src/keyfile.c
  9. +5 −2 src/keyfile.h
  10. +54 −8 src/main.c
  11. +15 −0 src/main.h
  12. +1 −1 src/makefile.win32
  13. +1 −1 src/prefs.c
  14. +8 −7 src/project.c
  15. +1 −1 src/project.h
  16. +588 −0 src/sm.c
  17. +32 −0 src/sm.h
  18. +46 −0 src/utils.c
  19. +2 −0 src/utils.h
  20. +13 −2 wscript
View
255 ChangeLog.sm
@@ -0,0 +1,255 @@
+2012-03-06:
+
+ * src/sm.c:
+ On normal (non-sm) exit, only delete the session file if
+ running under Gnome, to preserve permanent sessions.
+ * src/main.c:
+ Updated for the new load_startup_files().
+
+2012-01-06 Dimitar Zhekov <dimitar(dot)zhekov(at)gmail(dot)com>
+
+ * src/sm.c:
+ Don't interact if running under xfce sm - until they fix it.
+ Don't save yourself if main window is not realized, that's some
+ startup message loop and the configuration is not initialized.
+
+2011-07-10 Dimitar Zhekov <dimitar(dot)zhekov(at)gmail(dot)com>
+
+ * src/callbacks.c:
+ Include sm.h for sm_discard().
+ * src/sm.c:
+ Include glib/gstdio.h for g_unlink().
+ Don't disable the main window while exiting. Nobody does, and if
+ anything, the Window Manager should do it.
+
+2011-04-24 Dimitar Zhekov <dimitar(dot)zhekov(at)gmail(dot)com>
+
+ * src/keyfile.c, src/keyfile.h, src/sm.c:
+ Updated to free the string returned from utils_build_path().
+
+2011-04-09 Dimitar Zhekov <dimitar(dot)zhekov(at)gmail(dot)com>
+
+ * src/keyfile.c, src/keyfile.h, src/sm.c:
+ Updated to use utils_build_path().
+ * src/sm.c:
+ Use g_path_is_absolute() instead of checking for G_DIR_SEPARATOR.
+ * src/document.c:
+ Don't check for external document changes while exiting a session
+ to prevent reload file prompts.
+
+2010-10-16 Dimitar Zhekov <dimitar(dot)zhekov(at)gmail(dot)com>
+
+ * src/main.c:
+ Replaced new_session with load_default_session. May also be used
+ for "Open at the same workspace", to inhibit loading the default
+ session if another primary instance is already running.
+
+2010-10-09 Dimitar Zhekov <dimitar(dot)zhekov(at)gmail(dot)com>
+
+ * src/main.c, src/main.h:
+ Reverted cl_options.socket_filename, since "Open files in Geany at
+ the same workspace" was reverted.
+
+ * src/sm.c:
+ Some refactoring and documentation fixes.
+
+2010-10-08 Dimitar Zhekov <dimitar(dot)zhekov(at)gmail(dot)com>
+
+ * src/sm.c:
+ On shutdown cancelled: don't call save done if already done, signal
+ save yourself failed.
+ If interaction is required but refused, at least save configuration
+ and signal success.
+
+2010-09-26 Dimitar Zhekov <dimitar(dot)zhekov(at)gmail(dot)com>
+
+ * src/stash.c:
+ Reverted 5121's always write the default value for [missing]
+ hidden preference. Not significant enough for a workaround.
+
+2010-09-25 Dimitar Zhekov <dimitar(dot)zhekov(at)gmail(dot)com>
+
+ * src/main.c, src/main.h
+ Replaced cl_options.socket_filename with socket_info.file_name to
+ support "Open files in Geany at the same workspace".
+
+2010-07-05 Dimitar Zhekov <dimitar(dot)zhekov(at)gmail(dot)com>
+
+ * src/sm.c:
+ Try to expand relative executable name. For GSM.
+
+ * src/sm.c, src/callbacks.c:
+ Remove the session file on normal (non-sm) quit. For GSM.
+
+ * src/keyfile.c:
+ Don't check the session file with G_FILE_TEST_IS_SYMLINK.
+
+ * src/sm.c, src/sm.h, src/main.c, src/utils.c, src/utils.h:
+ Replaced type * identifier with type *identifier.
+
+2010-06-20 Dimitar Zhekov <dimitar(dot)zhekov(at)gmail(dot)com>
+
+ * src/main.c:
+ Simply g_strdup(alternate_config) to preserve it, as suggested by
+ Eugene Arshinov.
+
+ * src/utils.c, src/sm.c:
+ Store the filenames directly instead of using tm_get_real_path().
+
+ * src/utils.c:
+ Don't store zero integer arguments.
+
+2010-05-20 Dimitar Zhekov <hamster(at)mbox(dot)contact(dot)bg>
+
+ * src/sm.c:
+ Don't save configuration on interactive SmSaveGlobal.
+ Don't ask to cancel shutdown if not a shutdown.
+ Don't set the userid property if the user name can't be determined.
+
+2010-05-19 Dimitar Zhekov <hamster(at)mbox(dot)contact(dot)bg>
+
+ * src/main.c:
+ Altered setup_config_dir() to avoid discarding alternate_config.
+
+ * src/document.c, src/keyfile.c:
+ Fixed a few bugs.
+
+ * src/sm.c:
+ Moved the interactive configuration_save() after SmcInteractDone(),
+ so that any Untitled files are remembered with their new names.
+
+2010-05-18 Dimitar Zhekov <hamster(at)mbox(dot)contact(dot)bg>
+
+ * src/sm.c, src/main.h, src/callbacks.c:
+ Prevent user interaction with Geany between save yourself done and
+ save complete / die / cancel shutdown. See sm_prevent_interaction().
+
+ * src/keyfile.c, src/keyfile.h
+ configuration_load(): renamed suffix to libsm_client_id, fallback to
+ geany.conf if session file not found.
+ configuration_load(): renamed suffix to libsm_client_id, save file
+ list if libsm_client_id != NULL.
+
+2010-05-17 Dimitar Zhekov <hamster(at)mbox(dot)contact(dot)bg>
+
+ * src/sm.c, src/sm.h:
+ Modified all sm_ callbacks to handle the sm1 shutdown.
+
+ * src/document.c, src/document.h:
+ Extracted the prompt-and-save part of document_account_for_unsaved()
+ in a new document_prompt_for_unsaved() function to preserve the
+ "modified" file flags in the case of a cancelled shutdown.
+
+ * src/callbacks.c:
+ Reverted the changes not required for sm1.
+
+2010-05-16 Dimitar Zhekov <hamster(at)mbox(dot)contact(dot)bg>
+
+ * src/utils.c, src/utils.h:
+ Altered utils_option_entry_reverse_parse() to support only the types
+ used by optentries[] and to expand relative filenames to absolute.
+
+ * src/keyfile.c, src/keyfile.h:
+ Added configuration_name(suffix), added support for suffix in
+ configuration_load() and configuration_save(). Altered some other
+ functions to use configuration_name().
+
+2010-05-15 Dimitar Zhekov <hamster(at)mbox(dot)contact(dot)bg>
+
+ * src/main.c:
+ Changes to load_session_project_file(), load_settings() and
+ load_startup_files() to handle the x11 session startup variant.
+
+ * src/project.c, src/project.h:
+ Added load_session parameter project_load_file() and load_config().
+ X11 session startup passes FALSE to avoid loading project files.
+
+ * src/sm.c:
+ Combined sm_*_props() into sm_set_props(), invoked from sm_init().
+
+2010-05-14 Dimitar Zhekov <hamster(at)mbox(dot)contact(dot)bg>
+
+ * geany.glade, src/interface.c, src/keyfile.c, src/prefs.c,
+ src/prefs.h, src/project.c, src/project.h, srs/utils.c, src/utils.h,
+ src/main.c, src/main.h:
+ Removed the changes that will not be required for sm1: "load session
+ even if any files opened", "--new-instance implies --no-session",
+ "--project". Reverted load_startup_files().
+
+ * src/main.c:
+ Marked "--no-session" as non-persistent. The open files should be
+ restored as they were when Geany was stopped, not started.
+
+ * src/main.c:
+ Moved sm_init() before main_init() so that the session main window
+ attributes can be applied. Replaced gtk_set_client_id() with
+ gdk_set_sm_client_id().
+
+2010-02-23 Eugene Arshinov <earshinov(at)gmail(dot)com>
+
+ * src/main.c:
+ Change the behaviour of the setting controlled by "Preferences >
+ General > Misc > Use project-based session files" check button. Now
+ if you open a project via command-line, files from the default
+ session are not automatically appended to it.
+
+
+2010-02-22 Eugene Arshinov <earshinov(at)gmail(dot)com>
+
+ * src/main.c, src/main.h, src/socket.c:
+ Use a separate command-line option to specify a project to be opened.
+ * src/sm.c:
+ Remember opened project across restarts.
+ * src/socket.c:
+ Fix opening project in already running instance (via socket).
+
+
+2010-02-15 Eugene Arshinov <earshinov(at)gmail(dot)com>
+
+ * src/project.c, src/sm.c, src/utils.c, src/utils.h:
+ Use absolute paths to project files. Particularly, paths to recent
+ projects' files are now stored as absolute.
+ * src/main.c:
+ Load files from command line even if a project is being opened.
+ * geany.glade, src/interface.c, src/keyfile.c, src/main.c,
+ src/prefs.c, src/prefs.h:
+ Add a GUI preference to control whether the default session is
+ loaded if any files are opened via command-line (fix #2838686).
+ Location of the preference: General > Startup tab > Startup frame.
+
+
+2010-02-07 Eugene Arshinov <earshinov(at)gmail(dot)com>
+
+ * src/main.c:
+ Rewrite load_startup_files() function. Fix the bug with opening a
+ project while specifying -s command line option. E.g.,
+ `geany -s project.geany' now correctly loads the project instead of
+ showing a blank document and clearing the project silently.
+
+
+2010-01-24 Eugene Arshinov <earshinov(at)gmail(dot)com>
+
+ * doc/Doxyfile.in, src/Makefile.am, src/main.c, src/makefile.win32,
+ src/sm.c, src/sm.h, wscript:
+ Extract libSM-related code into separate sm.{c,h} files, make some
+ refactoring, and write code comments for Doxygen.
+ * src/keyfile.c, src/main.c:
+ Make --new-instance command line option imply --no-session.
+ * src/sm.c:
+ Handle --no-session command line option properly.
+ * src/main.c, src/main.h, src/sm.c, src/utils.c, src/utils.h:
+ Add "reverse parser" of GOptionEntry. Handle all command-line options.
+
+
+2009-12-08 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
+
+ * src/Makefile.am, configure.in, wscript:
+ Detect libSM X session management library (patch by Eugene Arshinov,
+ thanks).
+ * src/project.c, src/project.h, src/callbacks.c, src/document.c,
+ src/document.h, src/main.c, src/main.h:
+ Refactor quitting code into main_save() and main_finalize() (patch by
+ Eugene Arshinov, thanks).
+ * src/main.c:
+ Restart Geany and restore some state when logging in (patch by
+ Eugene Arshinov, thanks).
View
20 configure.ac
@@ -94,6 +94,26 @@ GEANY_CHECK_THE_FORCE dnl hehe
# i18n
GEANY_I18N
+# libSM for X session management
+SM_LIBS=""
+AC_ARG_ENABLE(libsm,
+ [ --enable-libsm enable X session management support [[]]],
+ [enable_libsm=$enableval], [enable_libsm=yes])
+AC_MSG_CHECKING([whether to use LibSM])
+if test x"$enable_libsm" = xyes; then
+ AC_MSG_RESULT(yes)
+ AC_CHECK_HEADERS(X11/SM/SMlib.h, [SM_LIBS="-lSM -lICE"], enable_libsm=no)
+ if test x"$enable_libsm" = xyes; then
+ AC_DEFINE(HAVE_LIBSM, 1, [Define to 1 if you have libSM installed])
+ else
+ AC_MSG_WARN([X session management will not be supported])
+ fi
+else
+ AC_MSG_RESULT(no)
+fi
+AC_SUBST(SM_LIBS)
+
+
GEANY_DATA_DIR=`eval echo ${datarootdir}/geany`
AC_SUBST([GEANY_DATA_DIR])
View
2 doc/Doxyfile.in
@@ -229,7 +229,7 @@ SEARCH_INCLUDES = NO
INCLUDE_PATH =
INCLUDE_FILE_PATTERNS =
# make G_GNUC_PRINTF a no-op unless doxygen would ignore functions with varargs
-PREDEFINED = "G_GNUC_PRINTF(x,y)=" HAVE_PLUGINS GEANY_FUNCTIONS_H
+PREDEFINED = "G_GNUC_PRINTF(x,y)=" HAVE_PLUGINS GEANY_FUNCTIONS_H HAVE_LIBSM
EXPAND_AS_DEFINED =
SKIP_FUNCTION_MACROS = NO
#---------------------------------------------------------------------------
View
2 src/Makefile.am
@@ -37,6 +37,7 @@ SRCS = \
project.c project.h \
sciwrappers.c sciwrappers.h \
search.c search.h \
+ sm.c sm.h \
socket.c socket.h \
stash.c stash.h \
support.h \
@@ -131,6 +132,7 @@ geany_LDADD = \
$(top_builddir)/tagmanager/src/libtagmanager.a \
@GTK_LIBS@ \
@GTHREAD_LIBS@ \
+ @SM_LIBS@ \
$(INTLLIBS)
AM_CFLAGS = -DGEANY_DATADIR=\""$(datadir)"\" \
View
24 src/callbacks.c
@@ -65,6 +65,7 @@
#include "toolbar.h"
#include "highlighting.h"
#include "pluginutils.h"
+#include "sm.h"
#ifdef HAVE_VTE
@@ -86,21 +87,6 @@ static gboolean insert_callback_from_menu = FALSE;
/*static gboolean switch_tv_notebook_page = FALSE; */
-static gboolean check_no_unsaved(void)
-{
- guint i;
-
- for (i = 0; i < documents_array->len; i++)
- {
- if (documents[i]->is_valid && documents[i]->changed)
- {
- return FALSE;
- }
- }
- return TRUE; /* no unsaved edits */
-}
-
-
/* set editor_info.click_pos to the current cursor position if insert_callback_from_menu is TRUE
* to prevent invalid cursor positions which can cause segfaults */
static void verify_click_pos(GeanyDocument *doc)
@@ -116,7 +102,7 @@ static void verify_click_pos(GeanyDocument *doc)
/* should only be called from on_exit_clicked */
static void quit_app(void)
{
- configuration_save();
+ configuration_save(NULL);
if (app->project != NULL)
project_close(FALSE); /* save project session files */
@@ -125,16 +111,20 @@ static void quit_app(void)
main_status.quitting = TRUE;
+ sm_discard();
main_quit();
}
/* wrapper function to abort exit process if cancel button is pressed */
G_MODULE_EXPORT gboolean on_exit_clicked(GtkWidget *widget, gpointer gdata)
{
+ if (main_status.prevent_interaction)
+ return FALSE;
+
main_status.quitting = TRUE;
- if (! check_no_unsaved())
+ if (document_any_unsaved())
{
if (document_account_for_unsaved())
{
View
38 src/document.c
@@ -2765,12 +2765,23 @@ GeanyDocument *document_clone(GeanyDocument *old_doc, const gchar *utf8_filename
}
-/* @note If successful, this should always be followed up with a call to
- * document_close_all().
- * @return TRUE if all files were saved or had their changes discarded. */
-gboolean document_account_for_unsaved(void)
+/* @return TRUE if there are some unsaved documents */
+gboolean document_any_unsaved(void)
{
- guint i, p, page_count;
+ guint i;
+
+ foreach_document(i)
+ if (documents[i]->changed)
+ return TRUE;
+ return FALSE;
+}
+
+
+/* @return TRUE if all files were saved or had their changes discarded.
+ * The files with discarded changes remain marked as modified. */
+gboolean document_prompt_for_unsaved(void)
+{
+ guint p, page_count;
page_count = gtk_notebook_get_n_pages(GTK_NOTEBOOK(main_widgets.notebook));
/* iterate over documents in tabs order */
@@ -2784,6 +2795,19 @@ gboolean document_account_for_unsaved(void)
return FALSE;
}
}
+ return TRUE;
+}
+
+
+/* @note If successful, this should always be followed up with a call to
+ * document_close_all().
+ * @return TRUE if all files were saved or had their changes discarded. */
+gboolean document_account_for_unsaved(void)
+{
+ guint i;
+
+ if (!document_prompt_for_unsaved())
+ return FALSE;
/* all documents should now be accounted for, so ignore any changes */
foreach_document (i)
{
@@ -2901,6 +2925,10 @@ gboolean document_check_disk_status(GeanyDocument *doc, gboolean force)
|| doc->real_path == NULL || doc->priv->is_remote)
return FALSE;
+ /* ignore changes while exiting session */
+ if (main_status.prevent_interaction)
+ return FALSE;
+
use_gio_filemon = (doc->priv->monitor != NULL);
if (use_gio_filemon)
View
4 src/document.h
@@ -206,6 +206,10 @@ void document_try_focus(GeanyDocument *doc, GtkWidget *source_widget);
gboolean document_close(GeanyDocument *doc);
+gboolean document_any_unsaved(void);
+
+gboolean document_prompt_for_unsaved(void);
+
gboolean document_account_for_unsaved(void);
gboolean document_close_all(void);
View
38 src/keyfile.c
@@ -573,10 +573,10 @@ static void save_ui_prefs(GKeyFile *config)
}
-void configuration_save(void)
+void configuration_save(const gchar *libsm_client_id)
{
GKeyFile *config = g_key_file_new();
- gchar *configfile = g_strconcat(app->configdir, G_DIR_SEPARATOR_S, "geany.conf", NULL);
+ gchar *configfile = configuration_name(libsm_client_id);
gchar *data;
g_key_file_load_from_file(config, configfile, G_KEY_FILE_NONE, NULL);
@@ -590,7 +590,7 @@ void configuration_save(void)
save_recent_files(config, ui_prefs.recent_queue, "recent_files");
save_recent_files(config, ui_prefs.recent_projects_queue, "recent_projects");
- if (cl_options.load_session)
+ if (cl_options.load_session || libsm_client_id)
configuration_save_session_files(config);
#ifdef HAVE_VTE
else if (vte_info.have_vte)
@@ -984,7 +984,7 @@ static void load_ui_prefs(GKeyFile *config)
*/
void configuration_save_default_session(void)
{
- gchar *configfile = g_strconcat(app->configdir, G_DIR_SEPARATOR_S, "geany.conf", NULL);
+ gchar *configfile = configuration_name(NULL);
gchar *data;
GKeyFile *config = g_key_file_new();
@@ -1008,7 +1008,7 @@ void configuration_save_default_session(void)
*/
void configuration_reload_default_session(void)
{
- gchar *configfile = g_build_filename(app->configdir, "geany.conf", NULL);
+ gchar *configfile = configuration_name(NULL);
GKeyFile *config = g_key_file_new();
g_key_file_load_from_file(config, configfile, G_KEY_FILE_NONE, NULL);
@@ -1020,11 +1020,18 @@ void configuration_reload_default_session(void)
}
-gboolean configuration_load(void)
+gboolean configuration_load(const gchar *libsm_client_id)
{
- gchar *configfile = g_build_filename(app->configdir, "geany.conf", NULL);
+ gchar *configfile = configuration_name(libsm_client_id);
GKeyFile *config = g_key_file_new();
+ if (libsm_client_id && ! g_file_test(configfile, G_FILE_TEST_IS_REGULAR))
+ { /* session config file does not exist, so try falling back to geany.conf */
+ geany_debug("No session config file found, trying to use default configuration.");
+ g_free(configfile);
+ configfile = configuration_name(NULL);
+ }
+
if (! g_file_test(configfile, G_FILE_TEST_IS_REGULAR))
{ /* config file does not (yet) exist, so try to load a global config file which may be */
/* created by distributors */
@@ -1216,3 +1223,20 @@ void configuration_finalize(void)
g_ptr_array_free(keyfile_groups, TRUE);
g_ptr_array_free(pref_groups, TRUE);
}
+
+
+gchar *configuration_name(const gchar *suffix)
+{
+ gchar *configfile;
+
+ if (suffix)
+ {
+ gchar *configbase = g_strconcat("geany-", suffix, ".conf", NULL);
+ configfile = g_build_filename(app->configdir, configbase, NULL);
+ g_free(configbase);
+ }
+ else
+ configfile = g_build_filename(app->configdir, "geany.conf", NULL);
+
+ return configfile;
+}
View
7 src/keyfile.h
@@ -37,9 +37,12 @@ void configuration_add_pref_group(struct StashGroup *group, gboolean for_prefs_d
void configuration_add_various_pref_group(struct StashGroup *group);
-void configuration_save(void);
+/* return newly allocated "app->configdir/geany[-suffix].conf" name */
+gchar *configuration_name(const gchar *suffix);
-gboolean configuration_load(void);
+void configuration_save(const gchar *libsm_client_id);
+
+gboolean configuration_load(const gchar *libsm_client_id);
void configuration_open_files(void);
View
62 src/main.c
@@ -71,6 +71,7 @@
#include "printing.h"
#include "toolbar.h"
#include "geanyobject.h"
+#include "sm.h"
#ifdef HAVE_SOCKET
# include "socket.h"
@@ -114,9 +115,17 @@ static gboolean print_prefix = FALSE;
static gboolean no_plugins = FALSE;
#endif
static gboolean dummy = FALSE;
+static gchar *libsm_client_id = NULL;
+/* WARNING: Do not change values of variables where values of command-line options are stored!
+ * This is, they should remain unchanged after `g_option_context_parse' returns.
+ * Otherwise, "restart command" for X session management may be filled improperly.
+ *
+ * NOTE: Currently optentries of type G_OPTION_ARG_CALLBACK are not supported by
+ * X session management support implementation.
+ */
/* in alphabetical order of short options */
-static GOptionEntry entries[] =
+GOptionEntry optentries[] =
{
{ "column", 0, 0, G_OPTION_ARG_INT, &cl_options.goto_column, N_("Set initial column number for the first opened file (useful in conjunction with --line)"), NULL },
{ "config", 'c', 0, G_OPTION_ARG_FILENAME, &alternate_config, N_("Use an alternate configuration directory"), NULL },
@@ -144,9 +153,41 @@ static GOptionEntry entries[] =
{ "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose_mode, N_("Be verbose"), NULL },
{ "version", 'V', 0, G_OPTION_ARG_NONE, &show_version, N_("Show version and exit"), NULL },
{ "dummy", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &dummy, NULL, NULL }, /* for +NNN line number arguments */
+ { "libsm-client-id", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &libsm_client_id, NULL, NULL },
+ /* add new options here and in `optentries_aux' below */
{ NULL, 0, 0, 0, NULL, NULL, NULL }
};
+GeanyOptionEntryAux optentries_aux[] = {
+ {FALSE}, /* "column" */
+ {TRUE}, /* "config" */
+ {FALSE}, /* "ft-names */
+ {FALSE}, /* "generate-tags" */
+ {FALSE}, /* "no-preprocessing" */
+#ifdef HAVE_SOCKET
+ {TRUE}, /* "new-instance" */
+ {TRUE}, /* "socket-file" */
+ {FALSE}, /* "list-documents" */
+#endif
+ {FALSE}, /* "line" */
+ {TRUE}, /* "no-msgwin" */
+ {TRUE}, /* "no-ctags" */
+#ifdef HAVE_PLUGINS
+ {TRUE}, /* "no-plugins" */
+#endif
+ {FALSE}, /* "print-prefix" */
+ {FALSE}, /* "read-only" */
+ {FALSE}, /* "no-session" */
+#ifdef HAVE_VTE
+ {TRUE}, /* "no-terminal" */
+ {TRUE}, /* "vte-lib" */
+#endif
+ {TRUE}, /* "verbose" */
+ {FALSE}, /* "version" */
+ {FALSE}, /* "dummy" */
+ {FALSE}, /* "libsm-client-id" option is handled separately */
+};
+
static void setup_window_position(void)
{
@@ -514,7 +555,7 @@ static void parse_command_line_options(gint *argc, gchar ***argv)
}
context = g_option_context_new(_("[FILES...]"));
- g_option_context_add_main_entries(context, entries, GETTEXT_PACKAGE);
+ g_option_context_add_main_entries(context, optentries, GETTEXT_PACKAGE);
g_option_group_set_translation_domain(g_option_context_get_main_group(context), GETTEXT_PACKAGE);
g_option_context_add_group(context, gtk_get_option_group(FALSE));
g_option_context_parse(context, argc, argv, &error);
@@ -566,7 +607,7 @@ static void parse_command_line_options(gint *argc, gchar ***argv)
if (alternate_config)
{
geany_debug("alternate config: %s", alternate_config);
- app->configdir = alternate_config;
+ app->configdir = g_strdup(alternate_config);
}
else
{
@@ -845,7 +886,7 @@ static void load_session_project_file(void)
locale_filename = utils_get_locale_from_utf8(project_prefs.session_file);
if (G_LIKELY(NZV(locale_filename)))
- project_load_file(locale_filename);
+ project_load_file(locale_filename, libsm_client_id == NULL);
g_free(locale_filename);
g_free(project_prefs.session_file); /* no longer needed */
@@ -854,7 +895,7 @@ static void load_session_project_file(void)
static void load_settings(void)
{
- configuration_load();
+ configuration_load(libsm_client_id);
/* let cmdline options overwrite configuration settings */
#ifdef HAVE_VTE
vte_info.have_vte = (no_vte) ? FALSE : vte_info.load_vte;
@@ -878,7 +919,7 @@ void main_load_project_from_command_line(const gchar *locale_filename, gboolean
if (use_session)
project_load_file_with_session(pfile);
else
- project_load_file(pfile);
+ project_load_file(pfile, TRUE);
}
g_free(pfile);
}
@@ -901,8 +942,10 @@ static void load_startup_files(gint argc, gchar **argv)
* 1. "Load files from the last session" is active.
* 2. --no-session is not specified.
* 3. We are a primary instance.
- * Has no effect if a CL project is loaded and using project-based session files. */
- if (prefs.load_session && cl_options.load_session && !cl_options.new_instance)
+ * Has no effect if a CL project is loaded and using project-based session files.
+ * Alternative XSMP case: load the xsmp session files, there is no CL project. */
+ if ((prefs.load_session && cl_options.load_session && !cl_options.new_instance) ||
+ libsm_client_id)
{
if (app->project == NULL)
load_session_project_file();
@@ -1024,6 +1067,7 @@ gint main(gint argc, gchar **argv)
geany_object = geany_object_new();
/* inits */
+ sm_init(argv[0], libsm_client_id);
main_init();
encodings_init();
@@ -1171,6 +1215,8 @@ void main_quit()
{
geany_debug("Quitting...");
+ sm_finalize();
+
#ifdef HAVE_SOCKET
socket_finalize();
#endif
View
15 src/main.h
@@ -23,6 +23,20 @@
#ifndef GEANY_MAIN_H
#define GEANY_MAIN_H
+/** Information about command line entries that can not be stored in GOptionEntry. */
+typedef struct
+{
+ /**
+ * Indicates whether the value of a command-line option should go to "restart command".
+ * See X session management support implementation in sm.{c,h}.
+ */
+ gboolean persist_upon_restart;
+}
+GeanyOptionEntryAux;
+
+extern GOptionEntry optentries[];
+extern GeanyOptionEntryAux optentries_aux[];
+
typedef struct
{
gboolean new_instance;
@@ -46,6 +60,7 @@ typedef struct GeanyStatus
* (used to prevent notebook switch page signals) */
gboolean quitting; /* state when Geany is quitting completely */
gboolean main_window_realized;
+ gboolean prevent_interaction; /* state while saving SM session; the main window is locked */
}
GeanyStatus;
View
2 src/makefile.win32
@@ -64,7 +64,7 @@ endif
OBJS = about.o build.o callbacks.o dialogs.o document.o editor.o encodings.o filetypes.o \
geanyentryaction.o geanymenubuttonaction.o geanyobject.o geanywraplabel.o highlighting.o \
keybindings.o keyfile.o log.o main.o msgwindow.o navqueue.o notebook.o \
- plugins.o pluginutils.o prefs.o printing.o project.o sciwrappers.o search.o \
+ plugins.o pluginutils.o prefs.o printing.o project.o sciwrappers.o search.o sm.o \
socket.o stash.o symbols.o templates.o toolbar.o tools.o sidebar.o \
ui_utils.o utils.o win32.o
View
2 src/prefs.c
@@ -1269,7 +1269,7 @@ on_prefs_dialog_response(GtkDialog *dialog, gint response, gpointer user_data)
ui_update_statusbar(doc, -1);
/* store all settings */
- configuration_save();
+ configuration_save(NULL);
}
if (response == GTK_RESPONSE_HELP)
View
15 src/project.c
@@ -80,7 +80,7 @@ typedef struct _PropertyDialogElements
static gboolean update_config(const PropertyDialogElements *e, gboolean new_project);
static void on_file_save_button_clicked(GtkButton *button, PropertyDialogElements *e);
-static gboolean load_config(const gchar *filename);
+static gboolean load_config(const gchar *filename, gboolean load_session);
static gboolean write_config(gboolean emit_signal);
static void on_name_entry_changed(GtkEditable *editable, PropertyDialogElements *e);
static void on_entries_changed(GtkEditable *editable, PropertyDialogElements *e);
@@ -211,7 +211,7 @@ void project_new(void)
gboolean project_load_file_with_session(const gchar *locale_file_name)
{
- if (project_load_file(locale_file_name))
+ if (project_load_file(locale_file_name, TRUE))
{
if (project_prefs.project_session)
{
@@ -929,11 +929,11 @@ static void on_radio_long_line_custom_toggled(GtkToggleButton *radio, GtkWidget
}
-gboolean project_load_file(const gchar *locale_file_name)
+gboolean project_load_file(const gchar *locale_file_name, gboolean load_session)
{
g_return_val_if_fail(locale_file_name != NULL, FALSE);
- if (load_config(locale_file_name))
+ if (load_config(locale_file_name, load_session))
{
gchar *utf8_filename = utils_get_utf8_from_locale(locale_file_name);
@@ -957,8 +957,9 @@ gboolean project_load_file(const gchar *locale_file_name)
/* Reads the given filename and creates a new project with the data found in the file.
* At this point there should not be an already opened project in Geany otherwise it will just
* return.
- * The filename is expected in the locale encoding. */
-static gboolean load_config(const gchar *filename)
+ * The filename is expected in the locale encoding. Both project_session preference and
+ * load_session must be TRUE for the project session files to be loaded. */
+static gboolean load_config(const gchar *filename, gboolean load_session)
{
GKeyFile *config;
GeanyProject *p;
@@ -992,7 +993,7 @@ static gboolean load_config(const gchar *filename)
apply_editor_prefs();
build_load_menu(config, GEANY_BCS_PROJ, (gpointer)p);
- if (project_prefs.project_session)
+ if (project_prefs.project_session && load_session)
{
/* save current (non-project) session (it could has been changed since program startup) */
configuration_save_default_session();
View
2 src/project.h
@@ -79,7 +79,7 @@ void project_build_properties(void);
gboolean project_ask_close(void);
-gboolean project_load_file(const gchar *locale_file_name);
+gboolean project_load_file(const gchar *locale_file_name, gboolean load_session);
gboolean project_load_file_with_session(const gchar *locale_file_name);
View
588 src/sm.c
@@ -0,0 +1,588 @@
+/*
+ * sm.c - this file is part of Geany, a fast and lightweight IDE
+ *
+ * Copyright 2009 Eugene Arshinov <earshinov(at)gmail(dot)com>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $Id$
+ */
+
+/*
+ * Changes by Dimitar Zhekov <dimitar(dot)zhekov(at)gmail(dot)com>
+ */
+
+/*
+ * @file sm.c
+ * Provides X session management protocol (XSMP) support using libSM library.
+ *
+ * In order to support XSMP, we have to support Inter-Client Exchange
+ * Protocol (ICE). This file takes care of the latter too.
+ *
+ * Typical usage:
+ * @c sm_init() is called when Geany is starting.
+ * @c sm_discard() is called on normal (non-sm) quit.
+ * @c sm_finalize() is called when Geany is quitting.
+ *
+ * According to libSM documentation, client should retain the same ID after
+ * it is restarted. The main module (@c main.c) maintains "--libsm-client-id"
+ * command-line option and passes the specified value (if any) to @c sm_init().
+ */
+
+/*
+ * As libSM is not available on Windows,
+ * it is safe enough to use POSIX-specific things here.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef HAVE_LIBSM
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include <X11/SM/SMlib.h>
+#include <X11/ICE/ICElib.h>
+
+#include "geany.h"
+#include "main.h" /* for cl_options, main_status */
+#include "ui_utils.h" /* for main_widgets */
+#include "document.h" /* for unsaved documents */
+#include "utils.h" /* for option reverse parsing */
+#include "keyfile.h" /* for configuration save */
+
+
+static void ice_connection_watch(IceConn icecon, IcePointer client_data, Bool opening,
+ IcePointer *watch_data);
+static gboolean ice_iochannel_watch(GIOChannel *source, GIOCondition condition, gpointer data);
+
+static SmcConn sm_connect(const char *libsm_client_id);
+static void sm_set_props(const char *argv0);
+static void sm_prevent_interaction(gboolean prevent);
+static void sm_finish_save(SmcConn smcon);
+
+static void sm_save_yourself_callback(SmcConn smcon, SmPointer client_data,
+ int save_type, Bool shutdown, int interact_style, Bool fast);
+static void sm_interact_callback(SmcConn smcon, SmPointer client_data);
+static void sm_save_complete_callback(SmcConn smcon, SmPointer client_data);
+static void sm_shutdown_cancelled_callback(SmcConn smcon, SmPointer client_data);
+static void sm_die_callback(SmcConn smcon, SmPointer client_data);
+
+
+/* LibSM connection object initialized in @c sm_init() and used in @c sm_finalize(). */
+static SmcConn smc_conn = 0;
+
+/* LibSM new client id. Initialized by @c sm_init() and used in many places. */
+static char *new_client_id = NULL;
+
+/* The save_type and shutdown values received by the last save yourself callback. */
+static int sm_last_save_type;
+static Bool sm_last_shutdown;
+/* Whether the last save yourself request was completed and signalled as done. */
+static gboolean sm_last_save_done;
+
+/* To check for improper Gnome/Xfce xsmp implementations */
+char *sm_vendor = NULL;
+
+#endif
+
+
+/*
+ * @name Exported functions
+ * @{
+ */
+
+/*
+ * Initialize XSMP support.
+ *
+ * @param argv0 Value of @c argv[0] used to define Geany's restart command.
+ * @param libsm_client_id Client-ID specified with "--libsm-client-id" command line
+ * option or @c NULL if the option was not passed.
+ *
+ * This function connects to the session manager using @c sm_connect(). If
+ * everything is successful, it stores libSM connection object and the new session id
+ * in the global variables @c smcon and @c new_client_id, and calls @c sm_set_props().
+ *
+ * This function should be called on Geany startup, before the main window is realized.
+ *
+ * When Geany is compiled without XSMP support, this function is a no-op.
+ */
+void sm_init(const char *argv0, const char *libsm_client_id)
+{
+ #ifdef HAVE_LIBSM
+ /* This function should be called once */
+ g_assert(!smc_conn);
+ if (smc_conn)
+ return;
+
+ smc_conn = sm_connect(libsm_client_id);
+ if (smc_conn)
+ {
+ sm_vendor = SmcVendor(smc_conn);
+ gdk_set_sm_client_id(new_client_id);
+ sm_set_props(argv0);
+ }
+ #endif
+}
+
+
+/*
+ * Remove the saved session file.
+ *
+ * Should be called on normal (non-sm) Geany quit, since GSM does not execute
+ * the discard command. Ignored in other managers to preserve permanent sessions.
+ *
+ * When Geany is compiled without XSMP support, this function is a no-op.
+ */
+void sm_discard(void)
+{
+ #ifdef HAVE_LIBSM
+ if (smc_conn && g_str_has_prefix(sm_vendor, "Gnome"))
+ {
+ gchar *configfile = configuration_name(new_client_id);
+ g_unlink(configfile);
+ g_free(configfile);
+ }
+ #endif
+}
+
+
+/*
+ * Perform cleanup.
+ *
+ * Call this function when XSMP support is no longer needed. In fact it is
+ * called when Geany is quitting.
+ *
+ * When Geany is compiled without XSMP support, this function is a no-op.
+ */
+void sm_finalize(void)
+{
+ #ifdef HAVE_LIBSM
+ if (smc_conn)
+ {
+ free(sm_vendor);
+ free(new_client_id);
+ SmcCloseConnection(smc_conn, 0, NULL);
+ smc_conn = 0;
+ }
+ #endif
+}
+
+
+/* @} */
+
+#ifdef HAVE_LIBSM
+
+/*
+ * @name ICE support
+ * @{
+ */
+
+/*
+ * ICE connection watcher used to attach a GIOChannel to each ICE connection
+ * so that this connection can be handled in GTK main loop.
+ *
+ * @param icecon ICE connection.
+ * @param client_data Client data specified in @c IceAddConnectionWatch() function call.
+ * Currently it is not used.
+ * @param opening Whether @c icecon is opening or closing.
+ * @param watch_data A piece of data that can be set when @c icecon is opening and
+ * read when it is closing. We store GIOChannel watcher ID here.
+ *
+ * We attach @c ice_iochannel_watch GIOChannel watcher to every created GIOChannel
+ * in order to handle messages. This is how session manager communicates to Geany.
+ *
+ * @see sm_connect()
+ */
+static void ice_connection_watch(IceConn icecon, IcePointer client_data,
+ Bool opening, IcePointer *watch_data)
+{
+ guint input_id;
+
+ if (opening)
+ {
+ GIOChannel *channel = g_io_channel_unix_new(IceConnectionNumber(icecon));
+
+ input_id = g_io_add_watch(channel, G_IO_IN | G_IO_HUP | G_IO_ERR,
+ ice_iochannel_watch, icecon);
+ g_io_channel_unref(channel);
+ *watch_data = (IcePointer) GUINT_TO_POINTER(input_id);
+ }
+ else
+ {
+ input_id = GPOINTER_TO_UINT((gpointer) *watch_data);
+ g_source_remove(input_id);
+ }
+}
+
+
+/*
+ * A watcher attached to a GIOChannel corresponding to an ICE connection.
+ *
+ * @param source A GIOChannel corresponding to an ICE connection.
+ * @param condition
+ * @param data Client data specified in @c g_io_add_watch() function call.
+ * An ICE connection object (of type @c IceConn) is stored here.
+ * @return Return FALSE to remove the GIOChannel.
+ *
+ * This function calls @c IceProcessMessages causing an appropriate libSM
+ * callback to be invoked.
+ *
+ * @see ice_connection_watch()
+ */
+static gboolean ice_iochannel_watch(GIOChannel *source, GIOCondition condition, gpointer data)
+{
+ IceConn icecon = (IceConn) data;
+ IceProcessMessages(icecon, NULL, NULL);
+ return TRUE;
+}
+
+
+/*
+ * @}
+ * @name libSM support implementation
+ * @{
+ */
+
+/*
+ * Connect to the session manager.
+ *
+ * @param libsm_client_id LibSM client ID saved from the previous session,
+ * or @c NULL if there was no previous session.
+ * @return libSM connection object or @c 0 if connection to the session manager fails.
+ */
+static SmcConn sm_connect(const char *libsm_client_id)
+{
+ static const SmcCallbacks callbacks = {
+ { sm_save_yourself_callback, NULL },
+ { sm_die_callback, NULL },
+ { sm_save_complete_callback, NULL },
+ { sm_shutdown_cancelled_callback, NULL } };
+
+ gchar err[256] = "";
+ SmcConn smcon;
+
+ if (!g_getenv("SESSION_MANAGER"))
+ return 0;
+
+ IceAddConnectionWatch(ice_connection_watch, NULL);
+
+ smcon = SmcOpenConnection(NULL, NULL,
+ SmProtoMajor, SmProtoMinor,
+ SmcSaveYourselfProcMask |
+ SmcDieProcMask |
+ SmcSaveCompleteProcMask |
+ SmcShutdownCancelledProcMask,
+ (SmcCallbacks *) &callbacks,
+ (char *) libsm_client_id, &new_client_id,
+ sizeof err, err);
+
+ if (!smcon)
+ {
+ g_warning("While connecting to session manager:\n%s.", err);
+ IceRemoveConnectionWatch(ice_connection_watch, NULL);
+ return 0;
+ }
+
+ return smcon;
+}
+
+
+/*
+ * Set the libSM properties.
+ *
+ * @param argv0 Value of @c argv[0].
+ */
+static void sm_set_props(const char *argv0)
+{
+ char *executable_path;
+ gchar *curdir = g_get_current_dir();
+ const gchar *username = g_get_user_name();
+ char *client_id_arg = g_strconcat("--libsm-client-id=", new_client_id, NULL);
+ gchar *configfile = configuration_name(new_client_id);
+
+ SmPropValue curdir_val = { strlen(curdir), curdir};
+ SmPropValue userid_val, program_val, client_id_val;
+ SmPropValue discard_val[3] = { { 2, "rm" }, { 2, "-f" },
+ { strlen(configfile), (char *) configfile } };
+
+ SmProp program_prop = { SmProgram, SmARRAY8, 1, &program_val };
+ SmProp userid_prop = { SmUserID, SmARRAY8, 1, &userid_val };
+ SmProp curdir_prop = { SmCurrentDirectory, SmARRAY8, 1, &curdir_val };
+ SmProp restart_prop, clone_prop;
+ SmProp discard_prop = { SmDiscardCommand, SmLISTofARRAY8, 3, discard_val };
+
+ SmProp *props[4] = { &program_prop, &curdir_prop, &restart_prop, &discard_prop };
+
+ int i, arg_max, argc;
+ SmPropValue *arg_val;
+
+ /* -- end of declarations -- */
+
+ /* GSM does not work with relative executable names, try to obtain absolute. */
+ if (g_path_is_absolute(argv0) || (executable_path = tm_get_real_path(argv0)) == NULL)
+ executable_path = g_strdup(argv0);
+
+ program_val.length = strlen(executable_path);
+ program_val.value = executable_path;
+ client_id_val.length = strlen(client_id_arg);
+ client_id_val.value = client_id_arg;
+
+ for (i = 0, arg_max = 2; optentries[i].long_name; i++)
+ if (optentries_aux[i].persist_upon_restart)
+ arg_max++;
+
+ arg_val = g_new(SmPropValue, arg_max);
+ arg_val[0] = program_val;
+ arg_val[1] = client_id_val;
+
+ for (i = 0, argc = 2; optentries[i].long_name; i++)
+ {
+ if (optentries_aux[i].persist_upon_restart)
+ {
+ char *option = utils_option_entry_reverse_parse(&optentries[i]);
+
+ if (option)
+ {
+ arg_val[argc].length = strlen(option);
+ arg_val[argc].value = option;
+ argc++;
+ }
+ }
+ }
+
+ restart_prop.name = SmRestartCommand;
+ restart_prop.type = SmLISTofARRAY8;
+ restart_prop.num_vals = argc;
+ restart_prop.vals = arg_val;
+
+ SmcSetProperties(smc_conn, 4, props);
+
+ arg_val[1] = program_val;
+ clone_prop.name = SmCloneCommand;
+ clone_prop.type = SmLISTofARRAY8;
+ clone_prop.num_vals = argc - 1;
+ clone_prop.vals = arg_val + 1;
+ props[0] = &clone_prop;
+ SmcSetProperties(smc_conn, 1, props);
+
+ if (username)
+ {
+ userid_val.length = strlen(username);
+ userid_val.value = (char *) username;
+ props[0] = &userid_prop;
+ SmcSetProperties(smc_conn, 1, props);
+ }
+
+ g_free(executable_path);
+ g_free(curdir);
+ g_free(client_id_arg);
+ g_free(configfile);
+ for (i = 2; i < argc; i++)
+ g_free(arg_val[i].value);
+ g_free(arg_val);
+}
+
+
+/*
+ * @}
+ * @name Utility functions used by the libSM callbacks.
+ * @{
+ */
+
+/*
+ * Prevent/allow user interaction with Geany if shutting down.
+ *
+ * @param prevent TRUE to prevent interaction, FALSE to allow.
+ *
+ * The new 6.9/7.0 XSMP specification is not very clear whether interaction should
+ * be prevented if not shutting down. But the rationale is to prevent anything the
+ * user does after the save being lost, which can only happen if shutting down.
+ */
+static void sm_prevent_interaction(gboolean prevent)
+{
+ if (sm_last_shutdown)
+ {
+ /* Disabling the main window takes time, nobody does it,
+ and that is window/session manager's job anyway. */
+ /*gtk_widget_set_sensitive(main_widgets.window, prevent == FALSE);*/
+ main_status.prevent_interaction = prevent;
+ }
+}
+
+
+/*
+ * Finish an interactive or non-interactive save yourself.
+ *
+ * @param smcon LibSM connection object.
+ */
+static void sm_finish_save(SmcConn smcon)
+{
+ if (sm_last_save_type == SmSaveLocal || sm_last_save_type == SmSaveBoth)
+ configuration_save(new_client_id);
+
+ /* SaveYourselfDone() may take some time, while the assignment
+ is a single instruction. So it's safer in that order. */
+ sm_last_save_done = TRUE;
+ SmcSaveYourselfDone(smcon, TRUE);
+ sm_prevent_interaction(TRUE);
+}
+
+
+/*
+ * @}
+ * @name libSM callbacks
+ * @{
+ */
+
+/*
+ * "Save Yourself" callback.
+ *
+ * @param smcon LibSM connection object.
+ * @param client_data Client data specified when the callback was registered.
+ * Currently it is not used.
+ * @param save_type Specifies the type of information that should be saved.
+ * @param shutdown Specifies if a shutdown is taking place.
+ * @param interact_style The type of interaction allowed with the user.
+ * @param fast If True, the client should save its state as quickly as possible.
+ *
+ * See libSM documentation for more details.
+ *
+ * If there are any unsaved documents and interaction with the user is allowed,
+ * we make an "Interact Request", so that the session manager sends us an "Interact"
+ * message, and return.
+ *
+ * Otherwise, we save the configuration and are ready to handle a "Save Complete",
+ * "Shutdown Cancelled" or "Die" message.
+ *
+ * @see sm_interact_callback()
+ * @see sm_save_complete_callback()
+ * @see sm_shutdown_cancelled_callback()
+ * @see sm_die_callback()
+ */
+static void sm_save_yourself_callback(SmcConn smcon, SmPointer client_data,
+ int save_type, Bool shutdown, int interact_style, Bool fast)
+{
+ /* Main window not realized means some startup dialog loop,
+ so the configuration/interface are not initialized yet. */
+ if (main_status.main_window_realized)
+ {
+ sm_last_save_type = save_type;
+ sm_last_shutdown = shutdown;
+ sm_last_save_done = FALSE;
+
+ if (!(save_type == SmSaveGlobal || save_type == SmSaveBoth) &&
+ interact_style == SmInteractStyleAny && document_any_unsaved() &&
+ !g_str_has_prefix(sm_vendor, "xfce") &&
+ SmcInteractRequest(smcon, SmDialogNormal, sm_interact_callback, NULL))
+ {
+ return; /* sm_interact_callback() takes over */
+ }
+
+ sm_finish_save(smcon);
+ }
+}
+
+
+/*
+ * "Interact" callback.
+ *
+ * @param smcon LibSM connection object.
+ * @param client_data Client data specified when the callback was registered.
+ * Currently it is not used.
+ *
+ * See libSM documentation for more details.
+ *
+ * The session manager sends us an "Interact" message after we make an
+ * "Interact Request" in @c sm_save_yourself_callback(). Here we are allowed to
+ * interact with the user, so we ask whether to the save changed documents (the
+ * user also can cancel the shutdown), and save the configuration. After that we
+ * are ready to handle a "Save Complete", "Shutdown Cancelled" or "Die" message.
+ *
+ * @see sm_shutdown_cancelled_callback()
+ * @see sm_die_callback()
+ */
+static void sm_interact_callback(SmcConn smcon, SmPointer client_data)
+{
+ gboolean unsaved = !document_prompt_for_unsaved();
+ SmcInteractDone(smcon, sm_last_shutdown && unsaved);
+ sm_finish_save(smcon);
+}
+
+
+/*
+ * "Save Complete" callback.
+ *
+ * @param smcon LibSM connection object.
+ * @param client_data Client data specified when the callback was registered.
+ * Currently it is not used.
+ *
+ * See libSM documentation for more details.
+ *
+ * Unfreeze and continue normally.
+ */
+static void sm_save_complete_callback(SmcConn smcon, SmPointer client_data)
+{
+ sm_prevent_interaction(FALSE);
+}
+
+
+/*
+ * "Shutdown Cancelled" callback.
+ *
+ * @param smcon LibSM connection object.
+ * @param client_data Client data specified when the callback was registered.
+ * Currently it is not used.
+ *
+ * See libSM documentation for more details.
+ *
+ * Signal save yourself if needed and unfreeze.
+ */
+static void sm_shutdown_cancelled_callback(SmcConn smcon, SmPointer client_data)
+{
+ if (!sm_last_save_done)
+ SmcSaveYourselfDone(smcon, FALSE);
+ sm_prevent_interaction(FALSE);
+}
+
+
+/*
+ * "Die" callback.
+ *
+ * @param smcon LibSM connection object.
+ * @param client_data Client data specified when the callback was registered.
+ * Currently it is not used.
+ *
+ * See libSM documentation for more details.
+ *
+ * The session manager asks us to quit Geany and we do it. When quitting, the
+ * main module (@c main.c) will call @c sm_finalize() where we will close the
+ * connection to the session manager.
+ */
+static void sm_die_callback(SmcConn smcon, SmPointer client_data)
+{
+ main_status.quitting = TRUE;
+ main_quit();
+}
+
+
+/* @} */
+
+#endif
View
32 src/sm.h
@@ -0,0 +1,32 @@
+/*
+ * sm.h - this file is part of Geany, a fast and lightweight IDE
+ *
+ * Copyright 2009 Eugene Arshinov <earshinov(at)gmail(dot)com>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $Id$
+ */
+
+#ifndef GEANY_SM_H
+#define GEANY_SM_H 1
+
+void sm_init(const char *argv0, const char *libsm_client_id);
+
+void sm_discard(void);
+
+void sm_finalize(void);
+
+#endif
View
46 src/utils.c
@@ -2068,6 +2068,52 @@ gchar **utils_copy_environment(const gchar **exclude_vars, const gchar *first_va
}
+/*
+ * "Reverse parse" @c GOptionEntry
+ *
+ * @param optentry Object to parse.
+ * @return Newly-allocated option string or NULL.
+ *
+ * Function takes the information about the option entry stored in @c optentry
+ * and the option's value by the address stored in @c optentry. It returns a newly
+ * allocated option string, or NULL if none required (or if the option type is not
+ * supported). Any relative file names are converted to absolute.
+ *
+ * The resulting string must be freed with @c g_free().
+ */
+gchar *utils_option_entry_reverse_parse(const GOptionEntry *optentry)
+{
+ gchar *s = NULL;
+
+ switch (optentry->arg)
+ {
+ case G_OPTION_ARG_NONE:
+ {
+ gboolean val = *(gboolean *)optentry->arg_data;
+ gboolean reverse = (optentry->flags & G_OPTION_FLAG_REVERSE);
+ if ((val && !reverse) || (!val && reverse)) /* logical XOR */
+ s = g_strdup_printf("--%s", optentry->long_name);
+ }
+ break;
+
+ case G_OPTION_ARG_INT:
+ if (*(gint *)optentry->arg_data)
+ s = g_strdup_printf("--%s=%d", optentry->long_name, *(gint *)optentry->arg_data);
+ break;
+
+ case G_OPTION_ARG_STRING:
+ case G_OPTION_ARG_FILENAME:
+ if (*(gchar **)optentry->arg_data)
+ s = g_strdup_printf("--%s=%s", optentry->long_name, *(gchar **)optentry->arg_data);
+ break;
+
+ default:
+ g_warning("%s: %s: %d\n", G_STRFUNC, "Unsupported option entry type", optentry->arg);
+ }
+ return s;
+}
+
+
/* Joins @a first and @a second into a new string vector, freeing the originals.
* The original contents are reused. */
gchar **utils_strv_join(gchar **first, gchar **second)
View
2 src/utils.h
@@ -277,6 +277,8 @@ gchar *utils_str_remove_chars(gchar *string, const gchar *chars);
gchar **utils_copy_environment(const gchar **exclude_vars, const gchar *first_varname, ...) G_GNUC_NULL_TERMINATED;
+gchar *utils_option_entry_reverse_parse(const GOptionEntry *optentry);
+
G_END_DECLS
#endif
View
15 wscript
@@ -129,7 +129,7 @@ geany_sources = set([
'src/highlighting.c', 'src/keybindings.c',
'src/keyfile.c', 'src/log.c', 'src/main.c', 'src/msgwindow.c', 'src/navqueue.c', 'src/notebook.c',
'src/plugins.c', 'src/pluginutils.c', 'src/prefix.c', 'src/prefs.c', 'src/printing.c', 'src/project.c',
- 'src/sciwrappers.c', 'src/search.c', 'src/socket.c', 'src/stash.c',
+ 'src/sciwrappers.c', 'src/search.c', 'src/sm.c', 'src/socket.c', 'src/stash.c',
'src/symbols.c',
'src/templates.c', 'src/toolbar.c', 'src/tools.c', 'src/sidebar.c',
'src/ui_utils.c', 'src/utils.c'])
@@ -181,6 +181,12 @@ def configure(conf):
gtk_version = conf.check_cfg(modversion='gtk+-2.0', uselib_store='GTK') or 'Unknown'
conf.check_cfg(package='gthread-2.0', uselib_store='GTHREAD', args='--cflags --libs')
+ # Find libSM
+ if not Options.options.no_libsm:
+ sm_version = conf.check_cfg(package='sm', uselib_store='SM', args='--cflags --libs', mandatory=False)
+ if sm_version is None:
+ Options.options.no_libsm = True
+
# Windows specials
if is_win32:
if conf.env['PREFIX'].lower() == tempfile.gettempdir().lower():
@@ -232,6 +238,7 @@ def configure(conf):
_define_from_opt(conf, 'HAVE_PLUGINS', not conf.options.no_plugins, None)
_define_from_opt(conf, 'HAVE_SOCKET', not conf.options.no_socket, None)
_define_from_opt(conf, 'HAVE_VTE', not conf.options.no_vte, None)
+ _define_from_opt(conf, 'HAVE_LIBSM', not Options.options.no_libsm, None)
conf.write_config_header('config.h', remove=False)
@@ -250,6 +257,7 @@ def configure(conf):
conf.msg('Using GTK version', gtk_version)
conf.msg('Build with plugin support', conf.options.no_plugins and 'no' or 'yes')
conf.msg('Use virtual terminal support', conf.options.no_vte and 'no' or 'yes')
+ conf.msg('Build with X session management support', conf.options.no_libsm and 'no' or 'yes')
if revision is not None:
conf.msg('Compiling Git revision', revision)
@@ -268,6 +276,9 @@ def options(opt):
opt.add_option('--disable-vte', action='store_true', default=False,
help='compile without support for an embedded virtual terminal [[default: No]',
dest='no_vte')
+ opt.add_option('--disable-libsm', action='store_true', default=target_is_win32(os.environ),
+ help='compile without X session management support [[default: No]',
+ dest='no_libsm')
# Paths
opt.add_option('--mandir', type='string', default='',
help='man documentation', dest='mandir')
@@ -363,7 +374,7 @@ def build(bld):
includes = ['.', 'scintilla/include', 'tagmanager/src'],
defines = ['G_LOG_DOMAIN="Geany"', 'GEANY_PRIVATE'],
linkflags = [] if is_win32 else ['-Wl,--export-dynamic'],
- uselib = ['GTK', 'GLIB', 'GMODULE', 'GIO', 'GTHREAD', 'WIN32', 'SUNOS_SOCKET'],
+ uselib = ['GTK', 'GLIB', 'GMODULE', 'GIO', 'GTHREAD', 'WIN32', 'SUNOS_SOCKET', 'SM'],
use = ['scintilla', 'ctags', 'tagmanager', 'mio'])
# geanyfunctions.h

0 comments on commit 0f07b31

Please sign in to comment.