1/* 2 * Xephyr - A kdrive X server that runs in a host X window. 3 * Authored by Matthew Allum <mallum@openedhand.com> 4 * 5 * Copyright © 2007 OpenedHand Ltd 6 * 7 * Permission to use, copy, modify, distribute, and sell this software and its 8 * documentation for any purpose is hereby granted without fee, provided that 9 * the above copyright notice appear in all copies and that both that 10 * copyright notice and this permission notice appear in supporting 11 * documentation, and that the name of OpenedHand Ltd not be used in 12 * advertising or publicity pertaining to distribution of the software without 13 * specific, written prior permission. OpenedHand Ltd makes no 14 * representations about the suitability of this software for any purpose. It 15 * is provided "as is" without express or implied warranty. 16 * 17 * OpenedHand Ltd DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 18 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 19 * EVENT SHALL OpenedHand Ltd BE LIABLE FOR ANY SPECIAL, INDIRECT OR 20 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 21 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 22 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 23 * PERFORMANCE OF THIS SOFTWARE. 24 * 25 * Authors: 26 * Dodji Seketeli <dodji@openedhand.com> 27 */ 28 29#ifdef HAVE_DIX_CONFIG_H 30#include <dix-config.h> 31#endif 32#include <string.h> 33#include <X11/extensions/Xv.h> 34#include <xcb/xcb.h> 35#include <xcb/xcb_aux.h> 36#include <xcb/xv.h> 37#include "ephyrlog.h" 38#include "kdrive.h" 39#include "kxv.h" 40#include "ephyr.h" 41#include "hostx.h" 42 43struct _EphyrXVPriv { 44 xcb_xv_query_adaptors_reply_t *host_adaptors; 45 KdVideoAdaptorPtr adaptors; 46 int num_adaptors; 47}; 48typedef struct _EphyrXVPriv EphyrXVPriv; 49 50struct _EphyrPortPriv { 51 int port_number; 52 KdVideoAdaptorPtr current_adaptor; 53 EphyrXVPriv *xv_priv; 54 unsigned char *image_buf; 55 int image_buf_size; 56 int image_id; 57 int drw_x, drw_y, drw_w, drw_h; 58 int src_x, src_y, src_w, src_h; 59 int image_width, image_height; 60}; 61typedef struct _EphyrPortPriv EphyrPortPriv; 62 63static Bool ephyrLocalAtomToHost(int a_local_atom, int *a_host_atom); 64 65static EphyrXVPriv *ephyrXVPrivNew(void); 66static void ephyrXVPrivDelete(EphyrXVPriv * a_this); 67static Bool ephyrXVPrivQueryHostAdaptors(EphyrXVPriv * a_this); 68static Bool ephyrXVPrivSetAdaptorsHooks(EphyrXVPriv * a_this); 69static Bool ephyrXVPrivRegisterAdaptors(EphyrXVPriv * a_this, 70 ScreenPtr a_screen); 71 72static Bool ephyrXVPrivIsAttrValueValid(XvAttributePtr a_attrs, 73 int a_attrs_len, 74 const char *a_attr_name, 75 int a_attr_value, Bool *a_is_valid); 76 77static Bool ephyrXVPrivGetImageBufSize(int a_port_id, 78 int a_image_id, 79 unsigned short a_width, 80 unsigned short a_height, int *a_size); 81 82static Bool ephyrXVPrivSaveImageToPortPriv(EphyrPortPriv * a_port_priv, 83 const unsigned char *a_image, 84 int a_image_len); 85 86static void ephyrStopVideo(KdScreenInfo * a_info, 87 void *a_xv_priv, Bool a_exit); 88 89static int ephyrSetPortAttribute(KdScreenInfo * a_info, 90 Atom a_attr_name, 91 int a_attr_value, void *a_port_priv); 92 93static int ephyrGetPortAttribute(KdScreenInfo * a_screen_info, 94 Atom a_attr_name, 95 int *a_attr_value, void *a_port_priv); 96 97static void ephyrQueryBestSize(KdScreenInfo * a_info, 98 Bool a_motion, 99 short a_src_w, 100 short a_src_h, 101 short a_drw_w, 102 short a_drw_h, 103 unsigned int *a_prefered_w, 104 unsigned int *a_prefered_h, void *a_port_priv); 105 106static int ephyrPutImage(KdScreenInfo * a_info, 107 DrawablePtr a_drawable, 108 short a_src_x, 109 short a_src_y, 110 short a_drw_x, 111 short a_drw_y, 112 short a_src_w, 113 short a_src_h, 114 short a_drw_w, 115 short a_drw_h, 116 int a_id, 117 unsigned char *a_buf, 118 short a_width, 119 short a_height, 120 Bool a_sync, 121 RegionPtr a_clipping_region, void *a_port_priv); 122 123static int ephyrReputImage(KdScreenInfo * a_info, 124 DrawablePtr a_drawable, 125 short a_drw_x, 126 short a_drw_y, 127 RegionPtr a_clipping_region, void *a_port_priv); 128 129static int ephyrPutVideo(KdScreenInfo * a_info, 130 DrawablePtr a_drawable, 131 short a_vid_x, short a_vid_y, 132 short a_drw_x, short a_drw_y, 133 short a_vid_w, short a_vid_h, 134 short a_drw_w, short a_drw_h, 135 RegionPtr a_clip_region, void *a_port_priv); 136 137static int ephyrGetVideo(KdScreenInfo * a_info, 138 DrawablePtr a_drawable, 139 short a_vid_x, short a_vid_y, 140 short a_drw_x, short a_drw_y, 141 short a_vid_w, short a_vid_h, 142 short a_drw_w, short a_drw_h, 143 RegionPtr a_clip_region, void *a_port_priv); 144 145static int ephyrPutStill(KdScreenInfo * a_info, 146 DrawablePtr a_drawable, 147 short a_vid_x, short a_vid_y, 148 short a_drw_x, short a_drw_y, 149 short a_vid_w, short a_vid_h, 150 short a_drw_w, short a_drw_h, 151 RegionPtr a_clip_region, void *a_port_priv); 152 153static int ephyrGetStill(KdScreenInfo * a_info, 154 DrawablePtr a_drawable, 155 short a_vid_x, short a_vid_y, 156 short a_drw_x, short a_drw_y, 157 short a_vid_w, short a_vid_h, 158 short a_drw_w, short a_drw_h, 159 RegionPtr a_clip_region, void *a_port_priv); 160 161static int ephyrQueryImageAttributes(KdScreenInfo * a_info, 162 int a_id, 163 unsigned short *a_w, 164 unsigned short *a_h, 165 int *a_pitches, int *a_offsets); 166static int s_base_port_id; 167 168/************** 169 * <helpers> 170 * ************/ 171 172static Bool 173adaptor_has_flags(const xcb_xv_adaptor_info_t *adaptor, uint32_t flags) 174{ 175 return (adaptor->type & flags) == flags; 176} 177 178static Bool 179ephyrLocalAtomToHost(int a_local_atom, int *a_host_atom) 180{ 181 xcb_connection_t *conn = hostx_get_xcbconn(); 182 xcb_intern_atom_cookie_t cookie; 183 xcb_intern_atom_reply_t *reply; 184 const char *atom_name = NULL; 185 186 EPHYR_RETURN_VAL_IF_FAIL(a_host_atom, FALSE); 187 188 if (!ValidAtom(a_local_atom)) 189 return FALSE; 190 191 atom_name = NameForAtom(a_local_atom); 192 193 if (!atom_name) 194 return FALSE; 195 196 cookie = xcb_intern_atom(conn, FALSE, strlen(atom_name), atom_name); 197 reply = xcb_intern_atom_reply(conn, cookie, NULL); 198 if (!reply || reply->atom == None) { 199 EPHYR_LOG_ERROR("no atom for string %s defined in host X\n", atom_name); 200 return FALSE; 201 } 202 203 *a_host_atom = reply->atom; 204 free(reply); 205 206 return TRUE; 207} 208 209/************** 210 *</helpers> 211 * ************/ 212 213Bool 214ephyrInitVideo(ScreenPtr pScreen) 215{ 216 Bool is_ok = FALSE; 217 218 KdScreenPriv(pScreen); 219 KdScreenInfo *screen = pScreenPriv->screen; 220 static EphyrXVPriv *xv_priv; 221 222 EPHYR_LOG("enter\n"); 223 224 if (screen->fb.bitsPerPixel == 8) { 225 EPHYR_LOG_ERROR("8 bits depth not supported\n"); 226 return FALSE; 227 } 228 229 if (!hostx_has_extension(&xcb_xv_id)) { 230 EPHYR_LOG_ERROR("Host has no XVideo extension\n"); 231 return FALSE; 232 } 233 234 if (!xv_priv) { 235 xv_priv = ephyrXVPrivNew(); 236 } 237 if (!xv_priv) { 238 EPHYR_LOG_ERROR("failed to create xv_priv\n"); 239 goto out; 240 } 241 242 if (!ephyrXVPrivRegisterAdaptors(xv_priv, pScreen)) { 243 EPHYR_LOG_ERROR("failed to register adaptors\n"); 244 goto out; 245 } 246 is_ok = TRUE; 247 248 out: 249 return is_ok; 250} 251 252static EphyrXVPriv * 253ephyrXVPrivNew(void) 254{ 255 EphyrXVPriv *xv_priv = NULL; 256 257 EPHYR_LOG("enter\n"); 258 259 xv_priv = calloc(1, sizeof(EphyrXVPriv)); 260 if (!xv_priv) { 261 EPHYR_LOG_ERROR("failed to create EphyrXVPriv\n"); 262 goto error; 263 } 264 265 if (!ephyrXVPrivQueryHostAdaptors(xv_priv)) { 266 EPHYR_LOG_ERROR("failed to query the host x for xv properties\n"); 267 goto error; 268 } 269 if (!ephyrXVPrivSetAdaptorsHooks(xv_priv)) { 270 EPHYR_LOG_ERROR("failed to set xv_priv hooks\n"); 271 goto error; 272 } 273 274 EPHYR_LOG("leave\n"); 275 return xv_priv; 276 277 error: 278 if (xv_priv) { 279 ephyrXVPrivDelete(xv_priv); 280 xv_priv = NULL; 281 } 282 return NULL; 283} 284 285static void 286ephyrXVPrivDelete(EphyrXVPriv * a_this) 287{ 288 EPHYR_LOG("enter\n"); 289 290 if (!a_this) 291 return; 292 if (a_this->host_adaptors) { 293 free(a_this->host_adaptors); 294 a_this->host_adaptors = NULL; 295 } 296 free(a_this->adaptors); 297 a_this->adaptors = NULL; 298 free(a_this); 299 EPHYR_LOG("leave\n"); 300} 301 302static Bool 303translate_video_encodings(KdVideoAdaptorPtr adaptor, 304 xcb_xv_adaptor_info_t *host_adaptor) 305{ 306 xcb_connection_t *conn = hostx_get_xcbconn(); 307 int i; 308 xcb_xv_query_encodings_cookie_t cookie; 309 xcb_xv_query_encodings_reply_t *reply; 310 xcb_xv_encoding_info_iterator_t encoding_it; 311 312 cookie = xcb_xv_query_encodings(conn, host_adaptor->base_id); 313 reply = xcb_xv_query_encodings_reply(conn, cookie, NULL); 314 if (!reply) 315 return FALSE; 316 317 adaptor->nEncodings = reply->num_encodings; 318 adaptor->pEncodings = calloc(adaptor->nEncodings, 319 sizeof(*adaptor->pEncodings)); 320 if (!adaptor->pEncodings) { 321 free(reply); 322 return FALSE; 323 } 324 325 encoding_it = xcb_xv_query_encodings_info_iterator(reply); 326 for (i = 0; i < adaptor->nEncodings; i++) { 327 xcb_xv_encoding_info_t *encoding_info = encoding_it.data; 328 KdVideoEncodingPtr encoding = &adaptor->pEncodings[i]; 329 330 encoding->id = encoding_info->encoding; 331 encoding->name = strndup(xcb_xv_encoding_info_name(encoding_info), 332 encoding_info->name_size); 333 encoding->width = encoding_info->width; 334 encoding->height = encoding_info->height; 335 encoding->rate.numerator = encoding_info->rate.numerator; 336 encoding->rate.denominator = encoding_info->rate.denominator; 337 338 xcb_xv_encoding_info_next(&encoding_it); 339 } 340 341 free(reply); 342 return TRUE; 343} 344 345static Bool 346translate_xv_attributes(KdVideoAdaptorPtr adaptor, 347 xcb_xv_adaptor_info_t *host_adaptor) 348{ 349 xcb_connection_t *conn = hostx_get_xcbconn(); 350 int i = 0; 351 xcb_xv_attribute_info_iterator_t it; 352 xcb_xv_query_port_attributes_cookie_t cookie = 353 xcb_xv_query_port_attributes(conn, host_adaptor->base_id); 354 xcb_xv_query_port_attributes_reply_t *reply = 355 xcb_xv_query_port_attributes_reply(conn, cookie, NULL); 356 357 if (!reply) 358 return FALSE; 359 360 adaptor->nAttributes = reply->num_attributes; 361 adaptor->pAttributes = calloc(reply->num_attributes, 362 sizeof(*adaptor->pAttributes)); 363 if (!adaptor->pAttributes) { 364 EPHYR_LOG_ERROR("failed to allocate attributes\n"); 365 free(reply); 366 return FALSE; 367 } 368 369 it = xcb_xv_query_port_attributes_attributes_iterator(reply); 370 for (i = 0; i < reply->num_attributes; i++) { 371 XvAttributePtr attribute = &adaptor->pAttributes[i]; 372 373 attribute->flags = it.data->flags; 374 attribute->min_value = it.data->min; 375 attribute->max_value = it.data->max; 376 attribute->name = strndup(xcb_xv_attribute_info_name(it.data), 377 it.data->size); 378 379 /* make sure atoms of attrs names are created in xephyr */ 380 MakeAtom(xcb_xv_attribute_info_name(it.data), it.data->size, TRUE); 381 382 xcb_xv_attribute_info_next(&it); 383 } 384 385 free(reply); 386 return TRUE; 387} 388 389static Bool 390translate_xv_image_formats(KdVideoAdaptorPtr adaptor, 391 xcb_xv_adaptor_info_t *host_adaptor) 392{ 393 xcb_connection_t *conn = hostx_get_xcbconn(); 394 int i = 0; 395 xcb_xv_list_image_formats_cookie_t cookie = 396 xcb_xv_list_image_formats(conn, host_adaptor->base_id); 397 xcb_xv_list_image_formats_reply_t *reply = 398 xcb_xv_list_image_formats_reply(conn, cookie, NULL); 399 xcb_xv_image_format_info_t *formats; 400 401 if (!reply) 402 return FALSE; 403 404 adaptor->nImages = reply->num_formats; 405 adaptor->pImages = calloc(reply->num_formats, sizeof(XvImageRec)); 406 if (!adaptor->pImages) { 407 free(reply); 408 return FALSE; 409 } 410 411 formats = xcb_xv_list_image_formats_format(reply); 412 for (i = 0; i < reply->num_formats; i++) { 413 XvImagePtr image = &adaptor->pImages[i]; 414 415 image->id = formats[i].id; 416 image->type = formats[i].type; 417 image->byte_order = formats[i].byte_order; 418 memcpy(image->guid, formats[i].guid, 16); 419 image->bits_per_pixel = formats[i].bpp; 420 image->format = formats[i].format; 421 image->num_planes = formats[i].num_planes; 422 image->depth = formats[i].depth; 423 image->red_mask = formats[i].red_mask; 424 image->green_mask = formats[i].green_mask; 425 image->blue_mask = formats[i].blue_mask; 426 image->y_sample_bits = formats[i].y_sample_bits; 427 image->u_sample_bits = formats[i].u_sample_bits; 428 image->v_sample_bits = formats[i].v_sample_bits; 429 image->horz_y_period = formats[i].vhorz_y_period; 430 image->horz_u_period = formats[i].vhorz_u_period; 431 image->horz_v_period = formats[i].vhorz_v_period; 432 image->vert_y_period = formats[i].vvert_y_period; 433 image->vert_u_period = formats[i].vvert_u_period; 434 image->vert_v_period = formats[i].vvert_v_period; 435 memcpy(image->component_order, formats[i].vcomp_order, 32); 436 image->scanline_order = formats[i].vscanline_order; 437 } 438 439 free(reply); 440 return TRUE; 441} 442 443static Bool 444ephyrXVPrivQueryHostAdaptors(EphyrXVPriv * a_this) 445{ 446 xcb_connection_t *conn = hostx_get_xcbconn(); 447 xcb_screen_t *xscreen = xcb_aux_get_screen(conn, hostx_get_screen()); 448 int base_port_id = 0, i = 0, port_priv_offset = 0; 449 Bool is_ok = FALSE; 450 xcb_generic_error_t *e = NULL; 451 xcb_xv_adaptor_info_iterator_t it; 452 453 EPHYR_RETURN_VAL_IF_FAIL(a_this, FALSE); 454 455 EPHYR_LOG("enter\n"); 456 457 { 458 xcb_xv_query_adaptors_cookie_t cookie = 459 xcb_xv_query_adaptors(conn, xscreen->root); 460 a_this->host_adaptors = xcb_xv_query_adaptors_reply(conn, cookie, &e); 461 if (e) { 462 free(e); 463 EPHYR_LOG_ERROR("failed to query host adaptors\n"); 464 goto out; 465 } 466 } 467 468 if (a_this->host_adaptors) 469 a_this->num_adaptors = a_this->host_adaptors->num_adaptors; 470 if (a_this->num_adaptors <= 0) { 471 EPHYR_LOG_ERROR("failed to get number of host adaptors\n"); 472 goto out; 473 } 474 EPHYR_LOG("host has %d adaptors\n", a_this->num_adaptors); 475 /* 476 * copy what we can from adaptors into a_this->adaptors 477 */ 478 if (a_this->num_adaptors) { 479 a_this->adaptors = calloc(a_this->num_adaptors, 480 sizeof(KdVideoAdaptorRec)); 481 if (!a_this->adaptors) { 482 EPHYR_LOG_ERROR("failed to create internal adaptors\n"); 483 goto out; 484 } 485 } 486 487 it = xcb_xv_query_adaptors_info_iterator(a_this->host_adaptors); 488 for (i = 0; i < a_this->num_adaptors; i++) { 489 xcb_xv_adaptor_info_t *cur_host_adaptor = it.data; 490 xcb_xv_format_t *format = xcb_xv_adaptor_info_formats(cur_host_adaptor); 491 int j = 0; 492 493 a_this->adaptors[i].nPorts = cur_host_adaptor->num_ports; 494 if (a_this->adaptors[i].nPorts <= 0) { 495 EPHYR_LOG_ERROR("Could not find any port of adaptor %d\n", i); 496 continue; 497 } 498 a_this->adaptors[i].type = cur_host_adaptor->type; 499 a_this->adaptors[i].type |= XvWindowMask; 500 a_this->adaptors[i].flags = 501 VIDEO_OVERLAID_IMAGES | VIDEO_CLIP_TO_VIEWPORT; 502 a_this->adaptors[i].name = 503 strndup(xcb_xv_adaptor_info_name(cur_host_adaptor), 504 cur_host_adaptor->name_size); 505 if (!a_this->adaptors[i].name) 506 a_this->adaptors[i].name = strdup("Xephyr Video Overlay"); 507 base_port_id = cur_host_adaptor->base_id; 508 if (base_port_id < 0) { 509 EPHYR_LOG_ERROR("failed to get port id for adaptor %d\n", i); 510 continue; 511 } 512 if (!s_base_port_id) 513 s_base_port_id = base_port_id; 514 515 if (!translate_video_encodings(&a_this->adaptors[i], 516 cur_host_adaptor)) { 517 EPHYR_LOG_ERROR("failed to get encodings for port port id %d," 518 " adaptors %d\n", base_port_id, i); 519 continue; 520 } 521 522 a_this->adaptors[i].nFormats = cur_host_adaptor->num_formats; 523 a_this->adaptors[i].pFormats = 524 calloc(cur_host_adaptor->num_formats, 525 sizeof(*a_this->adaptors[i].pFormats)); 526 for (j = 0; j < cur_host_adaptor->num_formats; j++) { 527 xcb_visualtype_t *visual = 528 xcb_aux_find_visual_by_id(xscreen, format[j].visual); 529 a_this->adaptors[i].pFormats[j].depth = format[j].depth; 530 a_this->adaptors[i].pFormats[j].class = visual->_class; 531 } 532 533 a_this->adaptors[i].pPortPrivates = 534 calloc(a_this->adaptors[i].nPorts, 535 sizeof(DevUnion) + sizeof(EphyrPortPriv)); 536 port_priv_offset = a_this->adaptors[i].nPorts; 537 for (j = 0; j < a_this->adaptors[i].nPorts; j++) { 538 EphyrPortPriv *port_privs_base = 539 (EphyrPortPriv *) &a_this->adaptors[i]. 540 pPortPrivates[port_priv_offset]; 541 EphyrPortPriv *port_priv = &port_privs_base[j]; 542 543 port_priv->port_number = base_port_id + j; 544 port_priv->current_adaptor = &a_this->adaptors[i]; 545 port_priv->xv_priv = a_this; 546 a_this->adaptors[i].pPortPrivates[j].ptr = port_priv; 547 } 548 549 if (!translate_xv_attributes(&a_this->adaptors[i], cur_host_adaptor)) { 550 { 551 EPHYR_LOG_ERROR("failed to get port attribute " 552 "for adaptor %d\n", i); 553 continue; 554 } 555 } 556 557 if (!translate_xv_image_formats(&a_this->adaptors[i], cur_host_adaptor)) { 558 EPHYR_LOG_ERROR("failed to get image formats " 559 "for adaptor %d\n", i); 560 continue; 561 } 562 563 xcb_xv_adaptor_info_next(&it); 564 } 565 is_ok = TRUE; 566 567 out: 568 EPHYR_LOG("leave\n"); 569 return is_ok; 570} 571 572static Bool 573ephyrXVPrivSetAdaptorsHooks(EphyrXVPriv * a_this) 574{ 575 int i = 0; 576 xcb_xv_adaptor_info_iterator_t it; 577 578 EPHYR_RETURN_VAL_IF_FAIL(a_this, FALSE); 579 580 EPHYR_LOG("enter\n"); 581 582 it = xcb_xv_query_adaptors_info_iterator(a_this->host_adaptors); 583 for (i = 0; i < a_this->num_adaptors; i++) { 584 xcb_xv_adaptor_info_t *cur_host_adaptor = it.data; 585 586 a_this->adaptors[i].ReputImage = ephyrReputImage; 587 a_this->adaptors[i].StopVideo = ephyrStopVideo; 588 a_this->adaptors[i].SetPortAttribute = ephyrSetPortAttribute; 589 a_this->adaptors[i].GetPortAttribute = ephyrGetPortAttribute; 590 a_this->adaptors[i].QueryBestSize = ephyrQueryBestSize; 591 a_this->adaptors[i].QueryImageAttributes = ephyrQueryImageAttributes; 592 593 if (adaptor_has_flags(cur_host_adaptor, 594 XCB_XV_TYPE_IMAGE_MASK | XCB_XV_TYPE_INPUT_MASK)) 595 a_this->adaptors[i].PutImage = ephyrPutImage; 596 597 if (adaptor_has_flags(cur_host_adaptor, 598 XCB_XV_TYPE_VIDEO_MASK | XCB_XV_TYPE_INPUT_MASK)) 599 a_this->adaptors[i].PutVideo = ephyrPutVideo; 600 601 if (adaptor_has_flags(cur_host_adaptor, 602 XCB_XV_TYPE_VIDEO_MASK | XCB_XV_TYPE_OUTPUT_MASK)) 603 a_this->adaptors[i].GetVideo = ephyrGetVideo; 604 605 if (adaptor_has_flags(cur_host_adaptor, 606 XCB_XV_TYPE_STILL_MASK | XCB_XV_TYPE_INPUT_MASK)) 607 a_this->adaptors[i].PutStill = ephyrPutStill; 608 609 if (adaptor_has_flags(cur_host_adaptor, 610 XCB_XV_TYPE_STILL_MASK | XCB_XV_TYPE_OUTPUT_MASK)) 611 a_this->adaptors[i].GetStill = ephyrGetStill; 612 } 613 EPHYR_LOG("leave\n"); 614 return TRUE; 615} 616 617static Bool 618ephyrXVPrivRegisterAdaptors(EphyrXVPriv * a_this, ScreenPtr a_screen) 619{ 620 Bool is_ok = FALSE; 621 622 EPHYR_RETURN_VAL_IF_FAIL(a_this && a_screen, FALSE); 623 624 EPHYR_LOG("enter\n"); 625 626 if (!a_this->num_adaptors) 627 goto out; 628 629 if (!KdXVScreenInit(a_screen, a_this->adaptors, a_this->num_adaptors)) { 630 EPHYR_LOG_ERROR("failed to register adaptors\n"); 631 goto out; 632 } 633 EPHYR_LOG("there are %d registered adaptors\n", a_this->num_adaptors); 634 is_ok = TRUE; 635 636 out: 637 638 EPHYR_LOG("leave\n"); 639 return is_ok; 640} 641 642static Bool 643ephyrXVPrivIsAttrValueValid(XvAttributePtr a_attrs, 644 int a_attrs_len, 645 const char *a_attr_name, 646 int a_attr_value, Bool *a_is_valid) 647{ 648 int i = 0; 649 650 EPHYR_RETURN_VAL_IF_FAIL(a_attrs && a_attr_name && a_is_valid, FALSE); 651 652 for (i = 0; i < a_attrs_len; i++) { 653 if (a_attrs[i].name && strcmp(a_attrs[i].name, a_attr_name)) 654 continue; 655 if (a_attrs[i].min_value > a_attr_value || 656 a_attrs[i].max_value < a_attr_value) { 657 *a_is_valid = FALSE; 658 EPHYR_LOG_ERROR("attribute was not valid\n" 659 "value:%d. min:%d. max:%d\n", 660 a_attr_value, 661 a_attrs[i].min_value, a_attrs[i].max_value); 662 } 663 else { 664 *a_is_valid = TRUE; 665 } 666 return TRUE; 667 } 668 return FALSE; 669} 670 671static Bool 672ephyrXVPrivGetImageBufSize(int a_port_id, 673 int a_image_id, 674 unsigned short a_width, 675 unsigned short a_height, int *a_size) 676{ 677 xcb_connection_t *conn = hostx_get_xcbconn(); 678 xcb_xv_query_image_attributes_cookie_t cookie; 679 xcb_xv_query_image_attributes_reply_t *reply; 680 Bool is_ok = FALSE; 681 682 EPHYR_RETURN_VAL_IF_FAIL(a_size, FALSE); 683 684 EPHYR_LOG("enter\n"); 685 686 cookie = xcb_xv_query_image_attributes(conn, 687 a_port_id, a_image_id, 688 a_width, a_height); 689 reply = xcb_xv_query_image_attributes_reply(conn, cookie, NULL); 690 if (!reply) 691 goto out; 692 693 *a_size = reply->data_size; 694 is_ok = TRUE; 695 696 free(reply); 697 698 out: 699 EPHYR_LOG("leave\n"); 700 return is_ok; 701} 702 703static Bool 704ephyrXVPrivSaveImageToPortPriv(EphyrPortPriv * a_port_priv, 705 const unsigned char *a_image_buf, 706 int a_image_len) 707{ 708 Bool is_ok = FALSE; 709 710 EPHYR_LOG("enter\n"); 711 712 if (a_port_priv->image_buf_size < a_image_len) { 713 unsigned char *buf = NULL; 714 715 buf = realloc(a_port_priv->image_buf, a_image_len); 716 if (!buf) { 717 EPHYR_LOG_ERROR("failed to realloc image buffer\n"); 718 goto out; 719 } 720 a_port_priv->image_buf = buf; 721 a_port_priv->image_buf_size = a_image_len; 722 } 723 memmove(a_port_priv->image_buf, a_image_buf, a_image_len); 724 is_ok = TRUE; 725 726 out: 727 return is_ok; 728 EPHYR_LOG("leave\n"); 729} 730 731static void 732ephyrStopVideo(KdScreenInfo * a_info, void *a_port_priv, Bool a_exit) 733{ 734 xcb_connection_t *conn = hostx_get_xcbconn(); 735 EphyrPortPriv *port_priv = a_port_priv; 736 EphyrScrPriv *scrpriv = a_info->driver; 737 738 EPHYR_RETURN_IF_FAIL(port_priv); 739 740 EPHYR_LOG("enter\n"); 741 xcb_xv_stop_video(conn, port_priv->port_number, scrpriv->win); 742 EPHYR_LOG("leave\n"); 743} 744 745static int 746ephyrSetPortAttribute(KdScreenInfo * a_info, 747 Atom a_attr_name, int a_attr_value, void *a_port_priv) 748{ 749 xcb_connection_t *conn = hostx_get_xcbconn(); 750 int res = Success, host_atom = 0; 751 EphyrPortPriv *port_priv = a_port_priv; 752 Bool is_attr_valid = FALSE; 753 754 EPHYR_RETURN_VAL_IF_FAIL(port_priv, BadMatch); 755 EPHYR_RETURN_VAL_IF_FAIL(port_priv->current_adaptor, BadMatch); 756 EPHYR_RETURN_VAL_IF_FAIL(port_priv->current_adaptor->pAttributes, BadMatch); 757 EPHYR_RETURN_VAL_IF_FAIL(port_priv->current_adaptor->nAttributes, BadMatch); 758 EPHYR_RETURN_VAL_IF_FAIL(ValidAtom(a_attr_name), BadMatch); 759 760 EPHYR_LOG("enter, portnum:%d, atomid:%d, attr_name:%s, attr_val:%d\n", 761 port_priv->port_number, 762 (int) a_attr_name, NameForAtom(a_attr_name), a_attr_value); 763 764 if (!ephyrLocalAtomToHost(a_attr_name, &host_atom)) { 765 EPHYR_LOG_ERROR("failed to convert local atom to host atom\n"); 766 res = BadMatch; 767 goto out; 768 } 769 770 if (!ephyrXVPrivIsAttrValueValid(port_priv->current_adaptor->pAttributes, 771 port_priv->current_adaptor->nAttributes, 772 NameForAtom(a_attr_name), 773 a_attr_value, &is_attr_valid)) { 774 EPHYR_LOG_ERROR("failed to validate attribute %s\n", 775 NameForAtom(a_attr_name)); 776 /* 777 res = BadMatch ; 778 goto out ; 779 */ 780 } 781 if (!is_attr_valid) { 782 EPHYR_LOG_ERROR("attribute %s is not valid\n", 783 NameForAtom(a_attr_name)); 784 /* 785 res = BadMatch ; 786 goto out ; 787 */ 788 } 789 790 xcb_xv_set_port_attribute(conn, port_priv->port_number, 791 host_atom, a_attr_value); 792 xcb_flush(conn); 793 794 res = Success; 795 out: 796 EPHYR_LOG("leave\n"); 797 return res; 798} 799 800static int 801ephyrGetPortAttribute(KdScreenInfo * a_screen_info, 802 Atom a_attr_name, int *a_attr_value, void *a_port_priv) 803{ 804 xcb_connection_t *conn = hostx_get_xcbconn(); 805 int res = Success, host_atom = 0; 806 EphyrPortPriv *port_priv = a_port_priv; 807 xcb_generic_error_t *e; 808 xcb_xv_get_port_attribute_cookie_t cookie; 809 xcb_xv_get_port_attribute_reply_t *reply; 810 811 EPHYR_RETURN_VAL_IF_FAIL(port_priv, BadMatch); 812 EPHYR_RETURN_VAL_IF_FAIL(ValidAtom(a_attr_name), BadMatch); 813 814 EPHYR_LOG("enter, portnum:%d, atomid:%d, attr_name:%s\n", 815 port_priv->port_number, 816 (int) a_attr_name, NameForAtom(a_attr_name)); 817 818 if (!ephyrLocalAtomToHost(a_attr_name, &host_atom)) { 819 EPHYR_LOG_ERROR("failed to convert local atom to host atom\n"); 820 res = BadMatch; 821 goto out; 822 } 823 824 cookie = xcb_xv_get_port_attribute(conn, port_priv->port_number, host_atom); 825 reply = xcb_xv_get_port_attribute_reply(conn, cookie, &e); 826 if (e) { 827 EPHYR_LOG_ERROR ("XvGetPortAttribute() failed: %d \n", e->error_code); 828 free(e); 829 res = BadMatch; 830 goto out; 831 } 832 *a_attr_value = reply->value; 833 834 free(reply); 835 836 res = Success; 837 out: 838 EPHYR_LOG("leave\n"); 839 return res; 840} 841 842static void 843ephyrQueryBestSize(KdScreenInfo * a_info, 844 Bool a_motion, 845 short a_src_w, 846 short a_src_h, 847 short a_drw_w, 848 short a_drw_h, 849 unsigned int *a_prefered_w, 850 unsigned int *a_prefered_h, void *a_port_priv) 851{ 852 xcb_connection_t *conn = hostx_get_xcbconn(); 853 EphyrPortPriv *port_priv = a_port_priv; 854 xcb_xv_query_best_size_cookie_t cookie = 855 xcb_xv_query_best_size(conn, 856 port_priv->port_number, 857 a_src_w, a_src_h, 858 a_drw_w, a_drw_h, 859 a_motion); 860 xcb_xv_query_best_size_reply_t *reply = 861 xcb_xv_query_best_size_reply(conn, cookie, NULL); 862 863 EPHYR_LOG("enter: frame (%dx%d), drw (%dx%d)\n", 864 a_src_w, a_src_h, a_drw_w, a_drw_h); 865 866 if (!reply) { 867 EPHYR_LOG_ERROR ("XvQueryBestSize() failed\n"); 868 return; 869 } 870 *a_prefered_w = reply->actual_width; 871 *a_prefered_h = reply->actual_height; 872 EPHYR_LOG("actual (%dx%d)\n", *a_prefered_w, *a_prefered_h); 873 free(reply); 874 875 EPHYR_LOG("leave\n"); 876} 877 878 879static Bool 880ephyrHostXVPutImage(KdScreenInfo * a_info, 881 EphyrPortPriv *port_priv, 882 int a_image_id, 883 int a_drw_x, 884 int a_drw_y, 885 int a_drw_w, 886 int a_drw_h, 887 int a_src_x, 888 int a_src_y, 889 int a_src_w, 890 int a_src_h, 891 int a_image_width, 892 int a_image_height, 893 unsigned char *a_buf, 894 BoxPtr a_clip_rects, int a_clip_rect_nums) 895{ 896 EphyrScrPriv *scrpriv = a_info->driver; 897 xcb_connection_t *conn = hostx_get_xcbconn(); 898 xcb_gcontext_t gc; 899 Bool is_ok = TRUE; 900 xcb_rectangle_t *rects = NULL; 901 int data_len, width, height; 902 xcb_xv_query_image_attributes_cookie_t image_attr_cookie; 903 xcb_xv_query_image_attributes_reply_t *image_attr_reply; 904 905 EPHYR_RETURN_VAL_IF_FAIL(a_buf, FALSE); 906 907 EPHYR_LOG("enter, num_clip_rects: %d\n", a_clip_rect_nums); 908 909 image_attr_cookie = xcb_xv_query_image_attributes(conn, 910 port_priv->port_number, 911 a_image_id, 912 a_image_width, 913 a_image_height); 914 image_attr_reply = xcb_xv_query_image_attributes_reply(conn, 915 image_attr_cookie, 916 NULL); 917 if (!image_attr_reply) 918 goto out; 919 data_len = image_attr_reply->data_size; 920 width = image_attr_reply->width; 921 height = image_attr_reply->height; 922 free(image_attr_reply); 923 924 gc = xcb_generate_id(conn); 925 xcb_create_gc(conn, gc, scrpriv->win, 0, NULL); 926 927 if (a_clip_rect_nums) { 928 int i = 0; 929 rects = calloc(a_clip_rect_nums, sizeof(xcb_rectangle_t)); 930 for (i=0; i < a_clip_rect_nums; i++) { 931 rects[i].x = a_clip_rects[i].x1; 932 rects[i].y = a_clip_rects[i].y1; 933 rects[i].width = a_clip_rects[i].x2 - a_clip_rects[i].x1; 934 rects[i].height = a_clip_rects[i].y2 - a_clip_rects[i].y1; 935 EPHYR_LOG("(x,y,w,h): (%d,%d,%d,%d)\n", 936 rects[i].x, rects[i].y, rects[i].width, rects[i].height); 937 } 938 xcb_set_clip_rectangles(conn, 939 XCB_CLIP_ORDERING_YX_BANDED, 940 gc, 941 0, 942 0, 943 a_clip_rect_nums, 944 rects); 945 free(rects); 946 } 947 xcb_xv_put_image(conn, 948 port_priv->port_number, 949 scrpriv->win, 950 gc, 951 a_image_id, 952 a_src_x, a_src_y, a_src_w, a_src_h, 953 a_drw_x, a_drw_y, a_drw_w, a_drw_h, 954 width, height, 955 data_len, a_buf); 956 xcb_free_gc(conn, gc); 957 958 is_ok = TRUE; 959 960out: 961 EPHYR_LOG("leave\n"); 962 return is_ok; 963} 964 965static int 966ephyrPutImage(KdScreenInfo * a_info, 967 DrawablePtr a_drawable, 968 short a_src_x, 969 short a_src_y, 970 short a_drw_x, 971 short a_drw_y, 972 short a_src_w, 973 short a_src_h, 974 short a_drw_w, 975 short a_drw_h, 976 int a_id, 977 unsigned char *a_buf, 978 short a_width, 979 short a_height, 980 Bool a_sync, RegionPtr a_clipping_region, void *a_port_priv) 981{ 982 EphyrPortPriv *port_priv = a_port_priv; 983 Bool is_ok = FALSE; 984 int result = BadImplementation, image_size = 0; 985 986 EPHYR_RETURN_VAL_IF_FAIL(a_info && a_info->pScreen, BadValue); 987 EPHYR_RETURN_VAL_IF_FAIL(a_drawable, BadValue); 988 989 EPHYR_LOG("enter\n"); 990 991 if (!ephyrHostXVPutImage(a_info, port_priv, 992 a_id, 993 a_drw_x, a_drw_y, a_drw_w, a_drw_h, 994 a_src_x, a_src_y, a_src_w, a_src_h, 995 a_width, a_height, a_buf, 996 RegionRects(a_clipping_region), 997 RegionNumRects(a_clipping_region))) { 998 EPHYR_LOG_ERROR("EphyrHostXVPutImage() failed\n"); 999 goto out; 1000 } 1001 1002 /* 1003 * Now save the image so that we can resend it to host it 1004 * later, in ReputImage. 1005 */ 1006 if (!ephyrXVPrivGetImageBufSize(port_priv->port_number, 1007 a_id, a_width, a_height, &image_size)) { 1008 EPHYR_LOG_ERROR("failed to get image size\n"); 1009 /*this is a minor error so we won't get bail out abruptly */ 1010 is_ok = FALSE; 1011 } 1012 else { 1013 is_ok = TRUE; 1014 } 1015 if (is_ok) { 1016 if (!ephyrXVPrivSaveImageToPortPriv(port_priv, a_buf, image_size)) { 1017 is_ok = FALSE; 1018 } 1019 else { 1020 port_priv->image_id = a_id; 1021 port_priv->drw_x = a_drw_x; 1022 port_priv->drw_y = a_drw_y; 1023 port_priv->drw_w = a_drw_w; 1024 port_priv->drw_h = a_drw_h; 1025 port_priv->src_x = a_src_x; 1026 port_priv->src_y = a_src_y; 1027 port_priv->src_w = a_src_w; 1028 port_priv->src_h = a_src_h; 1029 port_priv->image_width = a_width; 1030 port_priv->image_height = a_height; 1031 } 1032 } 1033 if (!is_ok) { 1034 if (port_priv->image_buf) { 1035 free(port_priv->image_buf); 1036 port_priv->image_buf = NULL; 1037 port_priv->image_buf_size = 0; 1038 } 1039 } 1040 1041 result = Success; 1042 1043 out: 1044 EPHYR_LOG("leave\n"); 1045 return result; 1046} 1047 1048static int 1049ephyrReputImage(KdScreenInfo * a_info, 1050 DrawablePtr a_drawable, 1051 short a_drw_x, 1052 short a_drw_y, RegionPtr a_clipping_region, void *a_port_priv) 1053{ 1054 EphyrPortPriv *port_priv = a_port_priv; 1055 int result = BadImplementation; 1056 1057 EPHYR_RETURN_VAL_IF_FAIL(a_info->pScreen, FALSE); 1058 EPHYR_RETURN_VAL_IF_FAIL(a_drawable && port_priv, BadValue); 1059 1060 EPHYR_LOG("enter\n"); 1061 1062 if (!port_priv->image_buf_size || !port_priv->image_buf) { 1063 EPHYR_LOG_ERROR("has null image buf in cache\n"); 1064 goto out; 1065 } 1066 if (!ephyrHostXVPutImage(a_info, 1067 port_priv, 1068 port_priv->image_id, 1069 a_drw_x, a_drw_y, 1070 port_priv->drw_w, port_priv->drw_h, 1071 port_priv->src_x, port_priv->src_y, 1072 port_priv->src_w, port_priv->src_h, 1073 port_priv->image_width, port_priv->image_height, 1074 port_priv->image_buf, 1075 RegionRects(a_clipping_region), 1076 RegionNumRects(a_clipping_region))) { 1077 EPHYR_LOG_ERROR("ephyrHostXVPutImage() failed\n"); 1078 goto out; 1079 } 1080 1081 result = Success; 1082 1083 out: 1084 EPHYR_LOG("leave\n"); 1085 return result; 1086} 1087 1088static int 1089ephyrPutVideo(KdScreenInfo * a_info, 1090 DrawablePtr a_drawable, 1091 short a_vid_x, short a_vid_y, 1092 short a_drw_x, short a_drw_y, 1093 short a_vid_w, short a_vid_h, 1094 short a_drw_w, short a_drw_h, 1095 RegionPtr a_clipping_region, void *a_port_priv) 1096{ 1097 EphyrScrPriv *scrpriv = a_info->driver; 1098 xcb_connection_t *conn = hostx_get_xcbconn(); 1099 xcb_gcontext_t gc; 1100 EphyrPortPriv *port_priv = a_port_priv; 1101 1102 EPHYR_RETURN_VAL_IF_FAIL(a_info->pScreen, BadValue); 1103 EPHYR_RETURN_VAL_IF_FAIL(a_drawable && port_priv, BadValue); 1104 1105 EPHYR_LOG("enter\n"); 1106 1107 gc = xcb_generate_id(conn); 1108 xcb_create_gc(conn, gc, scrpriv->win, 0, NULL); 1109 xcb_xv_put_video(conn, port_priv->port_number, 1110 scrpriv->win, gc, 1111 a_vid_x, a_vid_y, a_vid_w, a_vid_h, 1112 a_drw_x, a_drw_y, a_drw_w, a_drw_h); 1113 xcb_free_gc(conn, gc); 1114 1115 EPHYR_LOG("leave\n"); 1116 return Success; 1117} 1118 1119static int 1120ephyrGetVideo(KdScreenInfo * a_info, 1121 DrawablePtr a_drawable, 1122 short a_vid_x, short a_vid_y, 1123 short a_drw_x, short a_drw_y, 1124 short a_vid_w, short a_vid_h, 1125 short a_drw_w, short a_drw_h, 1126 RegionPtr a_clipping_region, void *a_port_priv) 1127{ 1128 EphyrScrPriv *scrpriv = a_info->driver; 1129 xcb_connection_t *conn = hostx_get_xcbconn(); 1130 xcb_gcontext_t gc; 1131 EphyrPortPriv *port_priv = a_port_priv; 1132 1133 EPHYR_RETURN_VAL_IF_FAIL(a_info && a_info->pScreen, BadValue); 1134 EPHYR_RETURN_VAL_IF_FAIL(a_drawable && port_priv, BadValue); 1135 1136 EPHYR_LOG("enter\n"); 1137 1138 gc = xcb_generate_id(conn); 1139 xcb_create_gc(conn, gc, scrpriv->win, 0, NULL); 1140 xcb_xv_get_video(conn, port_priv->port_number, 1141 scrpriv->win, gc, 1142 a_vid_x, a_vid_y, a_vid_w, a_vid_h, 1143 a_drw_x, a_drw_y, a_drw_w, a_drw_h); 1144 1145 xcb_free_gc(conn, gc); 1146 1147 EPHYR_LOG("leave\n"); 1148 return Success; 1149} 1150 1151static int 1152ephyrPutStill(KdScreenInfo * a_info, 1153 DrawablePtr a_drawable, 1154 short a_vid_x, short a_vid_y, 1155 short a_drw_x, short a_drw_y, 1156 short a_vid_w, short a_vid_h, 1157 short a_drw_w, short a_drw_h, 1158 RegionPtr a_clipping_region, void *a_port_priv) 1159{ 1160 EphyrScrPriv *scrpriv = a_info->driver; 1161 xcb_connection_t *conn = hostx_get_xcbconn(); 1162 xcb_gcontext_t gc; 1163 EphyrPortPriv *port_priv = a_port_priv; 1164 1165 EPHYR_RETURN_VAL_IF_FAIL(a_info && a_info->pScreen, BadValue); 1166 EPHYR_RETURN_VAL_IF_FAIL(a_drawable && port_priv, BadValue); 1167 1168 EPHYR_LOG("enter\n"); 1169 1170 gc = xcb_generate_id(conn); 1171 xcb_create_gc(conn, gc, scrpriv->win, 0, NULL); 1172 xcb_xv_put_still(conn, port_priv->port_number, 1173 scrpriv->win, gc, 1174 a_vid_x, a_vid_y, a_vid_w, a_vid_h, 1175 a_drw_x, a_drw_y, a_drw_w, a_drw_h); 1176 xcb_free_gc(conn, gc); 1177 1178 EPHYR_LOG("leave\n"); 1179 return Success; 1180} 1181 1182static int 1183ephyrGetStill(KdScreenInfo * a_info, 1184 DrawablePtr a_drawable, 1185 short a_vid_x, short a_vid_y, 1186 short a_drw_x, short a_drw_y, 1187 short a_vid_w, short a_vid_h, 1188 short a_drw_w, short a_drw_h, 1189 RegionPtr a_clipping_region, void *a_port_priv) 1190{ 1191 EphyrScrPriv *scrpriv = a_info->driver; 1192 xcb_connection_t *conn = hostx_get_xcbconn(); 1193 xcb_gcontext_t gc; 1194 EphyrPortPriv *port_priv = a_port_priv; 1195 1196 EPHYR_RETURN_VAL_IF_FAIL(a_info && a_info->pScreen, BadValue); 1197 EPHYR_RETURN_VAL_IF_FAIL(a_drawable && port_priv, BadValue); 1198 1199 EPHYR_LOG("enter\n"); 1200 1201 gc = xcb_generate_id(conn); 1202 xcb_create_gc(conn, gc, scrpriv->win, 0, NULL); 1203 xcb_xv_get_still(conn, port_priv->port_number, 1204 scrpriv->win, gc, 1205 a_vid_x, a_vid_y, a_vid_w, a_vid_h, 1206 a_drw_x, a_drw_y, a_drw_w, a_drw_h); 1207 xcb_free_gc(conn, gc); 1208 1209 EPHYR_LOG("leave\n"); 1210 return Success; 1211} 1212 1213static int 1214ephyrQueryImageAttributes(KdScreenInfo * a_info, 1215 int a_id, 1216 unsigned short *a_w, 1217 unsigned short *a_h, int *a_pitches, int *a_offsets) 1218{ 1219 xcb_connection_t *conn = hostx_get_xcbconn(); 1220 xcb_xv_query_image_attributes_cookie_t cookie; 1221 xcb_xv_query_image_attributes_reply_t *reply; 1222 int image_size = 0; 1223 1224 EPHYR_RETURN_VAL_IF_FAIL(a_w && a_h, FALSE); 1225 1226 EPHYR_LOG("enter: dim (%dx%d), pitches: %p, offsets: %p\n", 1227 *a_w, *a_h, a_pitches, a_offsets); 1228 1229 cookie = xcb_xv_query_image_attributes(conn, 1230 s_base_port_id, a_id, 1231 *a_w, *a_h); 1232 reply = xcb_xv_query_image_attributes_reply(conn, cookie, NULL); 1233 if (!reply) 1234 goto out; 1235 1236 *a_w = reply->width; 1237 *a_h = reply->height; 1238 if (a_pitches && a_offsets) { 1239 memcpy(a_pitches, xcb_xv_query_image_attributes_pitches(reply), 1240 reply->num_planes << 2); 1241 memcpy(a_offsets, xcb_xv_query_image_attributes_offsets(reply), 1242 reply->num_planes << 2); 1243 } 1244 image_size = reply->data_size; 1245 1246 free(reply); 1247 1248 EPHYR_LOG("image size: %d, dim (%dx%d)\n", image_size, *a_w, *a_h); 1249 1250 out: 1251 EPHYR_LOG("leave\n"); 1252 return image_size; 1253} 1254