summaryrefslogtreecommitdiffstats
path: root/app/surface/transport_dib_linux.cc
blob: 614cdb02b379c5ef092eec5d33c6c57e26b288d3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// Copyright (c) 2009 The Chromium 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 <errno.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#include "app/surface/transport_dib.h"
#include "base/logging.h"
#include "base/scoped_ptr.h"
#include "skia/ext/platform_canvas.h"
#include "ui/base/x/x11_util.h"
#include "ui/gfx/size.h"

// The shmat system call uses this as it's invalid return address
static void *const kInvalidAddress = (void*) -1;

TransportDIB::TransportDIB()
    : address_(kInvalidAddress),
      x_shm_(0),
      display_(NULL),
      size_(0) {
}

TransportDIB::~TransportDIB() {
  if (address_ != kInvalidAddress) {
    shmdt(address_);
    address_ = kInvalidAddress;
  }

  if (x_shm_) {
    DCHECK(display_);
    ui::DetachSharedMemory(display_, x_shm_);
  }
}

// static
TransportDIB* TransportDIB::Create(size_t size, uint32 sequence_num) {
  // We use a mode of 0666 since the X server won't attach to memory which is
  // 0600 since it can't know if it (as a root process) is being asked to map
  // someone else's private shared memory region.
  const int shmkey = shmget(IPC_PRIVATE, size, 0666);
  if (shmkey == -1) {
    DLOG(ERROR) << "Failed to create SysV shared memory region"
                << " errno:" << errno;
    return NULL;
  }

  void* address = shmat(shmkey, NULL /* desired address */, 0 /* flags */);
  // Here we mark the shared memory for deletion. Since we attached it in the
  // line above, it doesn't actually get deleted but, if we crash, this means
  // that the kernel will automatically clean it up for us.
  shmctl(shmkey, IPC_RMID, 0);
  if (address == kInvalidAddress)
    return NULL;

  TransportDIB* dib = new TransportDIB;

  dib->key_.shmkey = shmkey;
  dib->address_ = address;
  dib->size_ = size;
  return dib;
}

// static
TransportDIB* TransportDIB::Map(Handle handle) {
  scoped_ptr<TransportDIB> dib(CreateWithHandle(handle));
  if (!dib->Map())
    return NULL;
  return dib.release();
}

// static
TransportDIB* TransportDIB::CreateWithHandle(Handle shmkey) {
  TransportDIB* dib = new TransportDIB;
  dib->key_.shmkey = shmkey;
  return dib;
}

// static
bool TransportDIB::is_valid_handle(Handle dib) {
  return dib >= 0;
}

// static
bool TransportDIB::is_valid_id(Id id) {
  return id.shmkey != -1;
}

skia::PlatformCanvas* TransportDIB::GetPlatformCanvas(int w, int h) {
  if (address_ == kInvalidAddress && !Map())
    return NULL;
  scoped_ptr<skia::PlatformCanvas> canvas(new skia::PlatformCanvas);
  if (!canvas->initialize(w, h, true, reinterpret_cast<uint8_t*>(memory())))
    return NULL;
  return canvas.release();
}

bool TransportDIB::Map() {
  if (!is_valid_id(key_))
    return false;
  if (address_ != kInvalidAddress)
    return true;

  struct shmid_ds shmst;
  if (shmctl(key_.shmkey, IPC_STAT, &shmst) == -1)
    return false;

  void* address = shmat(key_.shmkey, NULL /* desired address */, 0 /* flags */);
  if (address == kInvalidAddress)
    return false;

  address_ = address;
  size_ = shmst.shm_segsz;
  return true;
}

void* TransportDIB::memory() const {
  DCHECK_NE(address_, kInvalidAddress);
  return address_;
}

TransportDIB::Id TransportDIB::id() const {
  return key_;
}

TransportDIB::Handle TransportDIB::handle() const {
  return key_.shmkey;
}

XID TransportDIB::MapToX(Display* display) {
  if (!x_shm_) {
    x_shm_ = ui::AttachSharedMemory(display, key_.shmkey);
    display_ = display;
  }

  return x_shm_;
}