blob: 4c3f14f08805fe5c85c2a08da2f8c28f0df3df17 [file] [log] [blame]
// Copyright 2010-2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "renderer/win32/win32_server.h"
#include <memory>
#include "base/logging.h"
#include "base/port.h"
#include "base/run_level.h"
#include "base/util.h"
#include "renderer/renderer_command.pb.h"
#include "renderer/win32/window_manager.h"
using std::unique_ptr;
namespace mozc {
namespace renderer {
namespace {
bool IsIMM32Message(const commands::RendererCommand &command) {
if (!command.has_application_info()) {
return false;
}
if (!command.application_info().has_input_framework()) {
return false;
}
return (command.application_info().input_framework() ==
commands::RendererCommand::ApplicationInfo::IMM32);
}
bool IsTSFMessage(const commands::RendererCommand &command) {
if (!command.has_application_info()) {
return false;
}
if (!command.application_info().has_input_framework()) {
return false;
}
return (command.application_info().input_framework() ==
commands::RendererCommand::ApplicationInfo::TSF);
}
} // namespace
namespace win32 {
Win32Server::Win32Server()
: event_(nullptr),
window_manager_(new WindowManager) {
// Manual reset event to notify we have a renderer command
// to be handled in the UI thread.
// The renderer command is serialized into "message_".
event_ = ::CreateEvent(nullptr, FALSE, FALSE, nullptr);
DCHECK_NE(nullptr, event_)
<< "CreateEvent failed, Error = " << ::GetLastError();
}
Win32Server::~Win32Server() {
::CloseHandle(event_);
}
void Win32Server::AsyncHide() {
{
// Cancel the remaining event
scoped_lock l(&mutex_);
::ResetEvent(event_);
}
window_manager_->AsyncHideAllWindows();
}
void Win32Server::AsyncQuit() {
{
// Cancel the remaining event
scoped_lock l(&mutex_);
::ResetEvent(event_);
}
window_manager_->AsyncQuitAllWindows();
}
bool Win32Server::Activate() {
// TODO(yukawa): Implement this.
return true;
}
bool Win32Server::IsAvailable() const {
// TODO(yukawa): Implement this.
return true;
}
bool Win32Server::ExecCommand(const commands::RendererCommand &command) {
VLOG(2) << command.DebugString();
switch (command.type()) {
case commands::RendererCommand::NOOP:
break;
case commands::RendererCommand::SHUTDOWN:
// Do not destroy windows here.
window_manager_->HideAllWindows();
break;
case commands::RendererCommand::UPDATE:
if (!command.visible()) {
window_manager_->HideAllWindows();
} else if (IsIMM32Message(command)) {
window_manager_->UpdateLayoutIMM32(command);
} else if (IsTSFMessage(command)) {
window_manager_->UpdateLayoutTSF(command);
} else {
LOG(WARNING) << "output/left/bottom are not set";
}
break;
default:
LOG(WARNING) << "Unknown command: " << command.type();
break;
}
return true;
}
void Win32Server::SetSendCommandInterface(
client::SendCommandInterface *send_command_interface) {
window_manager_->SetSendCommandInterface(send_command_interface);
}
bool Win32Server::AsyncExecCommand(string *proto_message) {
// Take the ownership of |proto_message|.
unique_ptr<string> proto_message_owner(proto_message);
scoped_lock l(&mutex_);
if (message_ == *proto_message_owner.get()) {
// This is exactly the same to the previous message. Theoretically it is
// safe to do nothing here.
return true;
}
// Since mozc rendering protocol is state-less, we can always ignore the
// previous content of |message_|.
message_.swap(*proto_message_owner.get());
// Set the event signaled to mark we have a message to render.
::SetEvent(event_);
return true;
}
int Win32Server::StartMessageLoop() {
window_manager_->Initialize();
int return_code = 0;
while (true) {
// WindowManager::IsAvailable() returns false at least one window does not
// have a valid window handle.
// - WindowManager::Initialize() somehow failed.
// - A window is closed as a result of WM_CLOSE sent from an external
// process. This may happen if the shell or restart manager wants to shut
// down the renderer.
if (!window_manager_->IsAvailable()) {
// Mark this thread to quit.
::PostQuitMessage(0);
break; // exit message pump.
}
// Wait for the next window message or next rendering message.
const DWORD wait_result =
::MsgWaitForMultipleObjects(1, &event_, FALSE, INFINITE, QS_ALLINPUT);
if (wait_result == WAIT_OBJECT_0) {
// "event_" is signaled so that we have to handle the renderer command
// stored in "message_"
string message;
{
scoped_lock l(&mutex_);
message.assign(message_);
::ResetEvent(event_);
}
commands::RendererCommand command;
if (command.ParseFromString(message)) {
ExecCommandInternal(command);
if (command.type() == commands::RendererCommand::SHUTDOWN) {
break; // exit message pump.
}
} else {
LOG(ERROR) << "ParseFromString failed";
}
} else if (wait_result == WAIT_OBJECT_0 + 1) {
// We have at least one window message. Let's handle them.
while (true) {
MSG msg = {};
if (::PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE) == 0) {
// No more message.
break; // exit message pump.
}
if (msg.message == WM_QUIT) {
return_code = msg.wParam;
VLOG(0) << "Reveiced WM_QUIT.";
break; // exit message pump.
}
window_manager_->PreTranslateMessage(msg);
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
} else if (wait_result == WAIT_ABANDONED_0) {
LOG(INFO) << "WAIT_ABANDONED_0";
} else {
LOG(ERROR) << "MsgWaitForMultipleObjects returned unexpected result: "
<< wait_result;
}
}
// Ensure that IPC thread is terminated.
// TODO(yukawa): Update the IPC server so that we can set a timeout here.
Terminate();
// Make sure all the windows are closed.
// WindowManager::DestroyAllWindows supports multiple calls on the UI thread.
window_manager_->DestroyAllWindows();
return return_code;
}
} // namespace win32
} // namespace renderer
} // namespace mozc