diff --git a/src/gfx.cpp b/src/gfx.cpp index 9ccb036d25..ad775803b7 100644 --- a/src/gfx.cpp +++ b/src/gfx.cpp @@ -1059,18 +1059,19 @@ void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub, /** * The code for setting up the blitter mode and sprite information before finally drawing the sprite. * @param sprite The sprite to draw. - * @param x The X location to draw. - * @param y The Y location to draw. - * @param mode The settings for the blitter to pass. - * @param sub Whether to only draw a sub set of the sprite. - * @param zoom The zoom level at which to draw the sprites. + * @param x The X location to draw. + * @param y The Y location to draw. + * @param mode The settings for the blitter to pass. + * @param sub Whether to only draw a sub set of the sprite. + * @param zoom The zoom level at which to draw the sprites. + * @param dst Optional parameter for a different blitting destination. * @tparam ZOOM_BASE The factor required to get the sub sprite information into the right size. * @tparam SCALED_XY Whether the X and Y are scaled or unscaled. */ template -static void GfxBlitter(const Sprite * const sprite, int x, int y, BlitterMode mode, const SubSprite * const sub, SpriteID sprite_id, ZoomLevel zoom) +static void GfxBlitter(const Sprite * const sprite, int x, int y, BlitterMode mode, const SubSprite * const sub, SpriteID sprite_id, ZoomLevel zoom, const DrawPixelInfo *dst = nullptr) { - const DrawPixelInfo *dpi = _cur_dpi; + const DrawPixelInfo *dpi = (dst != nullptr) ? dst : _cur_dpi; Blitter::BlitterParams bp; if (SCALED_XY) { @@ -1185,6 +1186,47 @@ static void GfxBlitter(const Sprite * const sprite, int x, int y, BlitterMode mo BlitterFactory::GetCurrentBlitter()->Draw(&bp, mode, zoom); } +/** + * Draws a sprite to a new RGBA buffer (see Colour union) instead of drawing to the screen. + * + * @param spriteId The sprite to draw. + * @return Pixel buffer, or nullptr if an 8bpp blitter is being used. + */ +std::unique_ptr DrawSpriteToRgbaBuffer(SpriteID spriteId) +{ + Blitter *blitter = BlitterFactory::GetCurrentBlitter(); + if (!blitter->Is32BppSupported()) return nullptr; + + /* Gather information about the sprite to write, reserve memory */ + const SpriteID real_sprite = GB(spriteId, 0, SPRITE_WIDTH); + const Sprite *sprite = GetSprite(real_sprite, ST_NORMAL); + std::unique_ptr result(new uint32[sprite->width * sprite->height]); + + /* Prepare new DrawPixelInfo - Normally this would be the screen but we want to draw to another buffer here. + * Normally, pitch would be scaled screen width, but in our case our "screen" is only the sprite width wide. */ + DrawPixelInfo dpi; + dpi.dst_ptr = result.get(); + dpi.pitch = sprite->width; + dpi.left = 0; + dpi.top = 0; + dpi.width = sprite->width; + dpi.height = sprite->height; + dpi.zoom = ZOOM_LVL_NORMAL; + + /* Zero out the allocated memory, there may be garbage present. */ + uint32 *writeHead = (uint32*)result.get(); + for (int i = 0; i < sprite->width * sprite->height; i++) { + writeHead[i] = 0; + } + + /* Temporarily disable screen animations while blitting - This prevents 40bpp_anim from writing to the animation buffer. */ + _screen_disable_anim = true; + GfxBlitter<1, false>(sprite, 0, 0, BM_NORMAL, nullptr, real_sprite, ZOOM_LVL_NORMAL, &dpi); + _screen_disable_anim = false; + + return result; +} + static void GfxMainBlitterViewport(const Sprite *sprite, int x, int y, BlitterMode mode, const SubSprite *sub, SpriteID sprite_id) { GfxBlitter(sprite, x, y, mode, sub, sprite_id, _cur_dpi->zoom); diff --git a/src/gfx_func.h b/src/gfx_func.h index f23f8bfee7..462f693b64 100644 --- a/src/gfx_func.h +++ b/src/gfx_func.h @@ -68,6 +68,7 @@ extern std::vector _resolutions; extern Dimension _cur_resolution; extern Palette _cur_palette; ///< Current palette +void HandleToolbarHotkey(int hotkey); void HandleKeypress(uint keycode, WChar key); void HandleTextInput(const char *str, bool marked = false, const char *caret = nullptr, const char *insert_location = nullptr, const char *replacement_end = nullptr); void HandleCtrlChanged(); @@ -90,6 +91,7 @@ void GfxScroll(int left, int top, int width, int height, int xo, int yo); Dimension GetSpriteSize(SpriteID sprid, Point *offset = nullptr, ZoomLevel zoom = ZOOM_LVL_GUI); void DrawSpriteViewport(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub = nullptr); void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub = nullptr, ZoomLevel zoom = ZOOM_LVL_GUI); +std::unique_ptr DrawSpriteToRgbaBuffer(SpriteID spriteId); int DrawString(int left, int right, int top, const char *str, TextColour colour = TC_FROMSTRING, StringAlignment align = SA_LEFT, bool underline = false, FontSize fontsize = FS_NORMAL); int DrawString(int left, int right, int top, const std::string &str, TextColour colour = TC_FROMSTRING, StringAlignment align = SA_LEFT, bool underline = false, FontSize fontsize = FS_NORMAL); diff --git a/src/os/macosx/osx_stdafx.h b/src/os/macosx/osx_stdafx.h index 9c4276d055..c8de60e22f 100644 --- a/src/os/macosx/osx_stdafx.h +++ b/src/os/macosx/osx_stdafx.h @@ -30,6 +30,10 @@ #define HAVE_OSX_1012_SDK #endif +#ifdef MAC_OS_X_VERSION_10_15 +#define HAVE_OSX_1015_SDK +#endif + /* It would seem that to ensure backward compatibility we have to ensure that we have defined MAC_OS_X_VERSION_10_x everywhere */ #ifndef MAC_OS_X_VERSION_10_3 #define MAC_OS_X_VERSION_10_3 1030 diff --git a/src/toolbar_gui.cpp b/src/toolbar_gui.cpp index ddc07854b7..f5b3fa3b7d 100644 --- a/src/toolbar_gui.cpp +++ b/src/toolbar_gui.cpp @@ -1944,49 +1944,6 @@ static ToolbarButtonProc * const _toolbar_button_procs[] = { ToolbarSwitchClick, }; -enum MainToolbarHotkeys { - MTHK_PAUSE, - MTHK_FASTFORWARD, - MTHK_SETTINGS, - MTHK_SAVEGAME, - MTHK_LOADGAME, - MTHK_SMALLMAP, - MTHK_TOWNDIRECTORY, - MTHK_SUBSIDIES, - MTHK_STATIONS, - MTHK_FINANCES, - MTHK_COMPANIES, - MTHK_STORY, - MTHK_GOAL, - MTHK_GRAPHS, - MTHK_LEAGUE, - MTHK_INDUSTRIES, - MTHK_TRAIN_LIST, - MTHK_ROADVEH_LIST, - MTHK_SHIP_LIST, - MTHK_AIRCRAFT_LIST, - MTHK_ZOOM_IN, - MTHK_ZOOM_OUT, - MTHK_BUILD_RAIL, - MTHK_BUILD_ROAD, - MTHK_BUILD_TRAM, - MTHK_BUILD_DOCKS, - MTHK_BUILD_AIRPORT, - MTHK_BUILD_TREES, - MTHK_MUSIC, - MTHK_LANDINFO, - MTHK_AI_DEBUG, - MTHK_SMALL_SCREENSHOT, - MTHK_ZOOMEDIN_SCREENSHOT, - MTHK_DEFAULTZOOM_SCREENSHOT, - MTHK_GIANT_SCREENSHOT, - MTHK_CHEATS, - MTHK_TERRAFORM, - MTHK_EXTRA_VIEWPORT, - MTHK_CLIENT_LIST, - MTHK_SIGN_LIST, -}; - /** Main toolbar. */ struct MainToolbarWindow : Window { GUITimer timer; diff --git a/src/toolbar_gui.h b/src/toolbar_gui.h index 6199e0eb73..5b8000f0c1 100644 --- a/src/toolbar_gui.h +++ b/src/toolbar_gui.h @@ -10,6 +10,49 @@ #ifndef TOOLBAR_GUI_H #define TOOLBAR_GUI_H +enum MainToolbarHotkeys { + MTHK_PAUSE, + MTHK_FASTFORWARD, + MTHK_SETTINGS, + MTHK_SAVEGAME, + MTHK_LOADGAME, + MTHK_SMALLMAP, + MTHK_TOWNDIRECTORY, + MTHK_SUBSIDIES, + MTHK_STATIONS, + MTHK_FINANCES, + MTHK_COMPANIES, + MTHK_STORY, + MTHK_GOAL, + MTHK_GRAPHS, + MTHK_LEAGUE, + MTHK_INDUSTRIES, + MTHK_TRAIN_LIST, + MTHK_ROADVEH_LIST, + MTHK_SHIP_LIST, + MTHK_AIRCRAFT_LIST, + MTHK_ZOOM_IN, + MTHK_ZOOM_OUT, + MTHK_BUILD_RAIL, + MTHK_BUILD_ROAD, + MTHK_BUILD_TRAM, + MTHK_BUILD_DOCKS, + MTHK_BUILD_AIRPORT, + MTHK_BUILD_TREES, + MTHK_MUSIC, + MTHK_LANDINFO, + MTHK_AI_DEBUG, + MTHK_SMALL_SCREENSHOT, + MTHK_ZOOMEDIN_SCREENSHOT, + MTHK_DEFAULTZOOM_SCREENSHOT, + MTHK_GIANT_SCREENSHOT, + MTHK_CHEATS, + MTHK_TERRAFORM, + MTHK_EXTRA_VIEWPORT, + MTHK_CLIENT_LIST, + MTHK_SIGN_LIST +}; + void AllocateToolbar(); void ToggleBoundingBoxes(); void ToggleDirtyBlocks(); diff --git a/src/video/cocoa/cocoa_wnd.h b/src/video/cocoa/cocoa_wnd.h index 3ddd5a4f04..0b5c51b994 100644 --- a/src/video/cocoa/cocoa_wnd.h +++ b/src/video/cocoa/cocoa_wnd.h @@ -11,6 +11,8 @@ #define COCOA_WND_H #import +#include "toolbar_gui.h" +#include "table/sprites.h" class VideoDriver_Cocoa; @@ -28,8 +30,67 @@ extern NSString *OTTDMainLaunchGameEngine; + (NSCursor *) clearCocoaCursor; @end +#ifdef HAVE_OSX_1015_SDK +/* 9 items can be displayed on the touch bar when using default buttons. */ +static NSArray *touchBarButtonIdentifiers = @[ + @"openttd.pause", + @"openttd.fastforward", + @"openttd.zoom_in", + @"openttd.zoom_out", + @"openttd.build_rail", + @"openttd.build_road", + @"openttd.build_tram", + @"openttd.build_docks", + @"openttd.build_airport", + NSTouchBarItemIdentifierOtherItemsProxy +]; + +static NSDictionary *touchBarButtonSprites = @{ + @"openttd.pause": [NSNumber numberWithInt:SPR_IMG_PAUSE], + @"openttd.fastforward": [NSNumber numberWithInt:SPR_IMG_FASTFORWARD], + @"openttd.zoom_in": [NSNumber numberWithInt:SPR_IMG_ZOOMIN], + @"openttd.zoom_out": [NSNumber numberWithInt:SPR_IMG_ZOOMOUT], + @"openttd.build_rail": [NSNumber numberWithInt:SPR_IMG_BUILDRAIL], + @"openttd.build_road": [NSNumber numberWithInt:SPR_IMG_BUILDROAD], + @"openttd.build_tram": [NSNumber numberWithInt:SPR_IMG_BUILDTRAMS], + @"openttd.build_docks": [NSNumber numberWithInt:SPR_IMG_BUILDWATER], + @"openttd.build_airport": [NSNumber numberWithInt:SPR_IMG_BUILDAIR], +}; + +static NSDictionary *touchBarButtonActions = @{ + @"openttd.pause": [NSNumber numberWithInt:MTHK_PAUSE], + @"openttd.fastforward": [NSNumber numberWithInt:MTHK_FASTFORWARD], + @"openttd.zoom_in": [NSNumber numberWithInt:MTHK_ZOOM_IN], + @"openttd.zoom_out": [NSNumber numberWithInt:MTHK_ZOOM_OUT], + @"openttd.build_rail": [NSNumber numberWithInt:MTHK_BUILD_RAIL], + @"openttd.build_road": [NSNumber numberWithInt:MTHK_BUILD_ROAD], + @"openttd.build_tram": [NSNumber numberWithInt:MTHK_BUILD_TRAM], + @"openttd.build_docks": [NSNumber numberWithInt:MTHK_BUILD_DOCKS], + @"openttd.build_airport": [NSNumber numberWithInt:MTHK_BUILD_AIRPORT], +}; + +static NSDictionary *touchBarFallbackText = @{ + @"openttd.pause": @"Pause", + @"openttd.fastforward": @"Fast Forward", + @"openttd.zoom_in": @"Zoom In", + @"openttd.zoom_out": @"Zoom Out", + @"openttd.build_rail": @"Rail", + @"openttd.build_road": @"Road", + @"openttd.build_tram": @"Tram", + @"openttd.build_docks": @"Docks", + @"openttd.build_airport": @"Airport", +}; +#endif + /** Subclass of NSWindow to cater our special needs */ +#ifdef HAVE_OSX_1015_SDK +@interface OTTD_CocoaWindow : NSWindow +@property (strong) NSSet *touchbarItems; +- (NSImage*)generateImage:(int)spriteId; +#else @interface OTTD_CocoaWindow : NSWindow +#endif + - (instancetype)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask backing:(NSBackingStoreType)backingType defer:(BOOL)flag driver:(VideoDriver_Cocoa *)drv; - (void)setFrame:(NSRect)frameRect display:(BOOL)flag; diff --git a/src/video/cocoa/cocoa_wnd.mm b/src/video/cocoa/cocoa_wnd.mm index 05b55e2a83..9ded8c80dc 100644 --- a/src/video/cocoa/cocoa_wnd.mm +++ b/src/video/cocoa/cocoa_wnd.mm @@ -32,7 +32,7 @@ #include "../../gfx_func.h" #include "../../window_func.h" #include "../../window_gui.h" - +#include "spritecache.h" /* Table data for key mapping. */ #include "cocoa_keys.h" @@ -405,6 +405,87 @@ void CocoaDialog(const char *title, const char *message, const char *buttonLabel return self; } +#ifdef HAVE_OSX_1015_SDK + +- (void)touchBarButtonAction:(id)sender +{ + if (@available(macOS 10.15, *)) { + NSButtonTouchBarItem *btn = (NSButtonTouchBarItem *)sender; + NSNumber *hotkeyIndex = [ touchBarButtonActions objectForKey:btn.identifier ]; + HandleToolbarHotkey(hotkeyIndex.intValue); + } +} + +#pragma mark NSTouchBarProvider +- (nullable NSTouchBar *)makeTouchBar +{ + NSTouchBar *bar = [ [ NSTouchBar alloc ] init ]; + bar.delegate = self; + bar.defaultItemIdentifiers = touchBarButtonIdentifiers; + + return bar; +} + +-(NSImage *)generateImage:(int)spriteId +{ + if (!SpriteExists(spriteId)) { + return nullptr; + } + + /* Fetch the sprite and create a new bitmap */ + const Sprite *fullspr = GetSprite(spriteId, ST_NORMAL); + const std::unique_ptr buffer = DrawSpriteToRgbaBuffer(spriteId); + if (!buffer) { + return nullptr; // failed to blit sprite or we're using an 8bpp blitter. + } + + NSBitmapImageRep *bitmap = [ [ NSBitmapImageRep alloc ] initWithBitmapDataPlanes:nil pixelsWide:fullspr->width pixelsHigh:fullspr->height bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO colorSpaceName:NSCalibratedRGBColorSpace bytesPerRow:0 bitsPerPixel:0 ]; + + /* Copy the sprite to the NSBitmapImageRep image buffer */ + const Colour *src = (const Colour *)buffer.get(); + for (int y = 0; y < fullspr->height; y++) { + for (int x = 0; x < fullspr->width; x++) { + NSUInteger pixel[4]; + pixel[0] = src->r; + pixel[1] = src->g; + pixel[2] = src->b; + pixel[3] = src->a; + [ bitmap setPixel:pixel atX:x y:y ]; + + src += 1; + } + } + + /* Finally, convert the NSBitmapImageRep we created to a NSimage we can put on the button and clean up. */ + NSImage *outImage = [ [ NSImage alloc ] initWithSize:NSMakeSize(fullspr->width, fullspr->height) ]; + [ outImage addRepresentation:bitmap ]; + [ bitmap release ]; + + return outImage; +} + +#pragma mark NSTouchBarDelegate +- (nullable NSTouchBarItem *)touchBar:(NSTouchBar *)touchBar makeItemForIdentifier:(NSTouchBarItemIdentifier)identifier +{ + if (@available(macOS 10.15, *)) { + NSButtonTouchBarItem *button = [ [ NSButtonTouchBarItem alloc ] initWithIdentifier:identifier ]; + button.target = self; + button.action = @selector(touchBarButtonAction:); + + NSNumber *num = touchBarButtonSprites[identifier]; + NSImage *generatedImage = [ self generateImage:num.unsignedIntValue ]; + if (generatedImage != nullptr) { + button.image = generatedImage; + } else { + button.title = NSLocalizedString(touchBarFallbackText[identifier], @""); + } + return button; + } else { + return nullptr; + } +} +#endif + /** * Define the rectangle we draw our window in */ diff --git a/src/window.cpp b/src/window.cpp index 4072885116..d07b597e40 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -2586,6 +2586,22 @@ EventState Window::HandleEditBoxKey(int wid, WChar key, uint16 keycode) return ES_HANDLED; } +/** + * Handle Toolbar hotkey events - can come from a source like the MacBook Touch Bar. + * @param hotkey Hotkey code + */ +void HandleToolbarHotkey(int hotkey) +{ + assert(HasModalProgress() || IsLocalCompany()); + + Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0); + if (w != nullptr) { + if (w->window_desc->hotkeys != nullptr) { + if (hotkey >= 0 && w->OnHotkey(hotkey) == ES_HANDLED) return; + } + } +} + /** * Handle keyboard input. * @param keycode Virtual keycode of the key.