1/* ______ ___ ___ 2 * /\ _ \ /\_ \ /\_ \ 3 * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___ 4 * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\ 5 * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \ 6 * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/ 7 * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/ 8 * /\____/ 9 * \_/__/ 10 * 11 * MacOS X quartz windowed gfx driver 12 * 13 * By Angelo Mottola. 14 * 15 * See readme.txt for copyright information. 16 */ 17 18 19#include "allegro.h" 20#include "allegro/internal/aintern.h" 21#include "allegro/platform/aintosx.h" 22 23#ifndef ALLEGRO_MACOSX 24 #error something is wrong with the makefile 25#endif 26 27 28static BITMAP *osx_qz_window_init(int, int, int, int, int); 29static void osx_qz_window_exit(BITMAP *); 30static void osx_qz_window_vsync(void); 31static void osx_qz_window_set_palette(AL_CONST struct RGB *, int, int, int); 32static void osx_signal_vsync(void); 33static BITMAP* osx_create_video_bitmap(int w, int h); 34static int osx_show_video_bitmap(BITMAP*); 35static void osx_destroy_video_bitmap(BITMAP*); 36static BITMAP* create_video_page(unsigned char*); 37 38static pthread_mutex_t vsync_mutex; 39static pthread_cond_t vsync_cond; 40static int lock_nesting = 0; 41static AllegroWindowDelegate *window_delegate = NULL; 42static char driver_desc[256]; 43static int requested_color_depth; 44static COLORCONV_BLITTER_FUNC *colorconv_blitter = NULL; 45static RgnHandle update_region = NULL; 46static RgnHandle temp_region = NULL; 47static AllegroView *qd_view = NULL; 48static int desktop_depth; 49static BITMAP* pseudo_screen = NULL; 50static BITMAP* first_page = NULL; 51static unsigned char *pseudo_screen_addr = NULL; 52static int pseudo_screen_pitch; 53static int pseudo_screen_depth; 54static char *dirty_lines = NULL; 55static GFX_VTABLE _special_vtable; /* special vtable for active video page */ 56static GFX_VTABLE _unspecial_vtable; /* special vtable for inactive video page */ 57static BITMAP* current_video_page = NULL; 58 59void* osx_window_mutex; 60 61 62GFX_DRIVER gfx_quartz_window = 63{ 64 GFX_QUARTZ_WINDOW, 65 empty_string, 66 empty_string, 67 "Quartz window", 68 osx_qz_window_init, 69 osx_qz_window_exit, 70 NULL, /* AL_METHOD(int, scroll, (int x, int y)); */ 71 osx_qz_window_vsync, 72 osx_qz_window_set_palette, 73 NULL, /* AL_METHOD(int, request_scroll, (int x, int y)); */ 74 NULL, /* AL_METHOD(int, poll_scroll, (void)); */ 75 NULL, /* AL_METHOD(void, enable_triple_buffer, (void)); */ 76 osx_create_video_bitmap, /* AL_METHOD(struct BITMAP *, create_video_bitmap, (int width, int height)); */ 77 osx_destroy_video_bitmap, /* AL_METHOD(void, destroy_video_bitmap, (struct BITMAP *bitmap)); */ 78 osx_show_video_bitmap, /* AL_METHOD(int, show_video_bitmap, (BITMAP *bitmap)); */ 79 NULL, /* AL_METHOD(int, request_video_bitmap, (BITMAP *bitmap)); */ 80 NULL, /* AL_METHOD(BITMAP *, create_system_bitmap, (int width, int height)); */ 81 NULL, /* AL_METHOD(void, destroy_system_bitmap, (BITMAP *bitmap)); */ 82 osx_mouse_set_sprite, /* AL_METHOD(int, set_mouse_sprite, (BITMAP *sprite, int xfocus, int yfocus)); */ 83 osx_mouse_show, /* AL_METHOD(int, show_mouse, (BITMAP *bmp, int x, int y)); */ 84 osx_mouse_hide, /* AL_METHOD(void, hide_mouse, (void)); */ 85 osx_mouse_move, /* AL_METHOD(void, move_mouse, (int x, int y)); */ 86 NULL, /* AL_METHOD(void, drawing_mode, (void)); */ 87 NULL, /* AL_METHOD(void, save_video_state, (void)); */ 88 NULL, /* AL_METHOD(void, restore_video_state, (void)); */ 89 NULL, /* AL_METHOD(void, set_blender_mode, (int mode, int r, int g, int b, int a)); */ 90 NULL, /* AL_METHOD(int, fetch_mode_list, (void)); */ 91 0, 0, /* physical (not virtual!) screen size */ 92 TRUE, /* true if video memory is linear */ 93 0, /* bank size, in bytes */ 94 0, /* bank granularity, in bytes */ 95 0, /* video memory size, in bytes */ 96 0, /* physical address of video memory */ 97 TRUE 98}; 99 100 101 102/* prepare_window_for_animation: 103 * Prepares the window for a (de)miniaturization animation. 104 * Called by the window display method when the window is about to be 105 * deminiaturized, this updates the QuickDraw view contents and sets the 106 * alpha component to 255 (opaque). 107 * When called from the miniaturize window method, only the alpha value 108 * is updated. 109 */ 110static void prepare_window_for_animation(int refresh_view) 111{ 112 struct GRAPHICS_RECT src_gfx_rect, dest_gfx_rect; 113 unsigned int *addr; 114 int pitch, y, x; 115 116 _unix_lock_mutex(osx_window_mutex); 117 while (![qd_view lockFocusIfCanDraw]); 118 while (!QDDone([qd_view qdPort])); 119 LockPortBits([qd_view qdPort]); 120 pitch = GetPixRowBytes(GetPortPixMap([qd_view qdPort])) / 4; 121 addr = (unsigned int *)GetPixBaseAddr(GetPortPixMap([qd_view qdPort])) + 122 ((int)([osx_window frame].size.height) - gfx_quartz_window.h) * pitch; 123 if (refresh_view && colorconv_blitter) { 124 src_gfx_rect.width = gfx_quartz_window.w; 125 src_gfx_rect.height = gfx_quartz_window.h; 126 src_gfx_rect.pitch = pseudo_screen_pitch; 127 src_gfx_rect.data = pseudo_screen_addr; 128 dest_gfx_rect.pitch = pitch * 4; 129 dest_gfx_rect.data = addr; 130 colorconv_blitter(&src_gfx_rect, &dest_gfx_rect); 131 } 132 for (y = gfx_quartz_window.h; y; y--) { 133 for (x = 0; x < gfx_quartz_window.w; x++) 134 *(addr + x) |= 0xff000000; 135 addr += pitch; 136 } 137 UnlockPortBits([qd_view qdPort]); 138 [qd_view unlockFocus]; 139 _unix_unlock_mutex(osx_window_mutex); 140} 141 142 143 144@implementation AllegroWindow 145 146/* display: 147 * Called when the window is about to be deminiaturized. 148 */ 149- (void)display 150{ 151 [super display]; 152 if (desktop_depth == 32) 153 prepare_window_for_animation(TRUE); 154} 155 156 157 158/* miniaturize: 159 * Called when the window is miniaturized. 160 */ 161- (void)miniaturize: (id)sender 162{ 163 if (desktop_depth == 32) 164 prepare_window_for_animation(FALSE); 165 [super miniaturize: sender]; 166} 167 168@end 169 170 171 172@implementation AllegroWindowDelegate 173 174/* windowShouldClose: 175 * Called when the user attempts to close the window. 176 * Default behaviour is to call the user callback (if any) and deny closing. 177 */ 178- (BOOL)windowShouldClose: (id)sender 179{ 180 if (osx_window_close_hook) 181 osx_window_close_hook(); 182 return NO; 183} 184 185 186 187/* windowDidDeminiaturize: 188 * Called when the window deminiaturization animation ends; marks the whole 189 * window contents as dirty, so it is updated on next refresh. 190 */ 191- (void)windowDidDeminiaturize: (NSNotification *)aNotification 192{ 193 _unix_lock_mutex(osx_window_mutex); 194 memset(dirty_lines, 1, gfx_quartz_window.h); 195 _unix_unlock_mutex(osx_window_mutex); 196} 197 198 199 200/* windowDidBecomeKey: 201 * Sent by the default notification center immediately after an NSWindow 202 * object has become key. 203 */ 204- (void)windowDidBecomeKey:(NSNotification *)notification 205{ 206 _unix_lock_mutex(osx_skip_events_processing_mutex); 207 osx_skip_events_processing = FALSE; 208 _unix_unlock_mutex(osx_skip_events_processing_mutex); 209} 210 211 212 213/* windowDidResignKey: 214 * Sent by the default notification center immediately after an NSWindow 215 * object has resigned its status as key window. 216 */ 217- (void)windowDidResignKey:(NSNotification *)notification 218{ 219 _unix_lock_mutex(osx_skip_events_processing_mutex); 220 osx_skip_events_processing = TRUE; 221 _unix_unlock_mutex(osx_skip_events_processing_mutex); 222} 223 224@end 225 226 227 228@implementation AllegroView 229 230/* resetCursorRects: 231 * Called when the view needs to reset its cursor rects; this restores the 232 * Allegro cursor rect and enables it. 233 */ 234- (void)resetCursorRects 235{ 236 [super resetCursorRects]; 237 [self addCursorRect: NSMakeRect(0, 0, gfx_quartz_window.w, gfx_quartz_window.h) 238 cursor: osx_cursor]; 239 [osx_cursor setOnMouseEntered: YES]; 240} 241 242@end 243 244 245 246/* osx_qz_acquire_win: 247* Bitmap locking for Quartz windowed mode. 248*/ 249static void osx_qz_acquire_win(BITMAP *bmp) 250{ 251 /* to prevent the drawing threads and the rendering proc 252 from concurrently accessing the dirty lines array */ 253 _unix_lock_mutex(osx_window_mutex); 254 if (lock_nesting++ == 0) { 255 bmp->id |= BMP_ID_LOCKED; 256 } 257} 258 259/* osx_qz_release_win: 260 * Bitmap unlocking for Quartz windowed mode. 261 */ 262static void osx_qz_release_win(BITMAP *bmp) 263{ 264 ASSERT(lock_nesting > 0); 265 if (--lock_nesting == 0) { 266 bmp->id &= ~BMP_ID_LOCKED; 267 } 268 269 _unix_unlock_mutex(osx_window_mutex); 270} 271 272 273 274/* osx_qz_write_line_win: 275 * Line switcher for Quartz windowed mode. 276 */ 277static unsigned long osx_qz_write_line_win(BITMAP *bmp, int line) 278{ 279 if (!(bmp->id & BMP_ID_LOCKED)) { 280 osx_qz_acquire_win(bmp); 281 if (bmp->extra) { 282 while (!QDDone(BMP_EXTRA(bmp)->port)); 283 while (LockPortBits(BMP_EXTRA(bmp)->port)); 284 } 285 bmp->id |= BMP_ID_AUTOLOCK; 286 } 287 dirty_lines[line + bmp->y_ofs] = 1; 288 289 return (unsigned long)(bmp->line[line]); 290} 291 292 293 294/* osx_qz_unwrite_line_win: 295 * Line updater for Quartz windowed mode. 296 */ 297static void osx_qz_unwrite_line_win(BITMAP *bmp) 298{ 299 if (bmp->id & BMP_ID_AUTOLOCK) { 300 osx_qz_release_win(bmp); 301 if (bmp->extra) 302 UnlockPortBits(BMP_EXTRA(bmp)->port); 303 bmp->id &= ~BMP_ID_AUTOLOCK; 304 } 305} 306 307 308 309/* update_dirty_lines: 310 * Dirty lines updater routine. This is always called from the main app 311 * thread only. 312 */ 313void osx_update_dirty_lines(void) 314{ 315 struct GRAPHICS_RECT src_gfx_rect, dest_gfx_rect; 316 Rect rect; 317 CGrafPtr qd_view_port; 318 int qd_view_pitch; 319 char *qd_view_addr; 320 321 if (![osx_window isVisible]) 322 return; 323 324 /* Skip everything if there are no dirty lines */ 325 _unix_lock_mutex(osx_window_mutex); 326 for (rect.top = 0; (rect.top < gfx_quartz_window.h) && (!dirty_lines[rect.top]); rect.top++) 327 ; 328 if (rect.top >= gfx_quartz_window.h) { 329 _unix_unlock_mutex(osx_window_mutex); 330 osx_signal_vsync(); 331 return; 332 } 333 334 /* Dirty lines need to be updated */ 335 if ([qd_view lockFocusIfCanDraw] == YES) { 336 while (!QDDone([qd_view qdPort])); 337 LockPortBits([qd_view qdPort]); 338 339 qd_view_port = [qd_view qdPort]; 340 if (qd_view_port) { 341 qd_view_pitch = GetPixRowBytes(GetPortPixMap(qd_view_port)); 342 qd_view_addr = GetPixBaseAddr(GetPortPixMap(qd_view_port)) + 343 ((int)([osx_window frame].size.height) - gfx_quartz_window.h) * qd_view_pitch; 344 345 if (colorconv_blitter || (osx_setup_colorconv_blitter() == 0)) { 346 SetEmptyRgn(update_region); 347 348 rect.left = 0; 349 rect.right = gfx_quartz_window.w; 350 351 while (rect.top < gfx_quartz_window.h) { 352 while ((!dirty_lines[rect.top]) && (rect.top < gfx_quartz_window.h)) 353 rect.top++; 354 if (rect.top >= gfx_quartz_window.h) 355 break; 356 rect.bottom = rect.top; 357 while ((dirty_lines[rect.bottom]) && (rect.bottom < gfx_quartz_window.h)) { 358 dirty_lines[rect.bottom] = 0; 359 rect.bottom++; 360 } 361 /* fill in source graphics rectangle description */ 362 src_gfx_rect.width = rect.right - rect.left; 363 src_gfx_rect.height = rect.bottom - rect.top; 364 src_gfx_rect.pitch = pseudo_screen_pitch; 365 src_gfx_rect.data = pseudo_screen_addr + 366 (rect.top * pseudo_screen_pitch) + 367 (rect.left * BYTES_PER_PIXEL(pseudo_screen_depth)); 368 369 /* fill in destination graphics rectangle description */ 370 dest_gfx_rect.pitch = qd_view_pitch; 371 dest_gfx_rect.data = qd_view_addr + 372 (rect.top * qd_view_pitch) + 373 (rect.left * BYTES_PER_PIXEL(desktop_depth)); 374 375 /* function doing the hard work */ 376 colorconv_blitter(&src_gfx_rect, &dest_gfx_rect); 377 378 RectRgn(temp_region, &rect); 379 UnionRgn(temp_region, update_region, update_region); 380 rect.top = rect.bottom; 381 } 382 } 383 QDFlushPortBuffer(qd_view_port, update_region); 384 } 385 UnlockPortBits([qd_view qdPort]); 386 [qd_view unlockFocus]; 387 } 388 _unix_unlock_mutex(osx_window_mutex); 389 390 osx_signal_vsync(); 391} 392 393 394 395/* osx_setup_colorconv_blitter: 396 * Sets up the window color conversion blitter function depending on the 397 * Allegro requested color depth and the current desktop color depth. 398 */ 399int osx_setup_colorconv_blitter() 400{ 401 CFDictionaryRef mode; 402 void *vp; 403 int dd; 404 if (qd_view && (vp = [qd_view qdPort])) 405 { 406 dd = GetPixDepth(GetPortPixMap(vp)); 407 } 408 else 409 { 410 mode = CGDisplayCurrentMode(kCGDirectMainDisplay); 411 CFNumberGetValue(CFDictionaryGetValue(mode, kCGDisplayBitsPerPixel), kCFNumberSInt32Type, &desktop_depth); 412 dd = desktop_depth; 413 } 414 if (dd == 16) dd = 15; 415 _unix_lock_mutex(osx_window_mutex); 416 if (colorconv_blitter) 417 _release_colorconv_blitter(colorconv_blitter); 418 colorconv_blitter = _get_colorconv_blitter(requested_color_depth, dd); 419 /* We also need to update the color conversion palette to reflect the change */ 420 if (colorconv_blitter) 421 _set_colorconv_palette(_current_palette, 0, 255); 422 /* Mark all the window as dirty */ 423 memset(dirty_lines, 1, gfx_quartz_window.h); 424 _unix_unlock_mutex(osx_window_mutex); 425 426 return (colorconv_blitter ? 0 : -1); 427} 428 429 430 431/* osx_qz_window_init: 432* Initializes windowed gfx mode. 433*/ 434static BITMAP *private_osx_qz_window_init(int w, int h, int v_w, int v_h, int color_depth) 435{ 436 CFDictionaryRef mode; 437 NSRect rect = NSMakeRect(0, 0, w, h); 438 int refresh_rate; 439 char tmp1[128], tmp2[128]; 440 441 pthread_cond_init(&vsync_cond, NULL); 442 pthread_mutex_init(&vsync_mutex, NULL); 443 osx_window_mutex=_unix_create_mutex(); 444 lock_nesting = 0; 445 446 if (1 447#ifdef ALLEGRO_COLOR8 448 && (color_depth != 8) 449#endif 450#ifdef ALLEGRO_COLOR16 451 && (color_depth != 15) 452 && (color_depth != 16) 453#endif 454#ifdef ALLEGRO_COLOR24 455 && (color_depth != 24) 456#endif 457#ifdef ALLEGRO_COLOR32 458 && (color_depth != 32) 459#endif 460 ) { 461 ustrzcpy(allegro_error, ALLEGRO_ERROR_SIZE, get_config_text("Unsupported color depth")); 462 return NULL; 463 } 464 465 if ((w == 0) && (h == 0)) { 466 w = 320; 467 h = 200; 468 } 469 470 if (v_w < w) v_w = w; 471 if (v_h < h) v_h = h; 472 473 if ((v_w != w) || (v_h != h)) { 474 ustrzcpy(allegro_error, ALLEGRO_ERROR_SIZE, get_config_text("Resolution not supported")); 475 return NULL; 476 } 477 478 osx_window = [[AllegroWindow alloc] initWithContentRect: rect 479 styleMask: NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask 480 backing: NSBackingStoreBuffered 481 defer: NO]; 482 483 window_delegate = [[[AllegroWindowDelegate alloc] init] autorelease]; 484 [osx_window setDelegate: window_delegate]; 485 [osx_window setOneShot: YES]; 486 [osx_window setAcceptsMouseMovedEvents: YES]; 487 [osx_window setViewsNeedDisplay: NO]; 488 [osx_window setReleasedWhenClosed: YES]; 489 [osx_window useOptimizedDrawing: YES]; 490 [osx_window center]; 491 492 qd_view = [[AllegroView alloc] initWithFrame: rect]; 493 if (!qd_view) { 494 ustrzcpy(allegro_error, ALLEGRO_ERROR_SIZE, get_config_text("Not enough memory")); 495 return NULL; 496 } 497 [osx_window setContentView: qd_view]; 498 499 set_window_title(osx_window_title); 500 [osx_window makeKeyAndOrderFront: nil]; 501 502 /* the last flag serves as an end of loop delimiter */ 503 dirty_lines = calloc(h + 1, sizeof(char)); 504 /* Mark all the window as dirty */ 505 memset(dirty_lines, 1, h + 1); 506 507 setup_direct_shifts(); 508 509 gfx_quartz_window.w = w; 510 gfx_quartz_window.h = h; 511 gfx_quartz_window.vid_mem = w * h * BYTES_PER_PIXEL(color_depth); 512 513 requested_color_depth = color_depth; 514 colorconv_blitter=NULL; 515 mode = CGDisplayCurrentMode(kCGDirectMainDisplay); 516 CFNumberGetValue(CFDictionaryGetValue(mode, kCGDisplayBitsPerPixel), kCFNumberSInt32Type, &desktop_depth); 517 CFNumberGetValue(CFDictionaryGetValue(mode, kCGDisplayRefreshRate), kCFNumberSInt32Type, &refresh_rate); 518 _set_current_refresh_rate(refresh_rate); 519 520 pseudo_screen_pitch = w * BYTES_PER_PIXEL(color_depth); 521 pseudo_screen_addr = _AL_MALLOC(h * pseudo_screen_pitch); 522 pseudo_screen = _make_bitmap(w, h, (unsigned long) pseudo_screen_addr, &gfx_quartz_window, color_depth, pseudo_screen_pitch); 523 if (!pseudo_screen) { 524 return NULL; 525 } 526 current_video_page = pseudo_screen; 527 first_page = NULL; 528 /* create a new special vtable for the pseudo screen */ 529 memcpy(&_special_vtable, &_screen_vtable, sizeof(GFX_VTABLE)); 530 _special_vtable.acquire = osx_qz_acquire_win; 531 _special_vtable.release = osx_qz_release_win; 532 _special_vtable.unwrite_bank = osx_qz_unwrite_line_win; 533 memcpy(&_unspecial_vtable, _get_vtable(color_depth), sizeof(GFX_VTABLE)); 534 pseudo_screen->read_bank = osx_qz_write_line_win; 535 pseudo_screen->write_bank = osx_qz_write_line_win; 536 pseudo_screen->vtable = &_special_vtable; 537 uszprintf(driver_desc, sizeof(driver_desc), uconvert_ascii("Cocoa window using QuickDraw view, %d bpp %s", tmp1), 538 color_depth, uconvert_ascii(color_depth == desktop_depth ? "in matching" : "in fast emulation", tmp2)); 539 gfx_quartz_window.desc = driver_desc; 540 541 update_region = NewRgn(); 542 temp_region = NewRgn(); 543 544 osx_mouse_tracking_rect = [qd_view addTrackingRect: rect 545 owner: NSApp 546 userData: nil 547 assumeInside: YES]; 548 549 osx_keyboard_focused(FALSE, 0); 550 clear_keybuf(); 551 osx_gfx_mode = OSX_GFX_WINDOW; 552 osx_skip_mouse_move = TRUE; 553 osx_window_first_expose = TRUE; 554 555 return pseudo_screen; 556} 557 558static BITMAP *osx_qz_window_init(int w, int h, int v_w, int v_h, int color_depth) 559{ 560 BITMAP *bmp; 561 _unix_lock_mutex(osx_event_mutex); 562 bmp = private_osx_qz_window_init(w, h, v_w, v_h, color_depth); 563 _unix_unlock_mutex(osx_event_mutex); 564 if (!bmp) 565 osx_qz_window_exit(bmp); 566 return bmp; 567} 568 569 570 571/* osx_qz_window_exit: 572 * Shuts down windowed gfx mode. 573 */ 574static void osx_qz_window_exit(BITMAP *bmp) 575{ 576 _unix_lock_mutex(osx_event_mutex); 577 578 if (update_region) { 579 DisposeRgn(update_region); 580 update_region = NULL; 581 } 582 if (temp_region) { 583 DisposeRgn(temp_region); 584 temp_region = NULL; 585 } 586 587 if (osx_window) { 588 [osx_window close]; 589 osx_window = NULL; 590 } 591 592 if (pseudo_screen_addr) { 593 free(pseudo_screen_addr); 594 pseudo_screen_addr = NULL; 595 } 596 597 598 if (dirty_lines) { 599 free(dirty_lines); 600 dirty_lines = NULL; 601 } 602 603 if (colorconv_blitter) { 604 _release_colorconv_blitter(colorconv_blitter); 605 colorconv_blitter = NULL; 606 } 607 608 _unix_destroy_mutex(osx_window_mutex); 609 pthread_cond_destroy(&vsync_cond); 610 pthread_mutex_destroy(&vsync_mutex); 611 612 osx_mouse_tracking_rect = -1; 613 614 osx_gfx_mode = OSX_GFX_NONE; 615 616 _unix_unlock_mutex(osx_event_mutex); 617} 618 619 620 621/* osx_qz_window_vsync: 622 * Quartz video vertical synchronization routine for windowed mode. 623 */ 624static void osx_qz_window_vsync(void) 625{ 626 if (lock_nesting==0) { 627 pthread_mutex_trylock(&vsync_mutex); 628 pthread_cond_wait(&vsync_cond, &vsync_mutex); 629 pthread_mutex_unlock(&vsync_mutex); 630 } 631 else { 632 ASSERT(0); /* Screen already acquired, don't call vsync() */ 633 } 634} 635 636 637 638/* osx_qz_window_set_palette: 639 * Sets palette for quartz window. 640 */ 641static void osx_qz_window_set_palette(AL_CONST struct RGB *p, int from, int to, int vsync) 642{ 643 if (vsync) 644 osx_qz_window_vsync(); 645 646 _unix_lock_mutex(osx_window_mutex); 647 _set_colorconv_palette(p, from, to); 648 649 /* invalidate the whole screen */ 650 memset(dirty_lines, 1, gfx_quartz_window.h); 651 652 _unix_unlock_mutex(osx_window_mutex); 653} 654 655void osx_signal_vsync(void) 656{ 657 pthread_mutex_lock(&vsync_mutex); 658 pthread_cond_broadcast(&vsync_cond); 659 pthread_mutex_unlock(&vsync_mutex); 660} 661 662// create_video_bitmap: 663// Create a video bitmap - actually a memory bitmap or 664// a pseudo-screen if the dimensions match the screen 665static BITMAP* osx_create_video_bitmap(int w, int h) 666{ 667 if ((w == gfx_quartz_window.w) && (h == gfx_quartz_window.h)) 668 { 669 if (first_page == NULL) 670 { 671 // First ever call, return a bitmap mapping the screen memory 672 first_page = create_video_page(pseudo_screen_addr); 673 return first_page; 674 } 675 else 676 { 677 // Must create another 678 return create_video_page(NULL); 679 } 680 } 681 else 682 { 683 return create_bitmap(w, h); 684 } 685} 686 687static BITMAP* create_video_page(unsigned char* addr) 688{ 689 BITMAP* page; 690 int i; 691 int w = gfx_quartz_window.w, h = gfx_quartz_window.h; 692 693 page = (BITMAP*) _AL_MALLOC(sizeof(BITMAP) + sizeof(char*) * h); 694 if (!page) { 695 ustrzcpy(allegro_error, ALLEGRO_ERROR_SIZE, get_config_text("Not enough memory")); 696 return NULL; 697 } 698 if (addr == NULL) 699 { 700 addr = _AL_MALLOC(gfx_quartz_window.vid_mem); 701 // Use page->dat to indicate that we own the memory 702 page->dat = addr; 703 page->vtable = &_unspecial_vtable; 704 page->write_bank = page->read_bank = _stub_bank_switch; 705 706 } 707 else 708 { 709 page->dat = NULL; 710 page->vtable = &_special_vtable; 711 page->write_bank = page->read_bank = osx_qz_write_line_win; 712 } 713 if (!addr) { 714 ustrzcpy(allegro_error, ALLEGRO_ERROR_SIZE, get_config_text("Not enough memory")); 715 return NULL; 716 } 717 page->w = page->cr = w; 718 page->h = page->cb = h; 719 page->clip = TRUE; 720 page->cl = page->ct = 0; 721 page->id = BMP_ID_VIDEO; 722 page->extra = NULL; 723 page->x_ofs = 0; 724 page->y_ofs = 0; 725 page->seg = _video_ds(); 726 for (i = 0; i < h; ++i) 727 { 728 page->line[i] = addr; 729 addr += pseudo_screen_pitch; 730 } 731 732 return page; 733} 734 735static int osx_show_video_bitmap(BITMAP* vb) 736{ 737 if (vb->vtable == &_special_vtable) 738 { 739 // This bitmap already switched in, therefore no-op 740 return 0; 741 } 742 if (vb->vtable == &_unspecial_vtable) 743 { 744 _unix_lock_mutex(osx_window_mutex); 745 746 // switch out the old one 747 if ((current_video_page == pseudo_screen) && (first_page != NULL)) 748 { 749 // switching out screen, also do page 1 750 first_page->vtable = &_unspecial_vtable; 751 first_page->write_bank = first_page->read_bank =_stub_bank_switch; 752 } 753 else if ((current_video_page == first_page) && (first_page != NULL)) 754 { 755 // If switching out page 1, also do screen 756 pseudo_screen->vtable = &_unspecial_vtable; 757 pseudo_screen->write_bank = pseudo_screen->read_bank = _stub_bank_switch; 758 } 759 if (current_video_page != NULL) 760 { 761 current_video_page->vtable = &_unspecial_vtable; 762 current_video_page->write_bank = current_video_page->read_bank = _stub_bank_switch; 763 } 764 // Switch in this one 765 if ((vb == pseudo_screen) && (first_page != NULL)) 766 { 767 // If asking for show_video_bitmap(screen), also do page 1 768 first_page->vtable = &_special_vtable; 769 first_page->write_bank = first_page->read_bank = osx_qz_write_line_win; 770 } 771 else if (vb == first_page) 772 { 773 // If asking for show_video_bitmap( page 1), also do screen 774 pseudo_screen->vtable = &_special_vtable; 775 pseudo_screen->write_bank = pseudo_screen->read_bank = osx_qz_write_line_win; 776 } 777 pseudo_screen_addr = vb->line[0]; 778 vb->vtable = &_special_vtable; 779 vb->write_bank = vb->read_bank = osx_qz_write_line_win; 780 current_video_page = vb; 781 // mark all lines dirty - they will be flushed to the screen. 782 memset(dirty_lines, 1, gfx_quartz_window.h); 783 _unix_unlock_mutex(osx_window_mutex); 784 785 return 0; 786 } 787 // Otherwise it's not eligible to be switched in 788 return -1; 789} 790 791static void osx_destroy_video_bitmap(BITMAP* bm) 792{ 793 if (bm == pseudo_screen) 794 { 795 // Don't do this! 796 return; 797 } 798 if (bm == first_page) 799 first_page = NULL; 800 if (bm->vtable == &_special_vtable) 801 { 802 osx_show_video_bitmap(pseudo_screen); 803 free(bm->dat); 804 free(bm); 805 } 806 else if (bm->vtable == &_unspecial_vtable) 807 { 808 free(bm->dat); 809 free(bm); 810 } 811 // Otherwise it wasn't a video page 812} 813/* Local variables: */ 814/* c-basic-offset: 3 */ 815/* indent-tabs-mode: nil */ 816/* c-file-style: "linux" */ 817/* End: */ 818