OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 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 "components/exo/wayland/server.h" |
| 6 |
| 7 #include <wayland-server-core.h> |
| 8 #include <wayland-server-protocol-core.h> |
| 9 |
| 10 #include <algorithm> |
| 11 |
| 12 #include "base/bind.h" |
| 13 #include "base/cancelable_callback.h" |
| 14 #include "base/strings/utf_string_conversions.h" |
| 15 #include "components/exo/buffer.h" |
| 16 #include "components/exo/display.h" |
| 17 #include "components/exo/shared_memory.h" |
| 18 #include "components/exo/shell_surface.h" |
| 19 #include "components/exo/surface.h" |
| 20 #include "third_party/skia/include/core/SkRegion.h" |
| 21 |
| 22 namespace exo { |
| 23 namespace wayland { |
| 24 namespace { |
| 25 |
| 26 template <class T> |
| 27 T* GetUserDataAs(wl_resource* resource) { |
| 28 return static_cast<T*>(wl_resource_get_user_data(resource)); |
| 29 } |
| 30 |
| 31 template <class T> |
| 32 scoped_ptr<T> TakeUserDataAs(wl_resource* resource) { |
| 33 scoped_ptr<T> user_data = make_scoped_ptr(GetUserDataAs<T>(resource)); |
| 34 wl_resource_set_user_data(resource, nullptr); |
| 35 return user_data.Pass(); |
| 36 } |
| 37 |
| 38 template <class T> |
| 39 void DestroyUserData(wl_resource* resource) { |
| 40 TakeUserDataAs<T>(resource); |
| 41 } |
| 42 |
| 43 template <class T> |
| 44 void SetImplementation(wl_resource* resource, |
| 45 const void* implementation, |
| 46 scoped_ptr<T> user_data) { |
| 47 wl_resource_set_implementation(resource, implementation, user_data.release(), |
| 48 DestroyUserData<T>); |
| 49 } |
| 50 |
| 51 //////////////////////////////////////////////////////////////////////////////// |
| 52 // wl_buffer_interface: |
| 53 |
| 54 void buffer_destroy(wl_client* client, wl_resource* resource) { |
| 55 wl_resource_destroy(resource); |
| 56 } |
| 57 |
| 58 const struct wl_buffer_interface buffer_implementation = {buffer_destroy}; |
| 59 |
| 60 //////////////////////////////////////////////////////////////////////////////// |
| 61 // wl_surface_interface: |
| 62 |
| 63 void surface_destroy(wl_client* client, wl_resource* resource) { |
| 64 wl_resource_destroy(resource); |
| 65 } |
| 66 |
| 67 void surface_attach(wl_client* client, |
| 68 wl_resource* resource, |
| 69 wl_resource* buffer, |
| 70 int32_t x, |
| 71 int32_t y) { |
| 72 // TODO(reveman): Implement buffer offset support. |
| 73 if (x || y) { |
| 74 wl_resource_post_no_memory(resource); |
| 75 return; |
| 76 } |
| 77 |
| 78 GetUserDataAs<Surface>(resource) |
| 79 ->Attach(buffer ? GetUserDataAs<Buffer>(buffer) : nullptr); |
| 80 } |
| 81 |
| 82 void surface_damage(wl_client* client, |
| 83 wl_resource* resource, |
| 84 int32_t x, |
| 85 int32_t y, |
| 86 int32_t width, |
| 87 int32_t height) { |
| 88 GetUserDataAs<Surface>(resource)->Damage(gfx::Rect(x, y, width, height)); |
| 89 } |
| 90 |
| 91 void handle_surface_frame_callback(wl_resource* resource, |
| 92 base::TimeTicks frame_time) { |
| 93 if (!frame_time.is_null()) { |
| 94 wl_callback_send_done(resource, |
| 95 (frame_time - base::TimeTicks()).InMilliseconds()); |
| 96 // TODO(reveman): Remove this potentially blocking flush and instead watch |
| 97 // the file descriptor to be ready for write without blocking. |
| 98 wl_client_flush(wl_resource_get_client(resource)); |
| 99 } |
| 100 wl_resource_destroy(resource); |
| 101 } |
| 102 |
| 103 void surface_frame(wl_client* client, |
| 104 wl_resource* resource, |
| 105 uint32_t callback) { |
| 106 wl_resource* callback_resource = |
| 107 wl_resource_create(client, &wl_callback_interface, 1, callback); |
| 108 if (!callback_resource) { |
| 109 wl_resource_post_no_memory(resource); |
| 110 return; |
| 111 } |
| 112 |
| 113 // base::Unretained is safe as the resource owns the callback. |
| 114 scoped_ptr<base::CancelableCallback<void(base::TimeTicks)>> |
| 115 cancelable_callback(new base::CancelableCallback<void(base::TimeTicks)>( |
| 116 base::Bind(&handle_surface_frame_callback, |
| 117 base::Unretained(callback_resource)))); |
| 118 |
| 119 GetUserDataAs<Surface>(resource) |
| 120 ->RequestFrameCallback(cancelable_callback->callback()); |
| 121 |
| 122 SetImplementation(callback_resource, nullptr, cancelable_callback.Pass()); |
| 123 } |
| 124 |
| 125 void surface_set_opaque_region(wl_client* client, |
| 126 wl_resource* resource, |
| 127 wl_resource* region_resource) { |
| 128 GetUserDataAs<Surface>(resource)->SetOpaqueRegion( |
| 129 region_resource ? *GetUserDataAs<SkRegion>(region_resource) |
| 130 : SkRegion(SkIRect::MakeEmpty())); |
| 131 } |
| 132 |
| 133 void surface_set_input_region(wl_client* client, |
| 134 wl_resource* resource, |
| 135 wl_resource* region_resource) { |
| 136 NOTIMPLEMENTED(); |
| 137 } |
| 138 |
| 139 void surface_commit(wl_client* client, wl_resource* resource) { |
| 140 GetUserDataAs<Surface>(resource)->Commit(); |
| 141 } |
| 142 |
| 143 void surface_set_buffer_transform(wl_client* client, |
| 144 wl_resource* resource, |
| 145 int transform) { |
| 146 NOTIMPLEMENTED(); |
| 147 } |
| 148 |
| 149 void surface_set_buffer_scale(wl_client* client, |
| 150 wl_resource* resource, |
| 151 int32_t scale) { |
| 152 NOTIMPLEMENTED(); |
| 153 } |
| 154 |
| 155 const struct wl_surface_interface surface_implementation = { |
| 156 surface_destroy, |
| 157 surface_attach, |
| 158 surface_damage, |
| 159 surface_frame, |
| 160 surface_set_opaque_region, |
| 161 surface_set_input_region, |
| 162 surface_commit, |
| 163 surface_set_buffer_transform, |
| 164 surface_set_buffer_scale}; |
| 165 |
| 166 //////////////////////////////////////////////////////////////////////////////// |
| 167 // wl_region_interface: |
| 168 |
| 169 void region_destroy(wl_client* client, wl_resource* resource) { |
| 170 wl_resource_destroy(resource); |
| 171 } |
| 172 |
| 173 void region_add(wl_client* client, |
| 174 wl_resource* resource, |
| 175 int32_t x, |
| 176 int32_t y, |
| 177 int32_t width, |
| 178 int32_t height) { |
| 179 GetUserDataAs<SkRegion>(resource) |
| 180 ->op(SkIRect::MakeXYWH(x, y, width, height), SkRegion::kUnion_Op); |
| 181 } |
| 182 |
| 183 static void region_subtract(wl_client* client, |
| 184 wl_resource* resource, |
| 185 int32_t x, |
| 186 int32_t y, |
| 187 int32_t width, |
| 188 int32_t height) { |
| 189 GetUserDataAs<SkRegion>(resource) |
| 190 ->op(SkIRect::MakeXYWH(x, y, width, height), SkRegion::kDifference_Op); |
| 191 } |
| 192 |
| 193 const struct wl_region_interface region_implementation = { |
| 194 region_destroy, region_add, region_subtract}; |
| 195 |
| 196 //////////////////////////////////////////////////////////////////////////////// |
| 197 // wl_compositor_interface: |
| 198 |
| 199 void compositor_create_surface(wl_client* client, |
| 200 wl_resource* resource, |
| 201 uint32_t id) { |
| 202 scoped_ptr<Surface> surface = |
| 203 GetUserDataAs<Display>(resource)->CreateSurface(); |
| 204 DCHECK(surface); |
| 205 |
| 206 wl_resource* surface_resource = wl_resource_create( |
| 207 client, &wl_surface_interface, wl_resource_get_version(resource), id); |
| 208 if (!surface_resource) { |
| 209 wl_resource_post_no_memory(resource); |
| 210 return; |
| 211 } |
| 212 |
| 213 SetImplementation(surface_resource, &surface_implementation, surface.Pass()); |
| 214 } |
| 215 |
| 216 void compositor_create_region(wl_client* client, |
| 217 wl_resource* resource, |
| 218 uint32_t id) { |
| 219 scoped_ptr<SkRegion> region(new SkRegion); |
| 220 |
| 221 wl_resource* region_resource = |
| 222 wl_resource_create(client, &wl_region_interface, 1, id); |
| 223 if (!region_resource) { |
| 224 wl_resource_post_no_memory(resource); |
| 225 return; |
| 226 } |
| 227 |
| 228 SetImplementation(region_resource, ®ion_implementation, region.Pass()); |
| 229 } |
| 230 |
| 231 const struct wl_compositor_interface compositor_implementation = { |
| 232 compositor_create_surface, compositor_create_region}; |
| 233 |
| 234 const uint32_t compositor_version = 3; |
| 235 |
| 236 void bind_compositor(wl_client* client, |
| 237 void* data, |
| 238 uint32_t version, |
| 239 uint32_t id) { |
| 240 wl_resource* resource = |
| 241 wl_resource_create(client, &wl_compositor_interface, |
| 242 std::min(version, compositor_version), id); |
| 243 if (!resource) { |
| 244 wl_client_post_no_memory(client); |
| 245 return; |
| 246 } |
| 247 |
| 248 wl_resource_set_implementation(resource, &compositor_implementation, data, |
| 249 nullptr); |
| 250 } |
| 251 |
| 252 //////////////////////////////////////////////////////////////////////////////// |
| 253 // wl_shm_pool_interface: |
| 254 |
| 255 const struct shm_supported_format { |
| 256 uint32_t shm_format; |
| 257 gfx::BufferFormat buffer_format; |
| 258 } shm_supported_formats[] = { |
| 259 {WL_SHM_FORMAT_XBGR8888, gfx::BufferFormat::RGBX_8888}, |
| 260 {WL_SHM_FORMAT_ABGR8888, gfx::BufferFormat::RGBA_8888}, |
| 261 {WL_SHM_FORMAT_XRGB8888, gfx::BufferFormat::BGRX_8888}, |
| 262 {WL_SHM_FORMAT_ARGB8888, gfx::BufferFormat::BGRA_8888}}; |
| 263 |
| 264 void shm_pool_create_buffer(wl_client* client, |
| 265 wl_resource* resource, |
| 266 uint32_t id, |
| 267 int32_t offset, |
| 268 int32_t width, |
| 269 int32_t height, |
| 270 int32_t stride, |
| 271 uint32_t format) { |
| 272 const auto supported_format = |
| 273 std::find_if(shm_supported_formats, |
| 274 shm_supported_formats + arraysize(shm_supported_formats), |
| 275 [format](const shm_supported_format& supported_format) { |
| 276 return supported_format.shm_format == format; |
| 277 }); |
| 278 if (supported_format == |
| 279 (shm_supported_formats + arraysize(shm_supported_formats))) { |
| 280 wl_resource_post_error(resource, WL_SHM_ERROR_INVALID_FORMAT, |
| 281 "invalid format 0x%x", format); |
| 282 return; |
| 283 } |
| 284 |
| 285 if (offset < 0) { |
| 286 wl_resource_post_error(resource, WL_SHM_ERROR_INVALID_FORMAT, |
| 287 "invalid offset %d", offset); |
| 288 return; |
| 289 } |
| 290 |
| 291 scoped_ptr<Buffer> buffer = |
| 292 GetUserDataAs<SharedMemory>(resource) |
| 293 ->CreateBuffer(gfx::Size(width, height), |
| 294 supported_format->buffer_format, offset, stride); |
| 295 if (!buffer) { |
| 296 wl_resource_post_no_memory(resource); |
| 297 return; |
| 298 } |
| 299 |
| 300 wl_resource* buffer_resource = |
| 301 wl_resource_create(client, &wl_buffer_interface, 1, id); |
| 302 if (!buffer_resource) { |
| 303 wl_resource_post_no_memory(resource); |
| 304 return; |
| 305 } |
| 306 |
| 307 buffer->set_release_callback( |
| 308 base::Bind(&wl_buffer_send_release, base::Unretained(buffer_resource))); |
| 309 |
| 310 SetImplementation(buffer_resource, &buffer_implementation, buffer.Pass()); |
| 311 } |
| 312 |
| 313 void shm_pool_destroy(wl_client* client, wl_resource* resource) { |
| 314 wl_resource_destroy(resource); |
| 315 } |
| 316 |
| 317 void shm_pool_resize(wl_client* client, wl_resource* resource, int32_t size) { |
| 318 // Nothing to do here. |
| 319 } |
| 320 |
| 321 const struct wl_shm_pool_interface shm_pool_implementation = { |
| 322 shm_pool_create_buffer, shm_pool_destroy, shm_pool_resize}; |
| 323 |
| 324 //////////////////////////////////////////////////////////////////////////////// |
| 325 // wl_shm_interface: |
| 326 |
| 327 void shm_create_pool(wl_client* client, |
| 328 wl_resource* resource, |
| 329 uint32_t id, |
| 330 int fd, |
| 331 int32_t size) { |
| 332 scoped_ptr<SharedMemory> shared_memory = |
| 333 GetUserDataAs<Display>(resource) |
| 334 ->CreateSharedMemory(base::FileDescriptor(fd, true), size); |
| 335 if (!shared_memory) { |
| 336 wl_resource_post_no_memory(resource); |
| 337 return; |
| 338 } |
| 339 |
| 340 wl_resource* shm_pool_resource = |
| 341 wl_resource_create(client, &wl_shm_pool_interface, 1, id); |
| 342 if (!shm_pool_resource) { |
| 343 wl_resource_post_no_memory(resource); |
| 344 return; |
| 345 } |
| 346 |
| 347 SetImplementation(shm_pool_resource, &shm_pool_implementation, |
| 348 shared_memory.Pass()); |
| 349 } |
| 350 |
| 351 const struct wl_shm_interface shm_implementation = {shm_create_pool}; |
| 352 |
| 353 void bind_shm(wl_client* client, void* data, uint32_t version, uint32_t id) { |
| 354 wl_resource* resource = wl_resource_create(client, &wl_shm_interface, 1, id); |
| 355 if (!resource) { |
| 356 wl_client_post_no_memory(client); |
| 357 return; |
| 358 } |
| 359 |
| 360 wl_resource_set_implementation(resource, &shm_implementation, data, nullptr); |
| 361 |
| 362 for (const auto& supported_format : shm_supported_formats) |
| 363 wl_shm_send_format(resource, supported_format.shm_format); |
| 364 } |
| 365 |
| 366 //////////////////////////////////////////////////////////////////////////////// |
| 367 // wl_shell_surface_interface: |
| 368 |
| 369 void shell_surface_pong(wl_client* client, |
| 370 wl_resource* resource, |
| 371 uint32_t serial) { |
| 372 NOTIMPLEMENTED(); |
| 373 } |
| 374 |
| 375 void shell_surface_move(wl_client* client, |
| 376 wl_resource* resource, |
| 377 wl_resource* seat_resource, |
| 378 uint32_t serial) { |
| 379 NOTIMPLEMENTED(); |
| 380 } |
| 381 |
| 382 void shell_surface_resize(wl_client* client, |
| 383 wl_resource* resource, |
| 384 wl_resource* seat_resource, |
| 385 uint32_t serial, |
| 386 uint32_t edges) { |
| 387 NOTIMPLEMENTED(); |
| 388 } |
| 389 |
| 390 void shell_surface_set_toplevel(wl_client* client, wl_resource* resource) { |
| 391 GetUserDataAs<ShellSurface>(resource)->SetToplevel(); |
| 392 } |
| 393 |
| 394 void shell_surface_set_transient(wl_client* client, |
| 395 wl_resource* resource, |
| 396 wl_resource* parent_resource, |
| 397 int x, |
| 398 int y, |
| 399 uint32_t flags) { |
| 400 NOTIMPLEMENTED(); |
| 401 } |
| 402 |
| 403 void shell_surface_set_fullscreen(wl_client* client, |
| 404 wl_resource* resource, |
| 405 uint32_t method, |
| 406 uint32_t framerate, |
| 407 wl_resource* output_resource) { |
| 408 GetUserDataAs<ShellSurface>(resource)->SetFullscreen(true); |
| 409 } |
| 410 |
| 411 void shell_surface_set_popup(wl_client* client, |
| 412 wl_resource* resource, |
| 413 wl_resource* seat_resource, |
| 414 uint32_t serial, |
| 415 wl_resource* parent_resource, |
| 416 int32_t x, |
| 417 int32_t y, |
| 418 uint32_t flags) { |
| 419 NOTIMPLEMENTED(); |
| 420 } |
| 421 |
| 422 void shell_surface_set_maximized(wl_client* client, |
| 423 wl_resource* resource, |
| 424 wl_resource* output_resource) { |
| 425 NOTIMPLEMENTED(); |
| 426 } |
| 427 |
| 428 void shell_surface_set_title(wl_client* client, |
| 429 wl_resource* resource, |
| 430 const char* title) { |
| 431 GetUserDataAs<ShellSurface>(resource) |
| 432 ->SetTitle(base::string16(base::ASCIIToUTF16(title))); |
| 433 } |
| 434 |
| 435 void shell_surface_set_class(wl_client* client, |
| 436 wl_resource* resource, |
| 437 const char* clazz) { |
| 438 NOTIMPLEMENTED(); |
| 439 } |
| 440 |
| 441 const struct wl_shell_surface_interface shell_surface_implementation = { |
| 442 shell_surface_pong, shell_surface_move, |
| 443 shell_surface_resize, shell_surface_set_toplevel, |
| 444 shell_surface_set_transient, shell_surface_set_fullscreen, |
| 445 shell_surface_set_popup, shell_surface_set_maximized, |
| 446 shell_surface_set_title, shell_surface_set_class}; |
| 447 |
| 448 //////////////////////////////////////////////////////////////////////////////// |
| 449 // wl_shell_interface: |
| 450 |
| 451 void shell_get_shell_surface(wl_client* client, |
| 452 wl_resource* resource, |
| 453 uint32_t id, |
| 454 wl_resource* surface) { |
| 455 scoped_ptr<ShellSurface> shell_surface = |
| 456 GetUserDataAs<Display>(resource) |
| 457 ->CreateShellSurface(GetUserDataAs<Surface>(surface)); |
| 458 if (!shell_surface) { |
| 459 wl_resource_post_no_memory(resource); |
| 460 return; |
| 461 } |
| 462 |
| 463 wl_resource* shell_surface_resource = |
| 464 wl_resource_create(client, &wl_shell_surface_interface, 1, id); |
| 465 if (!shell_surface_resource) { |
| 466 wl_resource_post_no_memory(resource); |
| 467 return; |
| 468 } |
| 469 |
| 470 SetImplementation(shell_surface_resource, &shell_surface_implementation, |
| 471 shell_surface.Pass()); |
| 472 } |
| 473 |
| 474 const struct wl_shell_interface shell_implementation = { |
| 475 shell_get_shell_surface}; |
| 476 |
| 477 void bind_shell(wl_client* client, void* data, uint32_t version, uint32_t id) { |
| 478 wl_resource* resource = |
| 479 wl_resource_create(client, &wl_shell_interface, 1, id); |
| 480 if (!resource) { |
| 481 wl_client_post_no_memory(client); |
| 482 return; |
| 483 } |
| 484 wl_resource_set_implementation(resource, &shell_implementation, data, |
| 485 nullptr); |
| 486 } |
| 487 |
| 488 } // namespace |
| 489 |
| 490 //////////////////////////////////////////////////////////////////////////////// |
| 491 // Server, public: |
| 492 |
| 493 Server::Server(Display* display) |
| 494 : display_(display), wl_display_(wl_display_create()) { |
| 495 wl_global_create(wl_display_.get(), &wl_compositor_interface, |
| 496 compositor_version, display_, bind_compositor); |
| 497 wl_global_create(wl_display_.get(), &wl_shm_interface, 1, display_, bind_shm); |
| 498 wl_global_create(wl_display_.get(), &wl_shell_interface, 1, display_, |
| 499 bind_shell); |
| 500 } |
| 501 |
| 502 Server::~Server() {} |
| 503 |
| 504 // static |
| 505 scoped_ptr<Server> Server::Create(Display* display) { |
| 506 scoped_ptr<Server> server(new Server(display)); |
| 507 int rv = wl_display_add_socket(server->wl_display_.get(), nullptr); |
| 508 DCHECK_EQ(rv, 0) << "wl_display_add_socket failed: " << rv; |
| 509 return server; |
| 510 } |
| 511 |
| 512 bool Server::AddSocket(const std::string name) { |
| 513 DCHECK(!name.empty()); |
| 514 return !wl_display_add_socket(wl_display_.get(), name.c_str()); |
| 515 } |
| 516 |
| 517 int Server::GetFileDescriptor() const { |
| 518 wl_event_loop* event_loop = wl_display_get_event_loop(wl_display_.get()); |
| 519 DCHECK(event_loop); |
| 520 return wl_event_loop_get_fd(event_loop); |
| 521 } |
| 522 |
| 523 void Server::Dispatch(base::TimeDelta timeout) { |
| 524 wl_event_loop* event_loop = wl_display_get_event_loop(wl_display_.get()); |
| 525 DCHECK(event_loop); |
| 526 wl_event_loop_dispatch(event_loop, timeout.InMilliseconds()); |
| 527 } |
| 528 |
| 529 void Server::Flush() { |
| 530 wl_display_flush_clients(wl_display_.get()); |
| 531 } |
| 532 |
| 533 } // namespace wayland |
| 534 } // namespace exo |
OLD | NEW |