📄 sdl_quartzvideo.m
字号:
goto ERR_NO_CAPTURE;
}
/* Do the physical switch */
if ( CGDisplayNoErr != CGDisplaySwitchToMode (display_id, mode) ) {
SDL_SetError ("Failed switching display resolution");
goto ERR_NO_SWITCH;
}
current->pixels = (Uint32*) CGDisplayBaseAddress (display_id);
current->pitch = CGDisplayBytesPerRow (display_id);
current->flags = 0;
current->w = width;
current->h = height;
current->flags |= SDL_FULLSCREEN;
current->flags |= SDL_HWSURFACE;
this->UpdateRects = QZ_DirectUpdate;
/* Setup some mode-dependant info */
if ( CGSDisplayCanHWFill (display_id) ) {
this->info.blit_fill = 1;
this->FillHWRect = QZ_FillHWRect;
}
if ( CGDisplayCanSetPalette (display_id) )
current->flags |= SDL_HWPALETTE;
/* Setup OpenGL for a fullscreen context */
if (flags & SDL_OPENGL) {
CGLError err;
CGLContextObj ctx;
if ( ! QZ_SetupOpenGL (this, bpp, flags) ) {
goto ERR_NO_GL;
}
ctx = [ gl_context cglContext ];
err = CGLSetFullScreen (ctx);
if (err) {
sprintf (QZ_Error, "Error setting OpenGL fullscreen: %s", CGLErrorString(err));
SDL_SetError (QZ_Error);
goto ERR_NO_GL;
}
[ gl_context makeCurrentContext];
glClear (GL_COLOR_BUFFER_BIT);
[ gl_context flushBuffer ];
current->flags |= SDL_OPENGL;
}
/* If we don't hide menu bar, it will get events and interrupt the program */
HideMenuBar ();
/* Fade the display to original gamma */
if (! gamma_error )
QZ_FadeGammaIn (this, &gamma_table);
/* Save the flags to ensure correct tear-down */
mode_flags = current->flags;
return current;
/* Since the blanking window covers *all* windows (even force quit) correct recovery is crucial */
ERR_NO_GL: CGDisplaySwitchToMode (display_id, save_mode);
ERR_NO_SWITCH: CGDisplayRelease (display_id);
ERR_NO_CAPTURE: if (!gamma_error) { QZ_FadeGammaIn (this, &gamma_table); }
ERR_NO_MATCH: return NULL;
}
static SDL_Surface* QZ_SetVideoWindowed (_THIS, SDL_Surface *current, int width,
int height, int bpp, Uint32 flags) {
unsigned int style;
NSRect rect;
rect = NSMakeRect (0, 0, width, height);
#if 1 // FIXME - the resize button doesn't show? Also need resize events...
flags &= ~SDL_RESIZABLE;
#endif
/* Set the window style based on input flags */
if ( flags & SDL_NOFRAME ) {
style = NSBorderlessWindowMask;
} else {
style = NSTitledWindowMask;
style |= (NSMiniaturizableWindowMask | NSClosableWindowMask);
if ( flags & SDL_RESIZABLE )
style |= NSResizableWindowMask;
}
/* Manually create a window, avoids having a nib file resource */
qz_window = [ [ SDL_QuartzWindow alloc ] initWithContentRect:rect
styleMask:style backing:NSBackingStoreBuffered defer:NO ];
if (qz_window == nil) {
SDL_SetError ("Could not create the Cocoa window");
return NULL;
}
current->flags = 0;
current->w = width;
current->h = height;
[ qz_window setReleasedWhenClosed:YES ];
QZ_SetCaption(this, this->wm_title, this->wm_icon);
[ qz_window setAcceptsMouseMovedEvents:YES ];
[ qz_window setViewsNeedDisplay:NO ];
[ qz_window center ];
[ qz_window setDelegate:
[ [ [ SDL_QuartzWindowDelegate alloc ] init ] autorelease ] ];
/* For OpenGL, we set the content view to a NSOpenGLView */
if ( flags & SDL_OPENGL ) {
if ( ! QZ_SetupOpenGL (this, bpp, flags) ) {
return NULL;
}
[ gl_context setView: [ qz_window contentView ] ];
[ gl_context makeCurrentContext];
[ qz_window makeKeyAndOrderFront:nil ];
current->flags |= SDL_OPENGL;
}
/* For 2D, we set the content view to a NSQuickDrawView */
else {
window_view = [ [ SDL_QuartzWindowView alloc ] init ];
[ qz_window setContentView:window_view ];
[ qz_window makeKeyAndOrderFront:nil ];
LockPortBits ( [ window_view qdPort ] );
current->pixels = GetPixBaseAddr ( GetPortPixMap ( [ window_view qdPort ] ) );
current->pitch = GetPixRowBytes ( GetPortPixMap ( [ window_view qdPort ] ) );
current->flags |= SDL_SWSURFACE;
current->flags |= SDL_PREALLOC;
if ( flags & SDL_NOFRAME )
current->flags |= SDL_NOFRAME;
if ( flags & SDL_RESIZABLE )
current->flags |= SDL_RESIZABLE;
/* Offset 22 pixels down to fill the full content region */
if ( ! (current->flags & SDL_NOFRAME) ) {
current->pixels += 22 * current->pitch;
}
this->UpdateRects = QZ_UpdateRects;
}
/* Save flags to ensure correct teardown */
mode_flags = current->flags;
return current;
}
static SDL_Surface* QZ_SetVideoMode (_THIS, SDL_Surface *current, int width,
int height, int bpp, Uint32 flags) {
if (video_set == SDL_TRUE)
QZ_UnsetVideoMode (this);
current->flags = 0;
/* Setup full screen video */
if ( flags & SDL_FULLSCREEN ) {
current = QZ_SetVideoFullScreen (this, current, width, height, bpp, flags );
if (current == NULL)
return NULL;
}
/* Setup windowed video */
else {
/* Force bpp to the device's bpp */
bpp = device_bpp;
current = QZ_SetVideoWindowed (this, current, width, height, bpp, flags);
if (current == NULL)
return NULL;
}
/* Setup the new pixel format */
{
int amask = 0,
rmask = 0,
gmask = 0,
bmask = 0;
switch (bpp) {
case 16: /* (1)-5-5-5 RGB */
amask = 0;
rmask = 0x7C00;
gmask = 0x03E0;
bmask = 0x001F;
break;
case 24:
SDL_SetError ("24bpp is not available");
return NULL;
case 32: /* (8)-8-8-8 ARGB */
amask = 0x00000000;
rmask = 0x00FF0000;
gmask = 0x0000FF00;
bmask = 0x000000FF;
break;
}
if ( ! SDL_ReallocFormat (current, bpp,
rmask, gmask, bmask, amask ) ) {
SDL_SetError ("Couldn't reallocate pixel format");
return NULL;
}
}
/* Signal successful completion (used internally) */
video_set = SDL_TRUE;
return current;
}
static int QZ_ToggleFullScreen (_THIS, int on) {
return -1;
}
static int QZ_SetColors (_THIS, int first_color, int num_colors,
SDL_Color *colors) {
CGTableCount index;
CGDeviceColor color;
for (index = first_color; index < first_color+num_colors; index++) {
/* Clamp colors between 0.0 and 1.0 */
color.red = colors->r / 255.0;
color.blue = colors->b / 255.0;
color.green = colors->g / 255.0;
colors++;
CGPaletteSetColorAtIndex (palette, color, index);
}
if ( CGDisplayNoErr != CGDisplaySetPalette (display_id, palette) )
return 0;
return 1;
}
static void QZ_DirectUpdate (_THIS, int num_rects, SDL_Rect *rects) {
#pragma unused(this,num_rects,rects)
}
/**
* The obscured code is based on work by Matt Slot fprefect@ambrosiasw.com,
* who supplied sample code for Carbon.
**/
static int QZ_IsWindowObscured (NSWindow *window) {
//#define TEST_OBSCURED 1
#if TEST_OBSCURED
/* In order to determine if a direct copy to the screen is possible,
we must figure out if there are any windows covering ours (including shadows).
This can be done by querying the window server about the on screen
windows for their screen rectangle and window level.
The procedure used below is puts accuracy before speed; however, it aims to call
the window server the fewest number of times possible to keep things reasonable.
In my testing on a 300mhz G3, this routine typically takes < 2 ms. -DW
Notes:
-Calls into the Window Server involve IPC which is slow.
-Getting a rectangle seems slower than getting the window level
-The window list we get back is in sorted order, top to bottom
-On average, I suspect, most windows above ours are dock icon windows (hence optimization)
-Some windows above ours are always there, and cannot move or obscure us (menu bar)
Bugs:
-no way (yet) to deactivate direct drawing when a window is dragged,
or suddenly obscured, so drawing continues and can produce garbage
We need some kind of locking mechanism on window movement to prevent this
-deactivated normal windows use activated normal
window shadows (slight inaccuraccy)
*/
/* Cache the connection to the window server */
static CGSConnectionID cgsConnection = (CGSConnectionID) -1;
/* Cache the dock icon windows */
static CGSWindowID dockIcons[kMaxWindows];
static int numCachedDockIcons = 0;
CGSWindowID windows[kMaxWindows];
CGSWindowCount i, count;
CGSWindowLevel winLevel;
CGSRect winRect;
CGSRect contentRect;
int windowNumber;
//int isMainWindow;
int firstDockIcon;
int dockIconCacheMiss;
int windowContentOffset;
int obscured = SDL_TRUE;
if ( [ window isVisible ] ) {
/* walk the window list looking for windows over top of
(or casting a shadow on) ours */
/* Get a connection to the window server */
/* Should probably be moved out into SetVideoMode() or InitVideo() */
if (cgsConnection == (CGSConnectionID) -1) {
cgsConnection = (CGSConnectionID) 0;
cgsConnection = _CGSDefaultConnection ();
}
if (cgsConnection) {
if ( ! [ window styleMask ] & NSBorderlessWindowMask )
windowContentOffset = 22;
else
windowContentOffset = 0;
windowNumber = [ window windowNumber ];
//isMainWindow = [ window isMainWindow ];
/* The window list is sorted according to order on the screen */
count = 0;
CGSGetOnScreenWindowList (cgsConnection, 0, kMaxWindows, windows, &count);
CGSGetScreenRectForWindow (cgsConnection, windowNumber, &contentRect);
/* adjust rect for window title bar (if present) */
contentRect.origin.y += windowContentOffset;
contentRect.size.height -= windowContentOffset;
firstDockIcon = -1;
dockIconCacheMiss = SDL_FALSE;
/* The first window is always an empty window with level kCGSWindowLevelTop
so start at index 1 */
for (i = 1; i < count; i++) {
/* If we reach our window in the list, it cannot be obscured */
if (windows[i] == windowNumber) {
obscured = SDL_FALSE;
break;
}
else {
float shadowSide;
float shadowTop;
float shadowBottom;
CGSGetWindowLevel (cgsConnection, windows[i], &winLevel);
if (winLevel == kCGSWindowLevelDockIcon) {
int j;
if (firstDockIcon < 0) {
firstDockIcon = i;
if (numCachedDockIcons > 0) {
for (j = 0; j < numCachedDockIcons; j++) {
if (windows[i] == dockIcons[j])
i++;
else
break;
}
if (j != 0) {
i--;
if (j < numCachedDockIcons) {
dockIconCacheMiss = SDL_TRUE;
}
}
}
}
continue;
}
else if (winLevel == kCGSWindowLevelMenuIgnore
/* winLevel == kCGSWindowLevelTop */) {
continue; /* cannot obscure window */
}
else if (winLevel == kCGSWindowLevelDockMenu ||
winLevel == kCGSWindowLevelMenu) {
shadowSide = 18;
shadowTop = 4;
shadowBottom = 22;
}
else if (winLevel == kCGSWindowLevelUtility) {
shadowSide = 8;
shadowTop = 4;
shadowBottom = 12;
}
else if (winLevel == kCGSWindowLevelNormal) {
/* These numbers are for foreground windows,
they are too big (but will work) for background windows */
shadowSide = 20;
shadowTop = 10;
shadowBottom = 24;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -