Compare commits

...

1 Commits
master ... sm

Author SHA1 Message Date
Matthew Brush
0f07b31aa8 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
2012-07-10 20:48:05 -07:00
20 changed files with 1119 additions and 52 deletions

255
ChangeLog.sm Normal file
View File

@ -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 File

@ -94,6 +94,26 @@ GEANY_CHECK_THE_FORCE dnl hehe
# i18n # i18n
GEANY_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` GEANY_DATA_DIR=`eval echo ${datarootdir}/geany`
AC_SUBST([GEANY_DATA_DIR]) AC_SUBST([GEANY_DATA_DIR])

View File

@ -229,7 +229,7 @@ SEARCH_INCLUDES = NO
INCLUDE_PATH = INCLUDE_PATH =
INCLUDE_FILE_PATTERNS = INCLUDE_FILE_PATTERNS =
# make G_GNUC_PRINTF a no-op unless doxygen would ignore functions with varargs # 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 = EXPAND_AS_DEFINED =
SKIP_FUNCTION_MACROS = NO SKIP_FUNCTION_MACROS = NO
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------

View File

@ -37,6 +37,7 @@ SRCS = \
project.c project.h \ project.c project.h \
sciwrappers.c sciwrappers.h \ sciwrappers.c sciwrappers.h \
search.c search.h \ search.c search.h \
sm.c sm.h \
socket.c socket.h \ socket.c socket.h \
stash.c stash.h \ stash.c stash.h \
support.h \ support.h \
@ -131,6 +132,7 @@ geany_LDADD = \
$(top_builddir)/tagmanager/src/libtagmanager.a \ $(top_builddir)/tagmanager/src/libtagmanager.a \
@GTK_LIBS@ \ @GTK_LIBS@ \
@GTHREAD_LIBS@ \ @GTHREAD_LIBS@ \
@SM_LIBS@ \
$(INTLLIBS) $(INTLLIBS)
AM_CFLAGS = -DGEANY_DATADIR=\""$(datadir)"\" \ AM_CFLAGS = -DGEANY_DATADIR=\""$(datadir)"\" \

View File

@ -65,6 +65,7 @@
#include "toolbar.h" #include "toolbar.h"
#include "highlighting.h" #include "highlighting.h"
#include "pluginutils.h" #include "pluginutils.h"
#include "sm.h"
#ifdef HAVE_VTE #ifdef HAVE_VTE
@ -86,21 +87,6 @@ static gboolean insert_callback_from_menu = FALSE;
/*static gboolean switch_tv_notebook_page = 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 /* 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 */ * to prevent invalid cursor positions which can cause segfaults */
static void verify_click_pos(GeanyDocument *doc) 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 */ /* should only be called from on_exit_clicked */
static void quit_app(void) static void quit_app(void)
{ {
configuration_save(); configuration_save(NULL);
if (app->project != NULL) if (app->project != NULL)
project_close(FALSE); /* save project session files */ project_close(FALSE); /* save project session files */
@ -125,6 +111,7 @@ static void quit_app(void)
main_status.quitting = TRUE; main_status.quitting = TRUE;
sm_discard();
main_quit(); main_quit();
} }
@ -132,9 +119,12 @@ static void quit_app(void)
/* wrapper function to abort exit process if cancel button is pressed */ /* wrapper function to abort exit process if cancel button is pressed */
G_MODULE_EXPORT gboolean on_exit_clicked(GtkWidget *widget, gpointer gdata) G_MODULE_EXPORT gboolean on_exit_clicked(GtkWidget *widget, gpointer gdata)
{ {
if (main_status.prevent_interaction)
return FALSE;
main_status.quitting = TRUE; main_status.quitting = TRUE;
if (! check_no_unsaved()) if (document_any_unsaved())
{ {
if (document_account_for_unsaved()) if (document_account_for_unsaved())
{ {

View File

@ -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 /* @return TRUE if there are some unsaved documents */
* document_close_all(). gboolean document_any_unsaved(void)
* @return TRUE if all files were saved or had their changes discarded. */
gboolean document_account_for_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)); page_count = gtk_notebook_get_n_pages(GTK_NOTEBOOK(main_widgets.notebook));
/* iterate over documents in tabs order */ /* iterate over documents in tabs order */
@ -2784,6 +2795,19 @@ gboolean document_account_for_unsaved(void)
return FALSE; 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 */ /* all documents should now be accounted for, so ignore any changes */
foreach_document (i) foreach_document (i)
{ {
@ -2901,6 +2925,10 @@ gboolean document_check_disk_status(GeanyDocument *doc, gboolean force)
|| doc->real_path == NULL || doc->priv->is_remote) || doc->real_path == NULL || doc->priv->is_remote)
return FALSE; return FALSE;
/* ignore changes while exiting session */
if (main_status.prevent_interaction)
return FALSE;
use_gio_filemon = (doc->priv->monitor != NULL); use_gio_filemon = (doc->priv->monitor != NULL);
if (use_gio_filemon) if (use_gio_filemon)

View File

@ -206,6 +206,10 @@ void document_try_focus(GeanyDocument *doc, GtkWidget *source_widget);
gboolean document_close(GeanyDocument *doc); gboolean document_close(GeanyDocument *doc);
gboolean document_any_unsaved(void);
gboolean document_prompt_for_unsaved(void);
gboolean document_account_for_unsaved(void); gboolean document_account_for_unsaved(void);
gboolean document_close_all(void); gboolean document_close_all(void);

View File

@ -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(); 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; gchar *data;
g_key_file_load_from_file(config, configfile, G_KEY_FILE_NONE, NULL); 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_queue, "recent_files");
save_recent_files(config, ui_prefs.recent_projects_queue, "recent_projects"); 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); configuration_save_session_files(config);
#ifdef HAVE_VTE #ifdef HAVE_VTE
else if (vte_info.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) 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; gchar *data;
GKeyFile *config = g_key_file_new(); GKeyFile *config = g_key_file_new();
@ -1008,7 +1008,7 @@ void configuration_save_default_session(void)
*/ */
void configuration_reload_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(); GKeyFile *config = g_key_file_new();
g_key_file_load_from_file(config, configfile, G_KEY_FILE_NONE, NULL); 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(); 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)) 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 */ { /* config file does not (yet) exist, so try to load a global config file which may be */
/* created by distributors */ /* created by distributors */
@ -1216,3 +1223,20 @@ void configuration_finalize(void)
g_ptr_array_free(keyfile_groups, TRUE); g_ptr_array_free(keyfile_groups, TRUE);
g_ptr_array_free(pref_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 File

@ -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_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); void configuration_open_files(void);

View File

@ -71,6 +71,7 @@
#include "printing.h" #include "printing.h"
#include "toolbar.h" #include "toolbar.h"
#include "geanyobject.h" #include "geanyobject.h"
#include "sm.h"
#ifdef HAVE_SOCKET #ifdef HAVE_SOCKET
# include "socket.h" # include "socket.h"
@ -114,9 +115,17 @@ static gboolean print_prefix = FALSE;
static gboolean no_plugins = FALSE; static gboolean no_plugins = FALSE;
#endif #endif
static gboolean dummy = FALSE; 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 */ /* 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 }, { "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 }, { "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 }, { "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 }, { "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 */ { "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 } { 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) 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...]")); 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_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_add_group(context, gtk_get_option_group(FALSE));
g_option_context_parse(context, argc, argv, &error); 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) if (alternate_config)
{ {
geany_debug("alternate config: %s", alternate_config); geany_debug("alternate config: %s", alternate_config);
app->configdir = alternate_config; app->configdir = g_strdup(alternate_config);
} }
else else
{ {
@ -845,7 +886,7 @@ static void load_session_project_file(void)
locale_filename = utils_get_locale_from_utf8(project_prefs.session_file); locale_filename = utils_get_locale_from_utf8(project_prefs.session_file);
if (G_LIKELY(NZV(locale_filename))) 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(locale_filename);
g_free(project_prefs.session_file); /* no longer needed */ 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) static void load_settings(void)
{ {
configuration_load(); configuration_load(libsm_client_id);
/* let cmdline options overwrite configuration settings */ /* let cmdline options overwrite configuration settings */
#ifdef HAVE_VTE #ifdef HAVE_VTE
vte_info.have_vte = (no_vte) ? FALSE : vte_info.load_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) if (use_session)
project_load_file_with_session(pfile); project_load_file_with_session(pfile);
else else
project_load_file(pfile); project_load_file(pfile, TRUE);
} }
g_free(pfile); 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. * 1. "Load files from the last session" is active.
* 2. --no-session is not specified. * 2. --no-session is not specified.
* 3. We are a primary instance. * 3. We are a primary instance.
* Has no effect if a CL project is loaded and using project-based session files. */ * 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) * 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) if (app->project == NULL)
load_session_project_file(); load_session_project_file();
@ -1024,6 +1067,7 @@ gint main(gint argc, gchar **argv)
geany_object = geany_object_new(); geany_object = geany_object_new();
/* inits */ /* inits */
sm_init(argv[0], libsm_client_id);
main_init(); main_init();
encodings_init(); encodings_init();
@ -1171,6 +1215,8 @@ void main_quit()
{ {
geany_debug("Quitting..."); geany_debug("Quitting...");
sm_finalize();
#ifdef HAVE_SOCKET #ifdef HAVE_SOCKET
socket_finalize(); socket_finalize();
#endif #endif

View File

@ -23,6 +23,20 @@
#ifndef GEANY_MAIN_H #ifndef GEANY_MAIN_H
#define 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 typedef struct
{ {
gboolean new_instance; gboolean new_instance;
@ -46,6 +60,7 @@ typedef struct GeanyStatus
* (used to prevent notebook switch page signals) */ * (used to prevent notebook switch page signals) */
gboolean quitting; /* state when Geany is quitting completely */ gboolean quitting; /* state when Geany is quitting completely */
gboolean main_window_realized; gboolean main_window_realized;
gboolean prevent_interaction; /* state while saving SM session; the main window is locked */
} }
GeanyStatus; GeanyStatus;

View File

@ -64,7 +64,7 @@ endif
OBJS = about.o build.o callbacks.o dialogs.o document.o editor.o encodings.o filetypes.o \ 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 \ geanyentryaction.o geanymenubuttonaction.o geanyobject.o geanywraplabel.o highlighting.o \
keybindings.o keyfile.o log.o main.o msgwindow.o navqueue.o notebook.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 \ socket.o stash.o symbols.o templates.o toolbar.o tools.o sidebar.o \
ui_utils.o utils.o win32.o ui_utils.o utils.o win32.o

View File

@ -1269,7 +1269,7 @@ on_prefs_dialog_response(GtkDialog *dialog, gint response, gpointer user_data)
ui_update_statusbar(doc, -1); ui_update_statusbar(doc, -1);
/* store all settings */ /* store all settings */
configuration_save(); configuration_save(NULL);
} }
if (response == GTK_RESPONSE_HELP) if (response == GTK_RESPONSE_HELP)

View File

@ -80,7 +80,7 @@ typedef struct _PropertyDialogElements
static gboolean update_config(const PropertyDialogElements *e, gboolean new_project); static gboolean update_config(const PropertyDialogElements *e, gboolean new_project);
static void on_file_save_button_clicked(GtkButton *button, PropertyDialogElements *e); 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 gboolean write_config(gboolean emit_signal);
static void on_name_entry_changed(GtkEditable *editable, PropertyDialogElements *e); static void on_name_entry_changed(GtkEditable *editable, PropertyDialogElements *e);
static void on_entries_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) 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) 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); 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); 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. /* 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 * At this point there should not be an already opened project in Geany otherwise it will just
* return. * return.
* The filename is expected in the locale encoding. */ * The filename is expected in the locale encoding. Both project_session preference and
static gboolean load_config(const gchar *filename) * 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; GKeyFile *config;
GeanyProject *p; GeanyProject *p;
@ -992,7 +993,7 @@ static gboolean load_config(const gchar *filename)
apply_editor_prefs(); apply_editor_prefs();
build_load_menu(config, GEANY_BCS_PROJ, (gpointer)p); 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) */ /* save current (non-project) session (it could has been changed since program startup) */
configuration_save_default_session(); configuration_save_default_session();

View File

@ -79,7 +79,7 @@ void project_build_properties(void);
gboolean project_ask_close(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); gboolean project_load_file_with_session(const gchar *locale_file_name);

588
src/sm.c Normal file
View File

@ -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

32
src/sm.h Normal file
View File

@ -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 File

@ -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. /* Joins @a first and @a second into a new string vector, freeing the originals.
* The original contents are reused. */ * The original contents are reused. */
gchar **utils_strv_join(gchar **first, gchar **second) gchar **utils_strv_join(gchar **first, gchar **second)

View File

@ -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_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 G_END_DECLS
#endif #endif

15
wscript
View File

@ -129,7 +129,7 @@ geany_sources = set([
'src/highlighting.c', 'src/keybindings.c', '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/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/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/symbols.c',
'src/templates.c', 'src/toolbar.c', 'src/tools.c', 'src/sidebar.c', 'src/templates.c', 'src/toolbar.c', 'src/tools.c', 'src/sidebar.c',
'src/ui_utils.c', 'src/utils.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' 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') 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 # Windows specials
if is_win32: if is_win32:
if conf.env['PREFIX'].lower() == tempfile.gettempdir().lower(): 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_PLUGINS', not conf.options.no_plugins, None)
_define_from_opt(conf, 'HAVE_SOCKET', not conf.options.no_socket, 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_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) conf.write_config_header('config.h', remove=False)
@ -250,6 +257,7 @@ def configure(conf):
conf.msg('Using GTK version', gtk_version) conf.msg('Using GTK version', gtk_version)
conf.msg('Build with plugin support', conf.options.no_plugins and 'no' or 'yes') 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('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: if revision is not None:
conf.msg('Compiling Git revision', revision) conf.msg('Compiling Git revision', revision)
@ -268,6 +276,9 @@ def options(opt):
opt.add_option('--disable-vte', action='store_true', default=False, opt.add_option('--disable-vte', action='store_true', default=False,
help='compile without support for an embedded virtual terminal [[default: No]', help='compile without support for an embedded virtual terminal [[default: No]',
dest='no_vte') 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 # Paths
opt.add_option('--mandir', type='string', default='', opt.add_option('--mandir', type='string', default='',
help='man documentation', dest='mandir') help='man documentation', dest='mandir')
@ -363,7 +374,7 @@ def build(bld):
includes = ['.', 'scintilla/include', 'tagmanager/src'], includes = ['.', 'scintilla/include', 'tagmanager/src'],
defines = ['G_LOG_DOMAIN="Geany"', 'GEANY_PRIVATE'], defines = ['G_LOG_DOMAIN="Geany"', 'GEANY_PRIVATE'],
linkflags = [] if is_win32 else ['-Wl,--export-dynamic'], 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']) use = ['scintilla', 'ctags', 'tagmanager', 'mio'])
# geanyfunctions.h # geanyfunctions.h