diff --git a/eekboard/eekboard-client.c b/eekboard/eekboard-client.c
index bc76e33f..b853ae9d 100644
--- a/eekboard/eekboard-client.c
+++ b/eekboard/eekboard-client.c
@@ -325,6 +325,38 @@ eekboard_client_pop_context (EekboardClient *client,
                        NULL);
 }
 
+void
+eekboard_client_show_keyboard (EekboardClient  *client,
+                               GCancellable    *cancellable)
+{
+    g_return_if_fail (EEKBOARD_IS_CLIENT(client));
+
+    g_dbus_proxy_call (G_DBUS_PROXY(client),
+                       "ShowKeyboard",
+                       NULL,
+                       G_DBUS_CALL_FLAGS_NONE,
+                       -1,
+                       cancellable,
+                       eekboard_async_ready_callback,
+                       NULL);
+}
+
+void
+eekboard_client_hide_keyboard (EekboardClient *client,
+                               GCancellable   *cancellable)
+{
+    g_return_if_fail (EEKBOARD_IS_CLIENT(client));
+
+    g_dbus_proxy_call (G_DBUS_PROXY(client),
+                       "HideKeyboard",
+                       NULL,
+                       G_DBUS_CALL_FLAGS_NONE,
+                       -1,
+                       cancellable,
+                       eekboard_async_ready_callback,
+                       NULL);
+}
+
 static void
 send_destroy_context (EekboardClient  *client,
                       EekboardContext *context,
diff --git a/eekboard/eekboard-client.h b/eekboard/eekboard-client.h
index c19c0525..b09db298 100644
--- a/eekboard/eekboard-client.h
+++ b/eekboard/eekboard-client.h
@@ -67,6 +67,10 @@ void             eekboard_client_push_context    (EekboardClient  *eekboard,
                                                   GCancellable    *cancellable);
 void             eekboard_client_pop_context     (EekboardClient  *eekboard,
                                                   GCancellable    *cancellable);
+void             eekboard_client_show_keyboard   (EekboardClient  *eekboard,
+                                                  GCancellable    *cancellable);
+void             eekboard_client_hide_keyboard   (EekboardClient  *eekboard,
+                                                  GCancellable    *cancellable);
 void             eekboard_client_destroy_context (EekboardClient  *eekboard,
                                                   EekboardContext *context,
                                                   GCancellable    *cancellable);
diff --git a/eekboard/eekboard-context-service.c b/eekboard/eekboard-context-service.c
index 11bd518a..4eeab661 100644
--- a/eekboard/eekboard-context-service.c
+++ b/eekboard/eekboard-context-service.c
@@ -201,6 +201,26 @@ eekboard_context_service_real_create_keyboard (EekboardContextService *self,
     return keyboard;
 }
 
+static void
+eekboard_context_service_real_show_keyboard (EekboardContextService *self)
+{
+    gboolean visible = self->priv->visible;
+    self->priv->visible = TRUE;
+    if (visible != self->priv->visible)
+        emit_visibility_changed_signal (self,
+                                        self->priv->visible);
+}
+
+static void
+eekboard_context_service_real_hide_keyboard (EekboardContextService *self)
+{
+    gboolean visible = self->priv->visible;
+    self->priv->visible = FALSE;
+    if (visible != self->priv->visible)
+        emit_visibility_changed_signal (self,
+                                        self->priv->visible);
+}
+
 static void
 eekboard_context_service_set_property (GObject      *object,
                                        guint         prop_id,
@@ -209,7 +229,6 @@ eekboard_context_service_set_property (GObject      *object,
 {
     EekboardContextService *context = EEKBOARD_CONTEXT_SERVICE(object);
     GDBusConnection *connection;
-    gboolean was_visible;
 
     switch (prop_id) {
     case PROP_OBJECT_PATH:
@@ -234,11 +253,12 @@ eekboard_context_service_set_property (GObject      *object,
         context->priv->keyboard = g_value_get_object (value);
         break;
     case PROP_VISIBLE:
-        was_visible = context->priv->visible;
-        context->priv->visible = g_value_get_boolean (value);
-        if (was_visible != context->priv->visible)
-            emit_visibility_changed_signal (context,
-                                            context->priv->visible);
+        if (context->priv->keyboard) {
+            if (g_value_get_boolean (value))
+                eekboard_context_service_show_keyboard (context);
+            else
+                eekboard_context_service_hide_keyboard (context);
+        }
         break;
     case PROP_FULLSCREEN:
         context->priv->fullscreen = g_value_get_boolean (value);
@@ -358,8 +378,8 @@ eekboard_context_service_class_init (EekboardContextServiceClass *klass)
                               sizeof (EekboardContextServicePrivate));
 
     klass->create_keyboard = eekboard_context_service_real_create_keyboard;
-    klass->show_keyboard = NULL;
-    klass->hide_keyboard = NULL;
+    klass->show_keyboard = eekboard_context_service_real_show_keyboard;
+    klass->hide_keyboard = eekboard_context_service_real_hide_keyboard;
 
     gobject_class->constructed = eekboard_context_service_constructed;
     gobject_class->set_property = eekboard_context_service_set_property;
@@ -864,15 +884,13 @@ handle_method_call (GDBusConnection       *connection,
             return;
         }
 
-        if (klass->show_keyboard)
-            klass->show_keyboard (context);
+        eekboard_context_service_show_keyboard (context);
         g_dbus_method_invocation_return_value (invocation, NULL);
         return;
     }
 
     if (g_strcmp0 (method_name, "HideKeyboard") == 0) {
-        if (klass->hide_keyboard)
-            klass->hide_keyboard (context);
+        eekboard_context_service_hide_keyboard (context);
         g_dbus_method_invocation_return_value (invocation, NULL);
         return;
     }
@@ -999,6 +1017,24 @@ eekboard_context_service_disable (EekboardContextService *context)
     }
 }
 
+void
+eekboard_context_service_show_keyboard (EekboardContextService *context)
+{
+    g_return_if_fail (EEKBOARD_IS_CONTEXT_SERVICE(context));
+    g_return_if_fail (context->priv->connection);
+
+    EEKBOARD_CONTEXT_SERVICE_GET_CLASS(context)->show_keyboard (context);
+}
+
+void
+eekboard_context_service_hide_keyboard (EekboardContextService *context)
+{
+    g_return_if_fail (EEKBOARD_IS_CONTEXT_SERVICE(context));
+    g_return_if_fail (context->priv->connection);
+
+    EEKBOARD_CONTEXT_SERVICE_GET_CLASS(context)->hide_keyboard (context);
+}
+
 /**
  * eekboard_context_service_destroy:
  * @context: an #EekboardContextService
diff --git a/eekboard/eekboard-context-service.h b/eekboard/eekboard-context-service.h
index 3804ea4a..4d415e20 100644
--- a/eekboard/eekboard-context-service.h
+++ b/eekboard/eekboard-context-service.h
@@ -84,6 +84,10 @@ GType         eekboard_context_service_get_type
                                               (void) G_GNUC_CONST;
 void          eekboard_context_service_enable (EekboardContextService *context);
 void          eekboard_context_service_disable (EekboardContextService *context);
+void          eekboard_context_service_show_keyboard
+                                              (EekboardContextService *context);
+void          eekboard_context_service_hide_keyboard
+                                              (EekboardContextService *context);
 void          eekboard_context_service_destroy (EekboardContextService *context);
 EekKeyboard  *eekboard_context_service_get_keyboard
                                               (EekboardContextService *context);
diff --git a/eekboard/eekboard-service.c b/eekboard/eekboard-service.c
index ffde7c79..e27a9835 100644
--- a/eekboard/eekboard-service.c
+++ b/eekboard/eekboard-service.c
@@ -55,6 +55,7 @@ struct _EekboardServicePrivate {
 
     GHashTable *context_hash;
     GSList *context_stack;
+    gboolean visible;
 };
 
 G_DEFINE_TYPE (EekboardService, eekboard_service, G_TYPE_OBJECT);
@@ -70,6 +71,8 @@ static const gchar introspection_xml[] =
     "      "
     "    "
     "    "
+    "    "
+    "    "
     "    "
     /* signals */
     "  "
@@ -358,6 +361,14 @@ context_destroyed_cb (EekboardContextService *context, EekboardService *service)
     g_free (object_path);
 }
 
+static void
+on_notify_visible (GObject *object, GParamSpec *spec, gpointer user_data)
+{
+    EekboardService *service = user_data;
+
+    g_object_get (object, "visible", &service->priv->visible, NULL);
+}
+
 static void
 handle_method_call (GDBusConnection       *connection,
                     const gchar           *sender,
@@ -424,6 +435,10 @@ handle_method_call (GDBusConnection       *connection,
         service->priv->context_stack = g_slist_prepend (service->priv->context_stack,
                                                g_object_ref (context));
         eekboard_context_service_enable (context);
+        g_signal_connect (context, "notify::visible",
+                          G_CALLBACK(on_notify_visible), service);
+        if (service->priv->visible)
+            eekboard_context_service_show_keyboard (context);
 
         g_dbus_method_invocation_return_value (invocation, NULL);
         return;
@@ -448,6 +463,9 @@ handle_method_call (GDBusConnection       *connection,
             }
             g_free (object_path);
                 
+            g_signal_handlers_disconnect_by_func (context,
+                                                  G_CALLBACK(on_notify_visible),
+                                                  service);
             eekboard_context_service_disable (context);
             service->priv->context_stack = g_slist_next (service->priv->context_stack);
             if (service->priv->context_stack)
@@ -458,6 +476,24 @@ handle_method_call (GDBusConnection       *connection,
         return;
     }
 
+    if (g_strcmp0 (method_name, "ShowKeyboard") == 0) {
+        if (service->priv->context_stack) {
+            eekboard_context_service_show_keyboard (service->priv->context_stack->data);
+        } else {
+            service->priv->visible = TRUE;
+        }
+        return;
+    }
+
+    if (g_strcmp0 (method_name, "HideKeyboard") == 0) {
+        if (service->priv->context_stack) {
+            eekboard_context_service_hide_keyboard (service->priv->context_stack->data);
+        } else {
+            service->priv->visible = FALSE;
+        }
+        return;
+    }
+
     if (g_strcmp0 (method_name, "Destroy") == 0) {
         g_signal_emit (service, signals[DESTROYED], 0);
         g_dbus_method_invocation_return_value (invocation, NULL);
diff --git a/src/client.c b/src/client.c
index ee0c5770..fed1809f 100644
--- a/src/client.c
+++ b/src/client.c
@@ -305,7 +305,7 @@ client_set_keyboards (Client              *client,
     gboolean retval;
     retval = set_keyboards (client, keyboards);
     if (retval && IS_KEYBOARD_VISIBLE (client))
-        eekboard_context_show_keyboard (client->context, NULL);
+        eekboard_client_show_keyboard (client->eekboard, NULL);
     return retval;
 }
 
@@ -347,7 +347,7 @@ client_enable_xkl (Client *client)
 
     retval = set_keyboards_from_xkl (client);
     if (IS_KEYBOARD_VISIBLE (client))
-        eekboard_context_show_keyboard (client->context, NULL);
+        eekboard_client_show_keyboard (client->eekboard, NULL);
 
     return retval;
 }
@@ -538,21 +538,21 @@ focus_listener_cb (const AtspiEvent *event,
         case ATSPI_ROLE_TERMINAL:
             if (strncmp (event->type, "focus", 5) == 0 || event->detail1 == 1) {
                 client->acc = accessible;
-                eekboard_context_show_keyboard (client->context, NULL);
+                eekboard_client_show_keyboard (client->eekboard, NULL);
             } else if (g_settings_get_boolean (client->settings, "auto-hide") &&
                        event->detail1 == 0 && accessible == client->acc) {
                 client->acc = NULL;
-                eekboard_context_hide_keyboard (client->context, NULL);
+                eekboard_client_hide_keyboard (client->eekboard, NULL);
             }
             break;
         case ATSPI_ROLE_ENTRY:
             if (strncmp (event->type, "focus", 5) == 0 || event->detail1 == 1) {
                 client->acc = accessible;
-                eekboard_context_show_keyboard (client->context, NULL);
+                eekboard_client_show_keyboard (client->eekboard, NULL);
             } else if (g_settings_get_boolean (client->settings, "auto-hide") &&
                        event->detail1 == 0) {
                 client->acc = NULL;
-                eekboard_context_hide_keyboard (client->context, NULL);
+                eekboard_client_hide_keyboard (client->eekboard, NULL);
             }
             break;
             
@@ -560,7 +560,7 @@ focus_listener_cb (const AtspiEvent *event,
             ;
         }
     } else {
-        eekboard_context_hide_keyboard (client->context, NULL);
+        eekboard_client_hide_keyboard (client->eekboard, NULL);
     }
 }
 
@@ -612,7 +612,7 @@ add_match_rule (GDBusConnection *connection,
 static gboolean
 on_hide_keyboard_timeout (Client *client)
 {
-    eekboard_context_hide_keyboard (client->context, NULL);
+    eekboard_client_hide_keyboard (client->eekboard, NULL);
     client->hide_keyboard_timeout_id = 0;
     return FALSE;
 }
@@ -635,7 +635,7 @@ focus_message_filter (GDBusConnection *connection,
                 g_source_remove (client->hide_keyboard_timeout_id);
                 client->hide_keyboard_timeout_id = 0;
             }
-            eekboard_context_show_keyboard (client->context, NULL);
+            eekboard_client_show_keyboard (client->eekboard, NULL);
         } else if (g_settings_get_boolean (client->settings, "auto-hide") &&
                    g_strcmp0 (member, "FocusOut") == 0) {
             guint delay;
diff --git a/src/server-context-service.c b/src/server-context-service.c
index ddbc2b53..e9723622 100644
--- a/src/server-context-service.c
+++ b/src/server-context-service.c
@@ -332,6 +332,9 @@ server_context_service_real_show_keyboard (EekboardContextService *_context)
         update_widget (context);
     g_assert (context->window);
     gtk_widget_show_all (context->window);
+
+    EEKBOARD_CONTEXT_SERVICE_CLASS (server_context_service_parent_class)->
+        show_keyboard (_context);
 }
 
 static void
@@ -341,6 +344,9 @@ server_context_service_real_hide_keyboard (EekboardContextService *_context)
 
     if (context->window)
         gtk_widget_hide (context->window);
+
+    EEKBOARD_CONTEXT_SERVICE_CLASS (server_context_service_parent_class)->
+        hide_keyboard (_context);
 }
 
 static void