mirror of https://github.com/OpenTTD/OpenTTD
(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
parent
7489a5d153
commit
53fffb3bab
|
@ -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. */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -36,4 +36,6 @@ static inline bool MacOSVersionIsAtLeast(long major, long minor, long bugfix)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool IsMonospaceFont(CFStringRef name);
|
||||
|
||||
#endif /* MACOS_H */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in New Issue