1 // Copyright (C) 2000-2011 Carsten Haitzler and various contributors.
2 // Copyright (c) 2013 Intel Corporation. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
6 #include "xwalk/tizen/mobile/ui/tizen_system_indicator_watcher.h"
12 #include "base/files/file_util.h"
13 #include "base/files/file_path.h"
14 #include "base/message_loop/message_loop.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "ipc/unix_domain_socket_util.h"
17 #include "base/strings/string_tokenizer.h"
18 #include "base/environment.h"
19 #include "ui/gfx/image/image_skia.h"
20 #include "ui/gfx/screen.h"
22 using content::BrowserThread;
26 const char kServicePortrait[] = "elm_indicator_portrait";
27 const char kServiceLandscape[] = "elm_indicator_landscape";
28 const char kServiceNumber[] = "0";
30 // Environment variable format is x, y, width, height.
31 const char kTizenSystemIndicatorGeometryVar[] = "ILLUME_IND";
33 // Copied from EFL 1.7, in src/lib/ecore_evas/ecore_evas_extn.c.
34 // Last updated from (tizen_2.1/20130514.052329)
65 TizenSystemIndicatorWatcher::TizenSystemIndicatorWatcher(
66 WatcherClient* client,
67 const gfx::Display& display)
76 weak_ptr_factory_(this) {
77 memset(¤t_msg_header_, 0, sizeof(current_msg_header_));
80 if (display.rotation() == gfx::Display::ROTATE_0 ||
81 display.rotation() == gfx::Display::ROTATE_180) {
82 service_name_ = kServicePortrait;
84 service_name_ = kServiceLandscape;
88 TizenSystemIndicatorWatcher::~TizenSystemIndicatorWatcher() {}
90 void TizenSystemIndicatorWatcher::OnFileCanReadWithoutBlocking(int fd) {
92 PLOG(ERROR) << "Error while getting header";
97 if (!ProcessPayload()) {
98 PLOG(ERROR) << "Error while processing payload";
103 void TizenSystemIndicatorWatcher::OnFileCanWriteWithoutBlocking(int fd) {
106 void TizenSystemIndicatorWatcher::StartWatching() {
107 base::MessageLoopForIO::current()->WatchFileDescriptor(
108 fd_, true, base::MessageLoopForIO::WATCH_READ, &fd_watcher_, this);
111 void TizenSystemIndicatorWatcher::StopWatching() {
112 BrowserThread::PostTask(
115 base::Bind(base::IgnoreResult(&base::MessagePumpLibevent::
116 FileDescriptorWatcher::
117 StopWatchingFileDescriptor),
118 base::Unretained(&fd_watcher_)));
121 bool TizenSystemIndicatorWatcher::Connect() {
122 base::FilePath path(base::GetHomeDir()
124 .Append(service_name_)
125 .Append(kServiceNumber));
126 bool success = IPC::CreateClientUnixDomainSocket(path, &fd_);
127 fd_closer_.reset(&fd_);
131 void TizenSystemIndicatorWatcher::OnMouseDown(int x, int y) {
132 struct IPCDataEvMouseDown ipc;
133 // Mouse down event in Elementary Plug don't send it's coordinates,
134 // it gets from the last mouse move. Some mouse down events need this
135 // coordinates correctly, to solve this we trigger OnMouseMove here.
137 writer_.SendEvent(OP_EV_MOUSE_DOWN, &ipc, sizeof(ipc));
140 void TizenSystemIndicatorWatcher::OnMouseUp() {
141 struct IPCDataEvMouseUp ipc;
142 writer_.SendEvent(OP_EV_MOUSE_UP, &ipc, sizeof(ipc));
145 void TizenSystemIndicatorWatcher::OnMouseMove(int x, int y) {
146 struct IPCDataEvMouseMove ipc;
147 ipc.x = static_cast<int>(x * display_.device_scale_factor());
148 ipc.y = static_cast<int>(y * display_.device_scale_factor());
150 writer_.SendEvent(OP_EV_MOUSE_MOVE, &ipc, sizeof(ipc));
153 gfx::Size TizenSystemIndicatorWatcher::GetSize() const {
154 return gfx::Size(width_, height_);
159 // Headers contain a set of "instructions" and a payload. The instructions are
160 // used to build the new header based on a previous header. This allows the
161 // system to save bytes when a certain header field wasn't updated.
163 // This utility class is used to construct the next header based on the previous
164 // one and the new instructions.
166 // This parser is compatible with the protocol implemented in EFL 1.7 on the
167 // Tizen Mobile 2.1 platform. See src/lib/ecore_ipc/ecore_ipc.c in ecore source.
170 HeaderParser(unsigned int instructions,
172 struct EcoreIPCMsgHeader* prev_header,
173 struct EcoreIPCMsgHeader* next_header)
174 : instructions_(instructions),
177 next_(next_header) {}
185 DLT_ADD8, // 1 bytes.
189 DLT_ADD16, // 2 bytes.
199 next_->major = ProcessNextInstruction(prev_->major);
200 next_->minor = ProcessNextInstruction(prev_->minor);
201 next_->ref = ProcessNextInstruction(prev_->ref);
202 next_->ref_to = ProcessNextInstruction(prev_->ref_to);
203 next_->response = ProcessNextInstruction(prev_->response);
204 next_->size = ProcessNextInstruction(prev_->size);
210 // The instruction determine how much data we are going to read from the
212 int ExtractInstructionData(Instruction instruction) {
213 if (instruction >= DLT_SET) {
215 uint8_t* dv = reinterpret_cast<uint8_t*>(&v);
216 dv[0] = *(payload_++);
217 dv[1] = *(payload_++);
218 dv[2] = *(payload_++);
219 dv[3] = *(payload_++);
220 return static_cast<int>(ntohl(v));
222 if (instruction >= DLT_ADD16) {
224 uint8_t* dv = reinterpret_cast<uint8_t*>(&v);
225 dv[0] = *(payload_++);
226 dv[1] = *(payload_++);
227 return static_cast<int>(ntohs(v));
229 if (instruction >= DLT_ADD8) {
232 return static_cast<int>(v);
237 // Takes the previous value for the field, reads the next instruction and
238 // calculates the value based on the instruction data and the previous value.
239 int ProcessNextInstruction(int prev) {
240 Instruction instruction = Instruction(instructions_ & 0xf);
242 int data = ExtractInstructionData(instruction);
244 switch (instruction) {
245 case DLT_ZERO: return 0;
246 case DLT_ONE: return 0xffffffff;
247 case DLT_SAME: return prev;
248 case DLT_SHL: return prev << 1;
249 case DLT_SHR: return prev >> 1;
250 case DLT_ADD8: return prev + data;
251 case DLT_DEL8: return prev - data;
252 case DLT_ADDU8: return prev + (data << 24);
253 case DLT_DELU8: return prev - (data << 24);
254 case DLT_ADD16: return prev + data;
255 case DLT_DEL16: return prev - data;
256 case DLT_ADDU16: return prev + (data << 16);
257 case DLT_DELU16: return prev - (data << 16);
258 case DLT_SET: return data;
259 case DLT_R1: return 0;
260 case DLT_R2: return 0;
265 unsigned int instructions_;
267 struct EcoreIPCMsgHeader* prev_;
268 struct EcoreIPCMsgHeader* next_;
271 bool ReadSafe(int fd, uint8_t* buffer, size_t len) {
274 ssize_t r = read(fd, buffer, todo);
278 if (errno == EAGAIN || errno == EINTR)
290 size_t TizenSystemIndicatorWatcher::GetHeaderSize(unsigned int
291 header_instructions) {
292 // Header size will depend on the instructions we read from the header. Each
293 // instruction is stored in a nibble.
295 for (int i = 0; i < 6; i++) {
296 int instruction = (header_instructions >> (4 * i)) & 0xf;
297 if (instruction >= HeaderParser::DLT_SET)
299 else if (instruction >= HeaderParser::DLT_ADD16)
301 else if (instruction >= HeaderParser::DLT_ADD8)
307 bool TizenSystemIndicatorWatcher::GetHeader() {
308 unsigned int header_instructions;
309 if (!ReadSafe(fd_, reinterpret_cast<uint8_t*>(&header_instructions),
310 sizeof(header_instructions))) {
312 PLOG(ERROR) << "Failed to read header_instructions";
316 header_instructions = ntohl(header_instructions);
317 size_t header_size = GetHeaderSize(header_instructions);
319 if (header_size == 0)
322 scoped_ptr<unsigned char[]> header_payload(new unsigned char[header_size]);
324 if (!header_payload) {
325 PLOG(ERROR) << "Failed to allocate memory for header_payload";
329 if (!ReadSafe(fd_, header_payload.get(), header_size)) {
330 PLOG(ERROR) << "Failed to read header_payload";
334 struct EcoreIPCMsgHeader next_msg_header;
335 HeaderParser parser(header_instructions, header_payload.get(),
336 ¤t_msg_header_, &next_msg_header);
339 if (next_msg_header.major != kPlugProtocolVersion) {
340 LOG(WARNING) << "Incorrect protocol version " << next_msg_header.major
341 << " expected " << kPlugProtocolVersion;
345 current_msg_header_ = next_msg_header;
349 bool TizenSystemIndicatorWatcher::MapSharedMemory() {
350 // We call shm_open() ourselves since the base::SharedMemory prefixes the name
351 // we pass to it, making it not work nicely with a name that doesn't follow
352 // the convention, e.g. shared memory created by other app. See
353 // SharedMemory::FilePathForMemoryName() in shared_memory_posix.cc.
354 int shared_fd = shm_open(shm_name_.c_str(), O_RDONLY, S_IRUSR | S_IWUSR);
356 PLOG(ERROR) << "Failed to open shared memory for System Indicator.";
360 // SharedMemory will take ownership of the file descriptor.
361 const bool auto_close = true;
362 const bool read_only = true;
363 shared_memory_.reset(new base::SharedMemory(
364 base::FileDescriptor(shared_fd, auto_close), read_only));
366 int size_in_bytes = width_ * height_ * 4;
367 if (!shared_memory_->Map(size_in_bytes)) {
368 LOG(ERROR) << "Failed to map shared memory for System Indicator.";
369 shared_memory_.reset();
376 bool TizenSystemIndicatorWatcher::OnResize(const uint8_t* payload,
378 memcpy(&width_, payload, sizeof(width_));
379 memcpy(&height_, payload + sizeof(width_), sizeof(height_));
381 if (!shared_memory_ && !MapSharedMemory()) {
382 LOG(WARNING) << "Failed to load shared memory.";
386 BrowserThread::PostTask(
387 BrowserThread::UI, FROM_HERE,
388 base::Bind(&TizenSystemIndicatorWatcher::ResizeIndicator,
389 weak_ptr_factory_.GetWeakPtr()));
394 bool TizenSystemIndicatorWatcher::OnUpdate() {
399 bool TizenSystemIndicatorWatcher::OnUpdateDone() {
401 LOG(WARNING) << "OnUpdateDone received without previous OnUpdate!";
405 if (!shared_memory_ && !MapSharedMemory()) {
406 LOG(WARNING) << "Failed to load shared memory.";
410 BrowserThread::PostTask(
411 BrowserThread::UI, FROM_HERE,
412 base::Bind(&TizenSystemIndicatorWatcher::UpdateIndicatorImage,
413 weak_ptr_factory_.GetWeakPtr()));
419 bool TizenSystemIndicatorWatcher::OnShmRef(const uint8_t* payload,
424 shm_name_ = std::string(reinterpret_cast<const char*>(payload), size);
425 // Extra information about the shared memory is passed in the header.
426 width_ = current_msg_header_.ref;
427 height_ = current_msg_header_.ref_to;
428 alpha_ = current_msg_header_.response;
433 bool TizenSystemIndicatorWatcher::ProcessPayload() {
434 PlugOperation op_code = PlugOperation(current_msg_header_.minor);
435 size_t payload_size = current_msg_header_.size;
437 scoped_ptr<unsigned char[]> payload(new unsigned char[1024]);
439 PLOG(ERROR) << "Could not allocate memory to payload";
443 if (!ReadSafe(fd_, payload.get(), payload_size)) {
444 PLOG(ERROR) << "Failed to read op payload";
451 ok = OnResize(payload.get(), payload_size);
455 // We are always updating the entire area, so we ignore the
456 // x, y, w, h passed on the payload.
465 ok = OnShmRef(payload.get(), payload_size);
474 case OP_EV_MOUSE_OUT:
476 case OP_EV_MOUSE_DOWN:
477 case OP_EV_MOUSE_MOVE:
478 case OP_EV_MOUSE_WHEEL:
480 case OP_EV_MULTI_DOWN:
481 case OP_EV_MULTI_MOVE:
487 // Not implemented yet.
491 PLOG(ERROR) << "Unhandled opcode " << op_code;
492 ok = true; // Ignore unknown opcodes.
499 void TizenSystemIndicatorWatcher::UpdateIndicatorImage() {
502 bitmap.setConfig(SkBitmap::kARGB_8888_Config, width_, height_);
503 bitmap.setPixels(shared_memory_->memory());
505 gfx::ImageSkia img_skia;
506 img_skia.AddRepresentation(gfx::ImageSkiaRep(bitmap,
507 display_.device_scale_factor()));
508 client_->OnImageUpdated(img_skia);
511 void TizenSystemIndicatorWatcher::SetSizeFromEnvVar() {
512 std::string preferred_size;
513 scoped_ptr<base::Environment> env(base::Environment::Create());
515 env->GetVar(kTizenSystemIndicatorGeometryVar, &preferred_size);
517 if (preferred_size.empty())
520 base::StringTokenizer t(preferred_size, ", ");
522 // Environment variable format is: x, y, width, height.
527 width_ = std::atoi(t.token().c_str());
530 height_ = std::atoi(t.token().c_str());
533 void TizenSystemIndicatorWatcher::ResizeIndicator() {
534 UpdateIndicatorImage();