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