Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / xwalk / tizen / mobile / ui / tizen_system_indicator_watcher.cc
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.
5
6 #include "xwalk/tizen/mobile/ui/tizen_system_indicator_watcher.h"
7
8 #include <arpa/inet.h>
9 #include <fcntl.h>
10 #include <sys/mman.h>
11
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"
21
22 using content::BrowserThread;
23
24 namespace {
25
26 const char kServicePortrait[] = "elm_indicator_portrait";
27 const char kServiceLandscape[] = "elm_indicator_landscape";
28 const char kServiceNumber[] = "0";
29
30 // Environment variable format is x, y, width, height.
31 const char kTizenSystemIndicatorGeometryVar[] = "ILLUME_IND";
32
33 // Copied from EFL 1.7, in src/lib/ecore_evas/ecore_evas_extn.c.
34 // Last updated from (tizen_2.1/20130514.052329)
35 enum PlugOperation {
36   OP_RESIZE,
37   OP_SHOW,
38   OP_HIDE,
39   OP_FOCUS,
40   OP_UNFOCUS,
41   OP_UPDATE,
42   OP_UPDATE_DONE,
43   OP_LOCK_FILE,
44   OP_SHM_REF,
45   OP_EV_MOUSE_IN,
46   OP_EV_MOUSE_OUT,
47   OP_EV_MOUSE_UP,
48   OP_EV_MOUSE_DOWN,
49   OP_EV_MOUSE_MOVE,
50   OP_EV_MOUSE_WHEEL,
51   OP_EV_MULTI_UP,
52   OP_EV_MULTI_DOWN,
53   OP_EV_MULTI_MOVE,
54   OP_EV_KEY_UP,
55   OP_EV_KEY_DOWN,
56   OP_EV_HOLD,
57   OP_MSG_PARENT,
58   OP_MSG
59 };
60
61 }  // namespace
62
63 namespace xwalk {
64
65 TizenSystemIndicatorWatcher::TizenSystemIndicatorWatcher(
66     WatcherClient* client,
67     const gfx::Display& display)
68   : client_(client),
69     display_(display),
70     writer_(&fd_),
71     fd_(-1),
72     width_(-1),
73     height_(-1),
74     alpha_(-1),
75     updated_(false),
76     weak_ptr_factory_(this) {
77   memset(&current_msg_header_, 0, sizeof(current_msg_header_));
78   SetSizeFromEnvVar();
79
80   if (display.rotation() == gfx::Display::ROTATE_0 ||
81       display.rotation() == gfx::Display::ROTATE_180) {
82     service_name_ = kServicePortrait;
83   } else {
84     service_name_ = kServiceLandscape;
85   }
86 }
87
88 TizenSystemIndicatorWatcher::~TizenSystemIndicatorWatcher() {}
89
90 void TizenSystemIndicatorWatcher::OnFileCanReadWithoutBlocking(int fd) {
91   if (!GetHeader()) {
92     PLOG(ERROR) << "Error while getting header";
93     StopWatching();
94     return;
95   }
96
97   if (!ProcessPayload()) {
98     PLOG(ERROR) << "Error while processing payload";
99     StopWatching();
100   }
101 }
102
103 void TizenSystemIndicatorWatcher::OnFileCanWriteWithoutBlocking(int fd) {
104 }
105
106 void TizenSystemIndicatorWatcher::StartWatching() {
107   base::MessageLoopForIO::current()->WatchFileDescriptor(
108      fd_, true, base::MessageLoopForIO::WATCH_READ, &fd_watcher_, this);
109 }
110
111 void TizenSystemIndicatorWatcher::StopWatching() {
112   BrowserThread::PostTask(
113       BrowserThread::IO,
114       FROM_HERE,
115       base::Bind(base::IgnoreResult(&base::MessagePumpLibevent::
116                                     FileDescriptorWatcher::
117                                     StopWatchingFileDescriptor),
118                  base::Unretained(&fd_watcher_)));
119 }
120
121 bool TizenSystemIndicatorWatcher::Connect() {
122   base::FilePath path(base::GetHomeDir()
123                       .Append(".ecore")
124                       .Append(service_name_)
125                       .Append(kServiceNumber));
126   bool success = IPC::CreateClientUnixDomainSocket(path, &fd_);
127   fd_closer_.reset(&fd_);
128   return success;
129 }
130
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.
136   OnMouseMove(x, y);
137   writer_.SendEvent(OP_EV_MOUSE_DOWN, &ipc, sizeof(ipc));
138 }
139
140 void TizenSystemIndicatorWatcher::OnMouseUp() {
141   struct IPCDataEvMouseUp ipc;
142   writer_.SendEvent(OP_EV_MOUSE_UP, &ipc, sizeof(ipc));
143 }
144
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());
149
150   writer_.SendEvent(OP_EV_MOUSE_MOVE, &ipc, sizeof(ipc));
151 }
152
153 gfx::Size TizenSystemIndicatorWatcher::GetSize() const {
154   return gfx::Size(width_, height_);
155 }
156
157 namespace {
158
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.
162 //
163 // This utility class is used to construct the next header based on the previous
164 // one and the new instructions.
165 //
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.
168 class HeaderParser {
169  public:
170   HeaderParser(unsigned int instructions,
171                uint8_t* payload,
172                struct EcoreIPCMsgHeader* prev_header,
173                struct EcoreIPCMsgHeader* next_header)
174       : instructions_(instructions),
175         payload_(payload),
176         prev_(prev_header),
177         next_(next_header) {}
178
179   enum Instruction {
180     DLT_ZERO,
181     DLT_ONE,
182     DLT_SAME,
183     DLT_SHL,
184     DLT_SHR,
185     DLT_ADD8,      // 1 bytes.
186     DLT_DEL8,
187     DLT_ADDU8,
188     DLT_DELU8,
189     DLT_ADD16,     // 2 bytes.
190     DLT_DEL16,
191     DLT_ADDU16,
192     DLT_DELU16,
193     DLT_SET,       // 4 bytes.
194     DLT_R1,
195     DLT_R2
196   };
197
198   void Parse() {
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);
205     if (next_->size < 0)
206       next_->size = 0;
207   }
208
209  private:
210   // The instruction determine how much data we are going to read from the
211   // payload.
212   int ExtractInstructionData(Instruction instruction) {
213     if (instruction >= DLT_SET) {
214       uint32_t v;
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));
221     }
222     if (instruction >= DLT_ADD16) {
223       uint16_t v;
224       uint8_t* dv = reinterpret_cast<uint8_t*>(&v);
225       dv[0] = *(payload_++);
226       dv[1] = *(payload_++);
227       return static_cast<int>(ntohs(v));
228     }
229     if (instruction >= DLT_ADD8) {
230       uint8_t v;
231       v = *(payload_++);
232       return static_cast<int>(v);
233     }
234     return 0;
235   }
236
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);
241     instructions_ >>= 4;
242     int data = ExtractInstructionData(instruction);
243
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;
261     }
262     return 0;
263   }
264
265   unsigned int instructions_;
266   uint8_t* payload_;
267   struct EcoreIPCMsgHeader* prev_;
268   struct EcoreIPCMsgHeader* next_;
269 };
270
271 bool ReadSafe(int fd, uint8_t* buffer, size_t len) {
272   size_t todo = len;
273   while (todo) {
274      ssize_t r = read(fd, buffer, todo);
275      if (r == 0)
276        return false;
277      if (r < 0) {
278        if (errno == EAGAIN || errno == EINTR)
279          continue;
280        return false;
281      }
282      todo -= r;
283      buffer += r;
284   }
285   return true;
286 }
287
288 }  // namespace
289
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.
294   int header_size = 0;
295   for (int i = 0; i < 6; i++) {
296     int instruction = (header_instructions >> (4 * i)) & 0xf;
297     if (instruction >= HeaderParser::DLT_SET)
298       header_size += 4;
299     else if (instruction >= HeaderParser::DLT_ADD16)
300       header_size += 2;
301     else if (instruction >= HeaderParser::DLT_ADD8)
302       header_size += 1;
303   }
304   return header_size;
305 }
306
307 bool TizenSystemIndicatorWatcher::GetHeader() {
308   unsigned int header_instructions;
309   if (!ReadSafe(fd_, reinterpret_cast<uint8_t*>(&header_instructions),
310                 sizeof(header_instructions))) {
311     if (errno != EAGAIN)
312       PLOG(ERROR) << "Failed to read header_instructions";
313     return false;
314   }
315
316   header_instructions = ntohl(header_instructions);
317   size_t header_size = GetHeaderSize(header_instructions);
318
319   if (header_size == 0)
320     return true;
321
322   scoped_ptr<unsigned char[]> header_payload(new unsigned char[header_size]);
323
324   if (!header_payload) {
325     PLOG(ERROR) << "Failed to allocate memory for header_payload";
326     return false;
327   }
328
329   if (!ReadSafe(fd_, header_payload.get(), header_size)) {
330     PLOG(ERROR) << "Failed to read header_payload";
331     return false;
332   }
333
334   struct EcoreIPCMsgHeader next_msg_header;
335   HeaderParser parser(header_instructions, header_payload.get(),
336                       &current_msg_header_, &next_msg_header);
337   parser.Parse();
338
339   if (next_msg_header.major != kPlugProtocolVersion) {
340     LOG(WARNING) << "Incorrect protocol version " << next_msg_header.major
341                  << " expected " << kPlugProtocolVersion;
342     return false;
343   }
344
345   current_msg_header_ = next_msg_header;
346   return true;
347 }
348
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);
355   if (shared_fd < 0) {
356     PLOG(ERROR) << "Failed to open shared memory for System Indicator.";
357     return false;
358   }
359
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));
365
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();
370     return false;
371   }
372
373   return true;
374 }
375
376 bool TizenSystemIndicatorWatcher::OnResize(const uint8_t* payload,
377                                            size_t size) {
378   memcpy(&width_, payload, sizeof(width_));
379   memcpy(&height_, payload + sizeof(width_), sizeof(height_));
380
381   if (!shared_memory_ && !MapSharedMemory()) {
382     LOG(WARNING) << "Failed to load shared memory.";
383     return false;
384   }
385
386   BrowserThread::PostTask(
387       BrowserThread::UI, FROM_HERE,
388       base::Bind(&TizenSystemIndicatorWatcher::ResizeIndicator,
389                  weak_ptr_factory_.GetWeakPtr()));
390
391   return true;
392 }
393
394 bool TizenSystemIndicatorWatcher::OnUpdate() {
395   updated_ = true;
396   return true;
397 }
398
399 bool TizenSystemIndicatorWatcher::OnUpdateDone() {
400   if (!updated_) {
401     LOG(WARNING) << "OnUpdateDone received without previous OnUpdate!";
402     return true;
403   }
404
405   if (!shared_memory_ && !MapSharedMemory()) {
406     LOG(WARNING) << "Failed to load shared memory.";
407     return false;
408   }
409
410   BrowserThread::PostTask(
411       BrowserThread::UI, FROM_HERE,
412       base::Bind(&TizenSystemIndicatorWatcher::UpdateIndicatorImage,
413                  weak_ptr_factory_.GetWeakPtr()));
414
415   updated_ = false;
416   return true;
417 }
418
419 bool TizenSystemIndicatorWatcher::OnShmRef(const uint8_t* payload,
420                                            size_t size) {
421   if (size <= 1)
422     return false;
423
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;
429
430   return true;
431 }
432
433 bool TizenSystemIndicatorWatcher::ProcessPayload() {
434   PlugOperation op_code = PlugOperation(current_msg_header_.minor);
435   size_t payload_size = current_msg_header_.size;
436
437   scoped_ptr<unsigned char[]> payload(new unsigned char[1024]);
438   if (!payload) {
439     PLOG(ERROR) << "Could not allocate memory to payload";
440     return false;
441   }
442
443   if (!ReadSafe(fd_, payload.get(), payload_size)) {
444     PLOG(ERROR) << "Failed to read op payload";
445     return false;
446   }
447
448   bool ok = false;
449   switch (op_code) {
450     case OP_RESIZE:
451       ok = OnResize(payload.get(), payload_size);
452       break;
453
454     case OP_UPDATE:
455       // We are always updating the entire area, so we ignore the
456       // x, y, w, h passed on the payload.
457       ok = OnUpdate();
458       break;
459
460     case OP_UPDATE_DONE:
461       ok = OnUpdateDone();
462       break;
463
464     case OP_SHM_REF:
465       ok = OnShmRef(payload.get(), payload_size);
466       break;
467
468     case OP_SHOW:
469     case OP_HIDE:
470     case OP_FOCUS:
471     case OP_UNFOCUS:
472     case OP_LOCK_FILE:
473     case OP_EV_MOUSE_IN:
474     case OP_EV_MOUSE_OUT:
475     case OP_EV_MOUSE_UP:
476     case OP_EV_MOUSE_DOWN:
477     case OP_EV_MOUSE_MOVE:
478     case OP_EV_MOUSE_WHEEL:
479     case OP_EV_MULTI_UP:
480     case OP_EV_MULTI_DOWN:
481     case OP_EV_MULTI_MOVE:
482     case OP_EV_KEY_UP:
483     case OP_EV_KEY_DOWN:
484     case OP_EV_HOLD:
485     case OP_MSG_PARENT:
486     case OP_MSG:
487       // Not implemented yet.
488       ok = true;
489       break;
490     default:
491       PLOG(ERROR) << "Unhandled opcode " << op_code;
492       ok = true;  // Ignore unknown opcodes.
493       break;
494   }
495
496   return ok;
497 }
498
499 void TizenSystemIndicatorWatcher::UpdateIndicatorImage() {
500   SkBitmap bitmap;
501
502   bitmap.setConfig(SkBitmap::kARGB_8888_Config, width_, height_);
503   bitmap.setPixels(shared_memory_->memory());
504
505   gfx::ImageSkia img_skia;
506   img_skia.AddRepresentation(gfx::ImageSkiaRep(bitmap,
507       display_.device_scale_factor()));
508   client_->OnImageUpdated(img_skia);
509 }
510
511 void TizenSystemIndicatorWatcher::SetSizeFromEnvVar() {
512   std::string preferred_size;
513   scoped_ptr<base::Environment> env(base::Environment::Create());
514
515   env->GetVar(kTizenSystemIndicatorGeometryVar, &preferred_size);
516
517   if (preferred_size.empty())
518     return;
519
520   base::StringTokenizer t(preferred_size, ", ");
521
522   // Environment variable format is: x, y, width, height.
523   t.GetNext();
524   t.GetNext();
525
526   if (t.GetNext())
527     width_ = std::atoi(t.token().c_str());
528
529   if (t.GetNext())
530     height_ = std::atoi(t.token().c_str());
531 }
532
533 void TizenSystemIndicatorWatcher::ResizeIndicator() {
534   UpdateIndicatorImage();
535 }
536
537 }  // namespace xwalk