00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include <config.h>
00027
00028 #include "session.h"
00029 #include <X11/Xatom.h>
00030
00031 #include <time.h>
00032
00033 #ifndef HAVE_SM
00034 void
00035 meta_session_init (const char *client_id,
00036 const char *save_file)
00037 {
00038 meta_topic (META_DEBUG_SM, "Compiled without session management support\n");
00039 }
00040
00041 void
00042 meta_session_shutdown (void)
00043 {
00044
00045 }
00046
00047 const MetaWindowSessionInfo*
00048 meta_window_lookup_saved_state (MetaWindow *window)
00049 {
00050 return NULL;
00051 }
00052
00053 void
00054 meta_window_release_saved_state (const MetaWindowSessionInfo *info)
00055 {
00056 ;
00057 }
00058 #else
00059
00060 #include <X11/ICE/ICElib.h>
00061 #include <X11/SM/SMlib.h>
00062 #include <unistd.h>
00063 #include <sys/stat.h>
00064 #include <sys/types.h>
00065 #include <fcntl.h>
00066 #include <errno.h>
00067 #include <glib.h>
00068 #include <string.h>
00069 #include <stdlib.h>
00070 #include <stdio.h>
00071 #include "main.h"
00072 #include "util.h"
00073 #include "display-private.h"
00074 #include "workspace.h"
00075
00076 static void ice_io_error_handler (IceConn connection);
00077
00078 static void new_ice_connection (IceConn connection, IcePointer client_data,
00079 Bool opening, IcePointer *watch_data);
00080
00081 static void save_state (void);
00082 static char* load_state (const char *previous_save_file);
00083 static void regenerate_save_file (void);
00084 static const char* full_save_file (void);
00085 static void warn_about_lame_clients_and_finish_interact (gboolean shutdown);
00086
00087
00088 static gboolean
00089 process_ice_messages (GIOChannel *channel,
00090 GIOCondition condition,
00091 gpointer client_data)
00092 {
00093 IceConn connection = (IceConn) client_data;
00094 IceProcessMessagesStatus status;
00095
00096
00097
00098
00099
00100 status = IceProcessMessages (connection, NULL, NULL);
00101
00102 if (status == IceProcessMessagesIOError)
00103 {
00104 #if 0
00105 IcePointer context = IceGetConnectionContext (connection);
00106 #endif
00107
00108
00109 IceSetShutdownNegotiation (connection, False);
00110 IceCloseConnection (connection);
00111
00112 return FALSE;
00113 }
00114
00115 return TRUE;
00116 }
00117
00118
00119
00120 static void
00121 new_ice_connection (IceConn connection, IcePointer client_data, Bool opening,
00122 IcePointer *watch_data)
00123 {
00124 guint input_id;
00125
00126 if (opening)
00127 {
00128
00129
00130
00131 GIOChannel *channel;
00132
00133 fcntl (IceConnectionNumber (connection), F_SETFD,
00134 fcntl (IceConnectionNumber (connection), F_GETFD, 0) | FD_CLOEXEC);
00135
00136 channel = g_io_channel_unix_new (IceConnectionNumber (connection));
00137
00138 input_id = g_io_add_watch (channel,
00139 G_IO_IN | G_IO_ERR,
00140 process_ice_messages,
00141 connection);
00142
00143 g_io_channel_unref (channel);
00144
00145 *watch_data = (IcePointer) GUINT_TO_POINTER (input_id);
00146 }
00147 else
00148 {
00149 input_id = GPOINTER_TO_UINT ((gpointer) *watch_data);
00150
00151 g_source_remove (input_id);
00152 }
00153 }
00154
00155 static IceIOErrorHandler ice_installed_handler;
00156
00157
00158
00159 static void
00160 ice_io_error_handler (IceConn connection)
00161 {
00162 if (ice_installed_handler)
00163 (*ice_installed_handler) (connection);
00164 }
00165
00166 static void
00167 ice_init (void)
00168 {
00169 static gboolean ice_initted = FALSE;
00170
00171 if (! ice_initted)
00172 {
00173 IceIOErrorHandler default_handler;
00174
00175 ice_installed_handler = IceSetIOErrorHandler (NULL);
00176 default_handler = IceSetIOErrorHandler (ice_io_error_handler);
00177
00178 if (ice_installed_handler == default_handler)
00179 ice_installed_handler = NULL;
00180
00181 IceAddConnectionWatch (new_ice_connection, NULL);
00182
00183 ice_initted = TRUE;
00184 }
00185 }
00186
00187 typedef enum
00188 {
00189 STATE_DISCONNECTED,
00190 STATE_IDLE,
00191 STATE_SAVING_PHASE_1,
00192 STATE_WAITING_FOR_PHASE_2,
00193 STATE_SAVING_PHASE_2,
00194 STATE_WAITING_FOR_INTERACT,
00195 STATE_DONE_WITH_INTERACT,
00196 STATE_SKIPPING_GLOBAL_SAVE,
00197 STATE_FROZEN,
00198 STATE_REGISTERING
00199 } ClientState;
00200
00201 static void save_phase_2_callback (SmcConn smc_conn,
00202 SmPointer client_data);
00203 static void interact_callback (SmcConn smc_conn,
00204 SmPointer client_data);
00205 static void shutdown_cancelled_callback (SmcConn smc_conn,
00206 SmPointer client_data);
00207 static void save_complete_callback (SmcConn smc_conn,
00208 SmPointer client_data);
00209 static void die_callback (SmcConn smc_conn,
00210 SmPointer client_data);
00211 static void save_yourself_callback (SmcConn smc_conn,
00212 SmPointer client_data,
00213 int save_style,
00214 Bool shutdown,
00215 int interact_style,
00216 Bool fast);
00217 static void set_clone_restart_commands (void);
00218
00219 static char *client_id = NULL;
00220 static gpointer session_connection = NULL;
00221 static ClientState current_state = STATE_DISCONNECTED;
00222 static gboolean interaction_allowed = FALSE;
00223
00224 void
00225 meta_session_init (const char *previous_client_id,
00226 const char *previous_save_file)
00227 {
00228
00229 char buf[256];
00230 unsigned long mask;
00231 SmcCallbacks callbacks;
00232 char *saved_client_id;
00233
00234 meta_topic (META_DEBUG_SM, "Initializing session with save file '%s'\n",
00235 previous_save_file ? previous_save_file : "(none)");
00236
00237 if (previous_save_file)
00238 {
00239 saved_client_id = load_state (previous_save_file);
00240 previous_client_id = saved_client_id;
00241 }
00242 else if (previous_client_id)
00243 {
00244 char *save_file = g_strconcat (previous_client_id, ".ms", NULL);
00245 saved_client_id = load_state (save_file);
00246 g_free (save_file);
00247 }
00248 else
00249 {
00250 saved_client_id = NULL;
00251 }
00252
00253 ice_init ();
00254
00255 mask = SmcSaveYourselfProcMask | SmcDieProcMask |
00256 SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask;
00257
00258 callbacks.save_yourself.callback = save_yourself_callback;
00259 callbacks.save_yourself.client_data = NULL;
00260
00261 callbacks.die.callback = die_callback;
00262 callbacks.die.client_data = NULL;
00263
00264 callbacks.save_complete.callback = save_complete_callback;
00265 callbacks.save_complete.client_data = NULL;
00266
00267 callbacks.shutdown_cancelled.callback = shutdown_cancelled_callback;
00268 callbacks.shutdown_cancelled.client_data = NULL;
00269
00270 session_connection =
00271 SmcOpenConnection (NULL,
00272 NULL,
00273 SmProtoMajor,
00274 SmProtoMinor,
00275 mask,
00276 &callbacks,
00277 (char*) previous_client_id,
00278 &client_id,
00279 255, buf);
00280
00281 if (session_connection == NULL)
00282 {
00283 meta_topic (META_DEBUG_SM,
00284 "Failed to a open connection to a session manager, so window positions will not be saved: %s\n",
00285 buf);
00286
00287 goto out;
00288 }
00289 else
00290 {
00291 if (client_id == NULL)
00292 meta_bug ("Session manager gave us a NULL client ID?");
00293 meta_topic (META_DEBUG_SM, "Obtained session ID '%s'\n", client_id);
00294 }
00295
00296 if (previous_client_id && strcmp (previous_client_id, client_id) == 0)
00297 current_state = STATE_IDLE;
00298 else
00299 current_state = STATE_REGISTERING;
00300
00301 {
00302 SmProp prop1, prop2, prop3, prop4, prop5, prop6, *props[6];
00303 SmPropValue prop1val, prop2val, prop3val, prop4val, prop5val, prop6val;
00304 char pid[32];
00305 char hint = SmRestartImmediately;
00306 char priority = 20;
00307
00308 prop1.name = SmProgram;
00309 prop1.type = SmARRAY8;
00310 prop1.num_vals = 1;
00311 prop1.vals = &prop1val;
00312 prop1val.value = "metacity";
00313 prop1val.length = strlen ("metacity");
00314
00315
00316
00317
00318 prop2.name = SmUserID;
00319 prop2.type = SmARRAY8;
00320 prop2.num_vals = 1;
00321 prop2.vals = &prop2val;
00322 prop2val.value = (char*) g_get_user_name ();
00323 prop2val.length = strlen (prop2val.value);
00324
00325 prop3.name = SmRestartStyleHint;
00326 prop3.type = SmCARD8;
00327 prop3.num_vals = 1;
00328 prop3.vals = &prop3val;
00329 prop3val.value = &hint;
00330 prop3val.length = 1;
00331
00332 sprintf (pid, "%d", getpid ());
00333 prop4.name = SmProcessID;
00334 prop4.type = SmARRAY8;
00335 prop4.num_vals = 1;
00336 prop4.vals = &prop4val;
00337 prop4val.value = pid;
00338 prop4val.length = strlen (prop4val.value);
00339
00340
00341 prop5.name = SmCurrentDirectory;
00342 prop5.type = SmARRAY8;
00343 prop5.num_vals = 1;
00344 prop5.vals = &prop5val;
00345 prop5val.value = (char*) g_get_home_dir ();
00346 prop5val.length = strlen (prop5val.value);
00347
00348 prop6.name = "_GSM_Priority";
00349 prop6.type = SmCARD8;
00350 prop6.num_vals = 1;
00351 prop6.vals = &prop6val;
00352 prop6val.value = &priority;
00353 prop6val.length = 1;
00354
00355 props[0] = &prop1;
00356 props[1] = &prop2;
00357 props[2] = &prop3;
00358 props[3] = &prop4;
00359 props[4] = &prop5;
00360 props[5] = &prop6;
00361
00362 SmcSetProperties (session_connection, 6, props);
00363 }
00364
00365 out:
00366 g_free (saved_client_id);
00367 }
00368
00369 void
00370 meta_session_shutdown (void)
00371 {
00372
00373
00374 SmProp prop1;
00375 SmPropValue prop1val;
00376 SmProp *props[1];
00377 char hint = SmRestartIfRunning;
00378
00379 if (session_connection == NULL)
00380 return;
00381
00382 prop1.name = SmRestartStyleHint;
00383 prop1.type = SmCARD8;
00384 prop1.num_vals = 1;
00385 prop1.vals = &prop1val;
00386 prop1val.value = &hint;
00387 prop1val.length = 1;
00388
00389 props[0] = &prop1;
00390
00391 SmcSetProperties (session_connection, 1, props);
00392 }
00393
00394 static void
00395 disconnect (void)
00396 {
00397 SmcCloseConnection (session_connection, 0, NULL);
00398 session_connection = NULL;
00399 current_state = STATE_DISCONNECTED;
00400 }
00401
00402 static void
00403 save_yourself_possibly_done (gboolean shutdown,
00404 gboolean successful)
00405 {
00406 meta_topic (META_DEBUG_SM,
00407 "save possibly done shutdown = %d success = %d\n",
00408 shutdown, successful);
00409
00410 if (current_state == STATE_SAVING_PHASE_1)
00411 {
00412 Status status;
00413
00414 status = SmcRequestSaveYourselfPhase2 (session_connection,
00415 save_phase_2_callback,
00416 GINT_TO_POINTER (shutdown));
00417
00418 if (status)
00419 current_state = STATE_WAITING_FOR_PHASE_2;
00420
00421 meta_topic (META_DEBUG_SM,
00422 "Requested phase 2, status = %d\n", status);
00423 }
00424
00425 if (current_state == STATE_SAVING_PHASE_2 &&
00426 interaction_allowed)
00427 {
00428 Status status;
00429
00430 status = SmcInteractRequest (session_connection,
00431
00432
00433
00434 SmDialogNormal,
00435 interact_callback,
00436 GINT_TO_POINTER (shutdown));
00437
00438 if (status)
00439 current_state = STATE_WAITING_FOR_INTERACT;
00440
00441 meta_topic (META_DEBUG_SM,
00442 "Requested interact, status = %d\n", status);
00443 }
00444
00445 if (current_state == STATE_SAVING_PHASE_1 ||
00446 current_state == STATE_SAVING_PHASE_2 ||
00447 current_state == STATE_DONE_WITH_INTERACT ||
00448 current_state == STATE_SKIPPING_GLOBAL_SAVE)
00449 {
00450 meta_topic (META_DEBUG_SM, "Sending SaveYourselfDone\n");
00451
00452 SmcSaveYourselfDone (session_connection,
00453 successful);
00454
00455 if (shutdown)
00456 current_state = STATE_FROZEN;
00457 else
00458 current_state = STATE_IDLE;
00459 }
00460 }
00461
00462 static void
00463 save_phase_2_callback (SmcConn smc_conn, SmPointer client_data)
00464 {
00465 gboolean shutdown;
00466
00467 meta_topic (META_DEBUG_SM, "Phase 2 save");
00468
00469 shutdown = GPOINTER_TO_INT (client_data);
00470
00471 current_state = STATE_SAVING_PHASE_2;
00472
00473 save_state ();
00474
00475 save_yourself_possibly_done (shutdown, TRUE);
00476 }
00477
00478 static void
00479 save_yourself_callback (SmcConn smc_conn,
00480 SmPointer client_data,
00481 int save_style,
00482 Bool shutdown,
00483 int interact_style,
00484 Bool fast)
00485 {
00486 gboolean successful;
00487
00488 meta_topic (META_DEBUG_SM, "SaveYourself received");
00489
00490 successful = TRUE;
00491
00492
00493
00494
00495
00496 #if 0
00497 if (current_state == STATE_REGISTERING)
00498 {
00499 current_state = STATE_IDLE;
00500
00501
00502 if (save_style == SmSaveLocal &&
00503 interact_style == SmInteractStyleNone &&
00504 !shutdown && !fast)
00505 {
00506
00507 SmcSaveYourselfDone (session_connection, successful);
00508 return;
00509 }
00510 }
00511 #endif
00512
00513
00514
00515
00516
00517
00518
00519
00520 if (save_style == SmSaveGlobal)
00521 {
00522 current_state = STATE_SKIPPING_GLOBAL_SAVE;
00523 save_yourself_possibly_done (shutdown, successful);
00524 return;
00525 }
00526
00527 interaction_allowed = interact_style != SmInteractStyleNone;
00528
00529 current_state = STATE_SAVING_PHASE_1;
00530
00531 regenerate_save_file ();
00532
00533 set_clone_restart_commands ();
00534
00535 save_yourself_possibly_done (shutdown, successful);
00536 }
00537
00538
00539 static void
00540 die_callback (SmcConn smc_conn, SmPointer client_data)
00541 {
00542 meta_topic (META_DEBUG_SM, "Exiting at request of session manager\n");
00543 disconnect ();
00544 meta_quit (META_EXIT_SUCCESS);
00545 }
00546
00547 static void
00548 save_complete_callback (SmcConn smc_conn, SmPointer client_data)
00549 {
00550
00551 meta_topic (META_DEBUG_SM, "SaveComplete received\n");
00552 }
00553
00554 static void
00555 shutdown_cancelled_callback (SmcConn smc_conn, SmPointer client_data)
00556 {
00557 meta_topic (META_DEBUG_SM, "Shutdown cancelled received\n");
00558
00559 if (session_connection != NULL &&
00560 (current_state != STATE_IDLE && current_state != STATE_FROZEN))
00561 {
00562 SmcSaveYourselfDone (session_connection, True);
00563 current_state = STATE_IDLE;
00564 }
00565 }
00566
00567 static void
00568 interact_callback (SmcConn smc_conn, SmPointer client_data)
00569 {
00570
00571 gboolean shutdown;
00572
00573 meta_topic (META_DEBUG_SM, "Interaction permission received\n");
00574
00575 shutdown = GPOINTER_TO_INT (client_data);
00576
00577 current_state = STATE_DONE_WITH_INTERACT;
00578
00579 warn_about_lame_clients_and_finish_interact (shutdown);
00580 }
00581
00582 static void
00583 set_clone_restart_commands (void)
00584 {
00585 char *restartv[10];
00586 char *clonev[10];
00587 char *discardv[10];
00588 int i;
00589 SmProp prop1, prop2, prop3, *props[3];
00590
00591
00592
00593 prop1.name = SmRestartCommand;
00594 prop1.type = SmLISTofARRAY8;
00595
00596 g_return_if_fail (client_id);
00597
00598 i = 0;
00599 restartv[i] = "metacity";
00600 ++i;
00601 restartv[i] = "--sm-client-id";
00602 ++i;
00603 restartv[i] = client_id;
00604 ++i;
00605 restartv[i] = NULL;
00606
00607 prop1.vals = g_new (SmPropValue, i);
00608 i = 0;
00609 while (restartv[i])
00610 {
00611 prop1.vals[i].value = restartv[i];
00612 prop1.vals[i].length = strlen (restartv[i]);
00613 ++i;
00614 }
00615 prop1.num_vals = i;
00616
00617
00618
00619 i = 0;
00620 clonev[i] = "metacity";
00621 ++i;
00622 clonev[i] = NULL;
00623
00624 prop2.name = SmCloneCommand;
00625 prop2.type = SmLISTofARRAY8;
00626
00627 prop2.vals = g_new (SmPropValue, i);
00628 i = 0;
00629 while (clonev[i])
00630 {
00631 prop2.vals[i].value = clonev[i];
00632 prop2.vals[i].length = strlen (clonev[i]);
00633 ++i;
00634 }
00635 prop2.num_vals = i;
00636
00637
00638
00639 i = 0;
00640 discardv[i] = "rm";
00641 ++i;
00642 discardv[i] = "-f";
00643 ++i;
00644 discardv[i] = (char*) full_save_file ();
00645 ++i;
00646 discardv[i] = NULL;
00647
00648 prop3.name = SmDiscardCommand;
00649 prop3.type = SmLISTofARRAY8;
00650
00651 prop3.vals = g_new (SmPropValue, i);
00652 i = 0;
00653 while (discardv[i])
00654 {
00655 prop3.vals[i].value = discardv[i];
00656 prop3.vals[i].length = strlen (discardv[i]);
00657 ++i;
00658 }
00659 prop3.num_vals = i;
00660
00661
00662 props[0] = &prop1;
00663 props[1] = &prop2;
00664 props[2] = &prop3;
00665
00666 SmcSetProperties (session_connection, 3, props);
00667
00668 g_free (prop1.vals);
00669 g_free (prop2.vals);
00670 g_free (prop3.vals);
00671 }
00672
00673
00674
00675
00676
00677
00678 static const char*
00679 window_type_to_string (MetaWindowType type)
00680 {
00681 switch (type)
00682 {
00683 case META_WINDOW_NORMAL:
00684 return "normal";
00685 case META_WINDOW_DESKTOP:
00686 return "desktop";
00687 case META_WINDOW_DOCK:
00688 return "dock";
00689 case META_WINDOW_DIALOG:
00690 return "dialog";
00691 case META_WINDOW_MODAL_DIALOG:
00692 return "modal_dialog";
00693 case META_WINDOW_TOOLBAR:
00694 return "toolbar";
00695 case META_WINDOW_MENU:
00696 return "menu";
00697 case META_WINDOW_SPLASHSCREEN:
00698 return "splashscreen";
00699 case META_WINDOW_UTILITY:
00700 return "utility";
00701 }
00702
00703 return "";
00704 }
00705
00706 static MetaWindowType
00707 window_type_from_string (const char *str)
00708 {
00709 if (strcmp (str, "normal") == 0)
00710 return META_WINDOW_NORMAL;
00711 else if (strcmp (str, "desktop") == 0)
00712 return META_WINDOW_DESKTOP;
00713 else if (strcmp (str, "dock") == 0)
00714 return META_WINDOW_DOCK;
00715 else if (strcmp (str, "dialog") == 0)
00716 return META_WINDOW_DIALOG;
00717 else if (strcmp (str, "modal_dialog") == 0)
00718 return META_WINDOW_MODAL_DIALOG;
00719 else if (strcmp (str, "toolbar") == 0)
00720 return META_WINDOW_TOOLBAR;
00721 else if (strcmp (str, "menu") == 0)
00722 return META_WINDOW_MENU;
00723 else if (strcmp (str, "utility") == 0)
00724 return META_WINDOW_UTILITY;
00725 else if (strcmp (str, "splashscreen") == 0)
00726 return META_WINDOW_SPLASHSCREEN;
00727 else
00728 return META_WINDOW_NORMAL;
00729 }
00730
00731 static int
00732 window_gravity_from_string (const char *str)
00733 {
00734 if (strcmp (str, "NorthWestGravity") == 0)
00735 return NorthWestGravity;
00736 else if (strcmp (str, "NorthGravity") == 0)
00737 return NorthGravity;
00738 else if (strcmp (str, "NorthEastGravity") == 0)
00739 return NorthEastGravity;
00740 else if (strcmp (str, "WestGravity") == 0)
00741 return WestGravity;
00742 else if (strcmp (str, "CenterGravity") == 0)
00743 return CenterGravity;
00744 else if (strcmp (str, "EastGravity") == 0)
00745 return EastGravity;
00746 else if (strcmp (str, "SouthWestGravity") == 0)
00747 return SouthWestGravity;
00748 else if (strcmp (str, "SouthGravity") == 0)
00749 return SouthGravity;
00750 else if (strcmp (str, "SouthEastGravity") == 0)
00751 return SouthEastGravity;
00752 else if (strcmp (str, "StaticGravity") == 0)
00753 return StaticGravity;
00754 else
00755 return NorthWestGravity;
00756 }
00757
00758 static char*
00759 encode_text_as_utf8_markup (const char *text)
00760 {
00761
00762
00763
00764 GString *str;
00765 const char *p;
00766 char *escaped;
00767
00768 str = g_string_new ("");
00769
00770 p = text;
00771 while (*p)
00772 {
00773 g_string_append_unichar (str, *p);
00774 ++p;
00775 }
00776
00777 escaped = g_markup_escape_text (str->str, str->len);
00778 g_string_free (str, TRUE);
00779
00780 return escaped;
00781 }
00782
00783 static char*
00784 decode_text_from_utf8 (const char *text)
00785 {
00786
00787 GString *str;
00788 const char *p;
00789
00790 str = g_string_new ("");
00791
00792 p = text;
00793 while (*p)
00794 {
00795
00796 g_string_append_c (str, g_utf8_get_char (p));
00797
00798 p = g_utf8_next_char (p);
00799 }
00800
00801 return g_string_free (str, FALSE);
00802 }
00803
00804 static void
00805 save_state (void)
00806 {
00807 char *metacity_dir;
00808 char *session_dir;
00809 FILE *outfile;
00810 GSList *windows;
00811 GSList *tmp;
00812 int stack_position;
00813
00814 g_assert (client_id);
00815
00816 outfile = NULL;
00817
00818
00819
00820
00821
00822
00823
00824
00825
00826 metacity_dir = g_strconcat (g_get_user_config_dir (),
00827 G_DIR_SEPARATOR_S "metacity",
00828 NULL);
00829
00830 session_dir = g_strconcat (metacity_dir,
00831 G_DIR_SEPARATOR_S "sessions",
00832 NULL);
00833
00834 if (mkdir (metacity_dir, 0700) < 0 &&
00835 errno != EEXIST)
00836 {
00837 meta_warning (_("Could not create directory '%s': %s\n"),
00838 metacity_dir, g_strerror (errno));
00839 }
00840
00841 if (mkdir (session_dir, 0700) < 0 &&
00842 errno != EEXIST)
00843 {
00844 meta_warning (_("Could not create directory '%s': %s\n"),
00845 session_dir, g_strerror (errno));
00846 }
00847
00848 meta_topic (META_DEBUG_SM, "Saving session to '%s'\n", full_save_file ());
00849
00850 outfile = fopen (full_save_file (), "w");
00851
00852 if (outfile == NULL)
00853 {
00854 meta_warning (_("Could not open session file '%s' for writing: %s\n"),
00855 full_save_file (), g_strerror (errno));
00856 goto out;
00857 }
00858
00859
00860
00861
00862
00863
00864
00865
00866
00867
00868
00869
00870
00871
00872
00873
00874
00875 fprintf (outfile, "<metacity_session id=\"%s\">\n",
00876 client_id);
00877
00878 windows = meta_display_list_windows (meta_get_display ());
00879 stack_position = 0;
00880
00881 windows = g_slist_sort (windows, meta_display_stack_cmp);
00882 tmp = windows;
00883 stack_position = 0;
00884
00885 while (tmp != NULL)
00886 {
00887 MetaWindow *window;
00888
00889 window = tmp->data;
00890
00891 if (window->sm_client_id)
00892 {
00893 char *sm_client_id;
00894 char *res_class;
00895 char *res_name;
00896 char *role;
00897 char *title;
00898
00899
00900
00901
00902
00903
00904 sm_client_id = encode_text_as_utf8_markup (window->sm_client_id);
00905 res_class = window->res_class ?
00906 encode_text_as_utf8_markup (window->res_class) : NULL;
00907 res_name = window->res_name ?
00908 encode_text_as_utf8_markup (window->res_name) : NULL;
00909 role = window->role ?
00910 encode_text_as_utf8_markup (window->role) : NULL;
00911 if (window->title)
00912 title = g_markup_escape_text (window->title, -1);
00913 else
00914 title = NULL;
00915
00916 meta_topic (META_DEBUG_SM, "Saving session managed window %s, client ID '%s'\n",
00917 window->desc, window->sm_client_id);
00918
00919 fprintf (outfile,
00920 " <window id=\"%s\" class=\"%s\" name=\"%s\" title=\"%s\" role=\"%s\" type=\"%s\" stacking=\"%d\">\n",
00921 sm_client_id,
00922 res_class ? res_class : "",
00923 res_name ? res_name : "",
00924 title ? title : "",
00925 role ? role : "",
00926 window_type_to_string (window->type),
00927 stack_position);
00928
00929 g_free (sm_client_id);
00930 g_free (res_class);
00931 g_free (res_name);
00932 g_free (role);
00933 g_free (title);
00934
00935
00936 if (window->on_all_workspaces)
00937 fputs (" <sticky/>\n", outfile);
00938
00939
00940 if (window->minimized)
00941 fputs (" <minimized/>\n", outfile);
00942
00943
00944 if (META_WINDOW_MAXIMIZED (window))
00945 {
00946 fprintf (outfile,
00947 " <maximized saved_x=\"%d\" saved_y=\"%d\" saved_width=\"%d\" saved_height=\"%d\"/>\n",
00948 window->saved_rect.x,
00949 window->saved_rect.y,
00950 window->saved_rect.width,
00951 window->saved_rect.height);
00952 }
00953
00954
00955 {
00956 int n;
00957 n = meta_workspace_index (window->workspace);
00958 fprintf (outfile,
00959 " <workspace index=\"%d\"/>\n", n);
00960 }
00961
00962
00963 {
00964 int x, y, w, h;
00965 meta_window_get_geometry (window, &x, &y, &w, &h);
00966
00967 fprintf (outfile,
00968 " <geometry x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" gravity=\"%s\"/>\n",
00969 x, y, w, h,
00970 meta_gravity_to_string (window->size_hints.win_gravity));
00971 }
00972
00973 fputs (" </window>\n", outfile);
00974 }
00975 else
00976 {
00977 meta_topic (META_DEBUG_SM, "Not saving window '%s', not session managed\n",
00978 window->desc);
00979 }
00980
00981 tmp = tmp->next;
00982 ++stack_position;
00983 }
00984
00985 g_slist_free (windows);
00986
00987 fputs ("</metacity_session>\n", outfile);
00988
00989 out:
00990 if (outfile)
00991 {
00992
00993 if (ferror (outfile))
00994 {
00995 meta_warning (_("Error writing session file '%s': %s\n"),
00996 full_save_file (), g_strerror (errno));
00997 }
00998 if (fclose (outfile))
00999 {
01000 meta_warning (_("Error closing session file '%s': %s\n"),
01001 full_save_file (), g_strerror (errno));
01002 }
01003 }
01004
01005 g_free (metacity_dir);
01006 g_free (session_dir);
01007 }
01008
01009 typedef enum
01010 {
01011 WINDOW_TAG_NONE,
01012 WINDOW_TAG_DESKTOP,
01013 WINDOW_TAG_STICKY,
01014 WINDOW_TAG_MINIMIZED,
01015 WINDOW_TAG_MAXIMIZED,
01016 WINDOW_TAG_GEOMETRY
01017 } WindowTag;
01018
01019 typedef struct
01020 {
01021 MetaWindowSessionInfo *info;
01022 char *previous_id;
01023 } ParseData;
01024
01025 static void session_info_free (MetaWindowSessionInfo *info);
01026 static MetaWindowSessionInfo* session_info_new (void);
01027
01028 static void start_element_handler (GMarkupParseContext *context,
01029 const gchar *element_name,
01030 const gchar **attribute_names,
01031 const gchar **attribute_values,
01032 gpointer user_data,
01033 GError **error);
01034 static void end_element_handler (GMarkupParseContext *context,
01035 const gchar *element_name,
01036 gpointer user_data,
01037 GError **error);
01038 static void text_handler (GMarkupParseContext *context,
01039 const gchar *text,
01040 gsize text_len,
01041 gpointer user_data,
01042 GError **error);
01043
01044 static GMarkupParser metacity_session_parser = {
01045 start_element_handler,
01046 end_element_handler,
01047 text_handler,
01048 NULL,
01049 NULL
01050 };
01051
01052 static GSList *window_info_list = NULL;
01053
01054 static char*
01055 load_state (const char *previous_save_file)
01056 {
01057 GMarkupParseContext *context;
01058 GError *error;
01059 ParseData parse_data;
01060 char *text;
01061 gsize length;
01062 char *session_file;
01063
01064 session_file = g_strconcat (g_get_user_config_dir (),
01065 G_DIR_SEPARATOR_S "metacity"
01066 G_DIR_SEPARATOR_S "sessions" G_DIR_SEPARATOR_S,
01067 previous_save_file,
01068 NULL);
01069
01070 error = NULL;
01071 if (!g_file_get_contents (session_file,
01072 &text,
01073 &length,
01074 &error))
01075 {
01076 char *canonical_session_file = session_file;
01077
01078
01079 session_file = g_strconcat (g_get_home_dir (),
01080 G_DIR_SEPARATOR_S ".metacity"
01081 G_DIR_SEPARATOR_S "sessions"
01082 G_DIR_SEPARATOR_S,
01083 previous_save_file,
01084 NULL);
01085
01086 if (!g_file_get_contents (session_file,
01087 &text,
01088 &length,
01089 NULL))
01090 {
01091
01092
01093 meta_warning (_("Failed to read saved session file %s: %s\n"),
01094 canonical_session_file, error->message);
01095 g_error_free (error);
01096 g_free (session_file);
01097 g_free (canonical_session_file);
01098 return NULL;
01099 }
01100
01101 g_free (canonical_session_file);
01102 }
01103
01104 meta_topic (META_DEBUG_SM, "Parsing saved session file %s\n", session_file);
01105 g_free (session_file);
01106 session_file = NULL;
01107
01108 parse_data.info = NULL;
01109 parse_data.previous_id = NULL;
01110
01111 context = g_markup_parse_context_new (&metacity_session_parser,
01112 0, &parse_data, NULL);
01113
01114 error = NULL;
01115 if (!g_markup_parse_context_parse (context,
01116 text,
01117 length,
01118 &error))
01119 goto error;
01120
01121
01122 error = NULL;
01123 if (!g_markup_parse_context_end_parse (context, &error))
01124 goto error;
01125
01126 g_markup_parse_context_free (context);
01127
01128 goto out;
01129
01130 error:
01131
01132 meta_warning (_("Failed to parse saved session file: %s\n"),
01133 error->message);
01134 g_error_free (error);
01135
01136 if (parse_data.info)
01137 session_info_free (parse_data.info);
01138
01139 g_free (parse_data.previous_id);
01140 parse_data.previous_id = NULL;
01141
01142 out:
01143
01144 g_free (text);
01145
01146 return parse_data.previous_id;
01147 }
01148
01149
01150 static void
01151 start_element_handler (GMarkupParseContext *context,
01152 const gchar *element_name,
01153 const gchar **attribute_names,
01154 const gchar **attribute_values,
01155 gpointer user_data,
01156 GError **error)
01157 {
01158 ParseData *pd;
01159
01160 pd = user_data;
01161
01162 if (strcmp (element_name, "metacity_session") == 0)
01163 {
01164
01165 int i;
01166
01167 i = 0;
01168 while (attribute_names[i])
01169 {
01170 const char *name;
01171 const char *val;
01172
01173 name = attribute_names[i];
01174 val = attribute_values[i];
01175
01176 if (pd->previous_id)
01177 {
01178 g_set_error (error,
01179 G_MARKUP_ERROR,
01180 G_MARKUP_ERROR_PARSE,
01181 _("<metacity_session> attribute seen but we already have the session ID"));
01182 return;
01183 }
01184
01185 if (strcmp (name, "id") == 0)
01186 {
01187 pd->previous_id = decode_text_from_utf8 (val);
01188 }
01189 else
01190 {
01191 g_set_error (error,
01192 G_MARKUP_ERROR,
01193 G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
01194 _("Unknown attribute %s on <metacity_session> element"),
01195 name);
01196 return;
01197 }
01198
01199 ++i;
01200 }
01201 }
01202 else if (strcmp (element_name, "window") == 0)
01203 {
01204 int i;
01205
01206 if (pd->info)
01207 {
01208 g_set_error (error,
01209 G_MARKUP_ERROR,
01210 G_MARKUP_ERROR_PARSE,
01211 _("nested <window> tag"));
01212 return;
01213 }
01214
01215 pd->info = session_info_new ();
01216
01217 i = 0;
01218 while (attribute_names[i])
01219 {
01220 const char *name;
01221 const char *val;
01222
01223 name = attribute_names[i];
01224 val = attribute_values[i];
01225
01226 if (strcmp (name, "id") == 0)
01227 {
01228 if (*val)
01229 pd->info->id = decode_text_from_utf8 (val);
01230 }
01231 else if (strcmp (name, "class") == 0)
01232 {
01233 if (*val)
01234 pd->info->res_class = decode_text_from_utf8 (val);
01235 }
01236 else if (strcmp (name, "name") == 0)
01237 {
01238 if (*val)
01239 pd->info->res_name = decode_text_from_utf8 (val);
01240 }
01241 else if (strcmp (name, "title") == 0)
01242 {
01243 if (*val)
01244 pd->info->title = g_strdup (val);
01245 }
01246 else if (strcmp (name, "role") == 0)
01247 {
01248 if (*val)
01249 pd->info->role = decode_text_from_utf8 (val);
01250 }
01251 else if (strcmp (name, "type") == 0)
01252 {
01253 if (*val)
01254 pd->info->type = window_type_from_string (val);
01255 }
01256 else if (strcmp (name, "stacking") == 0)
01257 {
01258 if (*val)
01259 {
01260 pd->info->stack_position = atoi (val);
01261 pd->info->stack_position_set = TRUE;
01262 }
01263 }
01264 else
01265 {
01266 g_set_error (error,
01267 G_MARKUP_ERROR,
01268 G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
01269 _("Unknown attribute %s on <window> element"),
01270 name);
01271 session_info_free (pd->info);
01272 pd->info = NULL;
01273 return;
01274 }
01275
01276 ++i;
01277 }
01278 }
01279 else if (strcmp (element_name, "workspace") == 0)
01280 {
01281 int i;
01282
01283 i = 0;
01284 while (attribute_names[i])
01285 {
01286 const char *name;
01287
01288 name = attribute_names[i];
01289
01290 if (strcmp (name, "index") == 0)
01291 {
01292 pd->info->workspace_indices =
01293 g_slist_prepend (pd->info->workspace_indices,
01294 GINT_TO_POINTER (atoi (attribute_values[i])));
01295 }
01296 else
01297 {
01298 g_set_error (error,
01299 G_MARKUP_ERROR,
01300 G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
01301 _("Unknown attribute %s on <window> element"),
01302 name);
01303 session_info_free (pd->info);
01304 pd->info = NULL;
01305 return;
01306 }
01307
01308 ++i;
01309 }
01310 }
01311 else if (strcmp (element_name, "sticky") == 0)
01312 {
01313 pd->info->on_all_workspaces = TRUE;
01314 pd->info->on_all_workspaces_set = TRUE;
01315 }
01316 else if (strcmp (element_name, "minimized") == 0)
01317 {
01318 pd->info->minimized = TRUE;
01319 pd->info->minimized_set = TRUE;
01320 }
01321 else if (strcmp (element_name, "maximized") == 0)
01322 {
01323 int i;
01324
01325 i = 0;
01326 pd->info->maximized = TRUE;
01327 pd->info->maximized_set = TRUE;
01328 while (attribute_names[i])
01329 {
01330 const char *name;
01331 const char *val;
01332
01333 name = attribute_names[i];
01334 val = attribute_values[i];
01335
01336 if (strcmp (name, "saved_x") == 0)
01337 {
01338 if (*val)
01339 {
01340 pd->info->saved_rect.x = atoi (val);
01341 pd->info->saved_rect_set = TRUE;
01342 }
01343 }
01344 else if (strcmp (name, "saved_y") == 0)
01345 {
01346 if (*val)
01347 {
01348 pd->info->saved_rect.y = atoi (val);
01349 pd->info->saved_rect_set = TRUE;
01350 }
01351 }
01352 else if (strcmp (name, "saved_width") == 0)
01353 {
01354 if (*val)
01355 {
01356 pd->info->saved_rect.width = atoi (val);
01357 pd->info->saved_rect_set = TRUE;
01358 }
01359 }
01360 else if (strcmp (name, "saved_height") == 0)
01361 {
01362 if (*val)
01363 {
01364 pd->info->saved_rect.height = atoi (val);
01365 pd->info->saved_rect_set = TRUE;
01366 }
01367 }
01368 else
01369 {
01370 g_set_error (error,
01371 G_MARKUP_ERROR,
01372 G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
01373 _("Unknown attribute %s on <maximized> element"),
01374 name);
01375 return;
01376 }
01377
01378 ++i;
01379 }
01380
01381 if (pd->info->saved_rect_set)
01382 meta_topic (META_DEBUG_SM, "Saved unmaximized size %d,%d %dx%d \n",
01383 pd->info->saved_rect.x,
01384 pd->info->saved_rect.y,
01385 pd->info->saved_rect.width,
01386 pd->info->saved_rect.height);
01387 }
01388 else if (strcmp (element_name, "geometry") == 0)
01389 {
01390 int i;
01391
01392 pd->info->geometry_set = TRUE;
01393
01394 i = 0;
01395 while (attribute_names[i])
01396 {
01397 const char *name;
01398 const char *val;
01399
01400 name = attribute_names[i];
01401 val = attribute_values[i];
01402
01403 if (strcmp (name, "x") == 0)
01404 {
01405 if (*val)
01406 pd->info->rect.x = atoi (val);
01407 }
01408 else if (strcmp (name, "y") == 0)
01409 {
01410 if (*val)
01411 pd->info->rect.y = atoi (val);
01412 }
01413 else if (strcmp (name, "width") == 0)
01414 {
01415 if (*val)
01416 pd->info->rect.width = atoi (val);
01417 }
01418 else if (strcmp (name, "height") == 0)
01419 {
01420 if (*val)
01421 pd->info->rect.height = atoi (val);
01422 }
01423 else if (strcmp (name, "gravity") == 0)
01424 {
01425 if (*val)
01426 pd->info->gravity = window_gravity_from_string (val);
01427 }
01428 else
01429 {
01430 g_set_error (error,
01431 G_MARKUP_ERROR,
01432 G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
01433 _("Unknown attribute %s on <geometry> element"),
01434 name);
01435 return;
01436 }
01437
01438 ++i;
01439 }
01440
01441 meta_topic (META_DEBUG_SM, "Loaded geometry %d,%d %dx%d gravity %s\n",
01442 pd->info->rect.x,
01443 pd->info->rect.y,
01444 pd->info->rect.width,
01445 pd->info->rect.height,
01446 meta_gravity_to_string (pd->info->gravity));
01447 }
01448 else
01449 {
01450 g_set_error (error,
01451 G_MARKUP_ERROR,
01452 G_MARKUP_ERROR_UNKNOWN_ELEMENT,
01453 _("Unknown element %s"),
01454 element_name);
01455 return;
01456 }
01457 }
01458
01459 static void
01460 end_element_handler (GMarkupParseContext *context,
01461 const gchar *element_name,
01462 gpointer user_data,
01463 GError **error)
01464 {
01465 ParseData *pd;
01466
01467 pd = user_data;
01468
01469 if (strcmp (element_name, "window") == 0)
01470 {
01471 g_assert (pd->info);
01472
01473 window_info_list = g_slist_prepend (window_info_list,
01474 pd->info);
01475
01476 meta_topic (META_DEBUG_SM, "Loaded window info from session with class: %s name: %s role: %s\n",
01477 pd->info->res_class ? pd->info->res_class : "(none)",
01478 pd->info->res_name ? pd->info->res_name : "(none)",
01479 pd->info->role ? pd->info->role : "(none)");
01480
01481 pd->info = NULL;
01482 }
01483 }
01484
01485 static void
01486 text_handler (GMarkupParseContext *context,
01487 const gchar *text,
01488 gsize text_len,
01489 gpointer user_data,
01490 GError **error)
01491 {
01492
01493
01494
01495 }
01496
01497 static gboolean
01498 both_null_or_matching (const char *a,
01499 const char *b)
01500 {
01501 if (a == NULL && b == NULL)
01502 return TRUE;
01503 else if (a && b && strcmp (a, b) == 0)
01504 return TRUE;
01505 else
01506 return FALSE;
01507 }
01508
01509 static GSList*
01510 get_possible_matches (MetaWindow *window)
01511 {
01512
01513 GSList *retval;
01514 GSList *tmp;
01515 gboolean ignore_client_id;
01516
01517 retval = NULL;
01518
01519 ignore_client_id = g_getenv ("METACITY_DEBUG_SM") != NULL;
01520
01521 tmp = window_info_list;
01522 while (tmp != NULL)
01523 {
01524 MetaWindowSessionInfo *info;
01525
01526 info = tmp->data;
01527
01528 if ((ignore_client_id ||
01529 both_null_or_matching (info->id, window->sm_client_id)) &&
01530 both_null_or_matching (info->res_class, window->res_class) &&
01531 both_null_or_matching (info->res_name, window->res_name) &&
01532 both_null_or_matching (info->role, window->role))
01533 {
01534 meta_topic (META_DEBUG_SM, "Window %s may match saved window with class: %s name: %s role: %s\n",
01535 window->desc,
01536 info->res_class ? info->res_class : "(none)",
01537 info->res_name ? info->res_name : "(none)",
01538 info->role ? info->role : "(none)");
01539
01540 retval = g_slist_prepend (retval, info);
01541 }
01542 else
01543 {
01544 if (meta_is_verbose ())
01545 {
01546 if (!both_null_or_matching (info->id, window->sm_client_id))
01547 meta_topic (META_DEBUG_SM, "Window %s has SM client ID %s, saved state has %s, no match\n",
01548 window->desc,
01549 window->sm_client_id ? window->sm_client_id : "(none)",
01550 info->id ? info->id : "(none)");
01551 else if (!both_null_or_matching (info->res_class, window->res_class))
01552 meta_topic (META_DEBUG_SM, "Window %s has class %s doesn't match saved class %s, no match\n",
01553 window->desc,
01554 window->res_class ? window->res_class : "(none)",
01555 info->res_class ? info->res_class : "(none)");
01556
01557 else if (!both_null_or_matching (info->res_name, window->res_name))
01558 meta_topic (META_DEBUG_SM, "Window %s has name %s doesn't match saved name %s, no match\n",
01559 window->desc,
01560 window->res_name ? window->res_name : "(none)",
01561 info->res_name ? info->res_name : "(none)");
01562 else if (!both_null_or_matching (info->role, window->role))
01563 meta_topic (META_DEBUG_SM, "Window %s has role %s doesn't match saved role %s, no match\n",
01564 window->desc,
01565 window->role ? window->role : "(none)",
01566 info->role ? info->role : "(none)");
01567 else
01568 meta_topic (META_DEBUG_SM, "???? should not happen - window %s doesn't match saved state %s for no good reason\n",
01569 window->desc, info->id);
01570 }
01571 }
01572
01573 tmp = tmp->next;
01574 }
01575
01576 return retval;
01577 }
01578
01579 static const MetaWindowSessionInfo*
01580 find_best_match (GSList *infos,
01581 MetaWindow *window)
01582 {
01583 GSList *tmp;
01584 const MetaWindowSessionInfo *matching_title;
01585 const MetaWindowSessionInfo *matching_type;
01586
01587 matching_title = NULL;
01588 matching_type = NULL;
01589
01590 tmp = infos;
01591 while (tmp != NULL)
01592 {
01593 MetaWindowSessionInfo *info;
01594
01595 info = tmp->data;
01596
01597 if (matching_title == NULL &&
01598 both_null_or_matching (info->title, window->title))
01599 matching_title = info;
01600
01601 if (matching_type == NULL &&
01602 info->type == window->type)
01603 matching_type = info;
01604
01605 tmp = tmp->next;
01606 }
01607
01608
01609
01610
01611
01612
01613
01614 if (matching_title)
01615 return matching_title;
01616 else if (matching_type)
01617 return matching_type;
01618 else
01619 return infos->data;
01620 }
01621
01622 const MetaWindowSessionInfo*
01623 meta_window_lookup_saved_state (MetaWindow *window)
01624 {
01625 GSList *possibles;
01626 const MetaWindowSessionInfo *info;
01627
01628
01629
01630
01631
01632
01633 if (window->sm_client_id == NULL)
01634 {
01635 meta_topic (META_DEBUG_SM,
01636 "Window %s is not session managed, not checking for saved state\n",
01637 window->desc);
01638 return NULL;
01639 }
01640
01641 possibles = get_possible_matches (window);
01642
01643 if (possibles == NULL)
01644 {
01645 meta_topic (META_DEBUG_SM, "Window %s has no possible matches in the list of saved window states\n",
01646 window->desc);
01647 return NULL;
01648 }
01649
01650 info = find_best_match (possibles, window);
01651
01652 g_slist_free (possibles);
01653
01654 return info;
01655 }
01656
01657 void
01658 meta_window_release_saved_state (const MetaWindowSessionInfo *info)
01659 {
01660
01661
01662
01663 window_info_list = g_slist_remove (window_info_list, info);
01664
01665 session_info_free ((MetaWindowSessionInfo*) info);
01666 }
01667
01668 static void
01669 session_info_free (MetaWindowSessionInfo *info)
01670 {
01671 g_free (info->id);
01672 g_free (info->res_class);
01673 g_free (info->res_name);
01674 g_free (info->title);
01675 g_free (info->role);
01676
01677 g_slist_free (info->workspace_indices);
01678
01679 g_free (info);
01680 }
01681
01682 static MetaWindowSessionInfo*
01683 session_info_new (void)
01684 {
01685 MetaWindowSessionInfo *info;
01686
01687 info = g_new0 (MetaWindowSessionInfo, 1);
01688
01689 info->type = META_WINDOW_NORMAL;
01690 info->gravity = NorthWestGravity;
01691
01692 return info;
01693 }
01694
01695 static char* full_save_path = NULL;
01696
01697 static void
01698 regenerate_save_file (void)
01699 {
01700 g_free (full_save_path);
01701
01702 if (client_id)
01703 full_save_path = g_strconcat (g_get_user_config_dir (),
01704 G_DIR_SEPARATOR_S "metacity"
01705 G_DIR_SEPARATOR_S "sessions" G_DIR_SEPARATOR_S,
01706 client_id,
01707 ".ms",
01708 NULL);
01709 else
01710 full_save_path = NULL;
01711 }
01712
01713 static const char*
01714 full_save_file (void)
01715 {
01716 return full_save_path;
01717 }
01718
01719 static int
01720 windows_cmp_by_title (MetaWindow *a,
01721 MetaWindow *b)
01722 {
01723 return g_utf8_collate (a->title, b->title);
01724 }
01725
01726 typedef struct
01727 {
01728 int child_pid;
01729 int child_pipe;
01730 gboolean shutdown;
01731 } LameClientsDialogData;
01732
01733 static void
01734 finish_interact (gboolean shutdown)
01735 {
01736 if (current_state == STATE_DONE_WITH_INTERACT)
01737 {
01738 SmcInteractDone (session_connection, False );
01739
01740 save_yourself_possibly_done (shutdown, TRUE);
01741 }
01742 }
01743
01744 static gboolean
01745 io_from_warning_dialog (GIOChannel *channel,
01746 GIOCondition condition,
01747 gpointer data)
01748 {
01749 LameClientsDialogData *d;
01750
01751 d = data;
01752
01753 meta_topic (META_DEBUG_PING,
01754 "IO handler from lame clients dialog, condition = %x\n",
01755 condition);
01756
01757 if (condition & (G_IO_HUP | G_IO_NVAL | G_IO_ERR))
01758 {
01759 finish_interact (d->shutdown);
01760
01761
01762 return FALSE;
01763 }
01764 else if (condition & G_IO_IN)
01765 {
01766
01767
01768 char buf[16];
01769 int ret;
01770
01771 ret = read (d->child_pipe, buf, sizeof (buf));
01772 if (ret == 0)
01773 {
01774 finish_interact (d->shutdown);
01775 return FALSE;
01776 }
01777 }
01778
01779
01780 return TRUE;
01781 }
01782
01783 static void
01784 warn_about_lame_clients_and_finish_interact (gboolean shutdown)
01785 {
01786 GSList *lame;
01787 GSList *windows;
01788 char **argv;
01789 int i;
01790 GSList *tmp;
01791 int len;
01792 int child_pid;
01793 int child_pipe;
01794 GError *err;
01795 GIOChannel *channel;
01796 LameClientsDialogData *d;
01797 guint32 timestamp;
01798 char timestampbuf[32];
01799
01800 lame = NULL;
01801 windows = meta_display_list_windows (meta_get_display ());
01802 tmp = windows;
01803 while (tmp != NULL)
01804 {
01805 MetaWindow *window;
01806
01807 window = tmp->data;
01808
01809
01810
01811
01812 if (window->sm_client_id == NULL &&
01813 window->type == META_WINDOW_NORMAL)
01814 lame = g_slist_prepend (lame, window);
01815
01816 tmp = tmp->next;
01817 }
01818
01819 g_slist_free (windows);
01820
01821 if (lame == NULL)
01822 {
01823
01824 finish_interact (shutdown);
01825 return;
01826 }
01827
01828 lame = g_slist_sort (lame, (GCompareFunc) windows_cmp_by_title);
01829
01830 timestamp = meta_display_get_current_time_roundtrip (meta_get_display ());
01831 sprintf (timestampbuf, "%u", timestamp);
01832
01833 len = g_slist_length (lame);
01834 len *= 2;
01835 len += 2;
01836 len += 1;
01837 len += 2;
01838
01839 argv = g_new0 (char*, len);
01840
01841 i = 0;
01842
01843 argv[i] = METACITY_LIBEXECDIR"/metacity-dialog";
01844 ++i;
01845 argv[i] = "--timestamp";
01846 ++i;
01847 argv[i] = timestampbuf;
01848 ++i;
01849 argv[i] = "--warn-about-no-sm-support";
01850 ++i;
01851
01852 tmp = lame;
01853 while (tmp != NULL)
01854 {
01855 MetaWindow *w = tmp->data;
01856
01857 argv[i] = w->title;
01858 ++i;
01859 argv[i] = w->res_class ? w->res_class : "";
01860 ++i;
01861
01862 tmp = tmp->next;
01863 }
01864
01865 child_pipe = -1;
01866 child_pid = -1;
01867 err = NULL;
01868 if (!g_spawn_async_with_pipes ("/",
01869 argv,
01870 NULL,
01871 0,
01872 NULL, NULL,
01873 &child_pid,
01874 NULL,
01875 &child_pipe,
01876 NULL,
01877 &err))
01878 {
01879 meta_warning (_("Error launching metacity-dialog to warn about apps that don't support session management: %s\n"),
01880 err->message);
01881 g_error_free (err);
01882 }
01883
01884 g_free (argv);
01885 g_slist_free (lame);
01886
01887 d = g_new0 (LameClientsDialogData, 1);
01888 d->child_pipe = child_pipe;
01889 d->child_pid = child_pid;
01890 d->shutdown = shutdown;
01891
01892 channel = g_io_channel_unix_new (d->child_pipe);
01893 g_io_add_watch_full (channel, G_PRIORITY_DEFAULT,
01894 G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
01895 io_from_warning_dialog,
01896 d, g_free);
01897 g_io_channel_unref (channel);
01898 }
01899
01900 #endif