Home | History | Annotate | Download | only in surface
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "ui/surface/transport_dib.h"
      6 
      7 // Desktop GTK Linux builds use the old-style SYSV SHM based DIBs.
      8 
      9 #include <errno.h>
     10 #include <stdlib.h>
     11 #include <sys/ipc.h>
     12 #include <sys/shm.h>
     13 
     14 #include "base/logging.h"
     15 #include "base/memory/scoped_ptr.h"
     16 #include "skia/ext/platform_canvas.h"
     17 #include "ui/base/x/x11_util.h"
     18 #include "ui/gfx/size.h"
     19 
     20 // The shmat system call uses this as it's invalid return address
     21 static void *const kInvalidAddress = (void*) -1;
     22 
     23 TransportDIB::TransportDIB()
     24     : address_(kInvalidAddress),
     25       x_shm_(0),
     26       display_(NULL),
     27       inflight_counter_(0),
     28       detached_(false),
     29       size_(0) {
     30 }
     31 
     32 TransportDIB::~TransportDIB() {
     33   if (address_ != kInvalidAddress) {
     34     shmdt(address_);
     35     address_ = kInvalidAddress;
     36   }
     37 
     38   if (x_shm_) {
     39     DCHECK(display_);
     40     ui::DetachSharedMemory(display_, x_shm_);
     41   }
     42 }
     43 
     44 // static
     45 TransportDIB* TransportDIB::Create(size_t size, uint32 sequence_num) {
     46   const int shmkey = shmget(IPC_PRIVATE, size, 0600);
     47   if (shmkey == -1) {
     48     DLOG(ERROR) << "Failed to create SysV shared memory region"
     49                 << " errno:" << errno;
     50     return NULL;
     51   } else {
     52     VLOG(1) << "Created SysV shared memory region " << shmkey;
     53   }
     54 
     55   void* address = shmat(shmkey, NULL /* desired address */, 0 /* flags */);
     56   // Here we mark the shared memory for deletion. Since we attached it in the
     57   // line above, it doesn't actually get deleted but, if we crash, this means
     58   // that the kernel will automatically clean it up for us.
     59   shmctl(shmkey, IPC_RMID, 0);
     60   if (address == kInvalidAddress)
     61     return NULL;
     62 
     63   TransportDIB* dib = new TransportDIB;
     64 
     65   dib->key_.shmkey = shmkey;
     66   dib->address_ = address;
     67   dib->size_ = size;
     68   return dib;
     69 }
     70 
     71 // static
     72 TransportDIB* TransportDIB::Map(Handle handle) {
     73   scoped_ptr<TransportDIB> dib(CreateWithHandle(handle));
     74   if (!dib->Map())
     75     return NULL;
     76   return dib.release();
     77 }
     78 
     79 // static
     80 TransportDIB* TransportDIB::CreateWithHandle(Handle shmkey) {
     81   TransportDIB* dib = new TransportDIB;
     82   dib->key_.shmkey = shmkey;
     83   return dib;
     84 }
     85 
     86 // static
     87 bool TransportDIB::is_valid_handle(Handle dib) {
     88   return dib >= 0;
     89 }
     90 
     91 // static
     92 bool TransportDIB::is_valid_id(Id id) {
     93   return id.shmkey != -1;
     94 }
     95 
     96 skia::PlatformCanvas* TransportDIB::GetPlatformCanvas(int w, int h) {
     97   if ((address_ == kInvalidAddress && !Map()) || !VerifyCanvasSize(w, h))
     98     return NULL;
     99   return skia::CreatePlatformCanvas(w, h, true,
    100                                     reinterpret_cast<uint8_t*>(memory()),
    101                                     skia::RETURN_NULL_ON_FAILURE);
    102 }
    103 
    104 bool TransportDIB::Map() {
    105   if (!is_valid_id(key_))
    106     return false;
    107   if (address_ != kInvalidAddress)
    108     return true;
    109 
    110   struct shmid_ds shmst;
    111   if (shmctl(key_.shmkey, IPC_STAT, &shmst) == -1)
    112     return false;
    113 
    114   void* address = shmat(key_.shmkey, NULL /* desired address */, 0 /* flags */);
    115   if (address == kInvalidAddress)
    116     return false;
    117 
    118   address_ = address;
    119   size_ = shmst.shm_segsz;
    120   return true;
    121 }
    122 
    123 void* TransportDIB::memory() const {
    124   DCHECK_NE(address_, kInvalidAddress);
    125   return address_;
    126 }
    127 
    128 TransportDIB::Id TransportDIB::id() const {
    129   return key_;
    130 }
    131 
    132 TransportDIB::Handle TransportDIB::handle() const {
    133   return key_.shmkey;
    134 }
    135 
    136 XID TransportDIB::MapToX(Display* display) {
    137   if (!x_shm_) {
    138     x_shm_ = ui::AttachSharedMemory(display, key_.shmkey);
    139     display_ = display;
    140   }
    141 
    142   return x_shm_;
    143 }
    144 
    145 void TransportDIB::DecreaseInFlightCounter() {
    146   CHECK(inflight_counter_);
    147   inflight_counter_--;
    148   if (!inflight_counter_ && detached_)
    149     delete this;
    150 }
    151 
    152 void TransportDIB::Detach() {
    153   CHECK(!detached_);
    154   detached_ = true;
    155   if (!inflight_counter_)
    156     delete this;
    157 }
    158 
    159