WIP: implement Linux IME support
diff --git a/mojo/services/native_viewport/interfaces/native_viewport.mojom b/mojo/services/native_viewport/interfaces/native_viewport.mojom
index 3465414..99e2bad 100644
--- a/mojo/services/native_viewport/interfaces/native_viewport.mojom
+++ b/mojo/services/native_viewport/interfaces/native_viewport.mojom
@@ -34,6 +34,7 @@
   Close();
   SetSize(Size size);
   SetEventDispatcher(NativeViewportEventDispatcher dispatcher);
+  SetKeyEventDispatcher(NativeViewportEventDispatcher dispatcher);
 
   // Requests a ContextProvider capable of producing contexts that draw to
   // this native viewport.
diff --git a/services/BUILD.gn b/services/BUILD.gn
index 0321fe7..e7b815d 100644
--- a/services/BUILD.gn
+++ b/services/BUILD.gn
@@ -41,6 +41,12 @@
     }
   }
 
+  if (is_linux) {
+    deps += [
+      "//services/keyboard",
+    ]
+  }
+
   if (is_linux && !is_fnl) {
     deps += [ "//services/python" ]
   }
diff --git a/services/keyboard/BUILD.gn b/services/keyboard/BUILD.gn
index a6b73fb..e3242cb 100644
--- a/services/keyboard/BUILD.gn
+++ b/services/keyboard/BUILD.gn
@@ -2,19 +2,43 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//build/config/android/config.gni")
-import("//build/config/android/rules.gni")
+if (is_android) {
+  import("//build/config/android/config.gni")
+  import("//build/config/android/rules.gni")
 
-android_library("keyboard") {
-  java_files = [
-    "src/org/chromium/mojo/keyboard/InputConnectionAdaptor.java",
-    "src/org/chromium/mojo/keyboard/KeyboardServiceImpl.java",
-    "src/org/chromium/mojo/keyboard/KeyboardServiceState.java",
-  ]
+  android_library("keyboard") {
+    java_files = [
+      "src/org/chromium/mojo/keyboard/InputConnectionAdaptor.java",
+      "src/org/chromium/mojo/keyboard/KeyboardServiceImpl.java",
+      "src/org/chromium/mojo/keyboard/KeyboardServiceState.java",
+    ]
 
-  deps = [
-    "//mojo/public/java:bindings",
-    "//mojo/public/java:system",
-    "//mojo/services/keyboard/interfaces:interfaces_java",
-  ]
+    deps = [
+      "//mojo/public/java:bindings",
+      "//mojo/public/java:system",
+      "//mojo/services/keyboard/interfaces:interfaces_java",
+    ]
+  }
+}
+
+if (is_linux) {
+  import("//mojo/public/mojo_application.gni")
+
+  mojo_native_application("keyboard") {
+    sources = [
+      "linux/main.cc",
+      "linux/keyboard_service_impl.cc",
+    ]
+
+    deps = [
+      "//base",
+      "//mojo/application",
+      "//mojo/public/cpp/application",
+      "//mojo/public/cpp/bindings:callback",
+      "//mojo/public/cpp/system",
+      "//mojo/services/keyboard/interfaces",
+      "//mojo/services/native_viewport/interfaces",
+      "//mojo/services/native_viewport/cpp:args",
+    ]
+  }
 }
diff --git a/services/keyboard/linux/keyboard_service_impl.cc b/services/keyboard/linux/keyboard_service_impl.cc
new file mode 100644
index 0000000..8b004c0
--- /dev/null
+++ b/services/keyboard/linux/keyboard_service_impl.cc
@@ -0,0 +1,114 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/keyboard/linux/keyboard_service_impl.h"
+
+#include "base/logging.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/connect.h"
+#include "mojo/services/input_events/interfaces/input_key_codes.mojom.h"
+
+namespace keyboard {
+
+LinuxKeyboardServiceImpl::LinuxKeyboardServiceImpl(
+    mojo::InterfaceRequest<::keyboard::KeyboardService> request,
+    mojo::Shell* shell,
+    mojo::ApplicationConnection* connection)
+    : shell_(shell), connection_(connection),
+      binding_(this, request.Pass()),
+      event_dispatcher_binding_(this),
+      client_(nullptr) {
+  // Connect IME to provided client
+  mojo::NativeViewportPtr viewport_service_;
+  connection_->ConnectToService(&viewport_service_);
+
+  if (!viewport_service_) {
+    mojo::ServiceProviderPtr viewport_service_provider;
+    shell_->ConnectToApplication("mojo:native_viewport_service",
+                                 mojo::GetProxy(&viewport_service_provider),
+                                 nullptr);
+    mojo::ConnectToService(viewport_service_provider.get(), &viewport_service_);
+  }
+
+  if (viewport_service_) {
+    mojo::NativeViewportEventDispatcherPtr dispatcher;
+    event_dispatcher_binding_.Bind(GetProxy(&dispatcher));
+    viewport_service_->SetKeyEventDispatcher(dispatcher.Pass());
+  }
+
+}
+
+LinuxKeyboardServiceImpl::~LinuxKeyboardServiceImpl() {
+}
+
+void LinuxKeyboardServiceImpl::Show(::keyboard::KeyboardClientPtr client,
+                                    ::keyboard::KeyboardType type) {
+  client_ = client.Pass();
+}
+
+void LinuxKeyboardServiceImpl::ShowByRequest() {
+}
+
+void LinuxKeyboardServiceImpl::Hide() {
+  client_ = nullptr;
+}
+
+void LinuxKeyboardServiceImpl::SetText(const mojo::String&) {
+  // Not applicable for physical keyboards
+}
+
+void LinuxKeyboardServiceImpl::SetSelection(int32_t start, int32_t end) {
+  // Not applicable for physical keyboards
+}
+
+/*
+  void commitCompletion(CompletionData completion);
+  void commitCorrection(CorrectionData correction);
+  void commitText(String text, int newCursorPosition);
+  void deleteSurroundingText(int beforeLength, int afterLength);
+  void setComposingRegion(int start, int end);
+  void setComposingText(String text, int newCursorPosition);
+  void setSelection(int start, int end);
+  void submit(SubmitAction action);
+*/
+
+// |mojo::NativeViewportEventDispatcher| implementation:
+void LinuxKeyboardServiceImpl::OnEvent(mojo::EventPtr event,
+                                       const mojo::Callback<void()>& callback) {
+  if (event->action == mojo::EventType::KEY_PRESSED && event->key_data->is_char) {
+    if (client_ != nullptr) {
+      switch(event->key_data->windows_key_code) {
+        case mojo::KeyboardCode::BACK: // backspace
+          client_->DeleteSurroundingText(1, 0);
+          break;
+        case mojo::KeyboardCode::DELETE:
+          client_->DeleteSurroundingText(0, 1);
+          break;
+        case mojo::KeyboardCode::HOME:
+          client_->SetSelection(0, 0);
+          break;
+        case mojo::KeyboardCode::END:
+          client_->SetSelection(1, 1);
+          break;
+        case mojo::KeyboardCode::TAB: // tab
+          // TODO: Advance focus, in reverse if shifted
+          break;
+        case mojo::KeyboardCode::RETURN:
+          client_->Submit(::keyboard::SubmitAction::DONE);
+          break;
+        default:
+          base::string16 character;
+          character.push_back(event->key_data->character);
+          mojo::String text(base::UTF16ToUTF8(character));
+          client_->CommitText(text, 1);
+          break;
+      }
+    }
+  }
+  callback.Run();
+}
+
+}  // namespace keyboard
diff --git a/services/keyboard/linux/keyboard_service_impl.h b/services/keyboard/linux/keyboard_service_impl.h
new file mode 100644
index 0000000..dc1b4bd
--- /dev/null
+++ b/services/keyboard/linux/keyboard_service_impl.h
@@ -0,0 +1,48 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SERVICES_KEYBOARD_LINUX_KEYBOARD_SERVICE_IMPL_H_
+#define SERVICES_KEYBOARD_LINUX_KEYBOARD_SERVICE_IMPL_H_
+
+#include "base/macros.h"
+#include "mojo/public/cpp/application/interface_factory.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/interfaces/application/shell.mojom.h"
+#include "mojo/services/keyboard/interfaces/keyboard.mojom.h"
+#include "mojo/services/native_viewport/interfaces/native_viewport.mojom.h"
+
+namespace keyboard {
+
+class LinuxKeyboardServiceImpl : public ::keyboard::KeyboardService,
+                                 public mojo::NativeViewportEventDispatcher {
+ public:
+  explicit LinuxKeyboardServiceImpl(
+      mojo::InterfaceRequest<::keyboard::KeyboardService> request,
+      mojo::Shell* shell,
+      mojo::ApplicationConnection* connection);
+  ~LinuxKeyboardServiceImpl() override;
+  void Show(::keyboard::KeyboardClientPtr client,
+            ::keyboard::KeyboardType type) override;
+  void ShowByRequest() override;
+  void Hide() override;
+  void SetText(const mojo::String& text) override;
+  void SetSelection(int32_t start, int32_t end) override;
+
+  void OnEvent(mojo::EventPtr event,
+               const mojo::Callback<void()>& callback) override;
+
+ private:
+  mojo::Shell* shell_;
+  mojo::ApplicationConnection* connection_;
+  mojo::StrongBinding<::keyboard::KeyboardService> binding_;
+  mojo::Binding<mojo::NativeViewportEventDispatcher> event_dispatcher_binding_;
+
+  ::keyboard::KeyboardClientPtr client_;
+
+  DISALLOW_COPY_AND_ASSIGN(LinuxKeyboardServiceImpl);
+};
+
+}  // namespace keyboard
+
+#endif /* defined(SERVICES_KEYBOARD_LINUX_KEYBOARD_SERVICE_IMPL_H_) */
diff --git a/services/keyboard/linux/main.cc b/services/keyboard/linux/main.cc
new file mode 100644
index 0000000..c7a0bbb
--- /dev/null
+++ b/services/keyboard/linux/main.cc
@@ -0,0 +1,70 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/macros.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/application/connect.h"
+#include "mojo/public/cpp/application/interface_factory.h"
+#include "mojo/services/native_viewport/interfaces/native_viewport.mojom.h"
+#include "services/keyboard/linux/keyboard_service_impl.h"
+
+namespace keyboard {
+
+class KeyboardServiceApp : public mojo::ApplicationDelegate,
+                           public mojo::InterfaceFactory<KeyboardService> {
+ public:
+  KeyboardServiceApp() {}
+  ~KeyboardServiceApp() override {}
+
+ private:
+  // |ApplicationDelegate| override:
+  void Initialize(mojo::ApplicationImpl* app) override {
+    shell_ = app->shell();
+
+  /*
+  // WRONG TROUSERS
+    mojo::ServiceProviderPtr viewport_service_provider;
+    shell_->ConnectToApplication("mojo:native_viewport_service",
+                                 mojo::GetProxy(&viewport_service_provider),
+                                 nullptr);
+    mojo::NativeViewportPtr viewport_service_;
+    mojo::ConnectToService(viewport_service_provider.get(), &viewport_service_);
+
+    mojo::NativeViewportEventDispatcherPtr dispatcher;
+    mojo::Binding<mojo::NativeViewportEventDispatcher> event_dispatcher_binding_(this);
+    event_dispatcher_binding_.Bind(GetProxy(&dispatcher));
+    viewport_service_->SetKeyEventDispatcher(dispatcher.Pass());
+  */
+  }
+
+  // |ApplicationDelegate| override:
+  bool ConfigureIncomingConnection(mojo::ApplicationConnection* connection) override {
+    connection->AddService<KeyboardService>(this);
+    return true;
+  }
+
+  // |InterfaceFactory<KeyboardService>| implementation:
+  void Create(mojo::ApplicationConnection* connection,
+              mojo::InterfaceRequest<KeyboardService> request) override {
+    new LinuxKeyboardServiceImpl(request.Pass(), shell_, connection);
+  }
+
+ private:
+
+  mojo::Shell* shell_;
+  DISALLOW_COPY_AND_ASSIGN(KeyboardServiceApp);
+};
+
+}  // namespace keyboard
+
+MojoResult MojoMain(MojoHandle application_request) {
+  mojo::ApplicationRunnerChromium runner(
+      new keyboard::KeyboardServiceApp());
+  return runner.Run(application_request);
+}
diff --git a/services/native_viewport/native_viewport_impl.cc b/services/native_viewport/native_viewport_impl.cc
index 6b86586..42c3980 100644
--- a/services/native_viewport/native_viewport_impl.cc
+++ b/services/native_viewport/native_viewport_impl.cc
@@ -20,6 +20,8 @@
 
 namespace native_viewport {
 
+mojo::NativeViewportEventDispatcherPtr NativeViewportImpl::key_event_dispatcher_;
+
 NativeViewportImpl::NativeViewportImpl(
     mojo::ApplicationImpl* application,
     bool is_headless,
@@ -94,6 +96,11 @@
   event_dispatcher_ = dispatcher.Pass();
 }
 
+void NativeViewportImpl::SetKeyEventDispatcher(
+    mojo::NativeViewportEventDispatcherPtr dispatcher) {
+  key_event_dispatcher_ = dispatcher.Pass();
+}
+
 void NativeViewportImpl::OnMetricsChanged(mojo::ViewportMetricsPtr metrics) {
   if (metrics_->Equals(*metrics))
     return;
@@ -149,6 +156,13 @@
       pointers_waiting_on_ack_.erase(event->pointer_data->pointer_id);
       break;
 
+    case mojo::EventType::KEY_PRESSED:
+    case mojo::EventType::KEY_RELEASED:
+      if (key_event_dispatcher_.get())
+        key_event_dispatcher_->OnEvent(event.Pass(), callback);
+      return false;
+      break;
+
     default:
       break;
   }
diff --git a/services/native_viewport/native_viewport_impl.h b/services/native_viewport/native_viewport_impl.h
index 3391e88..0077fc2 100644
--- a/services/native_viewport/native_viewport_impl.h
+++ b/services/native_viewport/native_viewport_impl.h
@@ -56,6 +56,8 @@
       mojo::InterfaceRequest<mojo::ContextProvider> request) override;
   void SetEventDispatcher(
       mojo::NativeViewportEventDispatcherPtr dispatcher) override;
+  void SetKeyEventDispatcher(
+      mojo::NativeViewportEventDispatcherPtr dispatcher) override;
 
   // PlatformViewport::Delegate implementation.
   void OnMetricsChanged(mojo::ViewportMetricsPtr metrics) override;
@@ -79,6 +81,7 @@
   CreateCallback create_callback_;
   RequestMetricsCallback metrics_callback_;
   mojo::NativeViewportEventDispatcherPtr event_dispatcher_;
+  static mojo::NativeViewportEventDispatcherPtr key_event_dispatcher_;
   mojo::StrongBinding<mojo::NativeViewport> binding_;
 
   // Set of pointer_ids we've sent a move to and are waiting on an ack.