blob: 372cec18486385c430cd1404bede8d56843c2837 [file] [log] [blame]
// Copyright (c) 2010 The Chromium OS 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 "window_manager/mock_chrome.h"
#include <algorithm>
#include <string>
#include <cairomm/context.h>
#include "base/command_line.h"
#include "base/string_util.h"
#include "cros/chromeos_wm_ipc_enums.h"
#include "window_manager/atom_cache.h"
#include "window_manager/util.h"
#include "window_manager/wm_ipc.h"
#include "window_manager/x11/real_x_connection.h"
DEFINE_string(image_dir, "data/", "Path to static image files");
DEFINE_string(new_panel_image, "data/panel_chat.png",
"Image to use when creating a new panel");
DEFINE_int32(num_panels, 5, "Number of panels to open");
DEFINE_int32(num_windows, 3, "Number of windows to open");
DEFINE_string(panel_images, "data/panel_chat.png",
"Comma-separated images to use for panels");
DEFINE_string(panel_titles, "Chat",
"Comma-separated titles to use for panels");
DEFINE_string(screen_locker_image, "data/screen_locker.jpg",
"Image to use for screen locker windows");
DEFINE_string(tab_images,
"data/chrome_page_google.png,"
"data/chrome_page_gmail.png,"
"data/chrome_page_chrome.png",
"Comma-separated images to use for tabs");
DEFINE_string(tab_titles, "Google,Gmail,Google Chrome",
"Comma-separated titles to use for tabs");
DEFINE_int32(tabs_per_window, 3, "Number of tabs to add to each window");
DEFINE_int32(window_height, 640, "Window height");
DEFINE_int32(window_width, 920, "Window width");
using std::make_pair;
using std::max;
using std::string;
using std::tr1::shared_ptr;
using std::vector;
using window_manager::AtomCache;
using window_manager::RealXConnection;
using window_manager::WmIpc;
namespace mock_chrome {
const int PanelTitlebar::kWidth = 150;
const int PanelTitlebar::kHeight = 26;
Glib::RefPtr<Gdk::Pixbuf> PanelTitlebar::image_bg_;
Glib::RefPtr<Gdk::Pixbuf> PanelTitlebar::image_bg_focused_;
const char* PanelTitlebar::kFontFace = "Arial";
const double PanelTitlebar::kFontSize = 13;
const double PanelTitlebar::kFontPadding = 6;
const int PanelTitlebar::kDragThreshold = 10;
const int ChromeWindow::kTabDragThreshold = 10;
const char* ChromeWindow::kTabFontFace = "DejaVu Sans";
const double ChromeWindow::kTabFontSize = 13;
const int ChromeWindow::kTabFontPadding = 5;
const int ChromeWindow::kLockTimeoutMs = 400;
const int ChromeWindow::kShutdownTimeoutMs = 400;
const int ChromeWindow::kLockToShutdownThresholdMs = 400;
// Static images.
Glib::RefPtr<Gdk::Pixbuf> ChromeWindow::image_nav_bg_;
Glib::RefPtr<Gdk::Pixbuf> ChromeWindow::image_nav_left_;
Glib::RefPtr<Gdk::Pixbuf> ChromeWindow::image_nav_right_;
Glib::RefPtr<Gdk::Pixbuf> ChromeWindow::image_tab_bg_;
Glib::RefPtr<Gdk::Pixbuf> ChromeWindow::image_tab_hl_;
Glib::RefPtr<Gdk::Pixbuf> ChromeWindow::image_tab_nohl_;
Glib::RefPtr<Gdk::Pixbuf> ChromeWindow::image_tab_right_hl_left_nohl_;
Glib::RefPtr<Gdk::Pixbuf> ChromeWindow::image_tab_right_hl_left_none_;
Glib::RefPtr<Gdk::Pixbuf> ChromeWindow::image_tab_right_nohl_left_hl_;
Glib::RefPtr<Gdk::Pixbuf> ChromeWindow::image_tab_right_nohl_left_nohl_;
Glib::RefPtr<Gdk::Pixbuf> ChromeWindow::image_tab_right_nohl_left_none_;
Glib::RefPtr<Gdk::Pixbuf> ChromeWindow::image_tab_right_none_left_hl_;
Glib::RefPtr<Gdk::Pixbuf> ChromeWindow::image_tab_right_none_left_nohl_;
int ChromeWindow::kTabHeight = 0;
int ChromeWindow::kNavHeight = 0;
// Copy a GdkEventClient struct into an XEvent.
static bool GetWmIpcMessage(const GdkEventClient& event,
WmIpc* wm_ipc,
WmIpc::Message* msg_out) {
return wm_ipc->GetMessage(
GDK_WINDOW_XID(event.window),
gdk_x11_atom_to_xatom(event.message_type),
event.data_format,
event.data.l,
msg_out);
}
static void DrawImage(Glib::RefPtr<Gdk::Pixbuf>& image, // NOLINT
Gtk::Widget* widget,
int dest_x, int dest_y,
int dest_width, int dest_height) {
CHECK(widget);
CHECK(dest_width > 0);
CHECK(dest_height > 0);
// Only scale the original image if we have to.
Glib::RefPtr<Gdk::Pixbuf> scaled_image = image;
if (dest_width != image->get_width() || dest_height != image->get_height()) {
scaled_image = image->scale_simple(
dest_width, dest_height, Gdk::INTERP_BILINEAR);
}
scaled_image->render_to_drawable(
widget->get_window(),
widget->get_style()->get_black_gc(),
0, 0, // src position
dest_x, dest_y, // dest position
dest_width, dest_height,
Gdk::RGB_DITHER_NONE,
0, 0); // x and y dither
}
Tab::Tab(const string& image_filename, const string& title)
: image_(Gdk::Pixbuf::create_from_file(image_filename)),
title_(title) {
}
void Tab::RenderToGtkWidget(Gtk::Widget* widget,
int x, int y,
int width, int height) {
DrawImage(image_, widget, x, y, width, height);
}
ChromeWindow::ChromeWindow(MockChrome* chrome, int width, int height)
: chrome_(chrome),
width_(width),
height_(height),
active_tab_index_(-1),
dragging_tab_(false),
tab_drag_start_offset_x_(0),
tab_drag_start_offset_y_(0),
fullscreen_(false),
power_button_is_pressed_(false),
lock_timeout_id_(-1),
lock_to_shutdown_timeout_id_(-1),
shutdown_timeout_id_(-1) {
if (!image_nav_bg_) {
InitImages();
}
set_size_request(width_, height_);
realize();
xid_ = GDK_WINDOW_XWINDOW(Glib::unwrap(get_window()));
CHECK(chrome_->wm_ipc()->SetWindowType(
xid(), chromeos::WM_IPC_WINDOW_CHROME_TOPLEVEL, NULL));
add_events(Gdk::BUTTON_PRESS_MASK |
Gdk::BUTTON_RELEASE_MASK |
Gdk::POINTER_MOTION_MASK);
show_all();
}
void ChromeWindow::InsertTab(Tab* tab, size_t index) {
shared_ptr<TabInfo> info(new TabInfo(tab));
if (index > tabs_.size()) {
index = tabs_.size();
}
tabs_.insert(tabs_.begin() + index, info);
if (static_cast<int>(index) <= active_tab_index_) {
active_tab_index_++;
}
if (active_tab_index_ < 0) {
active_tab_index_ = 0;
DrawView();
}
DrawTabs();
}
Tab* ChromeWindow::RemoveTab(size_t index) {
CHECK(index < tabs_.size());
shared_ptr<TabInfo> info = tabs_[index];
tabs_.erase(tabs_.begin() + index);
if (active_tab_index_ >= static_cast<int>(tabs_.size())) {
active_tab_index_ = static_cast<int>(tabs_.size()) - 1;
}
return info->tab.release();
}
void ChromeWindow::ActivateTab(int index) {
CHECK(index >= 0);
CHECK(index < static_cast<int>(tabs_.size()));
if (index == active_tab_index_) {
return;
}
active_tab_index_ = index;
DrawTabs();
DrawView();
}
// static
void ChromeWindow::InitImages() {
CHECK(!image_nav_bg_);
image_nav_bg_ = Gdk::Pixbuf::create_from_file(
FLAGS_image_dir + "chrome_nav_bg.png");
image_nav_left_ = Gdk::Pixbuf::create_from_file(
FLAGS_image_dir + "chrome_nav_left.png");
image_nav_right_ = Gdk::Pixbuf::create_from_file(
FLAGS_image_dir + "chrome_nav_right.png");
image_tab_bg_ = Gdk::Pixbuf::create_from_file(
FLAGS_image_dir + "chrome_tab_bg.png");
image_tab_hl_ = Gdk::Pixbuf::create_from_file(
FLAGS_image_dir + "chrome_tab_hl.png");
image_tab_nohl_ = Gdk::Pixbuf::create_from_file(
FLAGS_image_dir + "chrome_tab_nohl.png");
image_tab_right_hl_left_nohl_ = Gdk::Pixbuf::create_from_file(
FLAGS_image_dir + "chrome_tab_right_hl_left_nohl.png");
image_tab_right_hl_left_none_ = Gdk::Pixbuf::create_from_file(
FLAGS_image_dir + "chrome_tab_right_hl_left_none.png");
image_tab_right_nohl_left_hl_ = Gdk::Pixbuf::create_from_file(
FLAGS_image_dir + "chrome_tab_right_nohl_left_hl.png");
image_tab_right_nohl_left_nohl_ = Gdk::Pixbuf::create_from_file(
FLAGS_image_dir + "chrome_tab_right_nohl_left_nohl.png");
image_tab_right_nohl_left_none_ = Gdk::Pixbuf::create_from_file(
FLAGS_image_dir + "chrome_tab_right_nohl_left_none.png");
image_tab_right_none_left_hl_ = Gdk::Pixbuf::create_from_file(
FLAGS_image_dir + "chrome_tab_right_none_left_hl.png");
image_tab_right_none_left_nohl_ = Gdk::Pixbuf::create_from_file(
FLAGS_image_dir + "chrome_tab_right_none_left_nohl.png");
kTabHeight = image_tab_hl_->get_height();
kNavHeight = image_nav_left_->get_height();
}
void ChromeWindow::DrawTabs() {
Cairo::RefPtr<Cairo::Context> cr =
get_window()->create_cairo_context();
cr->select_font_face(kTabFontFace,
Cairo::FONT_SLANT_NORMAL,
Cairo::FONT_WEIGHT_NORMAL);
cr->set_font_size(kTabFontSize);
Cairo::FontOptions font_options;
font_options.set_hint_style(Cairo::HINT_STYLE_MEDIUM);
font_options.set_hint_metrics(Cairo::HINT_METRICS_ON);
font_options.set_antialias(Cairo::ANTIALIAS_GRAY);
cr->set_font_options(font_options);
Cairo::FontExtents extents;
cr->get_font_extents(extents);
cr->set_source_rgb(0, 0, 0);
int x_offset = 0;
for (int i = 0; static_cast<size_t>(i) < tabs_.size(); ++i) {
bool active = (i == active_tab_index_);
tabs_[i]->start_x = x_offset;
// Draw the image on the left.
if (i == 0) {
Glib::RefPtr<Gdk::Pixbuf> left_image =
active ?
image_tab_right_none_left_hl_ :
image_tab_right_none_left_nohl_;
DrawImage(left_image,
this,
x_offset, 0,
left_image->get_width(), left_image->get_height());
x_offset += left_image->get_width();
}
// Draw the tab's background and its title.
Glib::RefPtr<Gdk::Pixbuf> image = active ? image_tab_hl_ : image_tab_nohl_;
DrawImage(image,
this,
x_offset, 0,
image->get_width(), image->get_height());
cr->move_to(x_offset + kTabFontPadding, extents.ascent + kTabFontPadding);
cr->show_text(tabs_[i]->tab->title());
x_offset += image->get_width();
// Draw the image on the right.
Glib::RefPtr<Gdk::Pixbuf> right_image;
if (static_cast<size_t>(i) == tabs_.size() - 1) {
// Last tab.
if (active) {
right_image = image_tab_right_hl_left_none_;
} else {
right_image = image_tab_right_nohl_left_none_;
}
} else if (active) {
// Active tab.
right_image = image_tab_right_hl_left_nohl_;
} else if (i + 1 == active_tab_index_) {
// Next tab is active.
right_image = image_tab_right_nohl_left_hl_;
} else {
// Neither tab is active.
right_image = image_tab_right_nohl_left_nohl_;
}
DrawImage(right_image,
this,
x_offset, 0, // x, y
right_image->get_width(), right_image->get_height());
x_offset += right_image->get_width();
tabs_[i]->width = x_offset - tabs_[i]->start_x;
}
if (x_offset < width_) {
DrawImage(image_tab_bg_, this,
x_offset, 0, // x, y
width_ - x_offset, image_tab_bg_->get_height());
}
}
void ChromeWindow::DrawNavBar() {
DrawImage(image_nav_bg_, this,
0, kTabHeight, // x, y
width_, image_nav_bg_->get_height());
DrawImage(image_nav_left_, this,
0, kTabHeight, // x, y
image_nav_left_->get_width(), image_nav_left_->get_height());
DrawImage(image_nav_right_, this,
width_ - image_nav_right_->get_width(), kTabHeight, // x, y
image_nav_right_->get_width(), image_nav_right_->get_height());
}
void ChromeWindow::DrawView() {
int x = 0;
int y = kTabHeight + kNavHeight;
int width = width_;
int height = height_ - y;
if (active_tab_index_ >= 0) {
CHECK(active_tab_index_ < static_cast<int>(tabs_.size()));
tabs_[active_tab_index_]->tab->RenderToGtkWidget(this, x, y, width, height);
} else {
get_window()->clear_area(x, y, width, height);
}
}
int ChromeWindow::GetTabIndexAtXPosition(int x) const {
if (x < 0) {
return -1;
}
for (int i = 0; static_cast<size_t>(i) < tabs_.size(); ++i) {
if (x >= tabs_[i]->start_x && x < tabs_[i]->start_x + tabs_[i]->width) {
return i;
}
}
return tabs_.size();
}
void ChromeWindow::OnLockTimeout() {
lock_timeout_id_ = -1;
chrome_->LockScreen();
lock_to_shutdown_timeout_id_ =
g_timeout_add(kLockToShutdownThresholdMs,
ChromeWindow::OnLockToShutdownTimeoutThunk,
this);
}
void ChromeWindow::OnLockToShutdownTimeout() {
lock_to_shutdown_timeout_id_ = -1;
AddShutdownTimeout();
}
void ChromeWindow::OnShutdownTimeout() {
shutdown_timeout_id_ = -1;
chrome_->ShutDown();
}
void ChromeWindow::AddShutdownTimeout() {
WmIpc::Message msg(
chromeos::WM_IPC_MESSAGE_WM_NOTIFY_POWER_BUTTON_STATE);
msg.set_param(0, chromeos::WM_IPC_POWER_BUTTON_PRE_SHUTDOWN);
chrome_->wm_ipc()->SendMessage(chrome_->wm_ipc()->wm_window(), msg);
shutdown_timeout_id_ =
g_timeout_add(kShutdownTimeoutMs,
ChromeWindow::OnShutdownTimeoutThunk,
this);
}
bool ChromeWindow::on_button_press_event(GdkEventButton* event) {
if (event->button == 2) {
chrome_->CloseWindow(this);
return true;
} else if (event->button != 1) {
return false;
}
DLOG(INFO) << "Got mouse down at (" << event->x << ", " << event->y << ")";
if (event->y < 0 || event->y > kTabHeight) {
// Don't do anything for clicks outside of the tab bar.
return false;
}
int tab_index = GetTabIndexAtXPosition(event->x);
if (tab_index < 0 || tab_index >= static_cast<int>(tabs_.size())) {
// Ignore clicks outside of tabs.
return false;
}
dragging_tab_ = true;
tab_drag_start_offset_x_ = event->x - tabs_[tab_index]->start_x;
tab_drag_start_offset_y_ = event->y;
if (tab_index != active_tab_index_) {
CHECK(tab_index < static_cast<int>(tabs_.size()));
active_tab_index_ = tab_index;
DrawTabs();
DrawView();
}
return true;
}
bool ChromeWindow::on_button_release_event(GdkEventButton* event) {
if (event->button != 1) {
return false;
}
DLOG(INFO) << "Got mouse up at (" << event->x << ", " << event->y << ")";
dragging_tab_ = false;
return true;
}
bool ChromeWindow::on_motion_notify_event(GdkEventMotion* event) {
if (!dragging_tab_) return false;
DLOG(INFO) << "Got motion at (" << event->x << ", " << event->y << ")";
if (active_tab_index_ >= 0) {
int tab_index = GetTabIndexAtXPosition(event->x);
// The tab is still within the tab bar; move it to a new position.
if (tab_index >= static_cast<int>(tabs_.size())) {
// GetTabIndexAtXPosition() returns tabs_.size() for positions in
// the empty space at the right of the tab bar, but we need to
// treat that space as belonging to the last tab when reordering.
tab_index = tabs_.size() - 1;
} else if (tab_index < 0) {
tab_index = 0;
}
if (tab_index != active_tab_index_) {
Tab* tab = RemoveTab(active_tab_index_);
InsertTab(tab, tab_index);
active_tab_index_ = tab_index;
DrawTabs();
}
}
return true;
}
bool ChromeWindow::on_key_press_event(GdkEventKey* event) {
if (strcmp(event->string, "p") == 0) {
// Create a new panel.
chrome_->CreatePanel(FLAGS_new_panel_image, "New Panel", true);
// Create a new window.
} else if (strcmp(event->string, "w") == 0) {
chrome_->CreateWindow(width_, height_);
// Toggle fullscreen mode.
} else if (strcmp(event->string, "f") == 0) {
if (fullscreen_) {
unfullscreen();
} else {
fullscreen();
}
} else if (strcmp(event->string, "l") == 0) {
// Pretend that the power button has been pressed.
if (!power_button_is_pressed_) {
power_button_is_pressed_ = true;
if (!chrome_->is_locked()) {
WmIpc::Message msg(
chromeos::WM_IPC_MESSAGE_WM_NOTIFY_POWER_BUTTON_STATE);
msg.set_param(0, chromeos::WM_IPC_POWER_BUTTON_PRE_LOCK);
chrome_->wm_ipc()->SendMessage(chrome_->wm_ipc()->wm_window(), msg);
lock_timeout_id_ = g_timeout_add(kLockTimeoutMs,
ChromeWindow::OnLockTimeoutThunk,
this);
} else if (!chrome_->is_shutting_down()) {
AddShutdownTimeout();
}
}
} else if (strcmp(event->string, "u") == 0) {
// Unlock the screen.
if (chrome_->is_locked())
chrome_->UnlockScreen();
}
return true;
}
bool ChromeWindow::on_key_release_event(GdkEventKey* event) {
// X reports autorepeated key events similarly to individual key presses, but
// we can detect that a release event is part of an autorepeated sequence by
// checking if the next event in the queue is a press event with a matching
// timestamp.
bool repeated = false;
if (XPending(GDK_DISPLAY())) {
XEvent xevent;
XPeekEvent(GDK_DISPLAY(), &xevent);
if (xevent.type == KeyPress &&
xevent.xkey.keycode == event->hardware_keycode &&
xevent.xkey.time == event->time) {
repeated = true;
}
}
if (strcmp(event->string, "l") == 0) {
// Pretend that the power button has been released.
if (!repeated) {
power_button_is_pressed_ = false;
if (lock_timeout_id_ >= 0) {
g_source_remove(lock_timeout_id_);
lock_timeout_id_ = -1;
WmIpc::Message msg(
chromeos::WM_IPC_MESSAGE_WM_NOTIFY_POWER_BUTTON_STATE);
msg.set_param(0, chromeos::WM_IPC_POWER_BUTTON_ABORTED_LOCK);
chrome_->wm_ipc()->SendMessage(chrome_->wm_ipc()->wm_window(), msg);
} else if (lock_to_shutdown_timeout_id_ >= 0) {
g_source_remove(lock_to_shutdown_timeout_id_);
lock_to_shutdown_timeout_id_ = -1;
} else if (shutdown_timeout_id_ >= 0) {
g_source_remove(shutdown_timeout_id_);
shutdown_timeout_id_ = -1;
WmIpc::Message msg(
chromeos::WM_IPC_MESSAGE_WM_NOTIFY_POWER_BUTTON_STATE);
msg.set_param(0, chromeos::WM_IPC_POWER_BUTTON_ABORTED_SHUTDOWN);
chrome_->wm_ipc()->SendMessage(chrome_->wm_ipc()->wm_window(), msg);
}
}
}
return true;
}
bool ChromeWindow::on_expose_event(GdkEventExpose* event) {
DrawTabs();
DrawNavBar();
DrawView();
return true;
}
bool ChromeWindow::on_client_event(GdkEventClient* event) {
WmIpc::Message msg;
if (!GetWmIpcMessage(*event, chrome_->wm_ipc(), &msg))
return false;
DLOG(INFO) << "Got message of type " << msg.type();
switch (msg.type()) {
default:
LOG(WARNING) << "Ignoring WM message of unknown type " << msg.type();
return false;
}
return true;
}
bool ChromeWindow::on_configure_event(GdkEventConfigure* event) {
width_ = event->width;
height_ = event->height;
DrawView();
Gtk::Window::on_configure_event(event);
return false;
}
bool ChromeWindow::on_window_state_event(GdkEventWindowState* event) {
fullscreen_ = (event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN);
DLOG(INFO) << "Fullscreen mode set to " << fullscreen_;
return true;
}
PanelTitlebar::PanelTitlebar(Panel* panel)
: panel_(panel),
mouse_down_(false),
mouse_down_abs_x_(0),
mouse_down_abs_y_(0),
mouse_down_offset_x_(0),
mouse_down_offset_y_(0),
dragging_(false),
focused_(false) {
if (!image_bg_) {
image_bg_ = Gdk::Pixbuf::create_from_file(
FLAGS_image_dir + "panel_titlebar_bg.png");
image_bg_focused_ = Gdk::Pixbuf::create_from_file(
FLAGS_image_dir + "panel_titlebar_bg_focused.png");
}
set_size_request(kWidth, kHeight);
realize();
xid_ = GDK_WINDOW_XWINDOW(Glib::unwrap(get_window()));
CHECK(panel_->chrome()->wm_ipc()->SetWindowType(
xid_, chromeos::WM_IPC_WINDOW_CHROME_PANEL_TITLEBAR, NULL));
add_events(Gdk::BUTTON_PRESS_MASK |
Gdk::BUTTON_RELEASE_MASK |
Gdk::POINTER_MOTION_MASK);
show_all();
}
void PanelTitlebar::Draw() {
DrawImage((focused_ ? image_bg_focused_ : image_bg_),
this, 0, 0, get_width(), get_height());
Cairo::RefPtr<Cairo::Context> cr =
get_window()->create_cairo_context();
cr->select_font_face(kFontFace,
Cairo::FONT_SLANT_NORMAL,
Cairo::FONT_WEIGHT_BOLD);
cr->set_font_size(kFontSize);
Cairo::FontOptions font_options;
font_options.set_hint_style(Cairo::HINT_STYLE_MEDIUM);
font_options.set_hint_metrics(Cairo::HINT_METRICS_ON);
font_options.set_antialias(Cairo::ANTIALIAS_GRAY);
cr->set_font_options(font_options);
Cairo::FontExtents extents;
cr->get_font_extents(extents);
int x = kFontPadding;
int y = kFontPadding + extents.ascent;
cr->set_source_rgb(1, 1, 1);
cr->move_to(x, y);
cr->show_text(panel_->title());
}
bool PanelTitlebar::on_expose_event(GdkEventExpose* event) {
Draw();
return true;
}
bool PanelTitlebar::on_button_press_event(GdkEventButton* event) {
if (event->button == 2) {
panel_->chrome()->ClosePanel(panel_);
return true;
} else if (event->button != 1) {
return false;
}
mouse_down_ = true;
mouse_down_abs_x_ = event->x_root;
mouse_down_abs_y_ = event->y_root;
int width = 1, height = 1;
get_size(width, height);
mouse_down_offset_x_ = event->x - width;
mouse_down_offset_y_ = event->y;
dragging_ = false;
return true;
}
bool PanelTitlebar::on_button_release_event(GdkEventButton* event) {
if (event->button != 1) {
return false;
}
// Only handle clicks that started in our window.
if (!mouse_down_) {
return false;
}
mouse_down_ = false;
if (!dragging_) {
WmIpc::Message msg(chromeos::WM_IPC_MESSAGE_WM_SET_PANEL_STATE);
msg.set_param(0, panel_->xid());
msg.set_param(1, !(panel_->expanded()));
CHECK(panel_->chrome()->wm_ipc()->SendMessage(
panel_->chrome()->wm_ipc()->wm_window(), msg));
// If the panel is getting expanded, tell the WM to focus it.
if (!panel_->expanded())
panel_->present();
} else {
WmIpc::Message msg(chromeos::WM_IPC_MESSAGE_WM_NOTIFY_PANEL_DRAG_COMPLETE);
msg.set_param(0, panel_->xid());
CHECK(panel_->chrome()->wm_ipc()->SendMessage(
panel_->chrome()->wm_ipc()->wm_window(), msg));
dragging_ = false;
}
return true;
}
bool PanelTitlebar::on_motion_notify_event(GdkEventMotion* event) {
if (!mouse_down_) {
return false;
}
if (!dragging_) {
if (abs(event->x_root - mouse_down_abs_x_) >= kDragThreshold ||
abs(event->y_root - mouse_down_abs_y_) >= kDragThreshold) {
dragging_ = true;
}
}
if (dragging_) {
WmIpc::Message msg(chromeos::WM_IPC_MESSAGE_WM_NOTIFY_PANEL_DRAGGED);
msg.set_param(0, panel_->xid());
msg.set_param(1, event->x_root - mouse_down_offset_x_);
msg.set_param(2, event->y_root - mouse_down_offset_y_);
CHECK(panel_->chrome()->wm_ipc()->SendMessage(
panel_->chrome()->wm_ipc()->wm_window(), msg));
}
return true;
}
Panel::Panel(MockChrome* chrome,
const string& image_filename,
const string& title,
bool expanded)
: chrome_(chrome),
titlebar_(new PanelTitlebar(this)),
image_(Gdk::Pixbuf::create_from_file(image_filename)),
width_(image_->get_width()),
height_(image_->get_height()),
expanded_(false),
title_(title),
fullscreen_(false) {
set_size_request(width_, height_);
realize();
xid_ = GDK_WINDOW_XWINDOW(Glib::unwrap(get_window()));
vector<int> type_params;
type_params.push_back(titlebar_->xid());
type_params.push_back(expanded);
CHECK(chrome_->wm_ipc()->SetWindowType(
xid_, chromeos::WM_IPC_WINDOW_CHROME_PANEL_CONTENT, &type_params));
add_events(Gdk::BUTTON_PRESS_MASK);
show_all();
}
bool Panel::on_expose_event(GdkEventExpose* event) {
DrawImage(image_, this, 0, 0, width_, height_);
return true;
}
bool Panel::on_button_press_event(GdkEventButton* event) {
DLOG(INFO) << "Panel " << xid_ << " got button " << event->button;
if (event->button == 2) {
chrome_->ClosePanel(this);
}
return true;
}
bool Panel::on_key_press_event(GdkEventKey* event) {
if (strcmp(event->string, "+") == 0) {
width_ += 10;
height_ += 10;
resize(width_, height_);
} else if (strcmp(event->string, "-") == 0) {
width_ = max(width_ - 10, 1);
height_ = max(height_ - 10, 1);
resize(width_, height_);
} else if (strcmp(event->string, "f") == 0) {
fullscreen_ = !fullscreen_;
if (fullscreen_)
fullscreen();
else
unfullscreen();
} else if (strcmp(event->string, "u") == 0) {
set_urgency_hint(!get_urgency_hint());
} else {
DLOG(INFO) << "Panel " << xid_ << " got key press " << event->string;
}
return true;
}
bool Panel::on_client_event(GdkEventClient* event) {
WmIpc::Message msg;
if (!GetWmIpcMessage(*event, chrome_->wm_ipc(), &msg))
return false;
DLOG(INFO) << "Got message of type " << msg.type();
switch (msg.type()) {
case chromeos::WM_IPC_MESSAGE_CHROME_NOTIFY_PANEL_STATE: {
expanded_ = msg.param(0);
break;
}
default:
LOG(WARNING) << "Ignoring WM message of unknown type " << msg.type();
return false;
}
return true;
}
bool Panel::on_focus_in_event(GdkEventFocus* event) {
titlebar_->set_focused(true);
titlebar_->Draw();
return true;
}
bool Panel::on_focus_out_event(GdkEventFocus* event) {
titlebar_->set_focused(false);
titlebar_->Draw();
return true;
}
bool Panel::on_window_state_event(GdkEventWindowState* event) {
fullscreen_ = (event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN);
return true;
}
ScreenLockWindow::ScreenLockWindow(MockChrome* chrome)
: chrome_(chrome),
image_(Gdk::Pixbuf::create_from_file(FLAGS_screen_locker_image)) {
GdkScreen* screen = gdk_screen_get_default();
set_size_request(gdk_screen_get_width(screen),
gdk_screen_get_height(screen));
realize();
xid_ = GDK_WINDOW_XWINDOW(Glib::unwrap(get_window()));
CHECK(chrome_->wm_ipc()->SetWindowType(
xid(), chromeos::WM_IPC_WINDOW_CHROME_SCREEN_LOCKER, NULL));
show_all();
}
void ScreenLockWindow::Draw() {
DrawImage(image_,
this, // dest
0, 0, // x, y
get_width(), get_height());
}
bool ScreenLockWindow::on_expose_event(GdkEventExpose* event) {
Draw();
return false;
}
bool ScreenLockWindow::on_configure_event(GdkEventConfigure* event) {
Draw();
Gtk::Window::on_configure_event(event);
return false;
}
MockChrome::MockChrome()
: xconn_(new RealXConnection(GDK_DISPLAY())),
atom_cache_(new AtomCache(xconn_.get())),
wm_ipc_(new WmIpc(xconn_.get(), atom_cache_.get())),
is_shutting_down_(false) {
WmIpc::Message msg(chromeos::WM_IPC_MESSAGE_WM_NOTIFY_IPC_VERSION);
msg.set_param(0, 1);
wm_ipc_->SendMessage(wm_ipc_->wm_window(), msg);
}
ChromeWindow* MockChrome::CreateWindow(int width, int height) {
shared_ptr<ChromeWindow> win(new ChromeWindow(this, width, height));
CHECK(windows_.insert(make_pair(win->xid(), win)).second);
return win.get();
}
void MockChrome::CloseWindow(ChromeWindow* win) {
CHECK(win);
CHECK(windows_.erase(win->xid()) == 1);
}
Panel* MockChrome::CreatePanel(const string& image_filename,
const string& title,
bool expanded) {
shared_ptr<Panel> panel(
new Panel(this, image_filename, title, expanded));
CHECK(panels_.insert(make_pair(panel->xid(), panel)).second);
return panel.get();
}
void MockChrome::ClosePanel(Panel* panel) {
CHECK(panel);
CHECK(panels_.erase(panel->xid()) == 1);
}
void MockChrome::LockScreen() {
if (screen_lock_window_.get())
return;
LOG(INFO) << "Locking screen";
screen_lock_window_.reset(new ScreenLockWindow(this));
}
void MockChrome::UnlockScreen() {
if (!screen_lock_window_.get())
return;
LOG(INFO) << "Unlocking screen";
screen_lock_window_.reset();
}
void MockChrome::ShutDown() {
if (is_shutting_down_)
return;
LOG(INFO) << "Shutting down";
is_shutting_down_ = true;
WmIpc::Message msg(
chromeos::WM_IPC_MESSAGE_WM_NOTIFY_SHUTTING_DOWN);
wm_ipc_->SendMessage(wm_ipc_->wm_window(), msg);
}
} // namespace mock_chrome
int main(int argc, char** argv) {
Gtk::Main kit(argc, argv);
google::ParseCommandLineFlags(&argc, &argv, true);
CommandLine::Init(argc, argv);
logging::InitLogging(NULL,
logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG,
logging::DONT_LOCK_LOG_FILE,
logging::APPEND_TO_OLD_LOG_FILE);
vector<string> filenames;
SplitString(FLAGS_tab_images, ',', &filenames);
CHECK(!filenames.empty())
<< "At least one image must be supplied using --tab_images";
vector<string> titles;
SplitString(FLAGS_tab_titles, ',', &titles);
CHECK(filenames.size() == titles.size())
<< "Must specify same number of tab images and titles";
mock_chrome::MockChrome mock_chrome;
for (int i = 0; i < FLAGS_num_windows; ++i) {
mock_chrome::ChromeWindow* win =
mock_chrome.CreateWindow(FLAGS_window_width, FLAGS_window_height);
for (int j = 0; j < FLAGS_tabs_per_window; ++j) {
win->InsertTab(new mock_chrome::Tab(filenames[j % filenames.size()],
titles[j % titles.size()]),
win->num_tabs());
}
win->ActivateTab(i % win->num_tabs());
}
filenames.clear();
SplitString(FLAGS_panel_images, ',', &filenames);
CHECK(!filenames.empty())
<< "At least one image must be supplied using --panel_images";
titles.clear();
SplitString(FLAGS_panel_titles, ',', &titles);
CHECK(filenames.size() == titles.size())
<< "Must specify same number of panel images and titles";
for (int i = 0; i < FLAGS_num_panels; ++i) {
mock_chrome.CreatePanel(filenames[i % filenames.size()],
titles[i % titles.size()],
false); // expanded=false
}
Gtk::Main::run();
return 0;
}