1/* -*- Mode: Vala; indent-tabs-mode: nil; tab-width: 4 -*- 2 * 3 * Copyright (C) 2011 Canonical Ltd 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License version 3 as 7 * published by the Free Software Foundation. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 * 17 * Authored by: Robert Ancell <robert.ancell@canonical.com> 18 */ 19 20public const int grid_size = 40; 21 22public class SlickGreeter 23{ 24 public static SlickGreeter singleton; 25 26 public signal void show_message (string text, LightDM.MessageType type); 27 public signal void show_prompt (string text, LightDM.PromptType type); 28 public signal void authentication_complete (); 29 public signal void starting_session (); 30 31 public bool test_mode = false; 32 33 private string state_file; 34 private KeyFile state; 35 36 private Cairo.XlibSurface background_surface; 37 38 public bool orca_needs_kick; 39 private MainWindow main_window; 40 41 private LightDM.Greeter greeter; 42 43 private Canberra.Context canberra_context; 44 45 private static Timer log_timer; 46 47 private DialogDBusInterface dbus_object; 48 49 private SlickGreeter (bool test_mode_) 50 { 51 singleton = this; 52 test_mode = test_mode_; 53 54 /* Prepare to set the background */ 55 debug ("Creating background surface"); 56 background_surface = create_root_surface (Gdk.Screen.get_default ()); 57 58 greeter = new LightDM.Greeter (); 59 greeter.show_message.connect ((text, type) => { show_message (text, type); }); 60 greeter.show_prompt.connect ((text, type) => { show_prompt (text, type); }); 61 greeter.autologin_timer_expired.connect (() => { 62 try { 63 greeter.authenticate_autologin (); 64 } 65 catch(Error e) { 66 warning("Unable to authenticate autologin: %s", e.message); 67 } 68 }); 69 70 greeter.authentication_complete.connect (() => { authentication_complete (); }); 71 var connected = false; 72 try 73 { 74 connected = greeter.connect_to_daemon_sync (); 75 } 76 catch (Error e) 77 { 78 warning ("Failed to connect to LightDM daemon: %s", e.message); 79 } 80 if (!connected && !test_mode) 81 Posix.exit (Posix.EXIT_FAILURE); 82 83 var state_dir = Path.build_filename (Environment.get_user_cache_dir (), "slick-greeter"); 84 DirUtils.create_with_parents (state_dir, 0775); 85 86 var xdg_seat = GLib.Environment.get_variable("XDG_SEAT"); 87 var state_file_name = xdg_seat != null && xdg_seat != "seat0" ? xdg_seat + "-state" : "state"; 88 89 state_file = Path.build_filename (state_dir, state_file_name); 90 state = new KeyFile (); 91 try 92 { 93 state.load_from_file (state_file, KeyFileFlags.NONE); 94 } 95 catch (Error e) 96 { 97 if (!(e is FileError.NOENT)) 98 warning ("Failed to load state from %s: %s\n", state_file, e.message); 99 } 100 101 main_window = new MainWindow (); 102 main_window.destroy.connect(() => { kill_fake_wm (); }); 103 main_window.delete_event.connect(() => 104 { 105 Gtk.main_quit(); 106 return false; 107 }); 108 109 Bus.own_name (BusType.SESSION, "x.dm.SlickGreeter", BusNameOwnerFlags.NONE); 110 111 dbus_object = new DialogDBusInterface (); 112 dbus_object.open_dialog.connect ((type) => 113 { 114 ShutdownDialogType dialog_type; 115 switch (type) 116 { 117 default: 118 case 1: 119 dialog_type = ShutdownDialogType.LOGOUT; 120 break; 121 case 2: 122 dialog_type = ShutdownDialogType.RESTART; 123 break; 124 } 125 main_window.show_shutdown_dialog (dialog_type); 126 }); 127 dbus_object.close_dialog.connect ((type) => { main_window.close_shutdown_dialog (); }); 128 129 start_fake_wm (); 130 Gdk.threads_add_idle (ready_cb); 131 } 132 133 public string? get_state (string key) 134 { 135 try 136 { 137 return state.get_value ("greeter", key); 138 } 139 catch (Error e) 140 { 141 return null; 142 } 143 } 144 145 public void set_state (string key, string value) 146 { 147 state.set_value ("greeter", key, value); 148 var data = state.to_data (); 149 try 150 { 151 FileUtils.set_contents (state_file, data); 152 } 153 catch (Error e) 154 { 155 debug ("Failed to write state: %s", e.message); 156 } 157 } 158 159 public void push_list (GreeterList widget) 160 { 161 main_window.push_list (widget); 162 } 163 164 public void pop_list () 165 { 166 main_window.pop_list (); 167 } 168 169 public static void add_style_class (Gtk.Widget widget) 170 { 171 /* Add style context class lightdm-user-list */ 172 var ctx = widget.get_style_context (); 173 ctx.add_class ("lightdm"); 174 } 175 176 public static string? get_default_session () 177 { 178 var sessions = new List<string> (); 179 sessions.append ("cinnamon"); 180 sessions.append ("mate"); 181 sessions.append ("xfce"); 182 sessions.append ("plasma"); 183 sessions.append ("kde-plasma"); 184 sessions.append ("kde"); 185 sessions.append ("budgie-desktop"); 186 sessions.append ("gnome"); 187 sessions.append ("LXDE"); 188 sessions.append ("lxqt"); 189 sessions.append ("pekwm"); 190 sessions.append ("pantheon"); 191 sessions.append ("i3"); 192 sessions.append ("enlightenment"); 193 sessions.append ("deepin"); 194 sessions.append ("openbox"); 195 sessions.append ("awesome"); 196 sessions.append ("gnome-xorg"); 197 sessions.append ("ubuntu-xorg"); 198 sessions.append ("fynedesk"); 199 200 foreach (string session in sessions) { 201 var path = Path.build_filename ("/usr/local/share/xsessions/", session.concat(".desktop"), null); 202 if (FileUtils.test (path, FileTest.EXISTS)) { 203 return session; 204 } 205 } 206 207 warning ("Could not find a default session."); 208 return null; 209 } 210 211 public static string validate_session (string? session) 212 { 213 /* Make sure the given session actually exists. Return it if it does. 214 otherwise, return the default session. */ 215 if (session != null) { 216 var path = Path.build_filename ("/usr/local/share/xsessions/", session.concat(".desktop"), null); 217 if (!FileUtils.test (path, FileTest.EXISTS) ) { 218 debug ("Invalid session: '%s'", session); 219 session = null; 220 } 221 } 222 223 if (session == null) { 224 var default_session = SlickGreeter.get_default_session (); 225 debug ("Using default session: '%s'", default_session); 226 return default_session; 227 } 228 229 return session; 230 } 231 232 public bool start_session (string? session, Background bg) 233 { 234 /* Explicitly set the right scale before closing window */ 235 var display = Gdk.Display.get_default(); 236 var monitor = display.get_primary_monitor(); 237 var scale = monitor.get_scale_factor (); 238 background_surface.set_device_scale (scale, scale); 239 240 /* Paint our background onto the root window before we close our own window */ 241 // var c = new Cairo.Context (background_surface); 242 // bg.draw_full (c, Background.DrawFlags.NONE); 243 // c = null; 244 // refresh_background (screen, background_surface); 245 246 main_window.before_session_start(); 247 248 if (test_mode) 249 { 250 debug ("Successfully logged in! Quitting..."); 251 Gtk.main_quit (); 252 return true; 253 } 254 255 if (!session_is_valid (session)) 256 { 257 debug ("Session %s is not available, using system default %s instead", session, greeter.default_session_hint); 258 session = greeter.default_session_hint; 259 } 260 261 var result = false; 262 try 263 { 264 result = LightDM.greeter_start_session_sync (greeter, session); 265 } 266 catch (Error e) 267 { 268 warning ("Failed to start session: %s", e.message); 269 } 270 271 if (result) 272 starting_session (); 273 274 return result; 275 } 276 277 private bool session_is_valid (string? session) 278 { 279 if (session == null) 280 return true; 281 282 foreach (var s in LightDM.get_sessions ()) 283 if (s.key == session) 284 return true; 285 286 return false; 287 } 288 289 private bool ready_cb () 290 { 291 debug ("starting system-ready sound"); 292 293 /* Launch canberra */ 294 Canberra.Context.create (out canberra_context); 295 var sound_file = UGSettings.get_string (UGSettings.KEY_PLAY_READY_SOUND); 296 if (sound_file != "") 297 canberra_context.play (0, Canberra.PROP_MEDIA_FILENAME, sound_file); 298 return false; 299 } 300 301 public void show () 302 { 303 debug ("Showing main window"); 304 main_window.realize (); 305 main_window.setup_window(); 306 main_window.show (); 307 main_window.get_window ().focus (Gdk.CURRENT_TIME); 308 main_window.set_keyboard_state (); 309 } 310 311 public bool is_authenticated () 312 { 313 return greeter.is_authenticated; 314 } 315 316 public void authenticate (string? userid = null) 317 { 318 try { 319 greeter.authenticate (userid); 320 } 321 catch(Error e) { 322 warning ("Unable to authenticate greeter for %s, %s", userid, e.message); 323 } 324 } 325 326 public void authenticate_as_guest () 327 { 328 try { 329 greeter.authenticate_as_guest (); 330 } 331 catch(Error e) { 332 warning ("Unable to authenticate greeter for guest: %s", e.message); 333 } 334 } 335 336 public void authenticate_remote (string session, string? userid) 337 { 338 try { 339 SlickGreeter.singleton.greeter.authenticate_remote (session, userid); 340 } 341 catch (Error e) { 342 warning("Unable to authenticate session for user %s, %s", userid, e.message); 343 } 344 } 345 346 public void cancel_authentication () 347 { 348 try { 349 greeter.cancel_authentication (); 350 } 351 catch(Error e) { 352 warning ("Unable to cancel authentication: %s", e.message); 353 } 354 } 355 356 357 public void respond (string response) 358 { 359 try { 360 greeter.respond (response); 361 } 362 catch(Error e) { 363 warning ("Greeter unable to respond: %s", e.message); 364 } 365 } 366 367 public string authentication_user () 368 { 369 return greeter.authentication_user; 370 } 371 372 public string default_session_hint () 373 { 374 return greeter.default_session_hint; 375 } 376 377 public string select_user_hint () 378 { 379 return greeter.select_user_hint; 380 } 381 382 public bool show_manual_login_hint () 383 { 384 return greeter.show_manual_login_hint; 385 } 386 387 public bool show_remote_login_hint () 388 { 389 return greeter.show_remote_login_hint; 390 } 391 392 public bool hide_users_hint () 393 { 394 return greeter.hide_users_hint; 395 } 396 397 public bool has_guest_account_hint () 398 { 399 return greeter.has_guest_account_hint; 400 } 401 402 private Gdk.FilterReturn focus_upon_map (Gdk.XEvent gxevent, Gdk.Event event) 403 { 404 var xevent = (X.Event*)gxevent; 405 if (xevent.type == X.EventType.MapNotify) 406 { 407 var display = Gdk.X11.Display.lookup_for_xdisplay (xevent.xmap.display); 408 var xwin = xevent.xmap.window; 409 var win = new Gdk.X11.Window.foreign_for_display (display, xwin); 410 if (win != null && !xevent.xmap.override_redirect) 411 { 412 /* Check to see if this window is our onboard window, since we don't want to focus it. */ 413 X.Window keyboard_xid = 0; 414 if (main_window.menubar.keyboard_window != null) 415 keyboard_xid = (main_window.menubar.keyboard_window.get_window () as Gdk.X11.Window).get_xid (); 416 417 if (xwin != keyboard_xid && win.get_type_hint() != Gdk.WindowTypeHint.NOTIFICATION) 418 { 419 win.focus (Gdk.CURRENT_TIME); 420 421 /* Make sure to keep keyboard above */ 422 if (main_window.menubar.keyboard_window != null) 423 main_window.menubar.keyboard_window.get_window ().raise (); 424 } 425 } 426 } 427 else if (xevent.type == X.EventType.UnmapNotify) 428 { 429 // Since we aren't keeping track of focus (for example, we don't 430 // track the Z stack of windows) like a normal WM would, when we 431 // decide here where to return focus after another window unmaps, 432 // we don't have much to go on. X will tell us if we should take 433 // focus back. (I could not find an obvious way to determine this, 434 // but checking if the X input focus is RevertTo.None seems 435 // reliable.) 436 437 X.Window xwin; 438 int revert_to; 439 xevent.xunmap.display.get_input_focus (out xwin, out revert_to); 440 441 if (revert_to == X.RevertTo.None) 442 { 443 main_window.get_window ().focus (Gdk.CURRENT_TIME); 444 445 /* Make sure to keep keyboard above */ 446 if (main_window.menubar.keyboard_window != null) 447 main_window.menubar.keyboard_window.get_window ().raise (); 448 } 449 } 450 return Gdk.FilterReturn.CONTINUE; 451 } 452 453 private void start_fake_wm () 454 { 455 /* We want new windows (e.g. the shutdown dialog) to gain focus. 456 We don't really need anything more than that (don't need alt-tab 457 since any dialog should be "modal" or at least dealt with before 458 continuing even if not actually marked as modal) */ 459 var root = Gdk.get_default_root_window (); 460 root.set_events (root.get_events () | Gdk.EventMask.SUBSTRUCTURE_MASK); 461 root.add_filter (focus_upon_map); 462 } 463 464 private void kill_fake_wm () 465 { 466 var root = Gdk.get_default_root_window (); 467 root.remove_filter (focus_upon_map); 468 } 469 470 private static Cairo.XlibSurface? create_root_surface (Gdk.Screen screen) 471 { 472 var visual = screen.get_system_visual (); 473 474 unowned X.Display display = (screen.get_display () as Gdk.X11.Display).get_xdisplay (); 475 unowned X.Screen xscreen = (screen as Gdk.X11.Screen).get_xscreen (); 476 477 var pixmap = X.CreatePixmap (display, 478 (screen.get_root_window () as Gdk.X11.Window).get_xid (), 479 xscreen.width_of_screen (), 480 xscreen.height_of_screen (), 481 visual.get_depth ()); 482 483 /* Convert into a Cairo surface */ 484 var surface = new Cairo.XlibSurface (display, 485 pixmap, 486 (visual as Gdk.X11.Visual).get_xvisual (), 487 xscreen.width_of_screen (), xscreen.height_of_screen ()); 488 489 return surface; 490 } 491 492 // private static void refresh_background (Gdk.Screen screen, Cairo.XlibSurface surface) 493 // { 494 // Gdk.flush (); 495 496 // unowned X.Display display = (screen.get_display () as Gdk.X11.Display).get_xdisplay (); 497 498 // // Ensure Cairo has actually finished its drawing 499 // surface.flush (); 500 // // Use this pixmap for the background 501 // X.SetWindowBackgroundPixmap (display, 502 // (screen.get_root_window () as Gdk.X11.Window).get_xid (), 503 // surface.get_drawable ()); 504 505 // X.ClearWindow (display, (screen.get_root_window () as Gdk.X11.Window).get_xid ()); 506 // } 507 508 private static void log_cb (string? log_domain, LogLevelFlags log_level, string message) 509 { 510 string prefix; 511 switch (log_level & LogLevelFlags.LEVEL_MASK) 512 { 513 case LogLevelFlags.LEVEL_ERROR: 514 prefix = "ERROR:"; 515 break; 516 case LogLevelFlags.LEVEL_CRITICAL: 517 prefix = "CRITICAL:"; 518 break; 519 case LogLevelFlags.LEVEL_WARNING: 520 prefix = "WARNING:"; 521 break; 522 case LogLevelFlags.LEVEL_MESSAGE: 523 prefix = "MESSAGE:"; 524 break; 525 case LogLevelFlags.LEVEL_INFO: 526 prefix = "INFO:"; 527 break; 528 case LogLevelFlags.LEVEL_DEBUG: 529 prefix = "DEBUG:"; 530 break; 531 default: 532 prefix = "LOG:"; 533 break; 534 } 535 536 stderr.printf ("[%+.2fs] %s %s\n", log_timer.elapsed (), prefix, message); 537 } 538 539 private static void check_hidpi () 540 { 541 try { 542 string output; 543 Process.spawn_command_line_sync("/usr/local/bin/slick-greeter-check-hidpi", out output, null, null); 544 output = output.strip(); 545 if (output == "2") { 546 debug ("Activating HiDPI (2x scale ratio)"); 547 GLib.Environment.set_variable ("GDK_SCALE", "2", true); 548 } 549 } 550 catch (Error e){ 551 warning ("Error while setting HiDPI support: %s", e.message); 552 } 553 } 554 555 private static void set_keyboard_layout () 556 { 557 /* Avoid expensive Python execution where possible */ 558 if (!FileUtils.test("/usr/local/etc/default/keyboard", FileTest.EXISTS)) { 559 return; 560 } 561 562 try { 563 Process.spawn_command_line_sync("/usr/local/bin/slick-greeter-set-keyboard-layout", null, null, null); 564 } 565 catch (Error e){ 566 warning ("Error while setting the keyboard layout: %s", e.message); 567 } 568 } 569 570 private static void activate_numlock () 571 { 572 try { 573 Process.spawn_command_line_sync("/usr/local/bin/numlockx on", null, null, null); 574 } 575 catch (Error e){ 576 warning ("Error while activating numlock: %s", e.message); 577 } 578 } 579 580 public static int main (string[] args) 581 { 582 /* Protect memory from being paged to disk, as we deal with passwords 583 584 According to systemd-dev, 585 586 "mlockall() is generally a bad idea and certainly has no place in a graphical program. 587 A program like this uses lots of memory and it is crucial that this memory can be paged 588 out to relieve memory pressure." 589 590 With systemd version 239 the ulimit for RLIMIT_MEMLOCK was set to 16 MiB 591 and therefore the mlockall call would fail. This is lucky becasue the subsequent mmap would not fail. 592 593 With systemd version 240 the RLIMIT_MEMLOCK is now set to 64 MiB 594 and now the mlockall no longer fails. However, it not possible to mmap in all 595 the memory and because that would still exceed the MEMLOCK limit. 596 " 597 See https://bugzilla.redhat.com/show_bug.cgi?id=1662857 & 598 https://github.com/CanonicalLtd/lightdm/issues/55 599 600 RLIMIT_MEMLOCK = 64 MiB means, slick-greeter will most likely fail with 64 bit and 601 will always fail on 32 bit systems. 602 603 Hence we better disable it. */ 604 605 /*Posix.mlockall (Posix.MCL_CURRENT | Posix.MCL_FUTURE);*/ 606 607 /* Disable global menubar */ 608 Environment.unset_variable ("UBUNTU_MENUPROXY"); 609 610 /* Initialize i18n */ 611 Intl.setlocale (LocaleCategory.ALL, ""); 612 Intl.bindtextdomain (Config.GETTEXT_PACKAGE, Config.LOCALEDIR); 613 Intl.bind_textdomain_codeset (Config.GETTEXT_PACKAGE, "UTF-8"); 614 Intl.textdomain (Config.GETTEXT_PACKAGE); 615 616 /* Set up the accessibility stack, in case the user needs it for screen reading etc. */ 617 Environment.set_variable ("GTK_MODULES", "atk-bridge", false); 618 619 /* Fix for https://bugs.launchpad.net/ubuntu/+source/unity-greeter/+bug/1024482 620 Slick-greeter sets the mouse cursor on the root window. 621 Without GKD_CORE_DEVICE_EVENTS set, the DE is unable to apply its own cursor theme and size. 622 */ 623 GLib.Environment.set_variable ("GDK_CORE_DEVICE_EVENTS", "1", true); 624 625 log_timer = new Timer (); 626 Log.set_default_handler (log_cb); 627 628 /* Override dconf settings with /etc settings */ 629 UGSettings.apply_conf_settings (); 630 631 var hidpi = UGSettings.get_string (UGSettings.KEY_ENABLE_HIDPI); 632 debug ("HiDPI support: %s", hidpi); 633 if (hidpi == "auto") { 634 check_hidpi (); 635 } 636 else if (hidpi == "on") { 637 GLib.Environment.set_variable ("GDK_SCALE", "2", true); 638 } 639 640 /* Set the keyboard layout */ 641 set_keyboard_layout (); 642 643 /* Set the numlock state */ 644 if (UGSettings.get_boolean (UGSettings.KEY_ACTIVATE_NUMLOCK)) { 645 debug ("Activating numlock"); 646 activate_numlock (); 647 } 648 649 Gtk.init (ref args); 650 651 debug ("Starting slick-greeter %s UID=%d LANG=%s", Config.VERSION, (int) Posix.getuid (), Environment.get_variable ("LANG")); 652 653 /* Set the cursor to not be the crap default */ 654 debug ("Setting cursor"); 655 Gdk.get_default_root_window ().set_cursor (new Gdk.Cursor.for_display (Gdk.Display.get_default (), Gdk.CursorType.LEFT_PTR)); 656 657 bool do_show_version = false; 658 bool do_test_mode = false; 659 OptionEntry versionOption = { "version", 'v', 0, OptionArg.NONE, ref do_show_version, 660 /* Help string for command line --version flag */ 661 N_("Show release version"), null }; 662 OptionEntry testOption = { "test-mode", 0, 0, OptionArg.NONE, ref do_test_mode, 663 /* Help string for command line --test-mode flag */ 664 N_("Run in test mode"), null }; 665 OptionEntry nullOption = { null }; 666 OptionEntry[] options = { versionOption, testOption, nullOption }; 667 668 debug ("Loading command line options"); 669 var c = new OptionContext ("- Slick Greeter"); 670 c.add_main_entries (options, Config.GETTEXT_PACKAGE); 671 c.add_group (Gtk.get_option_group (true)); 672 try 673 { 674 c.parse (ref args); 675 } 676 catch (Error e) 677 { 678 stderr.printf ("%s\n", e.message); 679 stderr.printf (/* Text printed out when an unknown command-line argument provided */ 680 _("Run '%s --help' to see a full list of available command line options."), args[0]); 681 stderr.printf ("\n"); 682 return Posix.EXIT_FAILURE; 683 } 684 if (do_show_version) 685 { 686 /* Note, not translated so can be easily parsed */ 687 stderr.printf ("slick-greeter %s\n", Config.VERSION); 688 return Posix.EXIT_SUCCESS; 689 } 690 691 if (do_test_mode) 692 debug ("Running in test mode"); 693 694 /* Set GTK+ settings */ 695 debug ("Setting GTK+ settings"); 696 var settings = Gtk.Settings.get_default (); 697 var value = UGSettings.get_string (UGSettings.KEY_THEME_NAME); 698 if (value != "") 699 settings.set ("gtk-theme-name", value, null); 700 value = UGSettings.get_string (UGSettings.KEY_ICON_THEME_NAME); 701 if (value != "") 702 settings.set ("gtk-icon-theme-name", value, null); 703 value = UGSettings.get_string (UGSettings.KEY_FONT_NAME); 704 if (value != "") 705 settings.set ("gtk-font-name", value, null); 706 var double_value = UGSettings.get_double (UGSettings.KEY_XFT_DPI); 707 if (double_value != 0.0) 708 settings.set ("gtk-xft-dpi", (int) (1024 * double_value), null); 709 var boolean_value = UGSettings.get_boolean (UGSettings.KEY_XFT_ANTIALIAS); 710 settings.set ("gtk-xft-antialias", boolean_value, null); 711 value = UGSettings.get_string (UGSettings.KEY_XFT_HINTSTYLE); 712 if (value != "") 713 settings.set ("gtk-xft-hintstyle", value, null); 714 value = UGSettings.get_string (UGSettings.KEY_XFT_RGBA); 715 if (value != "") 716 settings.set ("gtk-xft-rgba", value, null); 717 718 debug ("Creating Slick Greeter"); 719 var greeter = new SlickGreeter (do_test_mode); 720 721 debug ("Showing greeter"); 722 greeter.show (); 723 724 /* Setup a handler for TERM so we quit cleanly */ 725 GLib.Unix.signal_add(GLib.ProcessSignal.TERM, () => { 726 debug("Got a SIGTERM"); 727 Gtk.main_quit(); 728 return true; 729 }); 730 731 debug ("Starting main loop"); 732 Gtk.main (); 733 734 debug ("Cleaning up"); 735 736 var screen = Gdk.Screen.get_default (); 737 unowned X.Display xdisplay = (screen.get_display () as Gdk.X11.Display).get_xdisplay (); 738 739 var window = xdisplay.default_root_window(); 740 var atom = xdisplay.intern_atom ("AT_SPI_BUS", true); 741 742 if (atom != X.None) { 743 xdisplay.delete_property (window, atom); 744 Gdk.flush(); 745 } 746 747 debug ("Exiting"); 748 749 return Posix.EXIT_SUCCESS; 750 } 751} 752 753[DBus (name="org.gnome.SessionManager.EndSessionDialog")] 754public class DialogDBusInterface : Object 755{ 756 public signal void open_dialog (uint32 type); 757 public signal void close_dialog (); 758 759 public void open (uint32 type, uint32 timestamp, uint32 seconds_to_stay_open, ObjectPath[] inhibitor_object_paths) throws GLib.DBusError, GLib.IOError 760 { 761 open_dialog (type); 762 } 763 764 public void close () throws GLib.DBusError, GLib.IOError 765 { 766 close_dialog (); 767 } 768} 769