1
0
Fork 0

(svn r25981) [1.3] -Backport from trunk:

- Fix: [Win32] Only forward key presses to the IME system if an edit box has the input focus (r25667)
- Fix: [OSX] Crash when unhiding the main window [FS#4689] (r25665)
- Fix: [OSX] Bootstrap downloading of a baseset did not work [FS#4847] (r25664)
- Fix: [OSX] Monospace font detection [FS#4857] (r25663, r25662)
release/1.3
rubidium 2013-11-13 21:30:39 +00:00
parent 7489a5d153
commit 53fffb3bab
8 changed files with 142 additions and 36 deletions

View File

@ -218,7 +218,7 @@ bool HandleBootstrap()
if (BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth() == 0) goto failure;
/* If there is no network or no freetype, then there is nothing we can do. Go straight to failure. */
#if defined(ENABLE_NETWORK) && defined(WITH_FREETYPE) && !defined(__APPLE__) && (defined(WITH_FONTCONFIG) || defined(WIN32))
#if defined(ENABLE_NETWORK) && defined(WITH_FREETYPE) && (defined(WITH_FONTCONFIG) || defined(WIN32) || defined(__APPLE__))
if (!_network_available) goto failure;
/* First tell the game we're bootstrapping. */

View File

@ -486,16 +486,28 @@ bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, i
for (CFIndex i = 0; descs != NULL && i < CFArrayGetCount(descs); i++) {
CTFontDescriptorRef font = (CTFontDescriptorRef)CFArrayGetValueAtIndex(descs, i);
/* Get font traits. */
CFDictionaryRef traits = (CFDictionaryRef)CTFontDescriptorCopyAttribute(font, kCTFontTraitsAttribute);
CTFontSymbolicTraits symbolic_traits;
CFNumberGetValue((CFNumberRef)CFDictionaryGetValue(traits, kCTFontSymbolicTrait), kCFNumberIntType, &symbolic_traits);
CFRelease(traits);
/* Skip symbol fonts and vertical fonts. */
if ((symbolic_traits & kCTFontClassMaskTrait) == (CTFontStylisticClass)kCTFontSymbolicClass || (symbolic_traits & kCTFontVerticalTrait)) continue;
/* Skip bold fonts (especially Arial Bold, which looks worse than regular Arial). */
if (symbolic_traits & kCTFontBoldTrait) continue;
/* Select monospaced fonts if asked for. */
if (((symbolic_traits & kCTFontMonoSpaceTrait) == kCTFontMonoSpaceTrait) != callback->Monospace()) continue;
/* Get font name. */
char name[128];
CFStringRef font_name = (CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontDisplayNameAttribute);
CFStringGetCString(font_name, name, lengthof(name), kCFStringEncodingUTF8);
CFRelease(font_name);
/* Skip some inappropriate or ugly looking fonts that have better alternatives. */
if (strncmp(name, "Courier", 7) == 0 || strncmp(name, "Apple Symbols", 13) == 0 ||
strncmp(name, ".Aqua", 5) == 0 || strncmp(name, "LastResort", 10) == 0 ||
strncmp(name, "GB18030 Bitmap", 14) == 0) continue;
/* There are some special fonts starting with an '.' and the last
* resort font that aren't usable. Skip them. */
if (name[0] == '.' || strncmp(name, "LastResort", 10) == 0) continue;
/* Save result. */
callback->SetFontNames(settings, name);
@ -520,15 +532,18 @@ bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, i
CFStringRef font_name;
ATSFontGetName(font, kATSOptionFlagsDefault, &font_name);
CFStringGetCString(font_name, name, lengthof(name), kCFStringEncodingUTF8);
bool monospace = IsMonospaceFont(font_name);
CFRelease(font_name);
/* Select monospaced fonts if asked for. */
if (monospace != callback->Monospace()) continue;
/* We only want the base font and not bold or italic variants. */
if (strstr(name, "Italic") != NULL || strstr(name, "Bold")) continue;
/* Skip some inappropriate or ugly looking fonts that have better alternatives. */
if (strncmp(name, "Courier", 7) == 0 || strncmp(name, "Apple Symbols", 13) == 0 ||
strncmp(name, ".Aqua", 5) == 0 || strncmp(name, "LastResort", 10) == 0 ||
strncmp(name, "GB18030 Bitmap", 14) == 0) continue;
if (name[0] == '.' || strncmp(name, "Apple Symbols", 13) == 0 || strncmp(name, "LastResort", 10) == 0) continue;
/* Save result. */
callback->SetFontNames(settings, name);

View File

@ -36,4 +36,6 @@ static inline bool MacOSVersionIsAtLeast(long major, long minor, long bugfix)
return true;
}
bool IsMonospaceFont(CFStringRef name);
#endif /* MACOS_H */

View File

@ -193,3 +193,15 @@ uint GetCPUCoreCount()
return count;
}
/**
* Check if a font is a monospace font.
* @param name Name of the font.
* @return True if the font is a monospace font.
*/
bool IsMonospaceFont(CFStringRef name)
{
NSFont *font = [ NSFont fontWithName:(NSString *)name size:0.0f ];
return font != NULL ? [ font isFixedPitch ] : false;
}

View File

@ -56,22 +56,50 @@ static bool _cocoa_video_dialog = false;
CocoaSubdriver *_cocoa_subdriver = NULL;
static const NSString *OTTDMainLaunchGameEngine = @"ottdmain_launch_game_engine";
/**
* The main class of the application, the application's delegate.
*/
@implementation OTTDMain
/**
* Stop the game engine. Must be called on main thread.
*/
- (void)stopEngine
{
[ NSApp stop:self ];
/* Send an empty event to return from the run loop. Without that, application is stuck waiting for an event. */
NSEvent *event = [ NSEvent otherEventWithType:NSApplicationDefined location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0.0 windowNumber:0 context:nil subtype:0 data1:0 data2:0 ];
[ NSApp postEvent:event atStart:YES ];
}
/**
* Start the game loop.
*/
- (void)launchGameEngine: (NSNotification*) note
{
/* Setup cursor for the current _game_mode. */
[ _cocoa_subdriver->cocoaview resetCursorRects ];
/* Hand off to main application code. */
QZ_GameLoop();
/* We are done, thank you for playing. */
[ self performSelectorOnMainThread:@selector(stopEngine) withObject:nil waitUntilDone:FALSE ];
}
/**
* Called when the internal event loop has just started running.
*/
- (void) applicationDidFinishLaunching: (NSNotification*) note
{
/* Hand off to main application code */
QZ_GameLoop();
/* Add a notification observer so we can restart the game loop later on if necessary. */
[ [ NSNotificationCenter defaultCenter ] addObserver:self selector:@selector(launchGameEngine:) name:OTTDMainLaunchGameEngine object:nil ];
/* We're done, thank you for playing */
[ NSApp stop:_ottd_main ];
/* Start game loop. */
[ [ NSNotificationCenter defaultCenter ] postNotificationName:OTTDMainLaunchGameEngine object:nil ];
}
/**
@ -79,11 +107,18 @@ CocoaSubdriver *_cocoa_subdriver = NULL;
*/
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*) sender
{
HandleExitGameRequest();
return NSTerminateCancel; // NSTerminateLater ?
}
/**
* Remove ourself as a notification observer.
*/
- (void)unregisterObserver
{
[ [ NSNotificationCenter defaultCenter ] removeObserver:self ];
}
@end
/**
@ -326,6 +361,8 @@ void VideoDriver_Cocoa::Stop()
{
if (!_cocoa_video_started) return;
[ _ottd_main unregisterObserver ];
delete _cocoa_subdriver;
_cocoa_subdriver = NULL;
@ -385,7 +422,11 @@ void VideoDriver_Cocoa::MakeDirty(int left, int top, int width, int height)
*/
void VideoDriver_Cocoa::MainLoop()
{
/* Start the main event loop */
/* Restart game loop if it was already running (e.g. after bootstrapping),
* otherwise this call is a no-op. */
[ [ NSNotificationCenter defaultCenter ] postNotificationName:OTTDMainLaunchGameEngine object:nil ];
/* Start the main event loop. */
[ NSApp run ];
}
@ -608,18 +649,12 @@ void cocoaReleaseAutoreleasePool()
- (void)appWillUnhide:(NSNotification*)note
{
driver->SetPortAlphaOpaque ();
/* save current visible surface */
[ self cacheImageInRect:[ driver->cocoaview frame ] ];
}
/**
* Unhide and restore display plane and re-activate driver
*/
- (void)appDidUnhide:(NSNotification*)note
{
/* restore cached image, since it may not be current, post expose event too */
[ self restoreCachedImage ];
driver->active = true;
}
/**
@ -708,7 +743,7 @@ void cocoaReleaseAutoreleasePool()
[ super resetCursorRects ];
[ self clearTrackingRect ];
[ self setTrackingRect ];
[ self addCursorRect:[ self bounds ] cursor:[ NSCursor clearCocoaCursor ] ];
[ self addCursorRect:[ self bounds ] cursor:(_game_mode == GM_BOOTSTRAP ? [ NSCursor arrowCursor ] : [ NSCursor clearCocoaCursor ]) ];
}
/**
* Prepare for moving the application window

View File

@ -21,9 +21,15 @@
#include "../texteff.hpp"
#include "../thread/thread.h"
#include "../progress.h"
#include "../window_func.h"
#include "win32_v.h"
#include <windows.h>
/* Missing define in MinGW headers. */
#ifndef MAPVK_VK_TO_CHAR
#define MAPVK_VK_TO_CHAR (2)
#endif
static struct {
HWND main_wnd;
HBITMAP dib_sect;
@ -432,6 +438,20 @@ static void PaintWindowThread(void *)
_draw_thread->Exit();
}
/** Forward key presses to the window system. */
static LRESULT HandleCharMsg(uint keycode, uint charcode)
{
#if !defined(UNICODE)
wchar_t w;
int len = MultiByteToWideChar(_codepage, 0, (char*)&charcode, 1, &w, 1);
charcode = len == 1 ? w : 0;
#endif /* UNICODE */
HandleKeypress(GB(charcode, 0, 16) | (keycode << 16));
return 0;
}
static LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static uint32 keycode = 0;
@ -592,31 +612,50 @@ static LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lP
return 0;
}
#if !defined(UNICODE)
wchar_t w;
int len = MultiByteToWideChar(_codepage, 0, (char*)&charcode, 1, &w, 1);
charcode = len == 1 ? w : 0;
#endif /* UNICODE */
/* IMEs and other input methods sometimes send a WM_CHAR without a WM_KEYDOWN,
* clear the keycode so a previous WM_KEYDOWN doesn't become 'stuck'. */
uint cur_keycode = keycode;
keycode = 0;
/* No matter the keyboard layout, we will map the '~' to the console */
scancode = scancode == 41 ? (int)WKC_BACKQUOTE : keycode;
HandleKeypress(GB(charcode, 0, 16) | (scancode << 16));
return 0;
return HandleCharMsg(cur_keycode, charcode);
}
case WM_KEYDOWN: {
keycode = MapWindowsKey(wParam);
/* No matter the keyboard layout, we will map the '~' to the console. */
uint scancode = GB(lParam, 16, 8);
keycode = scancode == 41 ? WKC_BACKQUOTE : MapWindowsKey(wParam);
/* Silently drop all messages handled by WM_CHAR. */
MSG msg;
if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
if (msg.message == WM_CHAR && GB(lParam, 16, 8) == GB(msg.lParam, 16, 8)) {
if ((msg.message == WM_CHAR || msg.message == WM_DEADCHAR) && GB(lParam, 16, 8) == GB(msg.lParam, 16, 8)) {
return 0;
}
}
HandleKeypress(0 | (keycode << 16));
return 0;
uint charcode = MapVirtualKey(wParam, MAPVK_VK_TO_CHAR);
/* No character translation? */
if (charcode == 0) {
HandleKeypress(0 | (keycode << 16));
return 0;
}
/* Is the console key a dead key? If yes, ignore the first key down event. */
if (HasBit(charcode, 31) && !console) {
if (scancode == 41) {
console = true;
return 0;
}
}
console = false;
/* IMEs and other input methods sometimes send a WM_CHAR without a WM_KEYDOWN,
* clear the keycode so a previous WM_KEYDOWN doesn't become 'stuck'. */
uint cur_keycode = keycode;
keycode = 0;
return HandleCharMsg(cur_keycode, LOWORD(charcode));
}
case WM_SYSKEYDOWN: // user presses F10 or Alt, both activating the title-menu
@ -979,7 +1018,8 @@ void VideoDriver_Win32::MainLoop()
while (PeekMessage(&mesg, NULL, 0, 0, PM_REMOVE)) {
InteractiveRandom(); // randomness
TranslateMessage(&mesg);
/* Convert key messages to char messages if we want text input. */
if (EditBoxInGlobalFocus()) TranslateMessage(&mesg);
DispatchMessage(&mesg);
}
if (_exit_game) return;

View File

@ -277,7 +277,7 @@ void SetFocusedWindow(Window *w)
* has a edit box as focused widget, or if a console is focused.
* @return returns true if an edit box is in global focus or if the focused window is a console, else false
*/
static bool EditBoxInGlobalFocus()
bool EditBoxInGlobalFocus()
{
if (_focused_window == NULL) return false;

View File

@ -52,4 +52,6 @@ void SetWindowClassesDirty(WindowClass cls);
void DeleteWindowById(WindowClass cls, WindowNumber number, bool force = true);
void DeleteWindowByClass(WindowClass cls);
bool EditBoxInGlobalFocus();
#endif /* WINDOW_FUNC_H */