Exemplo n.º 1
0
    def add_top_bar_widgets(self, hbox):
        #use an event box so we can style it:
        b = gtk.HBox()
        eb = gtk.EventBox()
        eb.add(b)
        eb.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(red=0, green=0, blue=0))
        white = gtk.gdk.Color(red=60000, green=63000, blue=65000)
        hbox.pack_start(eb, expand=True, fill=True)
        for i, l in {
                "encoding.png": "Encoding",
                "speed.png": "Speed",
                "information.png": "Information",
                "keyboard.png": "Keyboard",
                "clipboard.png": "Clipboard"
        }.items():
            icon = self._client.get_pixbuf(i)

            def clicked(*args):
                self.info("clicked(%s)", args)

            button = imagebutton(l,
                                 icon,
                                 clicked_callback=clicked,
                                 label_color=white)
            button.set_relief(gtk.RELIEF_NONE)
            b.add(button)
        icon = self._client.get_pixbuf("xpra.png")
        submenu = gtk.Menu()
        for l, a, s, r in (
            ("Check Option 1", True, True, False),
            ("Disabled Option", True, False, False),
            ("Radio 1", True, True, True),
            ("Radio 2", False, True, True),
        ):
            item = CheckMenuItem(l)

            def item_changed(item):
                self.info("item_changed(%s)", item)

            item.set_active(a)
            item.set_sensitive(s)
            item.set_draw_as_radio(r)
            item.connect("toggled", item_changed)
            submenu.append(item)
        submenu.show_all()

        def show_menu(btn, *args):
            self.info("show_menu(%s, %s)", btn, args)
            submenu.popup(None, None, None, 1, 0)

        menu_button = imagebutton("Xpra",
                                  icon,
                                  clicked_callback=show_menu,
                                  label_color=white)
        menu_button.set_relief(gtk.RELIEF_NONE)
        menu_button.show_all()
        b.add(menu_button)
Exemplo n.º 2
0
    def make_connect_widgets(self, recs, address, port, display):
        icon = self.get_pixbuf("connect.png")
        if len(recs) == 1:
            #single record, single uri:
            uri = self.get_uri(None, *recs[0])

            def clicked(*_args):
                password = self.password_entry.get_text()
                uri = self.get_uri(password, *recs[0])
                self.attach(uri)

            btn = imagebutton("Connect", icon, clicked_callback=clicked)
            return gtk.Label(uri), btn
        #multiple modes / uris
        uri_menu = gtk.combo_box_new_text()
        d = {}
        #sort by protocol so TCP comes first
        order = {"socket": 0, "ssl": 2, "tcp": 4, "ssh": 6}
        if WIN32:
            #on MS Windows, prefer ssh which has a GUI for accepting keys
            #and entering the password:
            order["ssh"] = 0

        def cmp_key(v):
            text = v[-1]  #the text record
            mode = (text or {}).get("mode", "")
            host = v[6]
            host_len = len(host)
            #log("cmp_key(%s) text=%s, mode=%s, host=%s, host_len=%s", v, text, mode, host, host_len)
            #prefer order (from mode), then shorter host string:
            return "%s-%s" % (order.get(mode, mode), host_len)

        srecs = sorted(recs, key=cmp_key)
        for rec in srecs:
            uri = self.get_uri(None, *rec)
            uri_menu.append_text(uri)
            d[uri] = rec

        def connect(*_args):
            uri = uri_menu.get_active_text()
            rec = d[uri]
            password = self.password_entry.get_text()
            uri = self.get_uri(password, *rec)
            self.attach(uri)

        uri_menu.set_active(0)
        btn = imagebutton("Connect", icon, clicked_callback=connect)
        #btn = gtk.Button(">")
        #btn.connect("clicked", connect)
        return uri_menu, btn
Exemplo n.º 3
0
 def image_button(self,
                  label="",
                  tooltip="",
                  icon_pixbuf=None,
                  clicked_cb=None):
     icon = Gtk.Image()
     icon.set_from_pixbuf(icon_pixbuf)
     return imagebutton(label, icon, tooltip, clicked_cb, icon_size=None)
Exemplo n.º 4
0
 def add_tab(self, icon_filename, title, populate_cb, contents):
     icon = self.get_pixbuf(icon_filename)
     def show_tab(*args):
         self.show_tab(contents)
     button = imagebutton(title, icon, clicked_callback=show_tab)
     button.connect("clicked", show_tab)
     button.set_relief(RELIEF_NONE)
     self.tab_button_box.add(button)
     self.tabs.append((title, button, contents, populate_cb))
Exemplo n.º 5
0
 def btn(label, tooltip, callback, default=False):
     btn = imagebutton(label,
                       tooltip=tooltip,
                       clicked_callback=callback,
                       icon_size=32,
                       default=default,
                       label_font=Pango.FontDescription("sans 16"))
     hbox.pack_start(btn)
     return btn
Exemplo n.º 6
0
 def add_tab(self, icon_filename, title, populate_cb, contents):
     icon = self.get_pixbuf(icon_filename)
     def show_tab(*args):
         self.show_tab(contents)
     button = imagebutton(title, icon, clicked_callback=show_tab)
     button.connect("clicked", show_tab)
     button.set_relief(RELIEF_NONE)
     self.tab_button_box.add(button)
     self.tabs.append((title, button, contents, populate_cb))
Exemplo n.º 7
0
def link_btn(link, label=None, icon_name="question.png"):
    def open_link():
        import webbrowser
        webbrowser.open(link)
    def help_clicked(*args):
        log("help_clicked%s opening '%s'", args, link)
        start_thread(open_link, "open-link", True)
    icon = get_icon_pixbuf(icon_name)
    btn = imagebutton("" if icon else label, icon, label, help_clicked, 12, False)
    return btn
Exemplo n.º 8
0
 def add_top_bar_widgets(self, hbox):
     #use an event box so we can style it:
     b = gtk.HBox()
     eb = gtk.EventBox()
     eb.add(b)
     eb.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(red=0, green=0, blue=0))
     white = gtk.gdk.Color(red=60000, green=63000, blue=65000)
     hbox.pack_start(eb, expand=True, fill=True)
     for i,l in {"encoding.png"  : "Encoding",
               "speed.png"       : "Speed",
               "information.png" : "Information",
               "keyboard.png"    : "Keyboard",
               "clipboard.png"   : "Clipboard"}.items():
         icon = self._client.get_pixbuf(i)
         def clicked(*args):
             self.info("clicked(%s)", args)
         button = imagebutton(l, icon, clicked_callback=clicked, label_color=white)
         button.set_relief(gtk.RELIEF_NONE)
         b.add(button)
     icon = self._client.get_pixbuf("xpra.png")
     submenu = gtk.Menu()
     for l, a, s, r in (
                        ("Check Option 1", True, True, False),
                        ("Disabled Option", True, False, False),
                        ("Radio 1", True, True, True),
                        ("Radio 2", False, True, True),
                        ):
         item = CheckMenuItem(l)
         def item_changed(item):
             self.info("item_changed(%s)", item)
         item.set_active(a)
         item.set_sensitive(s)
         item.set_draw_as_radio(r)
         item.connect("toggled", item_changed)
         submenu.append(item)
     submenu.show_all()
     def show_menu(btn, *args):
         self.info("show_menu(%s, %s)", btn, args)
         submenu.popup(None, None, None, 1, 0)
     menu_button = imagebutton("Xpra", icon, clicked_callback=show_menu, label_color=white)
     menu_button.set_relief(gtk.RELIEF_NONE)
     menu_button.show_all()
     b.add(menu_button)
Exemplo n.º 9
0
 def button(self, label_str, tooltip, relpath):
     def cb(_btn):
         cp = os.path.dirname(__file__)
         script = os.path.join(cp, relpath)
         if WIN32 and os.path.sep=="/":
             script = script.replace("/", "\\")
         if not os.path.exists(script):
             if os.path.exists(script+"c"):
                 script += "c"
             else:
                 log.warn("Warning: cannot find '%s'", os.path.basename(relpath))
                 return
         cmd = get_python_execfile_command()+[script]
         exec_command(cmd)
     return imagebutton(label_str, None,
                        tooltip, clicked_callback=cb,
                        icon_size=48)
Exemplo n.º 10
0
    def __init__(self, title="Xpra"):
        self.exit_code = 0
        self.start_session = None
        gtk.Window.__init__(self)
        self.set_title(title)
        self.set_border_width(10)
        self.set_resizable(True)
        self.set_decorated(True)
        self.set_position(WIN_POS_CENTER)
        icon = get_pixbuf("xpra")
        if icon:
            self.set_icon(icon)
        add_close_accel(self, self.quit)
        add_window_accel(self, 'F1', self.show_about)
        self.connect("delete_event", self.quit)

        self.vbox = gtk.VBox(False, 10)
        self.add(self.vbox)
        #with most window managers,
        #the window's title bar already shows "Xpra"
        #title_label = gtk.Label(title)
        #title_label.modify_font(pango.FontDescription("sans 14"))
        #self.vbox.add(title_label)
        self.widgets = []
        label_font = pango.FontDescription("sans 16")
        if has_client:
            icon = get_pixbuf("browse.png")
            self.browse_button = imagebutton(
                "Browse",
                icon,
                "Browse and connect to local sessions",
                clicked_callback=self.browse,
                icon_size=48,
                label_font=label_font)
            self.widgets.append(self.browse_button)
            icon = get_pixbuf("connect.png")
            self.connect_button = imagebutton(
                "Connect",
                icon,
                "Connect to a session",
                clicked_callback=self.show_launcher,
                icon_size=48,
                label_font=label_font)
            self.widgets.append(self.connect_button)
        if has_server:
            icon = get_pixbuf("server-connected.png")
            self.shadow_button = imagebutton(
                "Shadow",
                icon,
                "Start a shadow server",
                clicked_callback=self.start_shadow,
                icon_size=48,
                label_font=label_font)
            if not has_shadow:
                set_tooltip_text(
                    self.shadow_button,
                    "This build of Xpra does not support starting sessions")
                self.shadow_button.set_sensitive(False)
            self.widgets.append(self.shadow_button)
            icon = get_pixbuf("windows.png")
            self.start_button = imagebutton("Start",
                                            icon,
                                            "Start a session",
                                            clicked_callback=self.start,
                                            icon_size=48,
                                            label_font=label_font)
            #not all builds and platforms can start sessions:
            if OSX or WIN32:
                set_tooltip_text(
                    self.start_button,
                    "Starting sessions is not supported on %s" %
                    platform_name(sys.platform))
                self.start_button.set_sensitive(False)
            elif not has_server:
                set_tooltip_text(
                    self.start_button,
                    "This build of Xpra does not support starting sessions")
                self.start_button.set_sensitive(False)
            self.widgets.append(self.start_button)
        assert len(self.widgets) % 2 == 0
        table = gtk.Table(len(self.widgets) // 2, 2, True)
        for i, widget in enumerate(self.widgets):
            table.attach(widget,
                         i % 2,
                         i % 2 + 1,
                         i // 2,
                         i // 2 + 1,
                         xpadding=10,
                         ypadding=10)
        self.vbox.add(table)
        self.vbox.show_all()
        self.set_size_request(640, 100 + 100 * len(self.widgets) // 2)

        def focus_in(window, event):
            log("focus_in(%s, %s)", window, event)

        def focus_out(window, event):
            log("focus_out(%s, %s)", window, event)
            self.reset_cursors()

        self.connect("focus-in-event", focus_in)
        self.connect("focus-out-event", focus_out)
Exemplo n.º 11
0
    def make_connect_widgets(self, key, recs, address, port, display):
        d = {}

        proc = self.clients.get(key)
        if proc and proc.poll() is None:
            icon = self.get_pixbuf("disconnected.png")
            def disconnect_client(btn):
                log("disconnect_client(%s) proc=%s", btn, proc)
                self.clients_disconnecting.add(key)
                proc.terminate()
                self.populate()
            btn = imagebutton("Disconnect", icon, clicked_callback=disconnect_client)
            return gtk.Label("Already connected with pid=%i" % proc.pid), btn, gtk.Label("")

        icon = self.get_pixbuf("browser.png")
        bopen = imagebutton("Open", icon)

        icon = self.get_pixbuf("connect.png")
        if len(recs)==1:
            #single record, single uri:
            rec = recs[0]
            uri = self.get_uri(None, *rec)
            bopen.set_sensitive(uri.startswith("ws"))
            def browser_open(*_args):
                self.browser_open(rec)
            bopen.connect("clicked", browser_open)
            d[uri] = rec
            def clicked(*_args):
                password = self.password_entry.get_text()
                uri = self.get_uri(password, *rec)
                self.attach(key, uri)
            btn = imagebutton("Connect", icon, clicked_callback=clicked)
            return gtk.Label(uri), btn, bopen

        #multiple modes / uris
        uri_menu = gtk.combo_box_new_text()
        uri_menu.set_size_request(340, 48)
        #sort by protocol so TCP comes first
        order = {"socket" : 0, "ssl" :2, "wss" : 3, "tcp" : 4, "ssh" : 6, "ws" : 8}
        if WIN32:
            #on MS Windows, prefer ssh which has a GUI for accepting keys
            #and entering the password:
            order["ssh"] = 0
        def cmp_key(v):
            text = v[-1]    #the text record
            mode = (text or {}).get("mode", "")
            host = v[6]
            host_len = len(host)
            #log("cmp_key(%s) text=%s, mode=%s, host=%s, host_len=%s", v, text, mode, host, host_len)
            #prefer order (from mode), then shorter host string:
            return "%s-%s" % (order.get(mode, mode), host_len)
        srecs = sorted(recs, key=cmp_key)
        for rec in srecs:
            uri = self.get_uri(None, *rec)
            uri_menu.append_text(uri)
            d[uri] = rec
        def connect(*_args):
            uri = uri_menu.get_active_text()
            rec = d[uri]
            password = self.password_entry.get_text()
            uri = self.get_uri(password, *rec)
            self.attach(key, uri)
        uri_menu.set_active(0)
        btn = imagebutton("Connect", icon, clicked_callback=connect)
        def uri_changed(*_args):
            uri = uri_menu.get_active_text()
            bopen.set_sensitive(uri.startswith("ws"))
        uri_menu.connect("changed", uri_changed)
        uri_changed()
        def browser_open_option(*_args):
            uri = uri_menu.get_active_text()
            rec = d[uri]
            self.browser_open(rec)
        bopen.connect("clicked", browser_open_option)
        return uri_menu, btn, bopen
Exemplo n.º 12
0
    def __init__(self, options):
        self.set_options(options)
        self.exit_code = None
        self.options_window = None
        self.default_config = get_defaults()
        #log("default_config=%s", self.default_config)
        #log("options=%s (%s)", options, type(options))
        super().__init__()
        self.set_border_width(20)
        self.set_title("Start Xpra Session")
        self.set_position(Gtk.WindowPosition.CENTER)
        self.set_size_request(640, 300)
        icon = get_icon_pixbuf("xpra.png")
        if icon:
            self.set_icon(icon)
        self.connect("delete-event", self.quit)
        add_close_accel(self, self.quit)

        vbox = Gtk.VBox(False, 0)
        vbox.set_spacing(10)

        # choose the session type:
        hbox = Gtk.HBox(True, 40)
        def rb(sibling=None, label="", cb=None, tooltip_text=None):
            btn = Gtk.RadioButton.new_with_label_from_widget(sibling, label)
            if cb:
                btn.connect("toggled", cb)
            if tooltip_text:
                btn.set_tooltip_text(tooltip_text)
            sf(btn, "sans 16")
            hbox.add(btn)
            return btn
        self.seamless_btn = rb(None, "Seamless Session", self.session_toggled,
                               "Forward an application window(s) individually, seamlessly")
        self.desktop_btn = rb(self.seamless_btn, "Desktop Session", self.session_toggled,
                              "Forward a full desktop environment, contained in a window")
        self.shadow_btn = rb(self.seamless_btn, "Shadow Session", self.session_toggled,
                             "Forward an existing desktop session, shown in a window")
        vbox.pack_start(hbox, False)

        vbox.pack_start(Gtk.HSeparator(), True, False)

        options_box = Gtk.VBox(False, 10)
        vbox.pack_start(options_box, True, False, 20)
        # select host:
        host_box = Gtk.HBox(True, 20)
        options_box.pack_start(host_box, False)
        self.host_label = l("Host:")
        hbox = Gtk.HBox(True, 0)
        host_box.pack_start(self.host_label, True)
        host_box.pack_start(hbox, True, True)
        self.localhost_btn = rb(None, "Local System", self.host_toggled)
        self.remote_btn = rb(self.localhost_btn, "Remote")
        self.remote_btn.set_tooltip_text("Start sessions on a remote system")
        self.address_box = Gtk.HBox(False, 0)
        options_box.pack_start(xal(self.address_box), True, True)
        self.mode_combo = sf(Gtk.ComboBoxText())
        self.address_box.pack_start(xal(self.mode_combo), False)
        for mode in ("SSH", "TCP", "SSL", "WS", "WSS"):
            self.mode_combo.append_text(mode)
        self.mode_combo.set_active(0)
        self.mode_combo.connect("changed", self.mode_changed)
        self.username_entry = sf(Gtk.Entry())
        self.username_entry.set_width_chars(12)
        self.username_entry.set_placeholder_text("Username")
        self.username_entry.set_max_length(255)
        self.address_box.pack_start(xal(self.username_entry), False)
        self.address_box.pack_start(l("@"), False)
        self.host_entry = sf(Gtk.Entry())
        self.host_entry.set_width_chars(24)
        self.host_entry.set_placeholder_text("Hostname or IP address")
        self.host_entry.set_max_length(255)
        self.address_box.pack_start(xal(self.host_entry), False)
        self.address_box.pack_start(Gtk.Label(":"), False)
        self.port_entry = sf(Gtk.Entry())
        self.port_entry.set_text("22")
        self.port_entry.set_width_chars(5)
        self.port_entry.set_placeholder_text("Port")
        self.port_entry.set_max_length(5)
        self.address_box.pack_start(xal(self.port_entry, 0), False)

        self.display_box = Gtk.HBox(True, 20)
        options_box.pack_start(self.display_box, False, True, 20)
        self.display_label = l("Display:")
        self.display_entry = sf(Gtk.Entry())
        self.display_entry.connect('changed', self.display_changed)
        self.display_entry.set_width_chars(10)
        self.display_entry.set_placeholder_text("optional")
        self.display_entry.set_max_length(10)
        self.display_entry.set_tooltip_text("To use a specific X11 display number")
        self.display_combo = sf(Gtk.ComboBoxText())
        self.display_box.pack_start(self.display_label, True)
        self.display_box.pack_start(self.display_entry, True, False)
        self.display_box.pack_start(self.display_combo, True, False)

        # Label:
        self.entry_box = Gtk.HBox(True, 20)
        options_box.pack_start(self.entry_box, False, True, 20)
        self.entry_label = l("Command:")
        self.entry = sf(Gtk.Entry())
        self.entry.set_max_length(255)
        self.entry.set_width_chars(32)
        #self.entry.connect('activate', self.run_command)
        self.entry.connect('changed', self.entry_changed)
        self.entry_box.pack_start(self.entry_label, True)
        self.entry_box.pack_start(self.entry, True, False)

        # or use menus if we have xdg data:
        self.category_box = Gtk.HBox(True, 20)
        options_box.pack_start(self.category_box, False)
        self.category_label = l("Category:")
        self.category_combo = sf(Gtk.ComboBoxText())
        self.category_box.pack_start(self.category_label, True)
        self.category_box.pack_start(self.category_combo, True, True)
        self.category_combo.connect("changed", self.category_changed)
        self.categories = {}

        self.command_box = Gtk.HBox(True, 20)
        options_box.pack_start(self.command_box, False)
        self.command_label = l("Command:")
        self.command_combo = sf(Gtk.ComboBoxText())
        self.command_box.pack_start(self.command_label, True)
        self.command_box.pack_start(self.command_combo, True, True)
        self.command_combo.connect("changed", self.command_changed)
        self.commands = {}
        self.xsessions = None
        self.desktop_entry = None

        # start options:
        hbox = Gtk.HBox(False, 20)
        options_box.pack_start(hbox, False)
        self.exit_with_children_cb = sf(Gtk.CheckButton())
        self.exit_with_children_cb.set_label("exit with application")
        hbox.add(xal(self.exit_with_children_cb, 0.5))
        self.exit_with_children_cb.set_active(True)
        self.exit_with_client_cb = sf(Gtk.CheckButton())
        self.exit_with_client_cb.set_label("exit with client")
        hbox.add(xal(self.exit_with_client_cb, 0.5))
        self.exit_with_client_cb.set_active(False)
        # session options:
        hbox = Gtk.HBox(False, 12)
        hbox.pack_start(l("Options:"), True, False)
        for label_text, icon_name, tooltip_text, cb in (
            ("Features",    "features.png", "Session features", self.configure_features),
            ("Network",     "connect.png",  "Network options", self.configure_network),
            ("Display",     "display.png",  "Display settings", self.configure_display),
            ("Encodings",   "encoding.png", "Picture compression", self.configure_encoding),
            ("Keyboard",    "keyboard.png", "Keyboard layout and options", self.configure_keyboard),
            ("Audio",       "speaker.png",  "Audio forwarding options", self.configure_audio),
            ("Webcam",      "webcam.png",   "Webcam forwarding options", self.configure_webcam),
            ("Printing",    "printer.png",  "Printer forwarding options", self.configure_printing),
            ):
            icon = get_icon_pixbuf(icon_name)
            ib = imagebutton("", icon=icon, tooltip=label_text or tooltip_text,
                                  clicked_callback=cb, icon_size=32,
                                  label_font=Pango.FontDescription("sans 14"))
            hbox.pack_start(ib, True, False)
        options_box.pack_start(hbox, True, False)

        # Action buttons:
        hbox = Gtk.HBox(False, 20)
        vbox.pack_start(hbox, False, True, 20)
        def btn(label, tooltip, callback, default=False):
            ib = imagebutton(label, tooltip=tooltip, clicked_callback=callback, icon_size=32,
                            default=default, label_font=Pango.FontDescription("sans 16"))
            hbox.pack_start(ib)
            return ib
        self.cancel_btn = btn("Cancel", "",
                              self.quit)
        self.run_btn = btn("Start", "Start the xpra session",
                           self.run_command)
        self.runattach_btn = btn("Start & Attach", "Start the xpra session and attach to it",
                                 self.runattach_command, True)
        self.runattach_btn.set_sensitive(False)

        vbox.show_all()
        self.display_combo.hide()
        self.add(vbox)
        #load encodings in the background:
        self.load_codecs_thread = start_thread(self.load_codecs, "load-codecs", daemon=True)
        #poll the list of X11 displays in the background:
        self.display_list = ()
        if not OSX:
            self.load_displays_thread = start_thread(self.load_displays, "load-displays", daemon=True)
Exemplo n.º 13
0
    def __init__(self, title="Xpra"):
        self.exit_code = 0
        self.start_session = None
        Gtk.Window.__init__(self)

        hb = Gtk.HeaderBar()
        hb.set_show_close_button(True)
        hb.props.title = "Xpra"
        self.set_titlebar(hb)
        hb.add(self.button("About", "help-about", about))
        try:
            from xpra.client.gtk_base.toolbox import ToolboxGUI
        except ImportError:
            pass
        else:

            def show():
                w = None

                def hide(*_args):
                    w.hide()

                ToolboxGUI.quit = hide
                w = ToolboxGUI()
                w.show()

            hb.add(self.button("Toolbox", "applications-utilities", show))
            hb.show_all()

        self.set_title(title)
        self.set_border_width(10)
        self.set_resizable(True)
        self.set_decorated(True)
        self.set_position(Gtk.WindowPosition.CENTER)
        icon = get_icon_pixbuf("xpra.png")
        if icon:
            self.set_icon(icon)
        add_close_accel(self, self.quit)
        add_window_accel(self, 'F1', self.show_about)
        self.connect("delete_event", self.quit)
        self.set_wmclass("xpra-gui", "Xpra-GUI")

        self.vbox = Gtk.VBox(False, 10)
        self.add(self.vbox)
        #with most window managers,
        #the window's title bar already shows "Xpra"
        #title_label = Gtk.Label(title)
        #title_label.modify_font(pango.FontDescription("sans 14"))
        #self.vbox.add(title_label)
        self.widgets = []
        label_font = Pango.FontDescription("sans 16")
        if has_client:
            icon = get_icon_pixbuf("browse.png")
            self.browse_button = imagebutton(
                "Browse",
                icon,
                "Browse and connect to local sessions",
                clicked_callback=self.browse,
                icon_size=48,
                label_font=label_font)
            self.widgets.append(self.browse_button)
            icon = get_icon_pixbuf("connect.png")
            self.connect_button = imagebutton(
                "Connect",
                icon,
                "Connect to a session",
                clicked_callback=self.show_launcher,
                icon_size=48,
                label_font=label_font)
            self.widgets.append(self.connect_button)
        if has_server:
            icon = get_icon_pixbuf("server-connected.png")
            self.shadow_button = imagebutton(
                "Shadow",
                icon,
                "Start a shadow server",
                clicked_callback=self.start_shadow,
                icon_size=48,
                label_font=label_font)
            if not has_shadow:
                self.shadow_button.set_tooltip_text(
                    "This build of Xpra does not support starting sessions")
                self.shadow_button.set_sensitive(False)
            self.widgets.append(self.shadow_button)
            icon = get_icon_pixbuf("windows.png")
            self.start_button = imagebutton("Start",
                                            icon,
                                            "Start a session",
                                            clicked_callback=self.start,
                                            icon_size=48,
                                            label_font=label_font)
            #not all builds and platforms can start sessions:
            if OSX or WIN32:
                self.start_button.set_tooltip_text(
                    "Starting sessions is not supported on %s" %
                    platform_name(sys.platform))
                self.start_button.set_sensitive(False)
            elif not has_server:
                self.start_button.set_tooltip_text(
                    "This build of Xpra does not support starting sessions")
                self.start_button.set_sensitive(False)
            self.widgets.append(self.start_button)
        assert len(self.widgets) % 2 == 0
        table = Gtk.Table(len(self.widgets) // 2, 2, True)
        for i, widget in enumerate(self.widgets):
            table.attach(widget,
                         i % 2,
                         i % 2 + 1,
                         i // 2,
                         i // 2 + 1,
                         xpadding=10,
                         ypadding=10)
        self.vbox.add(table)
        self.vbox.show_all()
        self.set_size_request(640, 100 + 100 * len(self.widgets) // 2)

        def focus_in(window, event):
            log("focus_in(%s, %s)", window, event)

        def focus_out(window, event):
            log("focus_out(%s, %s)", window, event)
            self.reset_cursors()

        self.connect("focus-in-event", focus_in)
        self.connect("focus-out-event", focus_out)