Rework the way the ReinitSeparation command is called. The old way was way too danger...
[openttd-joker.git] / src / video / cocoa / wnd_quickdraw.mm
bloba8911824815f3c46f2de010723ff05d65fb1f648
1 /* $Id: wnd_quickdraw.mm 26108 2013-11-25 14:30:22Z rubidium $ */
3 /*
4  * This file is part of OpenTTD.
5  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8  */
10 /******************************************************************************
11  *                             Cocoa video driver                             *
12  * Known things left to do:                                                   *
13  *  List available resolutions.                                               *
14  ******************************************************************************/
16 #ifdef WITH_COCOA
17 #ifdef ENABLE_COCOA_QUICKDRAW
19 #define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_3
20 #include "../../stdafx.h"
21 #include "../../os/macosx/macos.h"
23 #define Rect  OTTDRect
24 #define Point OTTDPoint
25 #import <Cocoa/Cocoa.h>
26 #undef Rect
27 #undef Point
29 #include "../../debug.h"
30 #include "../../rev.h"
31 #include "../../core/geometry_type.hpp"
32 #include "cocoa_v.h"
33 #include "../../core/math_func.hpp"
34 #include "../../gfx_func.h"
36 /**
37  * Important notice regarding all modifications!!!!!!!
38  * There are certain limitations because the file is objective C++.
39  * gdb has limitations.
40  * C++ and objective C code can't be joined in all cases (classes stuff).
41  * Read http://developer.apple.com/releasenotes/Cocoa/Objective-C++.html for more information.
42  */
45 class WindowQuickdrawSubdriver;
48 class WindowQuickdrawSubdriver : public CocoaSubdriver {
49 private:
50         /**
51          * This function copies 32bpp pixels from the screen buffer in 16bpp windowed mode.
52          *
53          * @param left The x coord for the left edge of the box to blit.
54          * @param top The y coord for the top edge of the box to blit.
55          * @param right The x coord for the right edge of the box to blit.
56          * @param bottom The y coord for the bottom edge of the box to blit.
57          */
58         void Blit32ToView32(int left, int top, int right, int bottom);
60         /**
61          * This function copies 8bpp pixels from the screen buffer in 32bpp windowed mode.
62          *
63          * @param left The x coord for the left edge of the box to blit.
64          * @param top The y coord for the top edge of the box to blit.
65          * @param right The x coord for the right edge of the box to blit.
66          * @param bottom The y coord for the bottom edge of the box to blit.
67          */
68         void BlitIndexedToView32(int left, int top, int right, int bottom);
70         /**
71          * This function copies 8bpp pixels from the screen buffer in 16bpp windowed mode.
72          *
73          * @param left The x coord for the left edge of the box to blit.
74          * @param top The y coord for the top edge of the box to blit.
75          * @param right The x coord for the right edge of the box to blit.
76          * @param bottom The y coord for the bottom edge of the box to blit.
77          */
78         void BlitIndexedToView16(int left, int top, int right, int bottom);
80         inline void BlitToView(int left, int top, int right, int bottom);
81         void DrawResizeIcon();
83         virtual void GetDeviceInfo();
84         virtual bool SetVideoMode(int width, int height, int bpp);
86 public:
87         WindowQuickdrawSubdriver();
88         virtual ~WindowQuickdrawSubdriver();
90         virtual void Draw(bool force_update);
91         virtual void MakeDirty(int left, int top, int width, int height);
92         virtual void UpdatePalette(uint first_color, uint num_colors);
94         virtual uint ListModes(OTTD_Point *modes, uint max_modes);
96         virtual bool ChangeResolution(int w, int h, int bpp);
98         virtual bool IsFullscreen() { return false; }
100         virtual int GetWidth() { return window_width; }
101         virtual int GetHeight() { return window_height; }
102         virtual void *GetPixelBuffer() { return pixel_buffer; }
104         /* Convert local coordinate to window server (CoreGraphics) coordinate */
105         virtual CGPoint PrivateLocalToCG(NSPoint *p);
107         virtual NSPoint GetMouseLocation(NSEvent *event);
108         virtual bool MouseIsInsideView(NSPoint *pt);
110         virtual bool IsActive() { return active; }
112         void SetPortAlphaOpaque();
113         bool WindowResized();
116 static const int _resize_icon_width  = 16;
117 static const int _resize_icon_height = 16;
119 static bool _resize_icon[] = {
120         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
121         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
122         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
123         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
124         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1,
125         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1,
126         0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0,
127         0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0,
128         0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1,
129         0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1,
130         0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0,
131         0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0,
132         0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1,
133         0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1,
134         0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0,
135         1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0
139 void WindowQuickdrawSubdriver::GetDeviceInfo()
141         /* Initialize the video settings; this data persists between mode switches */
142         CFDictionaryRef cur_mode = CGDisplayCurrentMode(kCGDirectMainDisplay);
144         /* Gather some information that is useful to know about the display */
145         CFNumberGetValue((const __CFNumber*)CFDictionaryGetValue(cur_mode, kCGDisplayBitsPerPixel),
146                 kCFNumberSInt32Type, &this->device_depth);
148         CFNumberGetValue((const __CFNumber*)CFDictionaryGetValue(cur_mode, kCGDisplayWidth),
149                 kCFNumberSInt32Type, &this->device_width);
151         CFNumberGetValue((const __CFNumber*)CFDictionaryGetValue(cur_mode, kCGDisplayHeight),
152                 kCFNumberSInt32Type, &this->device_height);
155 bool WindowQuickdrawSubdriver::SetVideoMode(int width, int height, int bpp)
157         this->setup = true;
158         this->GetDeviceInfo();
160         if (bpp > this->device_depth) {
161                 DEBUG(driver, 0, "Cannot use a blitter with a higher screen depth than the display when running in windowed mode.");
162                 this->setup = false;
163                 return false;
164         }
166         if (width > this->device_width) width = this->device_width;
167         if (height > this->device_height) height = this->device_height;
169         NSRect contentRect = NSMakeRect(0, 0, width, height);
171         /* Check if we should recreate the window */
172         if (this->window == nil) {
173                 OTTD_CocoaWindowDelegate *delegate;
175                 /* Set the window style */
176                 unsigned int style = NSTitledWindowMask;
177                 style |= (NSMiniaturizableWindowMask | NSClosableWindowMask);
178                 style |= NSResizableWindowMask;
180                 /* Manually create a window, avoids having a nib file resource */
181                 this->window = [ [ OTTD_CocoaWindow alloc ] initWithContentRect:contentRect
182                                                 styleMask:style backing:NSBackingStoreBuffered defer:NO ];
184                 if (this->window == nil) {
185                         DEBUG(driver, 0, "Could not create the Cocoa window.");
186                         this->setup = false;
187                         return false;
188                 }
190                 [ this->window setDriver:this ];
192                 char caption[50];
193                 snprintf(caption, sizeof(caption), "OpenTTD %s", _openttd_revision);
194                 NSString *nsscaption = [ [ NSString alloc ] initWithUTF8String:caption ];
195                 [ this->window setTitle:nsscaption ];
196                 [ this->window setMiniwindowTitle:nsscaption ];
197                 [ nsscaption release ];
199                 [ this->window setContentMinSize:NSMakeSize(64.0f, 64.0f) ];
201                 [ this->window setAcceptsMouseMovedEvents:YES ];
202                 [ this->window setViewsNeedDisplay:NO ];
204                 delegate = [ [ OTTD_CocoaWindowDelegate alloc ] init ];
205                 [ delegate setDriver:this ];
206                 [ this->window setDelegate: [ delegate autorelease ] ];
207         } else {
208                 /* We already have a window, just change its size */
209                 [ this->window setContentSize:contentRect.size ];
210                 /* Ensure frame height - title bar height >= view height
211                  * The height of title bar of the window is 22 pixels */
212                 contentRect.size.height = Clamp(height, 0, [ this->window frame ].size.height - 22);
213                 height = contentRect.size.height;
214                 [ this->cocoaview setFrameSize:contentRect.size ];
215         }
217         /* Update again */
218         this->window_width = width;
219         this->window_height = height;
220         this->buffer_depth = bpp;
222         [ this->window center ];
224         /* Only recreate the view if it doesn't already exist */
225         if (this->cocoaview == nil) {
226                 this->cocoaview = [ [ NSQuickDrawView alloc ] initWithFrame:contentRect ];
227                 if (this->cocoaview == nil) {
228                         DEBUG(driver, 0, "Could not create the Quickdraw view.");
229                         this->setup = false;
230                         return false;
231                 }
233                 [ this->cocoaview setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ];
234                 [ [ this->window contentView ] addSubview:this->cocoaview ];
235                 [ this->cocoaview release ];
236                 [ this->window makeKeyAndOrderFront:nil ];
237         }
239         bool ret = this->WindowResized();
240         this->UpdatePalette(0, 256);
242         this->setup = false;
243         return ret;
246 void WindowQuickdrawSubdriver::Blit32ToView32(int left, int top, int right, int bottom)
248         const uint32 *src   = (uint32*)this->pixel_buffer;
249         uint32       *dst   = (uint32*)this->window_buffer;
250         uint          width = this->window_width;
251         uint          pitch = this->window_pitch / 4;
253         dst += top * pitch + left;
254         src += top * width + left;
256         for (int y = top; y < bottom; y++, dst+= pitch, src+= width) {
257                 memcpy(dst, src, (right - left) * 4);
258         }
261 void WindowQuickdrawSubdriver::BlitIndexedToView32(int left, int top, int right, int bottom)
263         const uint32 *pal   = this->palette;
264         const uint8  *src   = (uint8*)this->pixel_buffer;
265         uint32       *dst   = (uint32*)this->window_buffer;
266         uint          width = this->window_width;
267         uint          pitch = this->window_pitch / 4;
269         for (int y = top; y < bottom; y++) {
270                 for (int x = left; x < right; x++) {
271                         dst[y * pitch + x] = pal[src[y * width + x]];
272                 }
273         }
276 void WindowQuickdrawSubdriver::BlitIndexedToView16(int left, int top, int right, int bottom)
278         const uint32 *pal   = this->palette;
279         const uint8  *src   = (uint8*)this->pixel_buffer;
280         uint16       *dst   = (uint16*)this->window_buffer;
281         uint          width = this->window_width;
282         uint          pitch = this->window_pitch / 2;
284         for (int y = top; y < bottom; y++) {
285                 for (int x = left; x < right; x++) {
286                         dst[y * pitch + x] = pal[src[y * width + x]];
287                 }
288         }
292 inline void WindowQuickdrawSubdriver::BlitToView(int left, int top, int right, int bottom)
294         switch (this->device_depth) {
295                 case 32:
296                         switch (this->buffer_depth) {
297                                 case 32:
298                                         this->Blit32ToView32(left, top, right, bottom);
299                                         break;
300                                 case 8:
301                                         this->BlitIndexedToView32(left, top, right, bottom);
302                                         break;
303                         }
304                         break;
305                 case 16:
306                         this->BlitIndexedToView16(left, top, right, bottom);
307                         break;
308         }
311 void WindowQuickdrawSubdriver::DrawResizeIcon()
313         int xoff = this->window_width - _resize_icon_width;
314         int yoff = this->window_height - _resize_icon_height;
316         switch (this->device_depth) {
317                 case 32:
318                         for (int y = 0; y < _resize_icon_height; y++) {
319                                 uint32 *trg = (uint32*)this->window_buffer + (yoff + y) * this->window_pitch / 4 + xoff;
321                                 for (int x = 0; x < _resize_icon_width; x++, trg++) {
322                                         if (_resize_icon[y * _resize_icon_width + x]) *trg = 0xff000000;
323                                 }
324                         }
325                         break;
326                 case 16:
327                         for (int y = 0; y < _resize_icon_height; y++) {
328                                 uint16 *trg = (uint16*)this->window_buffer + (yoff + y) * this->window_pitch / 2 + xoff;
330                                 for (int x = 0; x < _resize_icon_width; x++, trg++) {
331                                         if (_resize_icon[y * _resize_icon_width + x]) *trg = 0x0000;
332                                 }
333                         }
334                         break;
335         }
339 WindowQuickdrawSubdriver::WindowQuickdrawSubdriver()
341         this->window_width  = 0;
342         this->window_height = 0;
343         this->buffer_depth  = 0;
344         this->pixel_buffer  = NULL;
345         this->active        = false;
346         this->setup         = false;
348         this->window = nil;
349         this->cocoaview = nil;
351         this->num_dirty_rects = MAX_DIRTY_RECTS;
354 WindowQuickdrawSubdriver::~WindowQuickdrawSubdriver()
356         /* Release window mode resources */
357         if (this->window != nil) [ this->window close ];
359         free(this->pixel_buffer);
362 void WindowQuickdrawSubdriver::Draw(bool force_update)
364         /* Check if we need to do anything */
365         if (this->num_dirty_rects == 0 || [ this->window isMiniaturized ]) return;
367         if (this->num_dirty_rects >= MAX_DIRTY_RECTS) {
368                 this->num_dirty_rects = 1;
369                 this->dirty_rects[0].left = 0;
370                 this->dirty_rects[0].top = 0;
371                 this->dirty_rects[0].right = this->window_width;
372                 this->dirty_rects[0].bottom = this->window_height;
373         }
375         RgnHandle dirty = NewRgn();
376         RgnHandle temp  = NewRgn();
378         SetEmptyRgn(dirty);
380         /* Build the region of dirty rectangles */
381         for (int i = 0; i < this->num_dirty_rects; i++) {
382                 this->BlitToView(this->dirty_rects[i].left, this->dirty_rects[i].top,
383                                 this->dirty_rects[i].right, this->dirty_rects[i].bottom);
385                 MacSetRectRgn(temp, this->dirty_rects[i].left, this->dirty_rects[i].top,
386                                 this->dirty_rects[i].right, this->dirty_rects[i].bottom);
387                 MacUnionRgn(dirty, temp, dirty);
388         }
390         this->DrawResizeIcon();
392         /* Flush the dirty region */
393         QDFlushPortBuffer( (OpaqueGrafPtr*) [ this->cocoaview qdPort ], dirty);
394         DisposeRgn(dirty);
395         DisposeRgn(temp);
397         this->num_dirty_rects = 0;
400 void WindowQuickdrawSubdriver::MakeDirty(int left, int top, int width, int height)
402         if (this->num_dirty_rects < MAX_DIRTY_RECTS) {
403                 this->dirty_rects[this->num_dirty_rects].left = left;
404                 this->dirty_rects[this->num_dirty_rects].top = top;
405                 this->dirty_rects[this->num_dirty_rects].right = left + width;
406                 this->dirty_rects[this->num_dirty_rects].bottom = top + height;
407         }
408         this->num_dirty_rects++;
411 void WindowQuickdrawSubdriver::UpdatePalette(uint first_color, uint num_colors)
413         if (this->buffer_depth != 8) return;
415         switch (this->device_depth) {
416                 case 32:
417                         for (uint i = first_color; i < first_color + num_colors; i++) {
418                                 uint32 clr32 = 0xff000000;
419                                 clr32 |= (uint32)_cur_palette.palette[i].r << 16;
420                                 clr32 |= (uint32)_cur_palette.palette[i].g << 8;
421                                 clr32 |= (uint32)_cur_palette.palette[i].b;
422                                 this->palette[i] = clr32;
423                         }
424                         break;
425                 case 16:
426                         for (uint i = first_color; i < first_color + num_colors; i++) {
427                                 uint16 clr16 = 0x0000;
428                                 clr16 |= (uint16)((_cur_palette.palette[i].r >> 3) & 0x1f) << 10;
429                                 clr16 |= (uint16)((_cur_palette.palette[i].g >> 3) & 0x1f) << 5;
430                                 clr16 |= (uint16)((_cur_palette.palette[i].b >> 3) & 0x1f);
431                                 this->palette[i] = clr16;
432                         }
433                         break;
434         }
436         this->num_dirty_rects = MAX_DIRTY_RECTS;
439 uint WindowQuickdrawSubdriver::ListModes(OTTD_Point *modes, uint max_modes)
441         return QZ_ListModes(modes, max_modes, kCGDirectMainDisplay, this->buffer_depth);
444 bool WindowQuickdrawSubdriver::ChangeResolution(int w, int h, int bpp)
446         int old_width  = this->window_width;
447         int old_height = this->window_height;
448         int old_bpp    = this->buffer_depth;
450         if (this->SetVideoMode(w, h, bpp)) return true;
452         if (old_width != 0 && old_height != 0) this->SetVideoMode(old_width, old_height, old_bpp);
454         return false;
457 /* Convert local coordinate to window server (CoreGraphics) coordinate */
458 CGPoint WindowQuickdrawSubdriver::PrivateLocalToCG(NSPoint *p)
460         *p = [ this->cocoaview convertPoint:*p toView: nil ];
461         *p = [ this->window convertBaseToScreen:*p ];
462         p->y = this->device_height - p->y;
464         return CGPointMake(p->x, p->y);
467 NSPoint WindowQuickdrawSubdriver::GetMouseLocation(NSEvent *event)
469         NSPoint pt = [ event locationInWindow ];
470         pt = [ this->cocoaview convertPoint:pt fromView:nil ];
472         return pt;
475 bool WindowQuickdrawSubdriver::MouseIsInsideView(NSPoint *pt)
477         return [ this->cocoaview mouse:*pt inRect:[ this->cocoaview bounds ] ];
481 /* This function makes the *game region* of the window 100% opaque.
482  * The genie effect uses the alpha component. Otherwise,
483  * it doesn't seem to matter what value it has.
484  */
485 void WindowQuickdrawSubdriver::SetPortAlphaOpaque()
487         if (this->device_depth != 32) return;
489         uint32 *pixels = (uint32*)this->window_buffer;
490         uint32  pitch  = this->window_pitch / 4;
492         for (int y = 0; y < this->window_height; y++)
493                 for (int x = 0; x < this->window_width; x++) {
494                 pixels[y * pitch + x] |= 0xFF000000;
495         }
498 bool WindowQuickdrawSubdriver::WindowResized()
500         if (this->window == nil || this->cocoaview == nil) return true;
502         NSRect   newframe = [ this->cocoaview frame ];
503         CGrafPtr thePort  = (OpaqueGrafPtr*) [ this->cocoaview qdPort ];
505         LockPortBits(thePort);
506         this->window_buffer = GetPixBaseAddr(GetPortPixMap(thePort));
507         this->window_pitch = GetPixRowBytes(GetPortPixMap(thePort));
508         UnlockPortBits(thePort);
510         /* _cocoa_video_data.realpixels now points to the window's pixels
511          * We want it to point to the *view's* pixels
512          */
513         int voff = [ this->window frame ].size.height - newframe.size.height - newframe.origin.y;
514         int hoff = [ this->cocoaview frame ].origin.x;
515         this->window_buffer = (uint8*)this->window_buffer + (voff * this->window_pitch) + hoff * (this->device_depth / 8);
517         this->window_width = newframe.size.width;
518         this->window_height = newframe.size.height;
520         free(this->pixel_buffer);
521         this->pixel_buffer = malloc(this->window_width * this->window_height * this->buffer_depth / 8);
522         if (this->pixel_buffer == NULL) {
523                 DEBUG(driver, 0, "Failed to allocate pixel buffer");
524                 return false;
525         }
527         QZ_GameSizeChanged();
529         /* Redraw screen */
530         this->num_dirty_rects = MAX_DIRTY_RECTS;
532         return true;
536 CocoaSubdriver *QZ_CreateWindowQuickdrawSubdriver(int width, int height, int bpp)
538         WindowQuickdrawSubdriver *ret;
540         if (MacOSVersionIsAtLeast(10, 5, 0)) {
541                 DEBUG(driver, 0, "The cocoa quickdraw subdriver is not recommended for Mac OS X 10.5 or later.");
542         }
544         if (bpp != 8 && bpp != 32) {
545                 DEBUG(driver, 0, "The cocoa quickdraw subdriver only supports 8 and 32 bpp.");
546                 return NULL;
547         }
549         ret = new WindowQuickdrawSubdriver();
551         if (!ret->ChangeResolution(width, height, bpp)) {
552                 delete ret;
553                 return NULL;
554         }
556         return ret;
559 #endif /* ENABLE_COCOA_QUICKDRAW */
560 #endif /* WITH_COCOA */